Update documentation
This commit is contained in:
@@ -19,6 +19,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
@@ -29,6 +30,7 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
|
||||
import org.springframework.data.mongodb.core.mapping.Unwrapped.Nullable;
|
||||
import org.springframework.data.mongodb.core.schema.JsonSchemaProperty;
|
||||
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -60,6 +62,7 @@ import org.springframework.util.Assert;
|
||||
* {@link org.bson.types.ObjectId} like {@link String} will be mapped to {@code type : 'object'} unless there is more
|
||||
* specific information available via the {@link org.springframework.data.mongodb.core.mapping.MongoId} annotation.
|
||||
* </p>
|
||||
* {@link Encrypted} properties will contain {@literal encrypt} information.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.2
|
||||
@@ -83,21 +86,43 @@ public interface MongoJsonSchemaCreator {
|
||||
*/
|
||||
MongoJsonSchemaCreator filter(Predicate<JsonSchemaPropertyContext> filter);
|
||||
|
||||
/**
|
||||
* The context in which a specific {@link #getProperty()} is encountered during schema creation.
|
||||
*
|
||||
* @since 3.3
|
||||
*/
|
||||
interface JsonSchemaPropertyContext {
|
||||
|
||||
/**
|
||||
* The path to a given field/property in dot notation.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* The current property.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
MongoPersistentProperty getProperty();
|
||||
|
||||
/**
|
||||
* Obtain the {@link MongoPersistentEntity} for a given property.
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @param <T>
|
||||
* @return {@literal null} if the property is not an entity. It is nevertheless recommend to check
|
||||
* {@link PersistentProperty#isEntity()} first.
|
||||
*/
|
||||
@Nullable
|
||||
<T> MongoPersistentEntity<T> resolveEntity(MongoPersistentProperty property);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter {@link Predicate} that matches
|
||||
* {@link org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty
|
||||
* encrypted properties} and those having nested ones.
|
||||
*
|
||||
* A filter {@link Predicate} that matches {@link Encrypted encrypted properties} and those having nested ones.
|
||||
*
|
||||
* @return new instance of {@link Predicate}.
|
||||
* @since 3.3
|
||||
*/
|
||||
@@ -105,7 +130,8 @@ public interface MongoJsonSchemaCreator {
|
||||
|
||||
return new Predicate<JsonSchemaPropertyContext>() {
|
||||
|
||||
Set<MongoPersistentProperty> seen = new HashSet<>();
|
||||
// cycle guard
|
||||
private final Set<MongoPersistentProperty> seen = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public boolean test(JsonSchemaPropertyContext context) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.lang.annotation.Target;
|
||||
* {@literal encryptMetadata}.
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Document
|
||||
* @Encrypted(keyId = "4fPYFM9qSgyRAjgQ2u+IMQ==")
|
||||
* public class Patient {
|
||||
* private ObjectId id;
|
||||
@@ -85,12 +86,27 @@ import java.lang.annotation.Target;
|
||||
public @interface Encrypted {
|
||||
|
||||
/**
|
||||
* @return the key id to use. May contain a parsable {@link org.springframework.expression.Expression expression}.
|
||||
* Get the {@code keyId} to use. The value must resolve to either the UUID representation of the key or a base64
|
||||
* encoded value representing the UUID value.
|
||||
* <p />
|
||||
* On {@link ElementType#TYPE} level the {@link #keyId()} can be left empty if explicitly set for fields. <br />
|
||||
* On {@link ElementType#FIELD} level the {@link #keyId()} can be left empty if inherited from
|
||||
* {@literal encryptMetadata}.
|
||||
*
|
||||
* @return the key id to use. May contain a parsable {@link org.springframework.expression.Expression expression}. In
|
||||
* this case the {@code #target} variable will hold the target element name.
|
||||
*/
|
||||
String[] keyId() default {};
|
||||
|
||||
/**
|
||||
* @return the algorithm.
|
||||
* Set the algorithm to use.
|
||||
* <p />
|
||||
* On {@link ElementType#TYPE} level the {@link #algorithm()} can be left empty if explicitly set for fields. <br />
|
||||
* On {@link ElementType#FIELD} level the {@link #algorithm()} can be left empty if inherited from
|
||||
* {@literal encryptMetadata}.
|
||||
*
|
||||
* @return the encryption algorithm.
|
||||
* @see org.springframework.data.mongodb.core.EncryptionAlgorithms
|
||||
*/
|
||||
String algorithm() default "";
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ public interface MongoJsonSchema {
|
||||
* {@link org.springframework.data.mongodb.core.convert.JsonSchemaMapper} to apply field name customization.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 3.3
|
||||
*/
|
||||
Document schemaDocument();
|
||||
|
||||
|
||||
@@ -225,6 +225,110 @@ MongoJsonSchema schema = MongoJsonSchema.builder()
|
||||
----
|
||||
====
|
||||
|
||||
Instead of defining encrypted fields manually it is possible leverage the `@Encrypted` annotation as shown in the snippet below.
|
||||
|
||||
.Client-Side Field Level Encryption via Json Schema
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Document
|
||||
@Encrypted(keyId = "xKVup8B1Q+CkHaVRx+qa+g==", algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Random") <1>
|
||||
static class Patient {
|
||||
|
||||
@Id String id;
|
||||
String name;
|
||||
|
||||
@Encrypted <2>
|
||||
String bloodType;
|
||||
|
||||
@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic") <3>
|
||||
Integer ssn;
|
||||
|
||||
}
|
||||
----
|
||||
<1> Default encryption settings that will be set for `encryptMetadata`.
|
||||
<2> Encrypted field using default encryption settings.
|
||||
<3> Encrypted field overriding the default encryption algorithm.
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The `@EncryptedAnnoation` supports resolving keyIds via SpEL Expressions.
|
||||
To do so additional environment metadata (via the `MappingContext`) is required and must be provided.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Document
|
||||
@Encrypted(keyId = "#{mongocrypt.keyId(#target)}")
|
||||
static class Patient {
|
||||
|
||||
@Id String id;
|
||||
String name;
|
||||
|
||||
@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Random")
|
||||
String bloodType;
|
||||
|
||||
@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic")
|
||||
Integer ssn;
|
||||
}
|
||||
|
||||
MongoJsonSchemaCreator schemaCreator = MongoJsonSchemaCreator.create(mappingContext);
|
||||
MongoJsonSchema personSchema = schemaCreator
|
||||
.filter(MongoJsonSchemaCreator.encryptedOnly())
|
||||
.createSchemaFor(Patient.class);
|
||||
----
|
||||
|
||||
The `mongocrypt.keyId` function is defined via an `EvaluationContextExtension` as shown in the snippet below.
|
||||
Providing a custom extension provides the most flexible way of computing keyIds.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public class EncryptionExtension implements EvaluationContextExtension {
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "mongocrypt";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Function> getFunctions() {
|
||||
return Collections.singletonMap("keyId", new Function(getMethod("computeKeyId", String.class), this));
|
||||
}
|
||||
|
||||
public String computeKeyId(String target) {
|
||||
// ... lookup via target element name
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
To combine derived encryption settings with `AutoEncryptionSettings` in a Spring Boot application use the `MongoClientSettingsBuilderCustomizer`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
MongoClientSettingsBuilderCustomizer customizer(MappingContext mappingContext) {
|
||||
return (builder) -> {
|
||||
|
||||
// ... keyVaultCollection, kmsProvider, ...
|
||||
|
||||
MongoJsonSchemaCreator schemaCreator = MongoJsonSchemaCreator.create(mappingContext);
|
||||
MongoJsonSchema patientSchema = schemaCreator
|
||||
.filter(MongoJsonSchemaCreator.encryptedOnly())
|
||||
.createSchemaFor(Patient.class);
|
||||
|
||||
AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder()
|
||||
.keyVaultNamespace(keyVaultCollection)
|
||||
.kmsProviders(kmsProviders)
|
||||
.extraOptions(extraOpts)
|
||||
.schemaMap(Collections.singletonMap("db.patient", patientSchema.schemaDocument().toBsonDocument()))
|
||||
.build();
|
||||
|
||||
builder.autoEncryptionSettings(autoEncryptionSettings);
|
||||
};
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
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]]
|
||||
|
||||
Reference in New Issue
Block a user