diff --git a/pom.xml b/pom.xml
index 7cb1d10f85..c267783e38 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3800-SNAPSHOT
pom
Spring Data MongoDB
diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml
index 0033bd11d5..2d2a2b2d83 100644
--- a/spring-data-mongodb-benchmarks/pom.xml
+++ b/spring-data-mongodb-benchmarks/pom.xml
@@ -7,7 +7,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3800-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index f62c8dc7f4..1bac395ec0 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3800-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 2f73c10eba..287cd1e87f 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3800-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EncryptionAlgorithms.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EncryptionAlgorithms.java
new file mode 100644
index 0000000000..0ed7340aa1
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EncryptionAlgorithms.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+/**
+ * Encryption algorithms supported by MongoDB Client Side Field Level Encryption.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+public final class EncryptionAlgorithms {
+
+ public static final String AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic";
+ public static final String AEAD_AES_256_CBC_HMAC_SHA_512_Random = "AEAD_AES_256_CBC_HMAC_SHA_512-Random";
+
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java
index ecbf8a4f07..a53ff8f5a5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java
@@ -20,13 +20,19 @@
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import org.bson.Document;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.convert.MongoConverter;
+import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
+import org.springframework.data.mongodb.core.mapping.Encrypted;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
+import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty;
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.ObjectJsonSchemaProperty;
import org.springframework.data.mongodb.core.schema.JsonSchemaObject;
import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type;
@@ -34,10 +40,12 @@
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
import org.springframework.data.mongodb.core.schema.MongoJsonSchema.MongoJsonSchemaBuilder;
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject;
+import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
/**
* {@link MongoJsonSchemaCreator} implementation using both {@link MongoConverter} and {@link MappingContext} to obtain
@@ -52,6 +60,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
private final MongoConverter converter;
private final MappingContext, MongoPersistentProperty> mappingContext;
+ private final Predicate filter;
/**
* Create a new instance of {@link MappingMongoJsonSchemaCreator}.
@@ -61,10 +70,24 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
@SuppressWarnings("unchecked")
MappingMongoJsonSchemaCreator(MongoConverter converter) {
+ this(converter, (MappingContext, MongoPersistentProperty>) converter.getMappingContext(),
+ (property) -> true);
+ }
+
+ @SuppressWarnings("unchecked")
+ MappingMongoJsonSchemaCreator(MongoConverter converter,
+ MappingContext, MongoPersistentProperty> mappingContext,
+ Predicate filter) {
+
Assert.notNull(converter, "Converter must not be null!");
this.converter = converter;
- this.mappingContext = (MappingContext, MongoPersistentProperty>) converter
- .getMappingContext();
+ this.mappingContext = mappingContext;
+ this.filter = filter;
+ }
+
+ @Override
+ public MongoJsonSchemaCreator filter(Predicate filter) {
+ return new MappingMongoJsonSchemaCreator(converter, mappingContext, filter);
}
/*
@@ -77,11 +100,29 @@ public MongoJsonSchema createSchemaFor(Class> type) {
MongoPersistentEntity> entity = mappingContext.getRequiredPersistentEntity(type);
MongoJsonSchemaBuilder schemaBuilder = MongoJsonSchema.builder();
+ {
+ Encrypted encrypted = entity.findAnnotation(Encrypted.class);
+ if (encrypted != null) {
+
+ Document encryptionMetadata = new Document();
+
+ Collection
+ * {@link Encrypted} properties will contain {@literal encrypt} information.
*
* @author Christoph Strobl
* @since 2.2
@@ -60,6 +77,88 @@ public interface MongoJsonSchemaCreator {
*/
MongoJsonSchema createSchemaFor(Class> type);
+ /**
+ * Filter matching {@link JsonSchemaProperty properties}.
+ *
+ * @param filter the {@link Predicate} to evaluate for inclusion. Must not be {@literal null}.
+ * @return new instance of {@link MongoJsonSchemaCreator}.
+ * @since 3.3
+ */
+ MongoJsonSchemaCreator filter(Predicate filter);
+
+ /**
+ * The context in which a specific {@link #getProperty()} is encountered during schema creation.
+ *
+ * @since 3.3
+ */
+ interface JsonSchemaPropertyContext {
+
+ /**
+ * The path to a given field/property in dot notation.
+ *
+ * @return never {@literal null}.
+ */
+ String getPath();
+
+ /**
+ * The current property.
+ *
+ * @return never {@literal null}.
+ */
+ MongoPersistentProperty getProperty();
+
+ /**
+ * Obtain the {@link MongoPersistentEntity} for a given property.
+ *
+ * @param property must not be {@literal null}.
+ * @param
+ * @return {@literal null} if the property is not an entity. It is nevertheless recommend to check
+ * {@link PersistentProperty#isEntity()} first.
+ */
+ @Nullable
+ MongoPersistentEntity resolveEntity(MongoPersistentProperty property);
+
+ }
+
+ /**
+ * A filter {@link Predicate} that matches {@link Encrypted encrypted properties} and those having nested ones.
+ *
+ * @return new instance of {@link Predicate}.
+ * @since 3.3
+ */
+ static Predicate encryptedOnly() {
+
+ return new Predicate() {
+
+ // cycle guard
+ private final Set seen = new HashSet<>();
+
+ @Override
+ public boolean test(JsonSchemaPropertyContext context) {
+ return extracted(context.getProperty(), context);
+ }
+
+ private boolean extracted(MongoPersistentProperty property, JsonSchemaPropertyContext context) {
+ if (property.isAnnotationPresent(Encrypted.class)) {
+ return true;
+ }
+
+ if (!property.isEntity() || seen.contains(property)) {
+ return false;
+ }
+
+ seen.add(property);
+
+ for (MongoPersistentProperty nested : context.resolveEntity(property)) {
+ if (extracted(nested, context)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+
/**
* Creates a new {@link MongoJsonSchemaCreator} that is aware of conversions applied by the given
* {@link MongoConverter}.
@@ -72,4 +171,41 @@ static MongoJsonSchemaCreator create(MongoConverter mongoConverter) {
Assert.notNull(mongoConverter, "MongoConverter must not be null!");
return new MappingMongoJsonSchemaCreator(mongoConverter);
}
+
+ /**
+ * Creates a new {@link MongoJsonSchemaCreator} that is aware of type mappings and potential
+ * {@link org.springframework.data.spel.spi.EvaluationContextExtension extensions}.
+ *
+ * @param mappingContext must not be {@literal null}.
+ * @return new instance of {@link MongoJsonSchemaCreator}.
+ * @since 3.3
+ */
+ static MongoJsonSchemaCreator create(MappingContext mappingContext) {
+
+ MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
+ converter.setCustomConversions(MongoCustomConversions.create(config -> {}));
+ converter.afterPropertiesSet();
+
+ return create(converter);
+ }
+
+ /**
+ * Creates a new {@link MongoJsonSchemaCreator} that does not consider potential extensions - suitable for testing. We
+ * recommend to use {@link #create(MappingContext)}.
+ *
+ * @return new instance of {@link MongoJsonSchemaCreator}.
+ * @since 3.3
+ */
+ static MongoJsonSchemaCreator create() {
+
+ MongoMappingContext mappingContext = new MongoMappingContext();
+ mappingContext.setSimpleTypeHolder(MongoSimpleTypes.HOLDER);
+ mappingContext.afterPropertiesSet();
+
+ MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
+ converter.setCustomConversions(MongoCustomConversions.create(config -> {}));
+ converter.afterPropertiesSet();
+
+ return create(converter);
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java
index 7bf8214aeb..6840fce5bf 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java
@@ -17,8 +17,12 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.springframework.data.annotation.Id;
@@ -28,6 +32,9 @@
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.mongodb.MongoCollectionUtils;
+import org.springframework.data.mongodb.util.encryption.EncryptionUtils;
+import org.springframework.data.spel.ExpressionDependencies;
+import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
@@ -212,6 +219,11 @@ public EvaluationContext getEvaluationContext(Object rootObject) {
return super.getEvaluationContext(rootObject);
}
+ @Override
+ public EvaluationContext getEvaluationContext(Object rootObject, ExpressionDependencies dependencies) {
+ return super.getEvaluationContext(rootObject, dependencies);
+ }
+
private void verifyFieldUniqueness() {
AssertFieldNameUniquenessHandler handler = new AssertFieldNameUniquenessHandler();
@@ -360,6 +372,32 @@ private void assertUniqueness(MongoPersistentProperty property) {
}
}
+ @Override
+ public Collection