From d49b1c33b74b324275c7852c0daa97bc78206d55 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 1 Jul 2019 14:18:04 +0200 Subject: [PATCH] DATAMONGO-2306 - Polishing. Add Nullable annotation to nullable method args. Remove IV from JSON Schema as it is not listed in Mongo specs. Tweak wording in docs. Parse encryption-settings-ref for MongoClientOptions. Add support for KeyId's in encrypted JSON schema properties. Original pull request: #766. --- .../mongodb/config/MongoParsingUtils.java | 2 ++ .../MongoEncryptionSettingsFactoryBean.java | 3 +- .../IdentifiableJsonSchemaProperty.java | 31 ++++++++++++------- .../core/schema/MongoJsonSchemaUnitTests.java | 18 +++++++++++ .../asciidoc/reference/mongo-json-schema.adoc | 4 +-- 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java index 63a294f55..36c2544c4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java @@ -35,6 +35,7 @@ import org.w3c.dom.Element; * @author Oliver Gierke * @author Thomas Darimont * @author Christoph Strobl + * @author Mark Paluch */ @SuppressWarnings("deprecation") abstract class MongoParsingUtils { @@ -92,6 +93,7 @@ abstract class MongoParsingUtils { setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-socket-timeout", "heartbeatSocketTimeout"); setPropertyValue(clientOptionsDefBuilder, optionsElement, "ssl", "ssl"); setPropertyReference(clientOptionsDefBuilder, optionsElement, "ssl-socket-factory-ref", "sslSocketFactory"); + setPropertyReference(clientOptionsDefBuilder, optionsElement, "encryption-settings-ref", "autoEncryptionSettings"); setPropertyValue(clientOptionsDefBuilder, optionsElement, "server-selection-timeout", "serverSelectionTimeout"); mongoClientBuilder.addPropertyValue("mongoClientOptions", clientOptionsDefBuilder.getBeanDefinition()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java index 65cd689c0..f09759039 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java @@ -20,6 +20,7 @@ import java.util.Map; import org.bson.BsonDocument; import org.springframework.beans.factory.FactoryBean; +import org.springframework.lang.Nullable; import com.mongodb.AutoEncryptionSettings; import com.mongodb.MongoClientSettings; @@ -104,7 +105,7 @@ public class MongoEncryptionSettingsFactoryBean implements FactoryBean Map orEmpty(Map source) { + private Map orEmpty(@Nullable Map source) { return source != null ? source : Collections.emptyMap(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java index 1a097f4ab..3b46d6b5f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java @@ -18,9 +18,12 @@ package org.springframework.data.mongodb.core.schema; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; +import java.util.UUID; import org.bson.Document; + import org.springframework.data.domain.Range; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ArrayJsonSchemaObject; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.BooleanJsonSchemaObject; @@ -1057,8 +1060,8 @@ public class IdentifiableJsonSchemaProperty implemen private final JsonSchemaProperty targetProperty; private final @Nullable String algorithm; - private final @Nullable char[] keyId; - private final @Nullable char[] iv; + private final @Nullable String keyId; + private final @Nullable List keyIds; /** * Create new instance of {@link EncryptedJsonSchemaProperty} wrapping the given {@link JsonSchemaProperty target}. @@ -1069,14 +1072,14 @@ public class IdentifiableJsonSchemaProperty implemen this(target, null, null, null); } - private EncryptedJsonSchemaProperty(JsonSchemaProperty target, @Nullable String algorithm, @Nullable char[] keyId, - @Nullable char[] iv) { + private EncryptedJsonSchemaProperty(JsonSchemaProperty target, @Nullable String algorithm, @Nullable String keyId, + @Nullable List keyIds) { Assert.notNull(target, "Target must not be null!"); this.targetProperty = target; this.algorithm = algorithm; this.keyId = keyId; - this.iv = iv; + this.keyIds = keyIds; } /** @@ -1113,19 +1116,23 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link EncryptedJsonSchemaProperty}. */ public EncryptedJsonSchemaProperty algorithm(String algorithm) { - return new EncryptedJsonSchemaProperty(targetProperty, algorithm, keyId, iv); + return new EncryptedJsonSchemaProperty(targetProperty, algorithm, keyId, keyIds); } /** * @param key * @return */ - public EncryptedJsonSchemaProperty keyId(char[] key) { - return new EncryptedJsonSchemaProperty(targetProperty, algorithm, key, iv); + public EncryptedJsonSchemaProperty keyId(String keyId) { + return new EncryptedJsonSchemaProperty(targetProperty, algorithm, keyId, null); } - public EncryptedJsonSchemaProperty keyId(String key) { - return keyId(key.toCharArray()); + /** + * @param keyId + * @return + */ + public EncryptedJsonSchemaProperty keys(UUID... keyId) { + return new EncryptedJsonSchemaProperty(targetProperty, algorithm, null, Arrays.asList(keyId)); } /* @@ -1141,7 +1148,9 @@ public class IdentifiableJsonSchemaProperty implemen Document enc = new Document(); if (!ObjectUtils.isEmpty(keyId)) { - enc.append("keyId", new String(keyId)); + enc.append("keyId", keyId); + } else if (!ObjectUtils.isEmpty(keyIds)) { + enc.append("keyId", keyIds); } Type type = extractPropertyType(propertySpecification); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaUnitTests.java index 7ffbbce36..f10b457db 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaUnitTests.java @@ -19,6 +19,8 @@ import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.*; import static org.springframework.data.mongodb.test.util.Assertions.*; import java.util.Arrays; +import java.util.Collections; +import java.util.UUID; import org.bson.Document; import org.junit.Test; @@ -87,6 +89,22 @@ public class MongoJsonSchemaUnitTests { .append("algorithm", "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").append("bsonType", "string")))))); } + @Test // DATAMONGO-2306 + public void rendersEncryptedPropertyWithKeyIdCorrectly() { + + UUID uuid = UUID.randomUUID(); + MongoJsonSchema schema = MongoJsonSchema.builder().properties( // + encrypted(string("ssn")) // + .aead_aes_256_cbc_hmac_sha_512_deterministic() // + .keys(uuid) // + ).build(); + + assertThat(schema.toDocument()).isEqualTo(new Document("$jsonSchema", + new Document("type", "object").append("properties", + new Document("ssn", new Document("encrypt", new Document("keyId", Collections.singletonList(uuid)) + .append("algorithm", "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").append("bsonType", "string")))))); + } + @Test // DATAMONGO-1835 public void throwsExceptionOnNullRoot() { assertThatIllegalArgumentException().isThrownBy(() -> MongoJsonSchema.of((JsonSchemaObject) null)); diff --git a/src/main/asciidoc/reference/mongo-json-schema.adoc b/src/main/asciidoc/reference/mongo-json-schema.adoc index e4e7f05c5..5a426061a 100644 --- a/src/main/asciidoc/reference/mongo-json-schema.adoc +++ b/src/main/asciidoc/reference/mongo-json-schema.adoc @@ -208,7 +208,7 @@ template.find(query(matchingDocumentStructure(schema)), Person.class); [[mongo.jsonSchema.encrypted-fields]] ==== Encrypted Fields -MongoDB 4.2 https://docs.mongodb.com/master/core/security-client-side-encryption/[Field Level Encryption] allows to directly secure certain properties. +MongoDB 4.2 https://docs.mongodb.com/master/core/security-client-side-encryption/[Field Level Encryption] allows to directly encrypt individual properties. Properties can be wrapped within an encrypted property when setting up the JSON Schema as shown in the example below. @@ -225,7 +225,7 @@ MongoJsonSchema schema = MongoJsonSchema.builder() ---- ==== -NOTE: Make sure to set the drivers `com.mongodb.AutoEncryptionSettings` to use client side encryption. +NOTE: Make sure to set the drivers `com.mongodb.AutoEncryptionSettings` to use client-side encryption. MongoDB does not support encryption for all field types. Specific data types require deterministic encryption to preserve equality comparison functionality. [[mongo.jsonSchema.types]] ==== JSON Schema Types