From a83ae2f92def17cc0b2acbfa8d43d622aefdf0d4 Mon Sep 17 00:00:00 2001 From: seonghyeoklee Date: Wed, 4 Mar 2026 22:58:35 +0900 Subject: [PATCH] Add collection variable to @Encrypted SpEL context. Supply the collection name as a #collection variable in the SpEL evaluation context for @Encrypted keyId expressions. This allows using different encryption keys per collection. The #collection variable is now available in both entity-level and property-level @Encrypted annotations. Closes #4304 Signed-off-by: seonghyeoklee --- .../mapping/BasicMongoPersistentEntity.java | 1 + .../mapping/BasicMongoPersistentProperty.java | 3 + ...appingMongoJsonSchemaCreatorUnitTests.java | 65 +++++++++++++++++++ 3 files changed, 69 insertions(+) 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 93f29a1a7..e90c34b71 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 @@ -382,6 +382,7 @@ public class BasicMongoPersistentEntity extends BasicPersistentEntity evaluationContext = Lazy.of(() -> { EvaluationContext ctx = getEvaluationContext(null); ctx.setVariable("target", getOwner().getType().getSimpleName() + "." + getName()); + if (getOwner() instanceof MongoPersistentEntity mongoPersistentEntity) { + ctx.setVariable("collection", mongoPersistentEntity.getCollection()); + } return ctx; }); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java index cf4574f5a..6005bd8a0 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java @@ -164,6 +164,24 @@ class MappingMongoJsonSchemaCreatorUnitTests { assertThat(schema.schemaDocument().toBsonDocument()).isEqualTo(BsonDocument.parse(ENC_FROM_METHOD_SCHEMA)); } + @Test // GH-4304 + void csfleWithKeyFromCollection() { + + GenericApplicationContext applicationContext = new GenericApplicationContext(); + applicationContext.registerBean("encryptionExtension", EncryptionExtension.class, () -> new EncryptionExtension()); + applicationContext.refresh(); + + MongoMappingContext mappingContext = new MongoMappingContext(); + mappingContext.setApplicationContext(applicationContext); + mappingContext.afterPropertiesSet(); + + MongoJsonSchema schema = MongoJsonSchemaCreator.create(mappingContext) // + .filter(MongoJsonSchemaCreator.encryptedOnly()) // + .createSchemaFor(EncryptionMetadataWithCollection.class); + + assertThat(schema.schemaDocument().toBsonDocument()).isEqualTo(BsonDocument.parse(ENC_FROM_COLLECTION_SCHEMA)); + } + // --> Combining Schemas and Properties @Test // GH-3870 @@ -648,6 +666,49 @@ class MappingMongoJsonSchemaCreatorUnitTests { String provider; } + static final String ENC_FROM_COLLECTION_KEY = "R2Vkm1DOCM6sBplG+MZYOQ=="; + static final String ENC_FROM_COLLECTION_SCHEMA = "{" + // + " 'encryptMetadata': {" + // + " 'keyId': [" + // + " {" + // + " '$binary': {" + // + " 'base64': '" + ENC_FROM_COLLECTION_KEY + "'," + // + " 'subType': '04'" + // + " }" + // + " }" + // + " ]" + // + " }," + // + " 'type': 'object'," + // + " 'properties': {" + // + " 'policyNumber': {" + // + " 'encrypt': {" + // + " 'keyId': [" + // + " [" + // + " {" + // + " '$binary': {" + // + " 'base64': '" + ENC_FROM_COLLECTION_KEY + "'," + // + " 'subType': '04'" + // + " }" + // + " }" + // + " ]" + // + " ]," + // + " 'bsonType': 'int'," + // + " 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'" + // + " }" + // + " }" + // + " }" + // + "}"; + + @org.springframework.data.mongodb.core.mapping.Document("patients") + @Encrypted(keyId = "#{mongocrypt.keyId(#collection)}") + static class EncryptionMetadataWithCollection { + + @Encrypted(keyId = "#{mongocrypt.keyId(#collection)}", algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic") // + Integer policyNumber; + + String provider; + } + public static class EncryptionExtension implements EvaluationContextExtension { @Override @@ -685,6 +746,10 @@ class MappingMongoJsonSchemaCreatorUnitTests { return ENC_FROM_METHOD_PROPOERTY_KEY; } + if (target.equals("patients")) { + return ENC_FROM_COLLECTION_KEY; + } + return "xKVup8B1Q+CkHaVRx+qa+g=="; } }