Skip to content

Commit 0714f57

Browse files
committed
Add the ClientEncryption.createEncryptedCollection helper method
JAVA-4679
1 parent 617432e commit 0714f57

File tree

18 files changed

+960
-4
lines changed

18 files changed

+960
-4
lines changed

config/spotbugs/exclude.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,18 @@
240240
<Method name="acquirePermitOrGetAvailableOpenedConnection"/>
241241
<Bug pattern="NS_NON_SHORT_CIRCUIT"/>
242242
</Match>
243+
244+
<!-- Can actually be null, but is not annotated as `@Nullable`. Annotating it as such causes warnings
245+
in other places where `null` is not handled, see https://jira.mongodb.org/browse/JAVA-4861.
246+
When the aforementioned ticket is done, it will be clear what to do with the warnings suppressed here. -->
247+
<Match>
248+
<Class name="com.mongodb.client.internal.ClientEncryptionImpl"/>
249+
<Method name="createEncryptedCollection"/>
250+
<Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"/>
251+
</Match>
252+
<Match>
253+
<Class name="com.mongodb.reactivestreams.client.internal.vault.ClientEncryptionImpl"/>
254+
<Method name="~.*createEncryptedCollection.*"/>
255+
<Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"/>
256+
</Match>
243257
</FindBugsFilter>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
package com.mongodb;
17+
18+
import com.mongodb.annotations.Beta;
19+
import org.bson.BsonDocument;
20+
21+
import static com.mongodb.assertions.Assertions.assertNotNull;
22+
23+
/**
24+
* An exception thrown by methods that may automatically create data encryption keys
25+
* where needed based on the {@code encryptedFields} configuration.
26+
*
27+
* @since 4.9
28+
*/
29+
@Beta(Beta.Reason.SERVER)
30+
public final class MongoUpdatedEncryptedFieldsException extends MongoClientException {
31+
private static final long serialVersionUID = 1;
32+
33+
private final BsonDocument encryptedFields;
34+
35+
/**
36+
* Not part of the public API.
37+
*
38+
* @param encryptedFields The (partially) updated {@code encryptedFields} document,
39+
* which allows users to infer which data keys are known to be created before the exception happened
40+
* (see {@link #getEncryptedFields()} for more details).
41+
* Reporting this back to a user may be helpful because creation of a data key includes persisting it in the key vault.
42+
* @param msg The message.
43+
* @param cause The cause.
44+
*/
45+
public MongoUpdatedEncryptedFieldsException(final BsonDocument encryptedFields, final String msg, final Throwable cause) {
46+
super(msg, assertNotNull(cause));
47+
this.encryptedFields = assertNotNull(encryptedFields);
48+
}
49+
50+
/**
51+
* The {@code encryptedFields} document that allows inferring which data keys are <strong>known to be created</strong>
52+
* before {@code this} exception happened by comparing this document with the original {@code encryptedFields} configuration.
53+
* Creation of a data key includes persisting it in the key vault.
54+
* <p>
55+
* Note that the returned {@code encryptedFields} document is not guaranteed to contain information about all the data keys that
56+
* may be created, only about those that the driver is certain about. For example, if persisting a data key times out,
57+
* the driver does not know whether it can be considered created or not, and does not include the information about the key in
58+
* the {@code encryptedFields} document. You can analyze whether the {@linkplain #getCause() cause} is a definite or indefinite
59+
* error, and rely on the returned {@code encryptedFields} to be containing information on all created keys
60+
* only if the error is definite.</p>
61+
*
62+
* @return The updated {@code encryptedFields} document.
63+
*/
64+
public BsonDocument getEncryptedFields() {
65+
return encryptedFields;
66+
}
67+
}

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import java.util.concurrent.TimeUnit;
2424

25+
import static com.mongodb.assertions.Assertions.fail;
2526
import static com.mongodb.assertions.Assertions.notNull;
2627

2728
/**
@@ -31,7 +32,7 @@
3132
* @mongodb.driver.manual core/timeseries-collections/ Time-series collections
3233
* @since 3.0
3334
*/
34-
public class CreateCollectionOptions {
35+
public class CreateCollectionOptions implements Cloneable {
3536
private long maxDocuments;
3637
private boolean capped;
3738
private long sizeInBytes;
@@ -360,4 +361,19 @@ public String toString() {
360361
+ ", encryptedFields=" + encryptedFields
361362
+ '}';
362363
}
364+
365+
/**
366+
* Creates a shallow copy of {@code this} {@link CreateCollectionOptions} by calling {@code super.clone()}.
367+
*
368+
* @return A shallow copy of {@code this} {@link CreateCollectionOptions}.
369+
* @since 4.9
370+
*/
371+
@Override
372+
public CreateCollectionOptions clone() {
373+
try {
374+
return (CreateCollectionOptions) super.clone();
375+
} catch (CloneNotSupportedException e) {
376+
throw fail(e.toString());
377+
}
378+
}
363379
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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;
18+
19+
import com.mongodb.annotations.Beta;
20+
import com.mongodb.client.model.vault.DataKeyOptions;
21+
import com.mongodb.lang.Nullable;
22+
import org.bson.BsonDocument;
23+
24+
import static com.mongodb.assertions.Assertions.notNull;
25+
26+
/**
27+
* Auxiliary parameters for creating an encrypted collection.
28+
*
29+
* @since 4.9
30+
*/
31+
@Beta(Beta.Reason.SERVER)
32+
public final class CreateEncryptedCollectionParams {
33+
private final String kmsProvider;
34+
@Nullable
35+
private BsonDocument masterKey;
36+
37+
/**
38+
* A constructor.
39+
*
40+
* @param kmsProvider The name of the KMS provider.
41+
*/
42+
public CreateEncryptedCollectionParams(final String kmsProvider) {
43+
this.kmsProvider = notNull("kmsProvider", kmsProvider);
44+
masterKey = null;
45+
}
46+
47+
/**
48+
* The name of the KMS provider.
49+
*
50+
* @return The name of the KMS provider.
51+
*/
52+
public String getKmsProvider() {
53+
return kmsProvider;
54+
}
55+
56+
/**
57+
* Sets the {@linkplain DataKeyOptions#getMasterKey() master key} for creating a data key.
58+
*
59+
* @param masterKey The master key for creating a data key.
60+
* @return {@code this}.
61+
*/
62+
public CreateEncryptedCollectionParams masterKey(@Nullable final BsonDocument masterKey) {
63+
this.masterKey = masterKey;
64+
return this;
65+
}
66+
67+
/**
68+
* The {@linkplain DataKeyOptions#getMasterKey() master key} for creating a data key.
69+
* The default is {@code null}.
70+
*
71+
* @return The master key for creating a data key.
72+
*/
73+
@Nullable
74+
public BsonDocument getMasterKey() {
75+
return masterKey;
76+
}
77+
78+
@Override
79+
public String toString() {
80+
return "CreateEncryptedCollectionParams{"
81+
+ ", kmsProvider=" + kmsProvider
82+
+ ", masterKey=" + masterKey
83+
+ '}';
84+
}
85+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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+
package com.mongodb.reactivestreams.client.internal;
17+
18+
import com.mongodb.annotations.NotThreadSafe;
19+
import org.reactivestreams.Publisher;
20+
import org.reactivestreams.Subscriber;
21+
import reactor.core.publisher.Flux;
22+
import reactor.core.publisher.Mono;
23+
24+
import java.util.function.Supplier;
25+
26+
/**
27+
* Allows creating {@link Publisher}s that do not allow calling {@link Publisher#subscribe(Subscriber)} more than once.
28+
*
29+
* <p>This class is not part of the public API and may be removed or changed at any time</p>
30+
*/
31+
public final class OneShotPublisher {
32+
public static <T> Publisher<T> from(final Publisher<T> publisher) {
33+
return Flux.defer(new OneShotSupplier<>(publisher));
34+
}
35+
36+
// A `Publisher` does not have to be thread-safe, therefore, `Publisher.subscribe` should not be called concurrently.
37+
// Hence, `OneShotSupplier.get` is not called concurrently and does not have to be thread-safe.
38+
@NotThreadSafe
39+
private static final class OneShotSupplier<T> implements Supplier<Publisher<T>> {
40+
private final Publisher<T> publisher;
41+
private boolean used;
42+
43+
OneShotSupplier(final Publisher<T> publisher) {
44+
this.publisher = publisher;
45+
}
46+
47+
@Override
48+
public Publisher<T> get() {
49+
if (used) {
50+
// we may also `throw` here, and `Flux.defer` will handle the exception and signal `onSubscribe` followed by `onError`
51+
return Mono.error(new IllegalStateException(
52+
"This is a one-shot publisher, it does not support subscribing to it more than once."));
53+
}
54+
used = true;
55+
return publisher;
56+
}
57+
58+
@Override
59+
public String toString() {
60+
return "OneShotSupplier{"
61+
+ "publisher=" + publisher
62+
+ ", used=" + used
63+
+ '}';
64+
}
65+
}
66+
67+
private OneShotPublisher() {
68+
}
69+
}

0 commit comments

Comments
 (0)