From 3923fefe7da50a82d16f4363361dd4d629e73dab Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 3 Apr 2025 16:48:22 +0200 Subject: [PATCH] some more updates --- .../core/convert/MongoConversionContext.java | 46 +++++--- .../mongodb/core/convert/QueryMapper.java | 8 +- .../encryption/ExplicitEncryptionContext.java | 6 +- .../encryption/MongoEncryptionConverter.java | 25 ++-- .../core/encryption/EncryptionContext.java | 4 +- .../data/mongodb/core/mapping/Queryable.java | 2 +- .../core/schema/DocumentJsonSchema.java | 3 - .../IdentifiableJsonSchemaProperty.java | 110 ++++++++++-------- ...ableEncryptionCollectionCreationTests.java | 2 +- .../core/encryption/RangeEncryptionTests.java | 29 ++--- 10 files changed, 125 insertions(+), 110 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java index 93a07c1d9..3daebfef9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java @@ -37,7 +37,7 @@ public class MongoConversionContext implements ValueConversionContext accessor, @Nullable MongoPersistentProperty persistentProperty, MongoConverter mongoConverter) { @@ -52,19 +52,19 @@ public class MongoConversionContext implements ValueConversionContext accessor, @Nullable MongoPersistentProperty persistentProperty, MongoConverter mongoConverter, - @Nullable ConversionOperation conversionOperation) { - this(accessor, persistentProperty, mongoConverter, null, conversionOperation); + @Nullable OperatorContext operatorContext) { + this(accessor, persistentProperty, mongoConverter, null, operatorContext); } public MongoConversionContext(PropertyValueProvider accessor, @Nullable MongoPersistentProperty persistentProperty, MongoConverter mongoConverter, - @Nullable SpELContext spELContext, @Nullable ConversionOperation conversionOperation) { + @Nullable SpELContext spELContext, @Nullable OperatorContext operatorContext) { this.accessor = accessor; this.persistentProperty = persistentProperty; this.mongoConverter = mongoConverter; this.spELContext = spELContext; - this.conversionOperation = conversionOperation; + this.operatorContext = operatorContext; } @Override @@ -100,25 +100,39 @@ public class MongoConversionContext implements ValueConversionContext implemen return jsonSchemaObjectDelegate.getTypes(); } - public static class QueryableJsonSchemaProperty implements JsonSchemaProperty { - - private final JsonSchemaProperty targetProperty; - private final QueryCharacteristics characteristics; - - public QueryableJsonSchemaProperty(JsonSchemaProperty target, QueryCharacteristics characteristics) { - this.targetProperty = target; - this.characteristics = characteristics; - } - - @Override - public Document toDocument() { - - Document doc = targetProperty.toDocument(); - Document propertySpecification = doc.get(targetProperty.getIdentifier(), Document.class); - - if (propertySpecification.containsKey("encrypt")) { - Document encrypt = propertySpecification.get("encrypt", Document.class); - List queries = characteristics.getCharacteristics().stream().map(QueryCharacteristic::toDocument) - .toList(); - encrypt.append("queries", queries); - } - - return doc; - } - - @Override - public String getIdentifier() { - return targetProperty.getIdentifier(); - } - - @Override - public Set getTypes() { - return targetProperty.getTypes(); - } - - boolean isEncrypted() { - return targetProperty instanceof EncryptedJsonSchemaProperty; - } - - public JsonSchemaProperty getTargetProperty() { - return targetProperty; - } - - public QueryCharacteristics getCharacteristics() { - return characteristics; - } - } - /** * Convenience {@link JsonSchemaProperty} implementation without a {@code type} property. * @@ -1164,6 +1115,11 @@ public class IdentifiableJsonSchemaProperty implemen return new EncryptedJsonSchemaProperty(targetProperty, algorithm, keyId, null); } + /** + * @param keyId must not be {@literal null}. + * @return new instance of {@link EncryptedJsonSchemaProperty}. + * @since 4.5 + */ public EncryptedJsonSchemaProperty keyId(Object keyId) { return new EncryptedJsonSchemaProperty(targetProperty, algorithm, keyId, null); } @@ -1247,4 +1203,60 @@ public class IdentifiableJsonSchemaProperty implemen return null; } } + + /** + * {@link JsonSchemaProperty} implementation typically wrapping {@link EncryptedJsonSchemaProperty encrypted + * properties} to mark them as queryable. + * + * @author Christoph Strobl + * @since 4.5 + */ + public static class QueryableJsonSchemaProperty implements JsonSchemaProperty { + + private final JsonSchemaProperty targetProperty; + private final QueryCharacteristics characteristics; + + public QueryableJsonSchemaProperty(JsonSchemaProperty target, QueryCharacteristics characteristics) { + this.targetProperty = target; + this.characteristics = characteristics; + } + + @Override + public Document toDocument() { + + Document doc = targetProperty.toDocument(); + Document propertySpecification = doc.get(targetProperty.getIdentifier(), Document.class); + + if (propertySpecification.containsKey("encrypt")) { + Document encrypt = propertySpecification.get("encrypt", Document.class); + List queries = characteristics.getCharacteristics().stream().map(QueryCharacteristic::toDocument) + .toList(); + encrypt.append("queries", queries); + } + + return doc; + } + + @Override + public String getIdentifier() { + return targetProperty.getIdentifier(); + } + + @Override + public Set getTypes() { + return targetProperty.getTypes(); + } + + boolean isEncrypted() { + return targetProperty instanceof EncryptedJsonSchemaProperty; + } + + public JsonSchemaProperty getTargetProperty() { + return targetProperty; + } + + public QueryCharacteristics getCharacteristics() { + return characteristics; + } + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/MongoQueryableEncryptionCollectionCreationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/MongoQueryableEncryptionCollectionCreationTests.java index abf68e2e6..d88bde68a 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/MongoQueryableEncryptionCollectionCreationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/MongoQueryableEncryptionCollectionCreationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2025. the original author or authors. + * Copyright 2025 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. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/RangeEncryptionTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/RangeEncryptionTests.java index 64bd2a4a9..36cfc7207 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/RangeEncryptionTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/RangeEncryptionTests.java @@ -26,15 +26,12 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; -import java.util.stream.Collectors; import org.bson.BsonBinary; import org.bson.BsonDocument; import org.bson.BsonInt32; import org.bson.BsonString; -import org.bson.BsonValue; import org.bson.Document; -import org.bson.types.Binary; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -64,6 +61,7 @@ import org.springframework.data.mongodb.test.util.MongoClientExtension; import org.springframework.data.util.Lazy; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.util.StringUtils; import com.mongodb.AutoEncryptionSettings; import com.mongodb.ClientEncryptionSettings; @@ -84,7 +82,6 @@ import com.mongodb.client.model.vault.RangeOptions; import com.mongodb.client.result.UpdateResult; import com.mongodb.client.vault.ClientEncryption; import com.mongodb.client.vault.ClientEncryptions; -import org.springframework.util.StringUtils; /** * @author Ross Lawley @@ -146,7 +143,7 @@ class RangeEncryptionTests { assertThat(result).containsEntry("encryptedInt", 101); } - @Test + @Test // GH-4185 void canLesserThanEqualMatchRangeEncryptedField() { Person source = createPerson(); @@ -156,7 +153,7 @@ class RangeEncryptionTests { assertThat(loaded).isEqualTo(source); } - @Test + @Test // GH-4185 void canQueryMixOfEqualityEncryptedAndUnencrypted() { Person source = template.insert(createPerson()); @@ -166,7 +163,7 @@ class RangeEncryptionTests { assertThat(loaded).isEqualTo(source); } - @Test + @Test // GH-4185 void canQueryMixOfRangeEncryptedAndUnencrypted() { Person source = template.insert(createPerson()); @@ -177,7 +174,7 @@ class RangeEncryptionTests { assertThat(loaded).isEqualTo(source); } - @Test + @Test // GH-4185 void canQueryEqualityEncryptedField() { Person source = createPerson(); @@ -187,7 +184,7 @@ class RangeEncryptionTests { assertThat(loaded).isEqualTo(source); } - @Test + @Test // GH-4185 void canExcludeSafeContentFromResult() { Person source = createPerson(); @@ -200,7 +197,7 @@ class RangeEncryptionTests { assertThat(loaded).isEqualTo(source); } - @Test + @Test // GH-4185 void canRangeMatchRangeEncryptedField() { Person source = createPerson(); @@ -211,7 +208,7 @@ class RangeEncryptionTests { assertThat(loaded).isEqualTo(source); } - @Test + @Test // GH-4185 void canReplaceEntityWithRangeEncryptedField() { Person source = createPerson(); @@ -225,7 +222,7 @@ class RangeEncryptionTests { assertThat(loaded).isEqualTo(source); } - @Test + @Test // GH-4185 void canUpdateRangeEncryptedField() { Person source = createPerson(); @@ -239,7 +236,7 @@ class RangeEncryptionTests { assertThat(loaded.encryptedLong).isEqualTo(5000L); } - @Test + @Test // GH-4185 void errorsWhenUsingNonRangeOperatorEqOnRangeEncryptedField() { Person source = createPerson(); @@ -250,10 +247,9 @@ class RangeEncryptionTests { .isInstanceOf(AssertionError.class) .hasMessageStartingWith("Not a valid range query. Querying a range encrypted field but " + "the query operator '$eq' for field path 'encryptedInt' is not a range query."); - } - @Test + @Test // GH-4185 void errorsWhenUsingNonRangeOperatorInOnRangeEncryptedField() { Person source = createPerson(); @@ -264,10 +260,10 @@ class RangeEncryptionTests { .isInstanceOf(AssertionError.class) .hasMessageStartingWith("Not a valid range query. Querying a range encrypted field but " + "the query operator '$in' for field path 'encryptedLong' is not a range query."); - } private Person createPerson() { + Person source = new Person(); source.id = "id-1"; source.unencryptedValue = "y2k"; @@ -460,7 +456,6 @@ class RangeEncryptionTests { String name; @ValueConverter(MongoEncryptionConverter.class) - // @Encrypted(algorithm = "Indexed", queries = {@Queryable(queryType = "equality", contentionFactor = 0)}) @Encrypted(algorithm = "Indexed") // @Queryable(queryType = "equality", contentionFactor = 0) // Integer age;