Skip to content

Commit

Permalink
JAVA: Add support for Range Indexes
Browse files Browse the repository at this point in the history
  • Loading branch information
rozza committed Jan 31, 2023
1 parent 23c274f commit d77f6a0
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,27 @@ public interface mongocrypt_random_fn extends Callback {
public static native boolean
mongocrypt_ctx_setopt_query_type (mongocrypt_ctx_t ctx, cstring query_type, int len);

/**
* Set options for explicit encryption with the "rangePreview" algorithm.
* NOTE: The RangePreview algorithm is experimental only. It is not intended for
* public use.
*
* opts is a BSON document of the form:
* {
* "min": Optional<BSON value>,
* "max": Optional<BSON value>,
* "sparsity": Int64,
* "precision": Optional<Int32>
* }
*
* @param ctx The @ref mongocrypt_ctx_t object.
* @param opts BSON.
* @return A boolean indicating success. If false, an error status is set.
* @since 1.7
*/
public static native boolean
mongocrypt_ctx_setopt_algorithm_range (mongocrypt_ctx_t ctx, mongocrypt_binary_t opts);

/**
* Initialize new @ref mongocrypt_t object.
*
Expand Down Expand Up @@ -825,6 +846,52 @@ public interface mongocrypt_random_fn extends Callback {
mongocrypt_ctx_explicit_encrypt_init (mongocrypt_ctx_t ctx,
mongocrypt_binary_t msg);

/**
* Explicit helper method to encrypt a Match Expression or Aggregate Expression.
* Contexts created for explicit encryption will not go through mongocryptd.
* Requires query_type to be "rangePreview".
* NOTE: The RangePreview algorithm is experimental only. It is not intended for
* public use.
*
* This method expects the passed-in BSON to be of the form:
* { "v" : FLE2RangeFindDriverSpec }
*
* FLE2RangeFindDriverSpec is a BSON document with one of these forms:
*
* 1. A Match Expression of this form:
* {$and: [{<field>: {<op>: <value1>, {<field>: {<op>: <value2> }}]}
* 2. An Aggregate Expression of this form:
* {$and: [{<op>: [<fieldpath>, <value1>]}, {<op>: [<fieldpath>, <value2>]}]
*
* may be $lt, $lte, $gt, or $gte.
*
* The value of "v" is expected to be the BSON value passed to a driver
* ClientEncryption.encryptExpression helper.
*
* Associated options for FLE 1:
* - @ref mongocrypt_ctx_setopt_key_id
* - @ref mongocrypt_ctx_setopt_key_alt_name
* - @ref mongocrypt_ctx_setopt_algorithm
*
* Associated options for Queryable Encryption:
* - @ref mongocrypt_ctx_setopt_key_id
* - @ref mongocrypt_ctx_setopt_index_key_id
* - @ref mongocrypt_ctx_setopt_contention_factor
* - @ref mongocrypt_ctx_setopt_query_type
* - @ref mongocrypt_ctx_setopt_algorithm_range
*
* An error is returned if FLE 1 and Queryable Encryption incompatible options
* are set.
*
* @param ctx A @ref mongocrypt_ctx_t.
* @param msg A @ref mongocrypt_binary_t the plaintext BSON value.
* @return A boolean indicating success.
* @since 1.7
*/
public static native boolean
mongocrypt_ctx_explicit_encrypt_expression_init (mongocrypt_ctx_t ctx,
mongocrypt_binary_t msg);

/**
* Initialize a context for decryption.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

package com.mongodb.crypt.capi;

import org.bson.BsonBinary;
import org.bson.BsonDocument;

import java.io.Closeable;
Expand Down Expand Up @@ -62,6 +61,16 @@ public interface MongoCrypt extends Closeable {
*/
MongoCryptContext createExplicitEncryptionContext(BsonDocument document, MongoExplicitEncryptOptions options);

/**
* Create a context to use for encryption
*
* @param document the document to encrypt, which must be in the form { "v" : BSON value to encrypt }
* @param options the expression encryption options
* @return the context
* @since 1.7
*/
MongoCryptContext createEncryptExpressionContext(BsonDocument document, MongoExplicitEncryptOptions options);

/**
* Create a context to use for encryption
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_decrypt_init;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_encrypt_init;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_explicit_decrypt_init;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_explicit_encrypt_expression_init;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_explicit_encrypt_init;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_new;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_rewrap_many_datakey_init;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_algorithm;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_algorithm_range;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_contention_factor;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_key_alt_name;
import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_key_encryption_key;
Expand Down Expand Up @@ -258,33 +260,23 @@ public MongoCryptContext createDataKeyContext(final String kmsProvider, final Mo
@Override
public MongoCryptContext createExplicitEncryptionContext(final BsonDocument document, final MongoExplicitEncryptOptions options) {
isTrue("open", !closed.get());
mongocrypt_ctx_t context = mongocrypt_ctx_new(wrapped);
if (context == null) {
throwExceptionFromStatus();
}
mongocrypt_ctx_t context = configureExplicitEncryption(options);

if (options.getKeyId() != null) {
try (BinaryHolder keyIdBinaryHolder = toBinary(ByteBuffer.wrap(options.getKeyId().getData()))) {
configure(() -> mongocrypt_ctx_setopt_key_id(context, keyIdBinaryHolder.getBinary()), context);
}
} else if (options.getKeyAltName() != null) {
try (BinaryHolder keyAltNameBinaryHolder = toBinary(new BsonDocument("keyAltName", new BsonString(options.getKeyAltName())))) {
configure(() -> mongocrypt_ctx_setopt_key_alt_name(context, keyAltNameBinaryHolder.getBinary()), context);
}
try (BinaryHolder documentBinaryHolder = toBinary(document)) {
configure(() -> mongocrypt_ctx_explicit_encrypt_init(context, documentBinaryHolder.getBinary()), context);
}

configure(() -> mongocrypt_ctx_setopt_algorithm(context, new cstring(options.getAlgorithm()), -1), context);
if (options.getQueryType() != null) {
configure(() -> mongocrypt_ctx_setopt_query_type(context, new cstring(options.getQueryType()), -1), context);
}
if (options.getContentionFactor() != null) {
configure(() -> mongocrypt_ctx_setopt_contention_factor(context, options.getContentionFactor()), context);
}
return new MongoCryptContextImpl(context);
}

@Override
public MongoCryptContext createEncryptExpressionContext(final BsonDocument document, final MongoExplicitEncryptOptions options) {
isTrue("open", !closed.get());
mongocrypt_ctx_t context = configureExplicitEncryption(options);

try (BinaryHolder documentBinaryHolder = toBinary(document)) {
configure(() -> mongocrypt_ctx_explicit_encrypt_init(context, documentBinaryHolder.getBinary()), context);
configure(() -> mongocrypt_ctx_explicit_encrypt_expression_init(context, documentBinaryHolder.getBinary()), context);
}

return new MongoCryptContextImpl(context);
}

Expand Down Expand Up @@ -339,6 +331,40 @@ public void close() {
}
}

private mongocrypt_ctx_t configureExplicitEncryption(final MongoExplicitEncryptOptions options) {
mongocrypt_ctx_t context = mongocrypt_ctx_new(wrapped);
if (context == null) {
throwExceptionFromStatus();
}

if (options.getKeyId() != null) {
try (BinaryHolder keyIdBinaryHolder = toBinary(ByteBuffer.wrap(options.getKeyId().getData()))) {
configure(() -> mongocrypt_ctx_setopt_key_id(context, keyIdBinaryHolder.getBinary()), context);
}
} else if (options.getKeyAltName() != null) {
try (BinaryHolder keyAltNameBinaryHolder = toBinary(new BsonDocument("keyAltName", new BsonString(options.getKeyAltName())))) {
configure(() -> mongocrypt_ctx_setopt_key_alt_name(context, keyAltNameBinaryHolder.getBinary()), context);
}
}

if (options.getAlgorithm() != null) {
configure(() -> mongocrypt_ctx_setopt_algorithm(context, new cstring(options.getAlgorithm()), -1), context);
}
if (options.getQueryType() != null) {
configure(() -> mongocrypt_ctx_setopt_query_type(context, new cstring(options.getQueryType()), -1), context);
}
if (options.getContentionFactor() != null) {
configure(() -> mongocrypt_ctx_setopt_contention_factor(context, options.getContentionFactor()), context);
}
if (options.getRangeOptions() != null) {
try (BinaryHolder rangeOptionsHolder = toBinary(options.getRangeOptions())) {
configure(() -> mongocrypt_ctx_setopt_algorithm_range(context, rangeOptionsHolder.getBinary()), context);
}
}
return context;
}


private void configure(final Supplier<Boolean> successSupplier) {
if (!successSupplier.get()) {
throwExceptionFromStatus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package com.mongodb.crypt.capi;

import org.bson.BsonBinary;
import org.bson.BsonDocument;

import java.util.Objects;

Expand All @@ -30,6 +31,7 @@ public class MongoExplicitEncryptOptions {
private final String algorithm;
private final Long contentionFactor;
private final String queryType;
private final BsonDocument rangeOptions;

/**
* The builder for the options
Expand All @@ -40,6 +42,7 @@ public static class Builder {
private String algorithm;
private Long contentionFactor;
private String queryType;
private BsonDocument rangeOptions;

private Builder() {
}
Expand Down Expand Up @@ -108,6 +111,20 @@ public Builder queryType(final String queryType) {
return this;
}

/**
* The Range Options.
*
* <p>It is an error to set rangeOptions when the algorithm is not "rangePreview".</p>
*
* @param rangeOptions the range options
* @return this
* @since 1.7
*/
public Builder rangeOptions(final BsonDocument rangeOptions) {
this.rangeOptions = rangeOptions;
return this;
}

/**
* Build the options.
*
Expand Down Expand Up @@ -169,17 +186,27 @@ public String getQueryType() {
return queryType;
}

/**
* Gets the range options
* @return the range options
* @since 1.7
*/
public BsonDocument getRangeOptions() {
return rangeOptions;
}

private MongoExplicitEncryptOptions(Builder builder) {
this.keyId = builder.keyId;
this.keyAltName = builder.keyAltName;
this.algorithm = builder.algorithm;
this.contentionFactor = builder.contentionFactor;
this.queryType = builder.queryType;
if (!Objects.equals(algorithm, "Indexed")) {
this.rangeOptions = builder.rangeOptions;
if (!(Objects.equals(algorithm, "Indexed") || Objects.equals(algorithm, "RangePreview"))) {
if (contentionFactor != null) {
throw new IllegalStateException("Invalid configuration, contentionFactor can only be set if algorithm is 'Indexed'");
throw new IllegalStateException("Invalid configuration, contentionFactor can only be set if algorithm is 'Indexed' or 'RangePreview'");
} else if (queryType != null) {
throw new IllegalStateException("Invalid configuration, queryType can only be set if algorithm is 'Indexed'");
throw new IllegalStateException("Invalid configuration, queryType can only be set if algorithm is 'Indexed' or 'RangePreview'");
}
}
}
Expand All @@ -191,7 +218,8 @@ public String toString() {
", keyAltName='" + keyAltName + '\'' +
", algorithm='" + algorithm + '\'' +
", contentionFactor=" + contentionFactor +
", queryType=" + queryType +
", queryType='" + queryType + '\'' +
", rangeOptions=" + rangeOptions +
'}';
}
}
Loading

0 comments on commit d77f6a0

Please sign in to comment.