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.api.config.EncryptRuleConfiguration;
24  import org.apache.shardingsphere.encrypt.api.config.rule.EncryptTableRuleConfiguration;
25  import org.apache.shardingsphere.encrypt.distsql.handler.converter.EncryptRuleStatementConverter;
26  import org.apache.shardingsphere.encrypt.distsql.segment.EncryptColumnItemSegment;
27  import org.apache.shardingsphere.encrypt.distsql.segment.EncryptColumnSegment;
28  import org.apache.shardingsphere.encrypt.distsql.segment.EncryptRuleSegment;
29  import org.apache.shardingsphere.encrypt.distsql.statement.CreateEncryptRuleStatement;
30  import org.apache.shardingsphere.encrypt.rule.EncryptRule;
31  import org.apache.shardingsphere.encrypt.spi.EncryptAlgorithm;
32  import org.apache.shardingsphere.infra.algorithm.core.exception.AlgorithmInitializationException;
33  import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
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.exception.kernel.metadata.resource.storageunit.EmptyStorageUnitException;
37  import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
38  import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
39  
40  import java.util.Collection;
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          Collection<String> currentRuleNames = new LinkedHashSet<>();
73          if (null != rule) {
74              currentRuleNames = rule.getConfiguration().getTables().stream().map(EncryptTableRuleConfiguration::getName).collect(Collectors.toSet());
75          }
76          return sqlStatement.getRules().stream().map(EncryptRuleSegment::getTableName).filter(currentRuleNames::contains).collect(Collectors.toSet());
77      }
78      
79      private void checkColumnNames(final CreateEncryptRuleStatement sqlStatement) {
80          for (EncryptRuleSegment each : sqlStatement.getRules()) {
81              ShardingSpherePreconditions.checkState(isColumnNameNotConflicts(each),
82                      () -> new InvalidRuleConfigurationException("encrypt", "assisted query column or like query column conflicts with logic column"));
83          }
84      }
85      
86      private boolean isColumnNameNotConflicts(final EncryptRuleSegment rule) {
87          return rule.getColumns().stream().noneMatch(each -> null != each.getLikeQuery() && each.getName().equals(each.getLikeQuery().getName())
88                  || null != each.getAssistedQuery() && each.getName().equals(each.getAssistedQuery().getName()));
89      }
90      
91      private void checkAlgorithmTypes(final CreateEncryptRuleStatement sqlStatement) {
92          sqlStatement.getRules().stream().flatMap(each -> each.getColumns().stream()).forEach(each -> {
93              checkStandardAlgorithmType(each.getCipher());
94              checkLikeAlgorithmType(each.getLikeQuery());
95              checkAssistedAlgorithmType(each.getAssistedQuery());
96          });
97      }
98      
99      private void checkStandardAlgorithmType(final EncryptColumnItemSegment itemSegment) {
100         if (null == itemSegment || null == itemSegment.getEncryptor()) {
101             return;
102         }
103         EncryptAlgorithm encryptAlgorithm = TypedSPILoader.getService(EncryptAlgorithm.class, itemSegment.getEncryptor().getName(), itemSegment.getEncryptor().getProps());
104         ShardingSpherePreconditions.checkState(encryptAlgorithm.getMetaData().isSupportDecrypt(), () -> new AlgorithmInitializationException(encryptAlgorithm, "Can not support decrypt"));
105     }
106     
107     private void checkLikeAlgorithmType(final EncryptColumnItemSegment itemSegment) {
108         if (null == itemSegment || null == itemSegment.getEncryptor()) {
109             return;
110         }
111         EncryptAlgorithm encryptAlgorithm = TypedSPILoader.getService(EncryptAlgorithm.class, itemSegment.getEncryptor().getName(), itemSegment.getEncryptor().getProps());
112         ShardingSpherePreconditions.checkState(encryptAlgorithm.getMetaData().isSupportLike(), () -> new AlgorithmInitializationException(encryptAlgorithm, "Can not support like"));
113     }
114     
115     private void checkAssistedAlgorithmType(final EncryptColumnItemSegment itemSegment) {
116         if (null == itemSegment || null == itemSegment.getEncryptor()) {
117             return;
118         }
119         EncryptAlgorithm encryptAlgorithm = TypedSPILoader.getService(EncryptAlgorithm.class, itemSegment.getEncryptor().getName(), itemSegment.getEncryptor().getProps());
120         ShardingSpherePreconditions.checkState(encryptAlgorithm.getMetaData().isSupportEquivalentFilter(),
121                 () -> new AlgorithmInitializationException(encryptAlgorithm, "Can not support assist query"));
122     }
123     
124     private void checkToBeCreatedEncryptors(final CreateEncryptRuleStatement sqlStatement) {
125         Collection<AlgorithmSegment> encryptors = new LinkedHashSet<>();
126         sqlStatement.getRules().forEach(each -> each.getColumns().forEach(column -> addToEncryptors(column, encryptors)));
127         encryptors.stream().filter(Objects::nonNull).forEach(each -> TypedSPILoader.checkService(EncryptAlgorithm.class, each.getName(), each.getProps()));
128     }
129     
130     private void addToEncryptors(final EncryptColumnSegment column, final Collection<AlgorithmSegment> result) {
131         result.add(column.getCipher().getEncryptor());
132         if (null != column.getAssistedQuery()) {
133             result.add(column.getAssistedQuery().getEncryptor());
134         }
135         if (null != column.getLikeQuery()) {
136             result.add(column.getLikeQuery().getEncryptor());
137         }
138     }
139     
140     private void checkDataSources() {
141         ShardingSpherePreconditions.checkNotEmpty(database.getResourceMetaData().getStorageUnits(), () -> new EmptyStorageUnitException(database.getName()));
142     }
143     
144     @Override
145     public EncryptRuleConfiguration buildToBeCreatedRuleConfiguration(final CreateEncryptRuleStatement sqlStatement) {
146         Collection<EncryptRuleSegment> segments = sqlStatement.getRules();
147         if (sqlStatement.isIfNotExists()) {
148             Collection<String> duplicatedRuleNames = getDuplicatedRuleNames(sqlStatement);
149             segments.removeIf(each -> duplicatedRuleNames.contains(each.getTableName()));
150         }
151         return EncryptRuleStatementConverter.convert(segments);
152     }
153     
154     @Override
155     public Class<EncryptRule> getRuleClass() {
156         return EncryptRule.class;
157     }
158     
159     @Override
160     public Class<CreateEncryptRuleStatement> getType() {
161         return CreateEncryptRuleStatement.class;
162     }
163 }