Skip to content

Commit 67215f1

Browse files
committed
Polishing.
Remove caching variant of MongoClientEncryption. Rename types for consistent key alt name scheme. Rename annotation to ExplicitEncrypted. Add package-info. Improve documentation wording. Reduce visibility of KeyId and KeyAltName to package-private. Original pull request: #4302 See: #4284
1 parent 3b33f90 commit 67215f1

21 files changed

+427
-368
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

+4-23
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,7 @@
1717

1818
import java.lang.reflect.Constructor;
1919
import java.lang.reflect.Method;
20-
import java.util.ArrayList;
21-
import java.util.Arrays;
22-
import java.util.Collection;
23-
import java.util.Collections;
24-
import java.util.HashSet;
25-
import java.util.LinkedHashMap;
26-
import java.util.List;
27-
import java.util.Map;
28-
import java.util.Optional;
29-
import java.util.Set;
20+
import java.util.*;
3021
import java.util.function.Predicate;
3122
import java.util.stream.Collectors;
3223

@@ -39,7 +30,6 @@
3930
import org.bson.conversions.Bson;
4031
import org.bson.json.JsonReader;
4132
import org.bson.types.ObjectId;
42-
4333
import org.springframework.beans.BeansException;
4434
import org.springframework.beans.factory.BeanClassLoaderAware;
4535
import org.springframework.context.ApplicationContext;
@@ -51,16 +41,7 @@
5141
import org.springframework.data.annotation.Reference;
5242
import org.springframework.data.convert.CustomConversions;
5343
import org.springframework.data.convert.TypeMapper;
54-
import org.springframework.data.mapping.AccessOptions;
55-
import org.springframework.data.mapping.Association;
56-
import org.springframework.data.mapping.InstanceCreatorMetadata;
57-
import org.springframework.data.mapping.MappingException;
58-
import org.springframework.data.mapping.Parameter;
59-
import org.springframework.data.mapping.PersistentEntity;
60-
import org.springframework.data.mapping.PersistentProperty;
61-
import org.springframework.data.mapping.PersistentPropertyAccessor;
62-
import org.springframework.data.mapping.PersistentPropertyPath;
63-
import org.springframework.data.mapping.PersistentPropertyPathAccessor;
44+
import org.springframework.data.mapping.*;
6445
import org.springframework.data.mapping.callback.EntityCallbacks;
6546
import org.springframework.data.mapping.context.MappingContext;
6647
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
@@ -902,7 +883,7 @@ protected void writePropertyInternal(@Nullable Object obj, DocumentAccessor acce
902883

903884
if (conversions.hasValueConverter(prop)) {
904885
accessor.put(prop, conversions.getPropertyValueConversions().getValueConverter(prop).write(obj,
905-
new MongoConversionContext(new PropertyValueProvider<MongoPersistentProperty>() {
886+
new MongoConversionContext(new PropertyValueProvider<>() {
906887
@Nullable
907888
@Override
908889
public <T> T getPropertyValue(MongoPersistentProperty property) {
@@ -1245,7 +1226,7 @@ private void writeSimpleInternal(@Nullable Object value, Bson bson, MongoPersist
12451226

12461227
if (conversions.hasValueConverter(property)) {
12471228
accessor.put(property, conversions.getPropertyValueConversions().getValueConverter(property).write(value,
1248-
new MongoConversionContext(new PropertyValueProvider<MongoPersistentProperty>() {
1229+
new MongoConversionContext(new PropertyValueProvider<>() {
12491230
@Nullable
12501231
@Override
12511232
public <T> T getPropertyValue(MongoPersistentProperty property) {

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java

+7-8
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,12 @@
1515
*/
1616
package org.springframework.data.mongodb.core.convert;
1717

18-
import java.util.function.Supplier;
19-
2018
import org.bson.conversions.Bson;
2119
import org.springframework.data.convert.ValueConversionContext;
22-
import org.springframework.data.mapping.PersistentPropertyAccessor;
2320
import org.springframework.data.mapping.model.PropertyValueProvider;
2421
import org.springframework.data.mapping.model.SpELContext;
25-
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
2622
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
2723
import org.springframework.data.util.TypeInformation;
28-
import org.springframework.expression.EvaluationContext;
2924
import org.springframework.lang.Nullable;
3025

3126
/**
@@ -36,18 +31,20 @@
3631
*/
3732
public class MongoConversionContext implements ValueConversionContext<MongoPersistentProperty> {
3833

39-
private final PropertyValueProvider accessor; // TODO: generics
34+
private final PropertyValueProvider<MongoPersistentProperty> accessor; // TODO: generics
4035
private final MongoPersistentProperty persistentProperty;
4136
private final MongoConverter mongoConverter;
4237

4338
@Nullable
4439
private final SpELContext spELContext;
4540

46-
public MongoConversionContext(PropertyValueProvider<?> accessor, MongoPersistentProperty persistentProperty, MongoConverter mongoConverter) {
41+
public MongoConversionContext(PropertyValueProvider<MongoPersistentProperty> accessor,
42+
MongoPersistentProperty persistentProperty, MongoConverter mongoConverter) {
4743
this(accessor, persistentProperty, mongoConverter, null);
4844
}
4945

50-
public MongoConversionContext(PropertyValueProvider<?> accessor, MongoPersistentProperty persistentProperty, MongoConverter mongoConverter, SpELContext spELContext) {
46+
public MongoConversionContext(PropertyValueProvider<MongoPersistentProperty> accessor,
47+
MongoPersistentProperty persistentProperty, MongoConverter mongoConverter, @Nullable SpELContext spELContext) {
5148

5249
this.accessor = accessor;
5350
this.persistentProperty = persistentProperty;
@@ -60,11 +57,13 @@ public MongoPersistentProperty getProperty() {
6057
return persistentProperty;
6158
}
6259

60+
@Nullable
6361
public Object getValue(String propertyPath) {
6462
return accessor.getPropertyValue(persistentProperty.getOwner().getRequiredPersistentProperty(propertyPath));
6563
}
6664

6765
@Override
66+
@SuppressWarnings("unchecked")
6867
public <T> T write(@Nullable Object value, TypeInformation<T> target) {
6968
return (T) mongoConverter.convertToMongoType(value, target);
7069
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/EncryptingConverter.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import org.springframework.data.mongodb.core.encryption.EncryptionContext;
2121

2222
/**
23-
* A specialized {@link MongoValueConverter} for {@literal en-/decrypting} properties.
23+
* A specialized {@link MongoValueConverter} for {@literal encryptiong} and {@literal decrypting} properties.
2424
*
2525
* @author Christoph Strobl
2626
* @since 4.1
@@ -32,20 +32,20 @@ default S read(Object value, MongoConversionContext context) {
3232
return decrypt(value, buildEncryptionContext(context));
3333
}
3434

35-
@Override
36-
default T write(Object value, MongoConversionContext context) {
37-
return encrypt(value, buildEncryptionContext(context));
38-
}
39-
4035
/**
4136
* Decrypt the given encrypted source value within the given {@link EncryptionContext context}.
42-
*
37+
*
4338
* @param encryptedValue the encrypted source.
4439
* @param context the context to operate in.
4540
* @return never {@literal null}.
4641
*/
4742
S decrypt(Object encryptedValue, EncryptionContext context);
4843

44+
@Override
45+
default T write(Object value, MongoConversionContext context) {
46+
return encrypt(value, buildEncryptionContext(context));
47+
}
48+
4949
/**
5050
* Encrypt the given raw source value within the given {@link EncryptionContext context}.
5151
*

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java

+49-37
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,17 @@
3939
import org.springframework.util.ObjectUtils;
4040

4141
/**
42+
* Default implementation of {@link EncryptingConverter}. Properties used with this converter must be annotated with
43+
* {@link Encrypted @Encrypted} to provide key and algorithm metadata.
44+
*
4245
* @author Christoph Strobl
4346
* @since 4.1
4447
*/
4548
public class MongoEncryptionConverter implements EncryptingConverter<Object, Object> {
4649

4750
private static final Log LOGGER = LogFactory.getLog(MongoEncryptionConverter.class);
4851

49-
private Encryption<BsonValue, BsonBinary> encryption;
52+
private final Encryption<BsonValue, BsonBinary> encryption;
5053
private final EncryptionKeyResolver keyResolver;
5154

5255
public MongoEncryptionConverter(Encryption<BsonValue, BsonBinary> encryption, EncryptionKeyResolver keyResolver) {
@@ -70,7 +73,7 @@ public Object decrypt(Object encryptedValue, EncryptionContext context) {
7073
if (encryptedValue instanceof Binary || encryptedValue instanceof BsonBinary) {
7174

7275
if (LOGGER.isDebugEnabled()) {
73-
LOGGER.debug(String.format("Decrypting %s.%s.", getProperty(context).getOwner().getName(),
76+
LOGGER.debug(String.format("Decrypting %s.%s.", getProperty(context).getOwner().getName(),
7477
getProperty(context).getName()));
7578
}
7679

@@ -83,18 +86,20 @@ public Object decrypt(Object encryptedValue, EncryptionContext context) {
8386
}
8487
}
8588

86-
MongoPersistentProperty persistentProperty = getProperty(context);
89+
MongoPersistentProperty persistentProperty = getProperty(context);
8790

8891
if (getProperty(context).isCollectionLike() && decryptedValue instanceof Iterable<?> iterable) {
92+
93+
int size = iterable instanceof Collection<?> c ? c.size() : 10;
94+
8995
if (!persistentProperty.isEntity()) {
90-
Collection<Object> collection = CollectionFactory.createCollection(persistentProperty.getType(), 10);
96+
Collection<Object> collection = CollectionFactory.createCollection(persistentProperty.getType(), size);
9197
iterable.forEach(it -> collection.add(BsonUtils.toJavaType((BsonValue) it)));
9298
return collection;
9399
} else {
94-
Collection<Object> collection = CollectionFactory.createCollection(persistentProperty.getType(), 10);
100+
Collection<Object> collection = CollectionFactory.createCollection(persistentProperty.getType(), size);
95101
iterable.forEach(it -> {
96-
collection.add(context.read(BsonUtils.toJavaType((BsonValue) it),
97-
persistentProperty.getActualType()));
102+
collection.add(context.read(BsonUtils.toJavaType((BsonValue) it), persistentProperty.getActualType()));
98103
});
99104
return collection;
100105
}
@@ -109,17 +114,12 @@ public Object decrypt(Object encryptedValue, EncryptionContext context) {
109114
}
110115

111116
if (persistentProperty.isEntity() && decryptedValue instanceof BsonDocument bsonDocument) {
112-
return context.read(BsonUtils.toJavaType(bsonDocument),
113-
persistentProperty.getTypeInformation().getType());
117+
return context.read(BsonUtils.toJavaType(bsonDocument), persistentProperty.getTypeInformation().getType());
114118
}
115119

116120
return decryptedValue;
117121
}
118122

119-
private MongoPersistentProperty getProperty(EncryptionContext context) {
120-
return context.getProperty();
121-
}
122-
123123
@Override
124124
public Object encrypt(Object value, EncryptionContext context) {
125125

@@ -128,15 +128,19 @@ public Object encrypt(Object value, EncryptionContext context) {
128128
getProperty(context).getName()));
129129
}
130130

131-
MongoPersistentProperty persistentProperty = getProperty(context);
131+
MongoPersistentProperty persistentProperty = getProperty(context);
132132

133133
Encrypted annotation = persistentProperty.findAnnotation(Encrypted.class);
134-
if(annotation == null) {
134+
if (annotation == null) {
135135
annotation = persistentProperty.getOwner().findAnnotation(Encrypted.class);
136136
}
137137

138-
EncryptionOptions encryptionOptions = new EncryptionOptions(annotation.algorithm());
139-
encryptionOptions.setKey(keyResolver.getKey(context));
138+
if (annotation == null) {
139+
throw new IllegalStateException(String.format("Property %s.%s is not annotated with @Encrypted",
140+
getProperty(context).getOwner().getName(), getProperty(context).getName()));
141+
}
142+
143+
EncryptionOptions encryptionOptions = new EncryptionOptions(annotation.algorithm(), keyResolver.getKey(context));
140144

141145
if (!persistentProperty.isEntity()) {
142146

@@ -162,36 +166,44 @@ public Object encrypt(Object value, EncryptionContext context) {
162166
return encryption.encrypt(BsonUtils.simpleToBsonValue(write), encryptionOptions);
163167
}
164168

165-
public BsonValue collectionLikeToBsonValue(Object value, MongoPersistentProperty property,
169+
private BsonValue collectionLikeToBsonValue(Object value, MongoPersistentProperty property,
166170
EncryptionContext context) {
167171

168172
BsonArray bsonArray = new BsonArray();
169-
if (!property.isEntity()) {
170-
if (value instanceof Collection values) {
171-
values.forEach(it -> bsonArray.add(BsonUtils.simpleToBsonValue(it)));
172-
} else if (ObjectUtils.isArray(value)) {
173-
for (Object o : ObjectUtils.toObjectArray(value)) {
174-
bsonArray.add(BsonUtils.simpleToBsonValue(o));
173+
boolean isEntity = property.isEntity();
174+
175+
if (value instanceof Collection<?> values) {
176+
values.forEach(it -> {
177+
178+
if (isEntity) {
179+
Document document = (Document) context.write(it, property.getTypeInformation());
180+
bsonArray.add(document == null ? null : document.toBsonDocument());
181+
} else {
182+
bsonArray.add(BsonUtils.simpleToBsonValue(it));
175183
}
176-
}
177-
return bsonArray;
178-
} else {
179-
if (value instanceof Collection values) {
180-
values.forEach(it -> {
181-
Document write = (Document) context.write(it, property.getTypeInformation());
182-
bsonArray.add(write.toBsonDocument());
183-
});
184-
} else if (ObjectUtils.isArray(value)) {
185-
for (Object o : ObjectUtils.toObjectArray(value)) {
186-
Document write = (Document) context.write(o, property.getTypeInformation());
187-
bsonArray.add(write.toBsonDocument());
184+
});
185+
} else if (ObjectUtils.isArray(value)) {
186+
187+
for (Object o : ObjectUtils.toObjectArray(value)) {
188+
189+
if (isEntity) {
190+
Document document = (Document) context.write(o, property.getTypeInformation());
191+
bsonArray.add(document == null ? null : document.toBsonDocument());
192+
} else {
193+
bsonArray.add(BsonUtils.simpleToBsonValue(o));
188194
}
189195
}
190-
return bsonArray;
191196
}
197+
198+
return bsonArray;
192199
}
193200

201+
@Override
194202
public EncryptionContext buildEncryptionContext(MongoConversionContext context) {
195203
return new ExplicitEncryptionContext(context);
196204
}
205+
206+
protected MongoPersistentProperty getProperty(EncryptionContext context) {
207+
return context.getProperty();
208+
}
197209
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* Converters integrating with
3+
* <a href="https://www.mongodb.com/docs/manual/core/csfle/fundamentals/manual-encryption/">explicit encryption
4+
* mechanism of Client-Side Field Level Encryption</a>.
5+
*/
6+
@org.springframework.lang.NonNullApi
7+
package org.springframework.data.mongodb.core.convert.encryption;

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/Encryption.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
package org.springframework.data.mongodb.core.encryption;
1717

1818
/**
19-
* Component responsible for en-/decrypting values.
19+
* Component responsible for encrypting and decrypting values.
2020
*
2121
* @author Christoph Strobl
2222
* @since 4.1

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/EncryptionContext.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@
1515
*/
1616
package org.springframework.data.mongodb.core.encryption;
1717

18-
import org.springframework.data.convert.ValueConversionContext;
1918
import org.springframework.data.mapping.PersistentProperty;
2019
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
2120
import org.springframework.data.util.TypeInformation;
2221
import org.springframework.expression.EvaluationContext;
2322
import org.springframework.lang.Nullable;
2423

2524
/**
25+
* Context to encapsulate encryption for a specific {@link MongoPersistentProperty}.
26+
*
2627
* @author Christoph Strobl
28+
* @since 4.1
2729
*/
2830
public interface EncryptionContext {
2931

@@ -36,7 +38,7 @@ public interface EncryptionContext {
3638

3739
/**
3840
* Shortcut for converting a given {@literal value} into its store representation using the root
39-
* {@link ValueConversionContext}.
41+
* {@code ValueConversionContext}.
4042
*
4143
* @param value
4244
* @return

0 commit comments

Comments
 (0)