Skip to content

Commit 13f5c5d

Browse files
committed
Support text indexes with encryption
JAVA-5851 JAVA-5903 JAVA-5924
1 parent 6dd96da commit 13f5c5d

File tree

21 files changed

+974
-138
lines changed

21 files changed

+974
-138
lines changed

driver-core/src/main/com/mongodb/client/model/vault/EncryptOptions.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.mongodb.client.model.vault;
1818

19+
import com.mongodb.annotations.Alpha;
20+
import com.mongodb.annotations.Reason;
1921
import com.mongodb.lang.Nullable;
2022
import org.bson.BsonBinary;
2123

@@ -31,6 +33,7 @@ public class EncryptOptions {
3133
private Long contentionFactor;
3234
private String queryType;
3335
private RangeOptions rangeOptions;
36+
private TextOptions textOptions;
3437

3538
/**
3639
* Construct an instance with the given algorithm.
@@ -51,8 +54,13 @@ public EncryptOptions(final String algorithm) {
5154
* <li>Indexed</li>
5255
* <li>Unindexed</li>
5356
* <li>Range</li>
57+
* <li>TextPreview</li>
5458
* </ul>
5559
*
60+
* <p>The "TextPreview" algorithm is in preview and should be used for experimental workloads only.
61+
* These features are unstable and their security is not guaranteed until released as Generally Available (GA).
62+
* The GA version of these features may not be backwards compatible with the preview version.</p>
63+
*
5664
* @return the encryption algorithm
5765
*/
5866
public String getAlgorithm() {
@@ -141,8 +149,8 @@ public Long getContentionFactor() {
141149
/**
142150
* The QueryType.
143151
*
144-
* <p>Currently, we support only "equality" or "range" queryType.</p>
145-
* <p>It is an error to set queryType when the algorithm is not "Indexed" or "Range".</p>
152+
* <p>Currently, we support only "equality", "range", "prefixPreview", "suffixPreview" or "substringPreview" queryType.</p>
153+
* <p>It is an error to set queryType when the algorithm is not "Indexed", "Range" or "TextPreview".</p>
146154
* @param queryType the query type
147155
* @return this
148156
* @since 4.7
@@ -194,6 +202,36 @@ public RangeOptions getRangeOptions() {
194202
return rangeOptions;
195203
}
196204

205+
/**
206+
* The TextOptions
207+
*
208+
* <p>It is an error to set TextOptions when the algorithm is not "TextPreview".
209+
* @param textOptions the text options
210+
* @return this
211+
* @since 5.6
212+
* @mongodb.server.release 8.2
213+
* @mongodb.driver.manual /core/queryable-encryption/ queryable encryption
214+
*/
215+
@Alpha(Reason.SERVER)
216+
public EncryptOptions textOptions(@Nullable final TextOptions textOptions) {
217+
this.textOptions = textOptions;
218+
return this;
219+
}
220+
221+
/**
222+
* Gets the TextOptions
223+
* @see #textOptions(TextOptions)
224+
* @return the text options or null if not set
225+
* @since 5.6
226+
* @mongodb.server.release 8.2
227+
* @mongodb.driver.manual /core/queryable-encryption/ queryable encryption
228+
*/
229+
@Alpha(Reason.SERVER)
230+
@Nullable
231+
public TextOptions getTextOptions() {
232+
return textOptions;
233+
}
234+
197235
@Override
198236
public String toString() {
199237
return "EncryptOptions{"
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.client.model.vault;
18+
19+
import com.mongodb.annotations.Alpha;
20+
import com.mongodb.annotations.Reason;
21+
import com.mongodb.lang.Nullable;
22+
import org.bson.BsonDocument;
23+
24+
/**
25+
* Text options for a Queryable Encryption field that supports text queries.
26+
*
27+
* <p>Note: TextOptions is in Alpha and subject to backwards breaking changes.
28+
*
29+
* @since 5.6
30+
* @mongodb.server.release 8.2
31+
* @mongodb.driver.manual /core/queryable-encryption/ queryable encryption
32+
*/
33+
@Alpha(Reason.SERVER)
34+
public class TextOptions {
35+
private Boolean caseSensitive;
36+
private Boolean diacriticSensitive;
37+
@Nullable
38+
private BsonDocument prefixOptions;
39+
@Nullable
40+
private BsonDocument suffixOptions;
41+
@Nullable
42+
private BsonDocument substringOptions;
43+
44+
/**
45+
* Construct a new instance
46+
*/
47+
public TextOptions() {
48+
}
49+
50+
/**
51+
* @return true if text indexes for this field are case sensitive.
52+
*/
53+
public boolean getCaseSensitive() {
54+
return caseSensitive;
55+
}
56+
57+
/**
58+
* Set case sensitivity
59+
*
60+
* @param caseSensitive true if text indexes are case sensitive
61+
* @return this
62+
*/
63+
public TextOptions caseSensitive(final boolean caseSensitive) {
64+
this.caseSensitive = caseSensitive;
65+
return this;
66+
}
67+
68+
/**
69+
* @return true if text indexes are diacritic sensitive
70+
*/
71+
public boolean getDiacriticSensitive() {
72+
return diacriticSensitive;
73+
}
74+
75+
/**
76+
* Set diacritic sensitivity
77+
*
78+
* @param diacriticSensitive true if text indexes are diacritic sensitive
79+
* @return this
80+
*/
81+
public TextOptions diacriticSensitive(final boolean diacriticSensitive) {
82+
this.diacriticSensitive = diacriticSensitive;
83+
return this;
84+
}
85+
86+
/**
87+
* Set the prefix options.
88+
*
89+
* <p>Expected to be a {@link BsonDocument} in the format of:</p>
90+
*
91+
* <pre>
92+
* {@code
93+
* {
94+
* // strMinQueryLength is the minimum allowed query length. Querying with a shorter string will error.
95+
* strMinQueryLength: BsonInt32,
96+
* // strMaxQueryLength is the maximum allowed query length. Querying with a longer string will error.
97+
* strMaxQueryLength: BsonInt32
98+
* }
99+
* }
100+
* </pre>
101+
*
102+
* @param prefixOptions the prefix options or null
103+
* @return this
104+
*/
105+
public TextOptions prefixOptions(@Nullable final BsonDocument prefixOptions) {
106+
this.prefixOptions = prefixOptions;
107+
return this;
108+
}
109+
110+
/**
111+
* @see #prefixOptions(BsonDocument)
112+
* @return the prefix options document or null
113+
*/
114+
@Nullable
115+
public BsonDocument getPrefixOptions() {
116+
return prefixOptions;
117+
}
118+
119+
/**
120+
* Set the suffix options.
121+
*
122+
* <p>Expected to be a {@link BsonDocument} in the format of:</p>
123+
*
124+
* <pre>
125+
* {@code
126+
* {
127+
* // strMinQueryLength is the minimum allowed query length. Querying with a shorter string will error.
128+
* strMinQueryLength: BsonInt32,
129+
* // strMaxQueryLength is the maximum allowed query length. Querying with a longer string will error.
130+
* strMaxQueryLength: BsonInt32
131+
* }
132+
* }
133+
* </pre>
134+
*
135+
* @param suffixOptions the suffix options or null
136+
* @return this
137+
*/
138+
public TextOptions suffixOptions(@Nullable final BsonDocument suffixOptions) {
139+
this.suffixOptions = suffixOptions;
140+
return this;
141+
}
142+
143+
/**
144+
* @see #suffixOptions(BsonDocument)
145+
* @return the suffix options document or null
146+
*/
147+
@Nullable
148+
public BsonDocument getSuffixOptions() {
149+
return suffixOptions;
150+
}
151+
152+
/**
153+
* Set the substring options.
154+
*
155+
* <p>Expected to be a {@link BsonDocument} in the format of:</p>
156+
*
157+
* <pre>
158+
* {@code
159+
* {
160+
* // strMaxLength is the maximum allowed length to insert. Inserting longer strings will error.
161+
* strMaxLength: BsonInt32,
162+
* // strMinQueryLength is the minimum allowed query length. Querying with a shorter string will error.
163+
* strMinQueryLength: BsonInt32,
164+
* // strMaxQueryLength is the maximum allowed query length. Querying with a longer string will error.
165+
* strMaxQueryLength: BsonInt32
166+
* }
167+
* }
168+
* </pre>
169+
*
170+
* @param substringOptions the substring options or null
171+
* @return this
172+
*/
173+
public TextOptions substringOptions(@Nullable final BsonDocument substringOptions) {
174+
this.substringOptions = substringOptions;
175+
return this;
176+
}
177+
178+
/**
179+
* @see #substringOptions(BsonDocument)
180+
* @return the substring options document or null
181+
*/
182+
@Nullable
183+
public BsonDocument getSubstringOptions() {
184+
return substringOptions;
185+
}
186+
187+
}

driver-core/src/main/com/mongodb/internal/client/vault/EncryptOptionsHelper.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
import com.mongodb.client.model.vault.EncryptOptions;
1919
import com.mongodb.client.model.vault.RangeOptions;
20+
import com.mongodb.client.model.vault.TextOptions;
2021
import com.mongodb.internal.crypt.capi.MongoExplicitEncryptOptions;
22+
import org.bson.BsonBoolean;
2123
import org.bson.BsonDocument;
2224
import org.bson.BsonInt32;
2325
import org.bson.BsonInt64;
@@ -70,6 +72,30 @@ public static MongoExplicitEncryptOptions asMongoExplicitEncryptOptions(final En
7072
}
7173
encryptOptionsBuilder.rangeOptions(rangeOptionsBsonDocument);
7274
}
75+
76+
TextOptions textOptions = options.getTextOptions();
77+
if (textOptions != null) {
78+
BsonDocument textOptionsDocument = new BsonDocument();
79+
textOptionsDocument.put("caseSensitive", BsonBoolean.valueOf(textOptions.getCaseSensitive()));
80+
textOptionsDocument.put("diacriticSensitive", BsonBoolean.valueOf(textOptions.getDiacriticSensitive()));
81+
82+
BsonDocument substringOptions = textOptions.getSubstringOptions();
83+
if (substringOptions != null) {
84+
textOptionsDocument.put("substring", substringOptions);
85+
}
86+
87+
BsonDocument prefixOptions = textOptions.getPrefixOptions();
88+
if (prefixOptions != null) {
89+
textOptionsDocument.put("prefix", prefixOptions);
90+
}
91+
92+
BsonDocument suffixOptions = textOptions.getSuffixOptions();
93+
if (suffixOptions != null) {
94+
textOptionsDocument.put("suffix", suffixOptions);
95+
}
96+
encryptOptionsBuilder.textOptions(textOptionsDocument);
97+
}
98+
7399
return encryptOptionsBuilder.build();
74100
}
75101
private EncryptOptionsHelper() {

driver-core/src/test/functional/com/mongodb/ClusterFixture.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import com.mongodb.internal.connection.StreamFactoryFactory;
6565
import com.mongodb.internal.connection.TlsChannelStreamFactoryFactory;
6666
import com.mongodb.internal.connection.netty.NettyStreamFactoryFactory;
67+
import com.mongodb.internal.crypt.capi.CAPI;
6768
import com.mongodb.internal.operation.BatchCursor;
6869
import com.mongodb.internal.operation.CommandReadOperation;
6970
import com.mongodb.internal.operation.DropDatabaseOperation;
@@ -148,6 +149,7 @@ public final class ClusterFixture {
148149
private static final Map<ReadPreference, ReadWriteBinding> BINDING_MAP = new HashMap<>();
149150
private static final Map<ReadPreference, AsyncReadWriteBinding> ASYNC_BINDING_MAP = new HashMap<>();
150151

152+
private static ServerVersion mongoCryptVersion;
151153
private static ServerVersion serverVersion;
152154
private static BsonDocument serverParameters;
153155

@@ -181,6 +183,13 @@ public static ClusterDescription getClusterDescription(final Cluster cluster) {
181183
}
182184
}
183185

186+
public static ServerVersion getMongoCryptVersion() {
187+
if (mongoCryptVersion == null) {
188+
mongoCryptVersion = new ServerVersion(getVersionList(CAPI.mongocrypt_version(null).toString()));
189+
}
190+
return mongoCryptVersion;
191+
}
192+
184193
public static ServerVersion getServerVersion() {
185194
if (serverVersion == null) {
186195
serverVersion = getVersion(new CommandReadOperation<>("admin",

driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474

7575
import static com.mongodb.ClusterFixture.executeAsync;
7676
import static com.mongodb.ClusterFixture.getBinding;
77+
import static java.lang.String.format;
7778
import static java.util.Arrays.asList;
7879
import static java.util.Collections.singletonList;
7980

@@ -154,6 +155,16 @@ public void drop(final WriteConcern writeConcern) {
154155
drop(namespace, writeConcern);
155156
}
156157

158+
public void dropAndCreate(final BsonDocument createOptions) {
159+
// Drop the collection and any encryption collections: enxcol_.<collectionName>.esc and enxcol_.<collectionName>.ecoc
160+
drop(namespace, WriteConcern.MAJORITY);
161+
drop(new MongoNamespace(namespace.getDatabaseName(), format("enxcol_.%s.esc", namespace.getCollectionName())),
162+
WriteConcern.MAJORITY);
163+
drop(new MongoNamespace(namespace.getDatabaseName(), format("enxcol_.%s.ecoc", namespace.getCollectionName())),
164+
WriteConcern.MAJORITY);
165+
create(WriteConcern.MAJORITY, createOptions);
166+
}
167+
157168
public void create() {
158169
create(namespace.getCollectionName(), new CreateCollectionOptions(), WriteConcern.ACKNOWLEDGED);
159170
}
Submodule specifications updated 213 files

0 commit comments

Comments
 (0)