1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.encrypt.rule;
19
20 import com.cedarsoftware.util.CaseInsensitiveMap;
21 import com.cedarsoftware.util.CaseInsensitiveSet;
22 import org.apache.shardingsphere.encrypt.config.EncryptRuleConfiguration;
23 import org.apache.shardingsphere.encrypt.config.rule.EncryptColumnRuleConfiguration;
24 import org.apache.shardingsphere.encrypt.config.rule.EncryptTableRuleConfiguration;
25 import org.apache.shardingsphere.encrypt.constant.EncryptOrder;
26 import org.apache.shardingsphere.encrypt.exception.metadata.EncryptTableNotFoundException;
27 import org.apache.shardingsphere.encrypt.exception.metadata.MismatchedEncryptAlgorithmTypeException;
28 import org.apache.shardingsphere.encrypt.rule.attribute.EncryptTableMapperRuleAttribute;
29 import org.apache.shardingsphere.encrypt.rule.table.EncryptTable;
30 import org.apache.shardingsphere.encrypt.spi.EncryptAlgorithm;
31 import org.apache.shardingsphere.infra.algorithm.core.config.AlgorithmConfiguration;
32 import org.apache.shardingsphere.infra.annotation.HighFrequencyInvocation;
33 import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
34 import org.apache.shardingsphere.infra.rule.PartialRuleUpdateSupported;
35 import org.apache.shardingsphere.infra.rule.attribute.RuleAttribute;
36 import org.apache.shardingsphere.infra.rule.attribute.RuleAttributes;
37 import org.apache.shardingsphere.infra.rule.scope.DatabaseRule;
38 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
39
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.LinkedList;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Map.Entry;
46 import java.util.Optional;
47 import java.util.concurrent.ConcurrentHashMap;
48 import java.util.concurrent.atomic.AtomicReference;
49 import java.util.stream.Collectors;
50
51
52
53
54 public final class EncryptRule implements DatabaseRule, PartialRuleUpdateSupported<EncryptRuleConfiguration> {
55
56 private final String databaseName;
57
58 private final AtomicReference<EncryptRuleConfiguration> ruleConfig = new AtomicReference<>();
59
60 private final Map<String, EncryptAlgorithm> encryptors;
61
62 private final Map<String, EncryptTable> tables = new CaseInsensitiveMap<>(Collections.emptyMap(), new ConcurrentHashMap<>());
63
64 private final AtomicReference<RuleAttributes> attributes = new AtomicReference<>();
65
66 public EncryptRule(final String databaseName, final EncryptRuleConfiguration ruleConfig) {
67 this.databaseName = databaseName;
68 this.ruleConfig.set(ruleConfig);
69 encryptors = createEncryptors(ruleConfig);
70 for (EncryptTableRuleConfiguration each : ruleConfig.getTables()) {
71 each.getColumns().forEach(this::checkEncryptorType);
72 tables.put(each.getName(), new EncryptTable(each, encryptors));
73 }
74 attributes.set(buildRuleAttributes());
75 }
76
77 private RuleAttributes buildRuleAttributes() {
78 List<RuleAttribute> ruleAttributes = new LinkedList<>();
79 ruleAttributes.add(new EncryptTableMapperRuleAttribute(tables.keySet()));
80 return new RuleAttributes(ruleAttributes.toArray(new RuleAttribute[]{}));
81 }
82
83 private Map<String, EncryptAlgorithm> createEncryptors(final EncryptRuleConfiguration ruleConfig) {
84 Map<String, EncryptAlgorithm> result = new CaseInsensitiveMap<>(Collections.emptyMap(), new ConcurrentHashMap<>(ruleConfig.getEncryptors().size(), 1F));
85 for (Entry<String, AlgorithmConfiguration> entry : ruleConfig.getEncryptors().entrySet()) {
86 result.put(entry.getKey(), TypedSPILoader.getService(EncryptAlgorithm.class, entry.getValue().getType(), entry.getValue().getProps()));
87 }
88 return result;
89 }
90
91
92 private void checkEncryptorType(final EncryptColumnRuleConfiguration columnRuleConfig) {
93 ShardingSpherePreconditions.checkState(encryptors.containsKey(columnRuleConfig.getCipher().getEncryptorName())
94 && encryptors.get(columnRuleConfig.getCipher().getEncryptorName()).getMetaData().isSupportDecrypt(),
95 () -> new MismatchedEncryptAlgorithmTypeException(databaseName, "Cipher", columnRuleConfig.getCipher().getEncryptorName(), "decrypt"));
96 columnRuleConfig.getAssistedQuery().ifPresent(optional -> ShardingSpherePreconditions.checkState(encryptors.containsKey(optional.getEncryptorName())
97 && encryptors.get(optional.getEncryptorName()).getMetaData().isSupportEquivalentFilter(),
98 () -> new MismatchedEncryptAlgorithmTypeException(databaseName, "Assisted query", columnRuleConfig.getCipher().getEncryptorName(), "equivalent filter")));
99 columnRuleConfig.getLikeQuery().ifPresent(optional -> ShardingSpherePreconditions.checkState(encryptors.containsKey(optional.getEncryptorName())
100 && encryptors.get(optional.getEncryptorName()).getMetaData().isSupportLike(),
101 () -> new MismatchedEncryptAlgorithmTypeException(databaseName, "Like query", columnRuleConfig.getCipher().getEncryptorName(), "like")));
102 }
103
104
105
106
107
108
109 public Collection<String> getAllTableNames() {
110 return tables.keySet();
111 }
112
113
114
115
116
117
118
119 @HighFrequencyInvocation
120 public Optional<EncryptTable> findEncryptTable(final String tableName) {
121 return Optional.ofNullable(tables.get(tableName));
122 }
123
124
125
126
127
128
129
130 @HighFrequencyInvocation
131 public EncryptTable getEncryptTable(final String tableName) {
132 return findEncryptTable(tableName).orElseThrow(() -> new EncryptTableNotFoundException(tableName));
133 }
134
135
136
137
138
139
140
141
142 @HighFrequencyInvocation
143 public Optional<EncryptAlgorithm> findQueryEncryptor(final String tableName, final String columnName) {
144 return findEncryptTable(tableName).flatMap(optional -> optional.findQueryEncryptor(columnName));
145 }
146
147 @Override
148 public RuleAttributes getAttributes() {
149 return attributes.get();
150 }
151
152 @Override
153 public EncryptRuleConfiguration getConfiguration() {
154 return ruleConfig.get();
155 }
156
157 @Override
158 public void updateConfiguration(final EncryptRuleConfiguration toBeUpdatedRuleConfig) {
159 ruleConfig.set(toBeUpdatedRuleConfig);
160 }
161
162 @Override
163 public boolean partialUpdate(final EncryptRuleConfiguration toBeUpdatedRuleConfig) {
164 if (handleAddedEncryptors(toBeUpdatedRuleConfig) || handleRemovedEncryptors(toBeUpdatedRuleConfig)) {
165 return false;
166 }
167 Collection<String> toBeUpdatedTablesNames = toBeUpdatedRuleConfig.getTables().stream().map(EncryptTableRuleConfiguration::getName).collect(Collectors.toCollection(CaseInsensitiveSet::new));
168 Collection<String> toBeRemovedTableNames = tables.keySet().stream().filter(each -> !toBeUpdatedTablesNames.contains(each)).collect(Collectors.toList());
169 if (!toBeRemovedTableNames.isEmpty()) {
170 toBeRemovedTableNames.forEach(tables::remove);
171 }
172 for (EncryptTableRuleConfiguration encryptTableRuleConfiguration : toBeUpdatedRuleConfig.getTables()) {
173 encryptTableRuleConfiguration.getColumns().forEach(this::checkEncryptorType);
174 tables.put(encryptTableRuleConfiguration.getName(), new EncryptTable(encryptTableRuleConfiguration, encryptors));
175 attributes.set(buildRuleAttributes());
176 }
177 return true;
178 }
179
180 private boolean handleAddedEncryptors(final EncryptRuleConfiguration toBeUpdatedRuleConfig) {
181 return toBeUpdatedRuleConfig.getEncryptors().entrySet().stream()
182 .filter(entry -> !encryptors.containsKey(entry.getKey()))
183 .peek(entry -> encryptors.computeIfAbsent(entry.getKey(), key -> TypedSPILoader.getService(EncryptAlgorithm.class, entry.getValue().getType(), entry.getValue().getProps())))
184 .findAny().isPresent();
185 }
186
187 private boolean handleRemovedEncryptors(final EncryptRuleConfiguration toBeUpdatedRuleConfig) {
188 return encryptors.entrySet().stream()
189 .filter(entry -> !toBeUpdatedRuleConfig.getEncryptors().containsKey(entry.getKey()))
190 .peek(entry -> encryptors.remove(entry.getKey())).findAny().isPresent();
191 }
192
193 @Override
194 public int getOrder() {
195 return EncryptOrder.ORDER;
196 }
197 }