View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shardingsphere.encrypt.distsql.handler.update;
19  
20  import lombok.Setter;
21  import org.apache.shardingsphere.distsql.handler.engine.update.rdl.rule.spi.database.DatabaseRuleCreateExecutor;
22  import org.apache.shardingsphere.distsql.segment.AlgorithmSegment;
23  import org.apache.shardingsphere.encrypt.config.EncryptRuleConfiguration;
24  import org.apache.shardingsphere.encrypt.distsql.handler.converter.EncryptRuleStatementConverter;
25  import org.apache.shardingsphere.encrypt.distsql.segment.EncryptColumnItemSegment;
26  import org.apache.shardingsphere.encrypt.distsql.segment.EncryptColumnSegment;
27  import org.apache.shardingsphere.encrypt.distsql.segment.EncryptRuleSegment;
28  import org.apache.shardingsphere.encrypt.distsql.statement.CreateEncryptRuleStatement;
29  import org.apache.shardingsphere.encrypt.rule.EncryptRule;
30  import org.apache.shardingsphere.encrypt.spi.EncryptAlgorithm;
31  import org.apache.shardingsphere.infra.algorithm.core.exception.AlgorithmInitializationException;
32  import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
33  import org.apache.shardingsphere.infra.exception.kernel.metadata.resource.storageunit.EmptyStorageUnitException;
34  import org.apache.shardingsphere.infra.exception.kernel.metadata.rule.DuplicateRuleException;
35  import org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
36  import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
37  import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
38  
39  import java.util.Collection;
40  import java.util.Collections;
41  import java.util.LinkedHashSet;
42  import java.util.Objects;
43  import java.util.stream.Collectors;
44  
45  /**
46   * Create encrypt rule executor.
47   */
48  @Setter
49  public final class CreateEncryptRuleExecutor implements DatabaseRuleCreateExecutor<CreateEncryptRuleStatement, EncryptRule, EncryptRuleConfiguration> {
50      
51      private ShardingSphereDatabase database;
52      
53      private EncryptRule rule;
54      
55      @Override
56      public void checkBeforeUpdate(final CreateEncryptRuleStatement sqlStatement) {
57          if (!sqlStatement.isIfNotExists()) {
58              checkDuplicateRuleNames(sqlStatement);
59          }
60          checkColumnNames(sqlStatement);
61          checkAlgorithmTypes(sqlStatement);
62          checkToBeCreatedEncryptors(sqlStatement);
63          checkDataSources();
64      }
65      
66      private void checkDuplicateRuleNames(final CreateEncryptRuleStatement sqlStatement) {
67          Collection<String> duplicatedRuleNames = getDuplicatedRuleNames(sqlStatement);
68          ShardingSpherePreconditions.checkMustEmpty(duplicatedRuleNames, () -> new DuplicateRuleException("encrypt", database.getName(), duplicatedRuleNames));
69      }
70      
71      private Collection<String> getDuplicatedRuleNames(final CreateEncryptRuleStatement sqlStatement) {
72          return null == rule ? Collections.emptyList() : sqlStatement.getRules().stream().map(EncryptRuleSegment::getTableName).filter(rule.getAllTableNames()::contains).collect(Collectors.toSet());
73      }
74      
75      private void checkColumnNames(final CreateEncryptRuleStatement sqlStatement) {
76          for (EncryptRuleSegment each : sqlStatement.getRules()) {
77              ShardingSpherePreconditions.checkState(isColumnNameNotConflicts(each),
78                      () -> new InvalidRuleConfigurationException("encrypt", "assisted query column or like query column conflicts with logic column"));
79          }
80      }
81      
82      private boolean isColumnNameNotConflicts(final EncryptRuleSegment rule) {
83          return rule.getColumns().stream().noneMatch(each -> null != each.getLikeQuery() && each.getName().equals(each.getLikeQuery().getName())
84                  || null != each.getAssistedQuery() && each.getName().equals(each.getAssistedQuery().getName()));
85      }
86      
87      private void checkAlgorithmTypes(final CreateEncryptRuleStatement sqlStatement) {
88          sqlStatement.getRules().stream().flatMap(each -> each.getColumns().stream()).forEach(each -> {
89              checkStandardAlgorithmType(each.getCipher());
90              checkLikeAlgorithmType(each.getLikeQuery());
91              checkAssistedAlgorithmType(each.getAssistedQuery());
92          });
93      }
94      
95      private void checkStandardAlgorithmType(final EncryptColumnItemSegment itemSegment) {
96          if (null == itemSegment || null == itemSegment.getEncryptor()) {
97              return;
98          }
99          EncryptAlgorithm encryptAlgorithm = TypedSPILoader.getService(EncryptAlgorithm.class, itemSegment.getEncryptor().getName(), itemSegment.getEncryptor().getProps());
100         ShardingSpherePreconditions.checkState(encryptAlgorithm.getMetaData().isSupportDecrypt(), () -> new AlgorithmInitializationException(encryptAlgorithm, "Can not support decrypt"));
101     }
102     
103     private void checkLikeAlgorithmType(final EncryptColumnItemSegment itemSegment) {
104         if (null == itemSegment || null == itemSegment.getEncryptor()) {
105             return;
106         }
107         EncryptAlgorithm encryptAlgorithm = TypedSPILoader.getService(EncryptAlgorithm.class, itemSegment.getEncryptor().getName(), itemSegment.getEncryptor().getProps());
108         ShardingSpherePreconditions.checkState(encryptAlgorithm.getMetaData().isSupportLike(), () -> new AlgorithmInitializationException(encryptAlgorithm, "Can not support like"));
109     }
110     
111     private void checkAssistedAlgorithmType(final EncryptColumnItemSegment itemSegment) {
112         if (null == itemSegment || null == itemSegment.getEncryptor()) {
113             return;
114         }
115         EncryptAlgorithm encryptAlgorithm = TypedSPILoader.getService(EncryptAlgorithm.class, itemSegment.getEncryptor().getName(), itemSegment.getEncryptor().getProps());
116         ShardingSpherePreconditions.checkState(encryptAlgorithm.getMetaData().isSupportEquivalentFilter(),
117                 () -> new AlgorithmInitializationException(encryptAlgorithm, "Can not support assist query"));
118     }
119     
120     private void checkToBeCreatedEncryptors(final CreateEncryptRuleStatement sqlStatement) {
121         Collection<AlgorithmSegment> encryptors = new LinkedHashSet<>();
122         sqlStatement.getRules().forEach(each -> each.getColumns().forEach(column -> addToEncryptors(column, encryptors)));
123         encryptors.stream().filter(Objects::nonNull).forEach(each -> TypedSPILoader.checkService(EncryptAlgorithm.class, each.getName(), each.getProps()));
124     }
125     
126     private void addToEncryptors(final EncryptColumnSegment column, final Collection<AlgorithmSegment> result) {
127         result.add(column.getCipher().getEncryptor());
128         if (null != column.getAssistedQuery()) {
129             result.add(column.getAssistedQuery().getEncryptor());
130         }
131         if (null != column.getLikeQuery()) {
132             result.add(column.getLikeQuery().getEncryptor());
133         }
134     }
135     
136     private void checkDataSources() {
137         ShardingSpherePreconditions.checkNotEmpty(database.getResourceMetaData().getStorageUnits(), () -> new EmptyStorageUnitException(database.getName()));
138     }
139     
140     @Override
141     public EncryptRuleConfiguration buildToBeCreatedRuleConfiguration(final CreateEncryptRuleStatement sqlStatement) {
142         Collection<EncryptRuleSegment> segments = sqlStatement.getRules();
143         if (sqlStatement.isIfNotExists()) {
144             Collection<String> duplicatedRuleNames = getDuplicatedRuleNames(sqlStatement);
145             segments.removeIf(each -> duplicatedRuleNames.contains(each.getTableName()));
146         }
147         return EncryptRuleStatementConverter.convert(segments);
148     }
149     
150     @Override
151     public Class<EncryptRule> getRuleClass() {
152         return EncryptRule.class;
153     }
154     
155     @Override
156     public Class<CreateEncryptRuleStatement> getType() {
157         return CreateEncryptRuleStatement.class;
158     }
159 }