diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java index e0a741afe4..919d963cc7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java @@ -26,7 +26,6 @@ import org.bson.BsonDocument; import org.bson.BsonValue; import org.bson.Document; -import org.bson.conversions.Bson; import org.bson.types.Binary; import org.springframework.core.CollectionFactory; import org.springframework.data.mongodb.core.convert.MongoConversionContext; @@ -96,7 +95,7 @@ public Object decrypt(Object encryptedValue, EncryptionContext context) { if (!persistentProperty.isEntity()) { Collection collection = CollectionFactory.createCollection(persistentProperty.getType(), size); iterable.forEach(it -> { - if(it instanceof BsonValue bsonValue) { + if (it instanceof BsonValue bsonValue) { collection.add(BsonUtils.toJavaType(bsonValue)); } else { collection.add(context.read(it, persistentProperty.getActualType())); @@ -107,7 +106,7 @@ public Object decrypt(Object encryptedValue, EncryptionContext context) { } else { Collection collection = CollectionFactory.createCollection(persistentProperty.getType(), size); iterable.forEach(it -> { - if(it instanceof BsonValue bsonValue) { + if (it instanceof BsonValue bsonValue) { collection.add(context.read(BsonUtils.toJavaType(bsonValue), persistentProperty.getActualType())); } else { collection.add(context.read(it, persistentProperty.getActualType())); @@ -118,14 +117,14 @@ public Object decrypt(Object encryptedValue, EncryptionContext context) { } if (!persistentProperty.isEntity() && persistentProperty.isMap()) { - if(persistentProperty.getType() != Document.class) { - if(decryptedValue instanceof BsonValue bsonValue) { + if (persistentProperty.getType() != Document.class) { + if (decryptedValue instanceof BsonValue bsonValue) { return new LinkedHashMap<>((Document) BsonUtils.toJavaType(bsonValue)); } - if(decryptedValue instanceof Document document) { + if (decryptedValue instanceof Document document) { return new LinkedHashMap<>(document); } - if(decryptedValue instanceof Map map) { + if (decryptedValue instanceof Map map) { return map; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java index 8321e28894..6858156257 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java @@ -287,36 +287,22 @@ static void removeFrom(Bson bson, String key) { */ public static Object toJavaType(BsonValue value) { - switch (value.getBsonType()) { - case INT32: - return value.asInt32().getValue(); - case INT64: - return value.asInt64().getValue(); - case STRING: - return value.asString().getValue(); - case DECIMAL128: - return value.asDecimal128().doubleValue(); - case DOUBLE: - return value.asDouble().getValue(); - case BOOLEAN: - return value.asBoolean().getValue(); - case OBJECT_ID: - return value.asObjectId().getValue(); - case DB_POINTER: - return new DBRef(value.asDBPointer().getNamespace(), value.asDBPointer().getId()); - case BINARY: - return value.asBinary().getData(); - case DATE_TIME: - return new Date(value.asDateTime().getValue()); - case SYMBOL: - return value.asSymbol().getSymbol(); - case ARRAY: - return value.asArray().toArray(); - case DOCUMENT: - return Document.parse(value.asDocument().toJson()); - default: - return value; - } + return switch (value.getBsonType()) { + case INT32 -> value.asInt32().getValue(); + case INT64 -> value.asInt64().getValue(); + case STRING -> value.asString().getValue(); + case DECIMAL128 -> value.asDecimal128().doubleValue(); + case DOUBLE -> value.asDouble().getValue(); + case BOOLEAN -> value.asBoolean().getValue(); + case OBJECT_ID -> value.asObjectId().getValue(); + case DB_POINTER -> new DBRef(value.asDBPointer().getNamespace(), value.asDBPointer().getId()); + case BINARY -> value.asBinary().getData(); + case DATE_TIME -> new Date(value.asDateTime().getValue()); + case SYMBOL -> value.asSymbol().getSymbol(); + case ARRAY -> value.asArray().toArray(); + case DOCUMENT -> Document.parse(value.asDocument().toJson()); + default -> value; + }; } /** @@ -341,6 +327,7 @@ public static BsonValue simpleToBsonValue(Object source) { * @throws IllegalArgumentException if {@literal source} does not correspond to a {@link BsonValue} type. * @since 4.2 */ + @SuppressWarnings("unchecked") public static BsonValue simpleToBsonValue(Object source, CodecRegistry codecRegistry) { if (source instanceof BsonValue bsonValue) { @@ -407,7 +394,7 @@ public static BsonValue simpleToBsonValue(Object source, CodecRegistry codecRegi /** * Merge the given {@link Document documents} into on in the given order. Keys contained within multiple documents are - * overwritten by their follow ups. + * overwritten by their follow-ups. * * @param documents must not be {@literal null}. Can be empty. * @return the document containing all key value pairs. @@ -730,8 +717,9 @@ private static String serializeValue(@Nullable Object value) { private static String toString(Map source) { + // Avoid String.format for performance return iterableToDelimitedString(source.entrySet(), "{ ", " }", - entry -> String.format("\"%s\" : %s", entry.getKey(), toJson(entry.getValue()))); + entry -> "\"" + entry.getKey() + "\" : " + toJson(entry.getValue())); } private static String toString(Collection source) { @@ -748,12 +736,13 @@ private static String iterableToDelimitedString(Iterable source, String p return joiner.toString(); } - private static class BsonCapturingWriter extends AbstractBsonWriter { + static class BsonCapturingWriter extends AbstractBsonWriter { - List values = new ArrayList<>(0); + private final List values = new ArrayList<>(0); public BsonCapturingWriter(Class type) { super(new BsonWriterSettings()); + if (ClassUtils.isAssignable(Map.class, type)) { setContext(new Context(null, BsonContextType.DOCUMENT)); } else if (ClassUtils.isAssignable(List.class, type) || type.isArray()) { @@ -763,6 +752,7 @@ public BsonCapturingWriter(Class type) { } } + @Nullable BsonValue getCapturedValue() { if (values.isEmpty()) { @@ -852,18 +842,14 @@ protected void doWriteJavaScript(String value) { @Override protected void doWriteJavaScriptWithScope(String value) { - values.add(new BsonJavaScriptWithScope(value, null)); + throw new UnsupportedOperationException("Cannot capture JavaScriptWith"); } @Override - protected void doWriteMaxKey() { - - } + protected void doWriteMaxKey() {} @Override - protected void doWriteMinKey() { - - } + protected void doWriteMinKey() {} @Override protected void doWriteNull() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/AbstractEncryptionTestBase.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/AbstractEncryptionTestBase.java index 606921b57b..473fad3d3f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/AbstractEncryptionTestBase.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/AbstractEncryptionTestBase.java @@ -25,7 +25,6 @@ import java.time.Month; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -34,14 +33,6 @@ import java.util.function.Function; import java.util.function.Supplier; -import com.mongodb.ClientEncryptionSettings; -import com.mongodb.ConnectionString; -import com.mongodb.MongoClientSettings; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.model.Filters; -import com.mongodb.client.model.IndexOptions; -import com.mongodb.client.model.Indexes; -import com.mongodb.client.vault.ClientEncryptions; import org.assertj.core.api.Assertions; import org.bson.BsonBinary; import org.bson.Document; @@ -61,13 +52,21 @@ import org.springframework.data.mongodb.core.convert.encryption.MongoEncryptionConverter; import org.springframework.data.mongodb.core.mapping.ExplicitEncrypted; import org.springframework.data.mongodb.core.query.Update; +import org.springframework.data.util.Lazy; +import com.mongodb.ClientEncryptionSettings; +import com.mongodb.ConnectionString; +import com.mongodb.MongoClientSettings; import com.mongodb.MongoNamespace; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; +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.vault.ClientEncryption; -import org.springframework.data.util.Lazy; +import com.mongodb.client.vault.ClientEncryptions; /** * @author Christoph Strobl @@ -87,7 +86,7 @@ void encryptAndDecryptSimpleValue() { verifyThat(source) // .identifiedBy(Person::getId) // - .wasSavedMatching(it -> assertThat(it.get("ssn")).isInstanceOf(Binary.class)) // + .wasSavedMatching(it -> assertThat(it.get("ssn")).isInstanceOf(Binary.class)) // .loadedIsEqualToSource(); } @@ -119,7 +118,7 @@ void encryptAndDecryptComplexValue() { verifyThat(source) // .identifiedBy(Person::getId) // - .wasSavedMatching(it -> assertThat(it.get("address")).isInstanceOf(Binary.class)) // + .wasSavedMatching(it -> assertThat(it.get("address")).isInstanceOf(Binary.class)) // .loadedIsEqualToSource(); } @@ -483,24 +482,17 @@ ClientEncryptionSettings encryptionSettings(MongoClient mongoClient) { MongoCollection collection = mongoClient.getDatabase(getDatabaseName()).getCollection("test"); collection.drop(); // Clear old data - final byte[] localMasterKey = new byte[96]; + byte[] localMasterKey = new byte[96]; new SecureRandom().nextBytes(localMasterKey); - Map> kmsProviders = new HashMap<>() { - { - put("local", new HashMap<>() { - { - put("key", localMasterKey); - } - }); - } - }; + Map> kmsProviders = Map.of("local", Map.of("key", localMasterKey)); // Create the ClientEncryption instance - ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder() + return ClientEncryptionSettings.builder() // .keyVaultMongoClientSettings( - MongoClientSettings.builder().applyConnectionString(new ConnectionString("mongodb://localhost")).build()) - .keyVaultNamespace(keyVaultNamespace.getFullName()).kmsProviders(kmsProviders).build(); - return clientEncryptionSettings; + MongoClientSettings.builder().applyConnectionString(new ConnectionString("mongodb://localhost")).build()) // + .keyVaultNamespace(keyVaultNamespace.getFullName()) // + .kmsProviders(kmsProviders) // + .build(); } } @@ -693,8 +685,7 @@ public String toString() { + ", wallet=" + this.getWallet() + ", address=" + this.getAddress() + ", encryptedZip=" + this.getEncryptedZip() + ", listOfString=" + this.getListOfString() + ", listOfComplex=" + this.getListOfComplex() + ", viaAltKeyNameField=" + this.getViaAltKeyNameField() + ", mapOfString=" - + this.getMapOfString() + ", mapOfComplex=" + this.getMapOfComplex() - + ", today=" + this.getToday() + ")"; + + this.getMapOfString() + ", mapOfComplex=" + this.getMapOfComplex() + ", today=" + this.getToday() + ")"; } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/BypassAutoEncryptionTest.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/BypassAutoEncryptionTest.java index 5ea600393e..90e6a9ba36 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/BypassAutoEncryptionTest.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/BypassAutoEncryptionTest.java @@ -68,9 +68,10 @@ protected void configureClientSettings(Builder builder) { ClientEncryptionSettings clientEncryptionSettings = encryptionSettings(mongoClient); mongoClient.close(); - builder.autoEncryptionSettings( - AutoEncryptionSettings.builder().kmsProviders(clientEncryptionSettings.getKmsProviders()) - .keyVaultNamespace(clientEncryptionSettings.getKeyVaultNamespace()).bypassAutoEncryption(true).build()); + builder.autoEncryptionSettings(AutoEncryptionSettings.builder() // + .kmsProviders(clientEncryptionSettings.getKmsProviders()) // + .keyVaultNamespace(clientEncryptionSettings.getKeyVaultNamespace()) // + .bypassAutoEncryption(true).build()); } @Override @@ -81,6 +82,7 @@ protected void configureConverters(MongoConverterConfigurationAdapter converterC } @Bean + @Override MongoEncryptionConverter encryptingConverter(MongoClientEncryption mongoClientEncryption) { Lazy dataKey = Lazy.of(() -> mongoClientEncryption.getClientEncryption().createDataKey("local", @@ -91,6 +93,7 @@ MongoEncryptionConverter encryptingConverter(MongoClientEncryption mongoClientEn } @Bean + @Override CachingMongoClientEncryption clientEncryption(ClientEncryptionSettings encryptionSettings) { return new CachingMongoClientEncryption(() -> ClientEncryptions.create(encryptionSettings)); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/EncryptionTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/EncryptionTests.java index d790390d49..78a930c731 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/EncryptionTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/EncryptionTests.java @@ -17,7 +17,6 @@ import java.security.SecureRandom; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import org.bson.BsonBinary; @@ -66,6 +65,7 @@ protected String getDatabaseName() { } @Bean + @Override public MongoClient mongoClient() { return super.mongoClient(); } @@ -106,24 +106,17 @@ ClientEncryptionSettings encryptionSettings(MongoClient mongoClient) { MongoCollection collection = mongoClient.getDatabase(getDatabaseName()).getCollection("test"); collection.drop(); // Clear old data - final byte[] localMasterKey = new byte[96]; + byte[] localMasterKey = new byte[96]; new SecureRandom().nextBytes(localMasterKey); - Map> kmsProviders = new HashMap<>() { - { - put("local", new HashMap<>() { - { - put("key", localMasterKey); - } - }); - } - }; + Map> kmsProviders = Map.of("local", Map.of("key", localMasterKey)); // Create the ClientEncryption instance - ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder() + return ClientEncryptionSettings.builder() .keyVaultMongoClientSettings( - MongoClientSettings.builder().applyConnectionString(new ConnectionString("mongodb://localhost")).build()) - .keyVaultNamespace(keyVaultNamespace.getFullName()).kmsProviders(kmsProviders).build(); - return clientEncryptionSettings; + MongoClientSettings.builder().applyConnectionString(new ConnectionString("mongodb://localhost")).build()) // + .keyVaultNamespace(keyVaultNamespace.getFullName()) // + .kmsProviders(kmsProviders) // + .build(); } } }