From f96eeae3e508d434f1c9f599e63a9aedc78a5561 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 20 Mar 2025 10:08:10 +0100 Subject: [PATCH] a plain tests with native aPI --- .../data/mongodb/core/EntityOperations.java | 6 +- .../data/mongodb/core/MongoTemplate.java | 1 + .../core/encryption/RangeEncryptionTests.java | 117 ++++++++---------- 3 files changed, 57 insertions(+), 67 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java index 7977b62c3..c0d12bb3a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java @@ -380,11 +380,7 @@ class EntityOperations { collectionOptions.getChangeStreamOptions().ifPresent(it -> result .changeStreamPreAndPostImagesOptions(new ChangeStreamPreAndPostImagesOptions(it.getPreAndPostImages()))); - if(collectionOptions.getEncryptedFields().isPresent()) { - EncryptedCollectionOptions encryptedCollectionOptions = collectionOptions.getEncryptedFields().get(); - - result.encryptedFields(encryptedCollectionOptions.toDocument()); - } + collectionOptions.getEncryptedFields().map(EncryptedCollectionOptions::toDocument).ifPresent(result::encryptedFields); return result; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 67ef3a308..4e983819d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -58,6 +58,7 @@ import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.MongoDatabaseUtils; import org.springframework.data.mongodb.SessionSynchronization; import org.springframework.data.mongodb.core.BulkOperations.BulkMode; +import org.springframework.data.mongodb.core.CollectionOptions.EncryptedCollectionOptions; import org.springframework.data.mongodb.core.CollectionPreparerSupport.CollectionPreparerDelegate; import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext; import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity; 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 29c8dc732..6e33edc5e 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 @@ -21,7 +21,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.springframework.data.mongodb.core.query.Criteria.where; import java.security.SecureRandom; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -38,7 +37,6 @@ import org.bson.BsonNull; import org.bson.BsonString; import org.bson.BsonValue; import org.bson.Document; -import org.bson.json.JsonWriterSettings; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -78,7 +76,8 @@ import com.mongodb.client.model.CreateEncryptedCollectionParams; import com.mongodb.client.model.Filters; import com.mongodb.client.model.IndexOptions; import com.mongodb.client.model.Indexes; -import com.mongodb.client.model.vault.DataKeyOptions; +import com.mongodb.client.model.vault.EncryptOptions; +import com.mongodb.client.model.vault.RangeOptions; import com.mongodb.client.vault.ClientEncryption; import com.mongodb.client.vault.ClientEncryptions; @@ -92,6 +91,8 @@ import com.mongodb.client.vault.ClientEncryptions; class RangeEncryptionTests { @Autowired MongoTemplate template; + @Autowired MongoClientEncryption clientEncryption; + @Autowired EncryptionKeyHolder keyHolder; @AfterEach void tearDown() { @@ -100,11 +101,33 @@ class RangeEncryptionTests { @Test void canGreaterThanEqualMatchRangeEncryptedField() { - Person source = createPerson(); - template.insert(source); - Person loaded = template.query(Person.class).matching(where("encryptedInt").gte(source.encryptedInt)).firstValue(); - assertThat(loaded).isEqualTo(source); + EncryptOptions encryptOptions = new EncryptOptions("Range").contentionFactor(1L) + .keyId(keyHolder.getEncryptionKey("encryptedInt")) + .rangeOptions(new RangeOptions().min(new BsonInt32(0)).max(new BsonInt32(200)).sparsity(1L)); + + EncryptOptions encryptExpressionOptions = new EncryptOptions("Range").contentionFactor(1L) + .rangeOptions(new RangeOptions().min(new BsonInt32(0)).max(new BsonInt32(200))) + .keyId(keyHolder.getEncryptionKey("encryptedInt")).queryType("range"); + + Document source = new Document("_id", "id-1"); + source.put("name", "It's a Me, Mario!"); + source.put("encryptedInt", clientEncryption.getClientEncryption().encrypt(new BsonInt32(101), encryptOptions)); + source.put("_class", Person.class.getName()); + + template.execute(Person.class, col -> col.insertOne(source)); + + Document result = template.execute(Person.class, col -> { + + BsonDocument filter = clientEncryption.getClientEncryption().encryptExpression( + new Document("$and", List.of(new BsonDocument("encryptedInt", new BsonDocument("$gte", new BsonInt32(100))))), + encryptExpressionOptions); + Document first = col.find(filter).first(); + System.out.println("first.toJson(): " + first.toJson()); + return first; + }); + + assertThat(result).containsEntry("encryptedInt", 101); } @Test @@ -205,17 +228,15 @@ class RangeEncryptionTests { } @Bean - MongoEncryptionConverter encryptingConverter(MongoClientEncryption mongoClientEncryption) { + EncryptionKeyHolder keyHolder(MongoClientEncryption mongoClientEncryption) { + Lazy> lazyDataKeyMap = Lazy.of(() -> { try (MongoClient client = mongoClient()) { + MongoDatabase database = client.getDatabase(getDatabaseName()); database.getCollection("test").drop(); ClientEncryption clientEncryption = mongoClientEncryption.getClientEncryption(); - BsonBinary dataKey1 = clientEncryption.createDataKey(LOCAL_KMS_PROVIDER, - new DataKeyOptions().keyAltNames(List.of("dek-1"))); - // BsonBinary dataKey2 = clientEncryption.createDataKey(LOCAL_KMS_PROVIDER, new - // DataKeyOptions().keyAltNames(List.of("dek-2"))); BsonDocument encryptedFields = new BsonDocument().append("fields", new BsonArray(asList( @@ -231,69 +252,29 @@ class RangeEncryptionTests { .append("trimFactor", new BsonInt32(1)).append("sparsity", new BsonInt64(1)) .append("min", new BsonInt64(1000)).append("max", new BsonInt64(9999)))))); - // BsonBinary dataKey2 = clientEncryption.createDataKey(LOCAL_KMS_PROVIDER, new - // DataKeyOptions().keyAltNames(List.of("dek-2"))); - // - // BsonDocument encryptedFields = new BsonDocument().append("fields", - // new BsonArray(asList( - // new BsonDocument("keyId", dataKey1).append("path", new BsonString("encryptedInt")) - // .append("bsonType", new BsonString("int")) - // .append("queries", - // new BsonDocument("queryType", new BsonString("range")).append("contention", new BsonInt64(0L)) - // .append("trimFactor", new BsonInt32(1)).append("sparsity", new BsonInt64(1)) - // .append("min", new BsonInt32(0)).append("max", new BsonInt32(200))), - // new BsonDocument("keyId", dataKey2).append("path", new BsonString("encryptedLong")) - // .append("bsonType", new BsonString("long")).append("queries", - // new BsonDocument("queryType", new BsonString("range")).append("contention", new BsonInt64(0L)) - // .append("trimFactor", new BsonInt32(1)).append("sparsity", new BsonInt64(1)) - // .append("min", new BsonInt64(1000)).append("max", new BsonInt64(9999)))))); - // - - // CollectionOptions encOptions = CollectionOptions.encrypted(options -> - // options - // .queryable( - // encrypted(int32("encryptedInt")), - // range().contention(0).trimFactor(1).sparsity(1).min(0).max(200) - // ).queryable( - // encrypted(int64("encryptedLong")), - // range().contention(0).trimFactor(1).sparsity(1).min(1000L).max(9999L) - // ) - // ); - MongoJsonSchema personSchema = MongoJsonSchemaCreator.create(new MongoMappingContext()) .filter(MongoJsonSchemaCreator.encryptedOnly()).createSchemaFor(Person.class); CollectionOptions options = CollectionOptions.encrypted(personSchema); - System.out.println("encrypted: " + options.getEncryptedFields().get().toDocument() - .toJson(JsonWriterSettings.builder().indent(true).build())); - - // Document document = personSchema.toDocument(); - // System.out.println("document.toJson(): " + - // document.toJson(JsonWriterSettings.builder().indent(true).build())); - // - // Document fromGenerated = encOptions.getEncryptedFields().get().toDocument(); - // System.out.println(fromGenerated.toJson(JsonWriterSettings.builder().indent(true).build())); - // - // System.out.println("-----"); - // - // System.out.println( encryptedFields.toJson(JsonWriterSettings.builder().indent(true).build())); BsonDocument local = clientEncryption.createEncryptedCollection(database, "test", // new CreateCollectionOptions().encryptedFields(encryptedFields), - // new CreateCollectionOptions().encryptedFields(fromGenerated), new CreateCollectionOptions().encryptedFields(options.getEncryptedFields().get().toDocument()), new CreateEncryptedCollectionParams(LOCAL_KMS_PROVIDER)); - Map x = local.getArray("fields").stream().map(BsonValue::asDocument).collect( + return local.getArray("fields").stream().map(BsonValue::asDocument).collect( Collectors.toMap(field -> field.getString("path").getValue(), field -> field.getBinary("keyId"))); - - HashMap stringBsonBinaryHashMap = new HashMap<>(x); - stringBsonBinaryHashMap.put("name", dataKey1); - return stringBsonBinaryHashMap; } }); + + return new EncryptionKeyHolder(lazyDataKeyMap); + } + + @Bean + MongoEncryptionConverter encryptingConverter(MongoClientEncryption mongoClientEncryption, + EncryptionKeyHolder keyHolder) { return new MongoEncryptionConverter(mongoClientEncryption, EncryptionKeyResolver - .annotated((ctx) -> EncryptionKey.keyId(lazyDataKeyMap.get().get(ctx.getProperty().getFieldName())))); + .annotated((ctx) -> EncryptionKey.keyId(keyHolder.getEncryptionKey(ctx.getProperty().getFieldName())))); } @Bean @@ -309,7 +290,6 @@ class RangeEncryptionTests { builder.autoEncryptionSettings(AutoEncryptionSettings.builder() // .kmsProviders(clientEncryptionSettings.getKmsProviders()) // .keyVaultNamespace(clientEncryptionSettings.getKeyVaultNamespace()) // - // .bypassAutoEncryption(true) .bypassQueryAnalysis(true).build()); } } @@ -362,6 +342,19 @@ class RangeEncryptionTests { } } + static class EncryptionKeyHolder { + + Supplier> lazyDataKeyMap; + + public EncryptionKeyHolder(Supplier> lazyDataKeyMap) { + this.lazyDataKeyMap = Lazy.of(lazyDataKeyMap); + } + + BsonBinary getEncryptionKey(String path) { + return lazyDataKeyMap.get().get(path); + } + } + @org.springframework.data.mongodb.core.mapping.Document("test") static class Person {