Skip to content

Commit e9a555b

Browse files
jyeminstIncMale
andauthored
Support configuration of KMS provider credentials with a Supplier (#894)
JAVA-4504 Co-authored-by: Valentin Kovalenko <[email protected]>
1 parent d25db9c commit e9a555b

File tree

8 files changed

+302
-30
lines changed

8 files changed

+302
-30
lines changed

driver-core/src/main/com/mongodb/AutoEncryptionSettings.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Collections;
2525
import java.util.HashMap;
2626
import java.util.Map;
27+
import java.util.function.Supplier;
2728

2829
import static com.mongodb.assertions.Assertions.notNull;
2930
import static java.util.Collections.unmodifiableMap;
@@ -62,6 +63,7 @@ public final class AutoEncryptionSettings {
6263
private final String keyVaultNamespace;
6364
private final Map<String, Map<String, Object>> kmsProviders;
6465
private final Map<String, SSLContext> kmsProviderSslContextMap;
66+
private final Map<String, Supplier<Map<String, Object>>> kmsProviderPropertySuppliers;
6567
private final Map<String, BsonDocument> schemaMap;
6668
private final Map<String, Object> extraOptions;
6769
private final boolean bypassAutoEncryption;
@@ -76,6 +78,7 @@ public static final class Builder {
7678
private String keyVaultNamespace;
7779
private Map<String, Map<String, Object>> kmsProviders;
7880
private Map<String, SSLContext> kmsProviderSslContextMap = new HashMap<>();
81+
private Map<String, Supplier<Map<String, Object>>> kmsProviderPropertySuppliers = new HashMap<>();
7982
private Map<String, BsonDocument> schemaMap = Collections.emptyMap();
8083
private Map<String, Object> extraOptions = Collections.emptyMap();
8184
private boolean bypassAutoEncryption;
@@ -109,13 +112,30 @@ public Builder keyVaultNamespace(final String keyVaultNamespace) {
109112
*
110113
* @param kmsProviders the KMS providers map, which may not be null
111114
* @return this
115+
* @see #kmsProviderPropertySuppliers(Map)
112116
* @see #getKmsProviders()
113117
*/
114118
public Builder kmsProviders(final Map<String, Map<String, Object>> kmsProviders) {
115119
this.kmsProviders = notNull("kmsProviders", kmsProviders);
116120
return this;
117121
}
118122

123+
/**
124+
* This method is similar to {@link #kmsProviders(Map)}, but instead of configuring properties for KMS providers,
125+
* it configures {@link Supplier}s of properties.
126+
*
127+
* @param kmsProviderPropertySuppliers A {@link Map} where keys identify KMS providers,
128+
* and values specify {@link Supplier}s of properties for the KMS providers.
129+
* Must not be null. Each {@link Supplier} must return non-empty properties.
130+
* @return this
131+
* @see #getKmsProviderPropertySuppliers()
132+
* @since 4.6
133+
*/
134+
public Builder kmsProviderPropertySuppliers(final Map<String, Supplier<Map<String, Object>>> kmsProviderPropertySuppliers) {
135+
this.kmsProviderPropertySuppliers = notNull("kmsProviderPropertySuppliers", kmsProviderPropertySuppliers);
136+
return this;
137+
}
138+
119139
/**
120140
* Sets the KMS provider to SSLContext map
121141
*
@@ -265,12 +285,34 @@ public String getKeyVaultNamespace() {
265285
* <li>key: byte[] of length 96, the local key</li>
266286
* </ul>
267287
*
288+
* <p>
289+
* It is also permitted for the value of a kms provider to be an empty map, in which case the driver will first
290+
* </p>
291+
* <ul>
292+
* <li>use the {@link Supplier} configured in {@link #getKmsProviderPropertySuppliers()} to obtain a non-empty map</li>
293+
* <li>attempt to obtain the properties from the environment</li>
294+
* </ul>
295+
*
268296
* @return map of KMS provider properties
297+
* @see #getKmsProviderPropertySuppliers()
269298
*/
270299
public Map<String, Map<String, Object>> getKmsProviders() {
271300
return unmodifiableMap(kmsProviders);
272301
}
273302

303+
/**
304+
* This method is similar to {@link #getKmsProviders()}, but instead of getting properties for KMS providers,
305+
* it gets {@link Supplier}s of properties.
306+
* <p>If {@link #getKmsProviders()} returns empty properties for a KMS provider,
307+
* the driver will use a {@link Supplier} of properties configured for the KMS provider to obtain non-empty properties.</p>
308+
*
309+
* @return A {@link Map} where keys identify KMS providers, and values specify {@link Supplier}s of properties for the KMS providers.
310+
* @since 4.6
311+
*/
312+
public Map<String, Supplier<Map<String, Object>>> getKmsProviderPropertySuppliers() {
313+
return unmodifiableMap(kmsProviderPropertySuppliers);
314+
}
315+
274316
/**
275317
* Gets the KMS provider to SSLContext map.
276318
*
@@ -355,6 +397,7 @@ private AutoEncryptionSettings(final Builder builder) {
355397
this.keyVaultNamespace = notNull("keyVaultNamespace", builder.keyVaultNamespace);
356398
this.kmsProviders = notNull("kmsProviders", builder.kmsProviders);
357399
this.kmsProviderSslContextMap = notNull("kmsProviderSslContextMap", builder.kmsProviderSslContextMap);
400+
this.kmsProviderPropertySuppliers = notNull("kmsProviderPropertySuppliers", builder.kmsProviderPropertySuppliers);
358401
this.schemaMap = notNull("schemaMap", builder.schemaMap);
359402
this.extraOptions = notNull("extraOptions", builder.extraOptions);
360403
this.bypassAutoEncryption = builder.bypassAutoEncryption;

driver-core/src/main/com/mongodb/ClientEncryptionSettings.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import javax.net.ssl.SSLContext;
2222
import java.util.HashMap;
2323
import java.util.Map;
24+
import java.util.function.Supplier;
2425

2526
import static com.mongodb.assertions.Assertions.notNull;
2627
import static java.util.Collections.unmodifiableMap;
@@ -39,6 +40,7 @@ public final class ClientEncryptionSettings {
3940
private final MongoClientSettings keyVaultMongoClientSettings;
4041
private final String keyVaultNamespace;
4142
private final Map<String, Map<String, Object>> kmsProviders;
43+
private final Map<String, Supplier<Map<String, Object>>> kmsProviderPropertySuppliers;
4244
private final Map<String, SSLContext> kmsProviderSslContextMap;
4345
/**
4446
* A builder for {@code ClientEncryptionSettings} so that {@code ClientEncryptionSettings} can be immutable, and to support easier
@@ -49,6 +51,7 @@ public static final class Builder {
4951
private MongoClientSettings keyVaultMongoClientSettings;
5052
private String keyVaultNamespace;
5153
private Map<String, Map<String, Object>> kmsProviders;
54+
private Map<String, Supplier<Map<String, Object>>> kmsProviderPropertySuppliers = new HashMap<>();
5255
private Map<String, SSLContext> kmsProviderSslContextMap = new HashMap<>();
5356

5457
/**
@@ -80,13 +83,30 @@ public Builder keyVaultNamespace(final String keyVaultNamespace) {
8083
*
8184
* @param kmsProviders the KMS providers map, which may not be null
8285
* @return this
86+
* @see #kmsProviderPropertySuppliers(Map)
8387
* @see #getKmsProviders()
8488
*/
8589
public Builder kmsProviders(final Map<String, Map<String, Object>> kmsProviders) {
8690
this.kmsProviders = notNull("kmsProviders", kmsProviders);
8791
return this;
8892
}
8993

94+
/**
95+
* This method is similar to {@link #kmsProviders(Map)}, but instead of setting properties for KMS providers,
96+
* it sets {@link Supplier}s of properties.
97+
*
98+
* @param kmsProviderPropertySuppliers A {@link Map} where keys identify KMS providers,
99+
* and values specify {@link Supplier}s of properties for the KMS providers.
100+
* Must not be null. Each {@link Supplier} must return non-empty properties.
101+
* @return this
102+
* @see #getKmsProviderPropertySuppliers()
103+
* @since 4.6
104+
*/
105+
public Builder kmsProviderPropertySuppliers(final Map<String, Supplier<Map<String, Object>>> kmsProviderPropertySuppliers) {
106+
this.kmsProviderPropertySuppliers = notNull("kmsProviderPropertySuppliers", kmsProviderPropertySuppliers);
107+
return this;
108+
}
109+
90110
/**
91111
* Sets the KMS provider to SSLContext map
92112
*
@@ -197,13 +217,33 @@ public String getKeyVaultNamespace() {
197217
* <ul>
198218
* <li>key: byte[] of length 96, the local key</li>
199219
* </ul>
200-
*
220+
* <p>
221+
* It is also permitted for the value of a kms provider to be an empty map, in which case the driver will first
222+
* </p>
223+
* <ul>
224+
* <li>use the {@link Supplier} configured in {@link #getKmsProviderPropertySuppliers()} to obtain a non-empty map</li>
225+
* <li>attempt to obtain the properties from the environment</li>
226+
* </ul>
201227
* @return map of KMS provider properties
228+
* @see #getKmsProviderPropertySuppliers()
202229
*/
203230
public Map<String, Map<String, Object>> getKmsProviders() {
204231
return unmodifiableMap(kmsProviders);
205232
}
206233

234+
/**
235+
* This method is similar to {@link #getKmsProviders()}, but instead of getting properties for KMS providers,
236+
* it gets {@link Supplier}s of properties.
237+
* <p>If {@link #getKmsProviders()} returns empty properties for a KMS provider,
238+
* the driver will use a {@link Supplier} of properties configured for the KMS provider to obtain non-empty properties.</p>
239+
*
240+
* @return A {@link Map} where keys identify KMS providers, and values specify {@link Supplier}s of properties for the KMS providers.
241+
* @since 4.6
242+
*/
243+
public Map<String, Supplier<Map<String, Object>>> getKmsProviderPropertySuppliers() {
244+
return unmodifiableMap(kmsProviderPropertySuppliers);
245+
}
246+
207247
/**
208248
* Gets the KMS provider to SSLContext map.
209249
*
@@ -223,6 +263,7 @@ private ClientEncryptionSettings(final Builder builder) {
223263
this.keyVaultMongoClientSettings = builder.keyVaultMongoClientSettings;
224264
this.keyVaultNamespace = notNull("keyVaultNamespace", builder.keyVaultNamespace);
225265
this.kmsProviders = notNull("kmsProviders", builder.kmsProviders);
266+
this.kmsProviderPropertySuppliers = notNull("kmsProviderPropertySuppliers", builder.kmsProviderPropertySuppliers);
226267
this.kmsProviderSslContextMap = notNull("kmsProviderSslContextMap", builder.kmsProviderSslContextMap);
227268
}
228269

driver-core/src/main/com/mongodb/internal/capi/MongoCryptHelper.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.mongodb.ConnectionString;
2222
import com.mongodb.MongoClientException;
2323
import com.mongodb.MongoClientSettings;
24+
import com.mongodb.MongoConfigurationException;
2425
import com.mongodb.connection.ClusterSettings;
2526
import com.mongodb.connection.SocketSettings;
2627
import com.mongodb.crypt.capi.MongoCryptOptions;
@@ -36,6 +37,9 @@
3637
import java.util.List;
3738
import java.util.Map;
3839
import java.util.concurrent.TimeUnit;
40+
import java.util.function.Supplier;
41+
42+
import static java.lang.String.format;
3943

4044
public final class MongoCryptHelper {
4145

@@ -50,8 +54,28 @@ public static MongoCryptOptions createMongoCryptOptions(final Map<String, Map<St
5054
return mongoCryptOptionsBuilder.build();
5155
}
5256

53-
public static BsonDocument fetchCredentials(final Map<String, Map<String, Object>> kmsProviders) {
57+
public static BsonDocument fetchCredentials(final Map<String, Map<String, Object>> kmsProviders,
58+
final Map<String, Supplier<Map<String, Object>>> kmsProviderPropertySuppliers) {
5459
BsonDocument kmsProvidersDocument = MongoCryptHelper.getKmsProvidersAsBsonDocument(kmsProviders);
60+
for (Map.Entry<String, Supplier<Map<String, Object>>> entry : kmsProviderPropertySuppliers.entrySet()) {
61+
String kmsProviderName = entry.getKey();
62+
if (!kmsProvidersDocument.get(kmsProviderName).asDocument().isEmpty()) {
63+
continue;
64+
}
65+
Map<String, Object> kmsProviderCredential;
66+
try {
67+
kmsProviderCredential = entry.getValue().get();
68+
} catch (Exception e) {
69+
throw new MongoConfigurationException(format("Exception getting credential for kms provider %s from configured Supplier.",
70+
kmsProviderName), e);
71+
}
72+
if (kmsProviderCredential == null || kmsProviderCredential.isEmpty()) {
73+
throw new MongoConfigurationException(format("Exception getting credential for kms provider %s from configured Supplier."
74+
+ " The returned value is %s.",
75+
kmsProviderName, kmsProviderCredential == null ? "null" : "empty"));
76+
}
77+
addToKmsProviderDocument(kmsProvidersDocument, kmsProviderName, kmsProviderCredential);
78+
}
5579
if (kmsProvidersDocument.containsKey("aws") && kmsProvidersDocument.get("aws").asDocument().isEmpty()) {
5680
AwsCredential awsCredential = AwsCredentialHelper.obtainFromEnvironment();
5781
if (awsCredential != null) {
@@ -70,11 +94,16 @@ public static BsonDocument fetchCredentials(final Map<String, Map<String, Object
7094
private static BsonDocument getKmsProvidersAsBsonDocument(final Map<String, Map<String, Object>> kmsProviders) {
7195
BsonDocument bsonKmsProviders = new BsonDocument();
7296
for (Map.Entry<String, Map<String, Object>> entry : kmsProviders.entrySet()) {
73-
bsonKmsProviders.put(entry.getKey(), new BsonDocumentWrapper<>(new Document(entry.getValue()), new DocumentCodec()));
97+
addToKmsProviderDocument(bsonKmsProviders, entry.getKey(), entry.getValue());
7498
}
7599
return bsonKmsProviders;
76100
}
77101

102+
private static void addToKmsProviderDocument(final BsonDocument kmsProvidersDocument, final String kmsProvider,
103+
final Map<String, Object> kmsProviderCredential) {
104+
kmsProvidersDocument.put(kmsProvider, new BsonDocumentWrapper<>(new Document(kmsProviderCredential), new DocumentCodec()));
105+
}
106+
78107
@SuppressWarnings("unchecked")
79108
public static List<String> createMongocryptdSpawnArgs(final Map<String, Object> options) {
80109
List<String> spawnArgs = new ArrayList<String>();

driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/crypt/Crypt.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class Crypt implements Closeable {
5151
private static final Logger LOGGER = Loggers.getLogger("client");
5252
private final MongoCrypt mongoCrypt;
5353
private final Map<String, Map<String, Object>> kmsProviders;
54+
private final Map<String, Supplier<Map<String, Object>>> kmsProviderPropertySuppliers;
5455
private final CollectionInfoRetriever collectionInfoRetriever;
5556
private final CommandMarker commandMarker;
5657
private final KeyRetriever keyRetriever;
@@ -67,9 +68,11 @@ public class Crypt implements Closeable {
6768
* @param keyRetriever the key retriever
6869
* @param keyManagementService the key management service
6970
*/
70-
Crypt(final MongoCrypt mongoCrypt, final Map<String, Map<String, Object>> kmsProviders, final KeyRetriever keyRetriever,
71+
Crypt(final MongoCrypt mongoCrypt, final Map<String, Map<String, Object>> kmsProviders,
72+
final Map<String, Supplier<Map<String, Object>>> kmsProviderPropertySuppliers,
73+
final KeyRetriever keyRetriever,
7174
final KeyManagementService keyManagementService) {
72-
this(mongoCrypt, kmsProviders, null, null, keyRetriever, keyManagementService, false, null);
75+
this(mongoCrypt, kmsProviders, kmsProviderPropertySuppliers, null, null, keyRetriever, keyManagementService, false, null);
7376
}
7477

7578
/**
@@ -83,6 +86,7 @@ public class Crypt implements Closeable {
8386
*/
8487
Crypt(final MongoCrypt mongoCrypt,
8588
final Map<String, Map<String, Object>> kmsProviders,
89+
final Map<String, Supplier<Map<String, Object>>> kmsProviderPropertySuppliers,
8690
@Nullable final CollectionInfoRetriever collectionInfoRetriever,
8791
@Nullable final CommandMarker commandMarker,
8892
final KeyRetriever keyRetriever,
@@ -91,6 +95,7 @@ public class Crypt implements Closeable {
9195
@Nullable final MongoClient internalClient) {
9296
this.mongoCrypt = mongoCrypt;
9397
this.kmsProviders = kmsProviders;
98+
this.kmsProviderPropertySuppliers = kmsProviderPropertySuppliers;
9499
this.collectionInfoRetriever = collectionInfoRetriever;
95100
this.commandMarker = commandMarker;
96101
this.keyRetriever = keyRetriever;
@@ -240,7 +245,7 @@ private void executeStateMachineWithSink(final MongoCryptContext cryptContext, @
240245
private void fetchCredentials(final MongoCryptContext cryptContext, @Nullable final String databaseName,
241246
final MonoSink<RawBsonDocument> sink) {
242247
try {
243-
cryptContext.provideKmsProviderCredentials(MongoCryptHelper.fetchCredentials(kmsProviders));
248+
cryptContext.provideKmsProviderCredentials(MongoCryptHelper.fetchCredentials(kmsProviders, kmsProviderPropertySuppliers));
244249
executeStateMachineWithSink(cryptContext, databaseName, sink);
245250
} catch (Exception e) {
246251
sink.error(e);

driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/crypt/Crypts.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public static Crypt createCrypt(final MongoClientImpl client, final AutoEncrypti
5353
return new Crypt(MongoCrypts.create(createMongoCryptOptions(options.getKmsProviders(),
5454
options.getSchemaMap())),
5555
options.getKmsProviders(),
56+
options.getKmsProviderPropertySuppliers(),
5657
options.isBypassAutoEncryption() ? null : new CollectionInfoRetriever(collectionInfoRetrieverClient),
5758
new CommandMarker(options.isBypassAutoEncryption(), options.getExtraOptions()),
5859
new KeyRetriever(keyVaultClient, new MongoNamespace(options.getKeyVaultNamespace())),
@@ -65,6 +66,7 @@ public static Crypt create(final MongoClient keyVaultClient, final ClientEncrypt
6566
return new Crypt(MongoCrypts.create(
6667
createMongoCryptOptions(options.getKmsProviders(), null)),
6768
options.getKmsProviders(),
69+
options.getKmsProviderPropertySuppliers(),
6870
new KeyRetriever(keyVaultClient, new MongoNamespace(options.getKeyVaultNamespace())),
6971
createKeyManagementService(options.getKmsProviderSslContextMap()));
7072
}

0 commit comments

Comments
 (0)