Skip to content

Commit 9751546

Browse files
committed
Client Side Encryption bypassAutoEncryption fix
If auto encryption is bypassed then the driver no longer tries to spawn mongocryptd. JAVA-3554
1 parent 2fd070d commit 9751546

File tree

10 files changed

+628
-20
lines changed

10 files changed

+628
-20
lines changed

driver-async/src/main/com/mongodb/async/client/internal/CommandMarker.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,19 @@
3333
import java.util.Map;
3434
import java.util.concurrent.TimeUnit;
3535

36+
import static com.mongodb.assertions.Assertions.notNull;
3637
import static com.mongodb.internal.capi.MongoCryptOptionsHelper.createMongocryptdSpawnArgs;
3738

3839
@SuppressWarnings("UseOfProcessBuilder")
3940
class CommandMarker implements Closeable {
40-
private MongoClient client;
41+
private final MongoClient client;
4142
private final ProcessBuilder processBuilder;
4243

43-
CommandMarker(final Map<String, Object> options) {
44-
String connectionString;
45-
46-
if (options.containsKey("mongocryptdURI")) {
47-
connectionString = (String) options.get("mongocryptdURI");
48-
} else {
49-
connectionString = "mongodb://localhost:27020";
44+
CommandMarker(final boolean isBypassAutoEncryption, final Map<String, Object> options) {
45+
if (isBypassAutoEncryption) {
46+
processBuilder = null;
47+
client = null;
48+
return;
5049
}
5150

5251
if (!options.containsKey("mongocryptdBypassSpawn") || !((Boolean) options.get("mongocryptdBypassSpawn"))) {
@@ -56,6 +55,14 @@ class CommandMarker implements Closeable {
5655
processBuilder = null;
5756
}
5857

58+
String connectionString;
59+
60+
if (options.containsKey("mongocryptdURI")) {
61+
connectionString = (String) options.get("mongocryptdURI");
62+
} else {
63+
connectionString = "mongodb://localhost:27020";
64+
}
65+
5966
client = MongoClients.create(MongoClientSettings.builder()
6067
.applyConnectionString(new ConnectionString(connectionString))
6168
.applyToClusterSettings(new Block<ClusterSettings.Builder>() {
@@ -68,6 +75,8 @@ public void apply(final ClusterSettings.Builder builder) {
6875
}
6976

7077
void mark(final String databaseName, final RawBsonDocument command, final SingleResultCallback<RawBsonDocument> callback) {
78+
notNull("client", client, callback);
79+
7180
final SingleResultCallback<RawBsonDocument> wrappedCallback = new SingleResultCallback<RawBsonDocument>() {
7281
@Override
7382
public void onResult(final RawBsonDocument result, final Throwable t) {
@@ -103,7 +112,9 @@ public void onResult(final Void result, final Throwable t) {
103112

104113
@Override
105114
public void close() {
106-
client.close();
115+
if (client != null) {
116+
client.close();
117+
}
107118
}
108119

109120
private void runCommand(final String databaseName, final RawBsonDocument command,

driver-async/src/main/com/mongodb/async/client/internal/Crypts.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public final class Crypts {
3535
public static Crypt createCrypt(final MongoClient client, final AutoEncryptionSettings options) {
3636
return new Crypt(MongoCrypts.create(createMongoCryptOptions(options.getKmsProviders(), options.getSchemaMap())),
3737
new CollectionInfoRetriever(client),
38-
new CommandMarker(options.getExtraOptions()),
38+
new CommandMarker(options.isBypassAutoEncryption(), options.getExtraOptions()),
3939
createKeyRetriever(client, options.getKeyVaultMongoClientSettings(), options.getKeyVaultNamespace()),
4040
createKeyManagementService(),
4141
options.isBypassAutoEncryption());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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.async.client;
18+
19+
import com.mongodb.AutoEncryptionSettings;
20+
import com.mongodb.ClientEncryptionSettings;
21+
import com.mongodb.MongoClientSettings;
22+
import com.mongodb.MongoNamespace;
23+
import com.mongodb.async.FutureResultCallback;
24+
import com.mongodb.async.client.vault.ClientEncryption;
25+
import com.mongodb.async.client.vault.ClientEncryptions;
26+
import com.mongodb.client.model.vault.DataKeyOptions;
27+
import com.mongodb.client.model.vault.EncryptOptions;
28+
import org.bson.BsonBinary;
29+
import org.bson.BsonString;
30+
import org.bson.Document;
31+
import org.junit.After;
32+
import org.junit.Before;
33+
import org.junit.Test;
34+
35+
import java.security.SecureRandom;
36+
import java.util.HashMap;
37+
import java.util.Map;
38+
39+
import static com.mongodb.ClusterFixture.isNotAtLeastJava8;
40+
import static com.mongodb.ClusterFixture.serverVersionAtLeast;
41+
import static com.mongodb.async.client.Fixture.getDefaultDatabaseName;
42+
import static com.mongodb.async.client.Fixture.getMongoClient;
43+
import static org.junit.Assert.assertEquals;
44+
import static org.junit.Assume.assumeFalse;
45+
import static org.junit.Assume.assumeTrue;
46+
47+
public class ClientSideEncryptionBypassAutoEncryptionTest {
48+
private MongoClient clientEncrypted;
49+
private ClientEncryption clientEncryption;
50+
51+
@Before
52+
public void setUp() {
53+
assumeFalse(isNotAtLeastJava8());
54+
assumeTrue(serverVersionAtLeast(4, 1));
55+
56+
MongoClient mongoClient = getMongoClient();
57+
58+
final byte[] localMasterKey = new byte[96];
59+
new SecureRandom().nextBytes(localMasterKey);
60+
61+
Map<String, Map<String, Object>> kmsProviders = new HashMap<String, Map<String, Object>>() {{
62+
put("local", new HashMap<String, Object>() {{
63+
put("key", localMasterKey);
64+
}});
65+
}};
66+
67+
68+
MongoNamespace keyVaultNamespace = new MongoNamespace(Fixture.getDefaultDatabaseName(), "testKeyVault");
69+
70+
Fixture.dropDatabase(Fixture.getDefaultDatabaseName());
71+
72+
ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder()
73+
.keyVaultMongoClientSettings(Fixture.getMongoClientSettings())
74+
.keyVaultNamespace(keyVaultNamespace.getFullName())
75+
.kmsProviders(kmsProviders)
76+
.build();
77+
78+
clientEncryption = ClientEncryptions.create(clientEncryptionSettings);
79+
80+
AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder()
81+
.keyVaultNamespace(keyVaultNamespace.getFullName())
82+
.kmsProviders(kmsProviders)
83+
.bypassAutoEncryption(true)
84+
.build();
85+
86+
MongoClientSettings clientSettings = Fixture.getMongoClientSettingsBuilder()
87+
.autoEncryptionSettings(autoEncryptionSettings)
88+
.build();
89+
clientEncrypted = MongoClients.create(clientSettings);
90+
}
91+
92+
@Test
93+
public void shouldAutoDecryptManuallyEncryptedData() {
94+
String fieldValue = "123456789";
95+
96+
FutureResultCallback<BsonBinary> binaryCallback = new FutureResultCallback<BsonBinary>();
97+
clientEncryption.createDataKey("local", new DataKeyOptions(), binaryCallback);
98+
BsonBinary dataKeyId = binaryCallback.get();
99+
100+
binaryCallback = new FutureResultCallback<BsonBinary>();
101+
clientEncryption.encrypt(new BsonString(fieldValue),
102+
new EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").keyId(dataKeyId), binaryCallback);
103+
BsonBinary encryptedFieldValue = binaryCallback.get();
104+
105+
MongoCollection<Document> collection = clientEncrypted.getDatabase(Fixture.getDefaultDatabaseName()).getCollection("test");
106+
107+
FutureResultCallback<Void> insertCallback = new FutureResultCallback<Void>();
108+
collection.insertOne(new Document("encryptedField", encryptedFieldValue), insertCallback);
109+
insertCallback.get();
110+
111+
FutureResultCallback<Document> resultCallback = new FutureResultCallback<Document>();
112+
collection.find().first(resultCallback);
113+
114+
assertEquals(fieldValue, resultCallback.get().getString("encryptedField"));
115+
}
116+
117+
@After
118+
public void after() {
119+
if (clientEncrypted != null) {
120+
Fixture.dropDatabase(getDefaultDatabaseName());
121+
clientEncrypted.close();
122+
}
123+
}
124+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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.async.client;
18+
19+
import com.mongodb.AutoEncryptionSettings;
20+
import com.mongodb.MongoClientSettings;
21+
import com.mongodb.MongoNamespace;
22+
import com.mongodb.async.FutureResultCallback;
23+
import org.bson.Document;
24+
import org.junit.Test;
25+
26+
import java.io.File;
27+
import java.io.IOException;
28+
import java.security.SecureRandom;
29+
import java.util.HashMap;
30+
import java.util.Map;
31+
32+
import static com.mongodb.ClusterFixture.serverVersionAtLeast;
33+
import static java.util.Arrays.asList;
34+
import static org.junit.Assert.assertFalse;
35+
import static org.junit.Assume.assumeTrue;
36+
37+
38+
public class ClientSideEncryptionMongocryptdSpawnBypassTest extends DatabaseTestCase {
39+
private final File pidFile;
40+
private final Map<String, Map<String, Object>> kmsProviders;
41+
private final MongoNamespace keyVaultNamespace = new MongoNamespace("admin.datakeys");
42+
43+
public ClientSideEncryptionMongocryptdSpawnBypassTest() throws IOException {
44+
super();
45+
pidFile = new File("bypass-spawning-mongocryptd.pid");
46+
47+
byte[] localMasterKey = new byte[96];
48+
new SecureRandom().nextBytes(localMasterKey);
49+
50+
Map<String, Object> keyMap = new HashMap<String, Object>();
51+
keyMap.put("key", localMasterKey);
52+
kmsProviders = new HashMap<String, Map<String, Object>>();
53+
kmsProviders.put("local", keyMap);
54+
}
55+
56+
57+
@Test
58+
public void shouldNotSpawnWhenMongocryptdBypassSpawnIsTrue() {
59+
assumeTrue(serverVersionAtLeast(4, 1));
60+
Map<String, Object> extraOptions = new HashMap<String, Object>();
61+
extraOptions.put("mongocryptdBypassSpawn", true);
62+
extraOptions.put("mongocryptdSpawnArgs", asList("--pidfilepath=" + pidFile.getAbsolutePath(), "--port=27099"));
63+
64+
AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder()
65+
.keyVaultNamespace(keyVaultNamespace.getFullName())
66+
.kmsProviders(kmsProviders)
67+
.extraOptions(extraOptions)
68+
.build();
69+
70+
MongoClientSettings clientSettings = Fixture.getMongoClientSettingsBuilder()
71+
.autoEncryptionSettings(autoEncryptionSettings)
72+
.build();
73+
MongoClient clientEncrypted = MongoClients.create(clientSettings);
74+
try {
75+
FutureResultCallback<Document> pingCallback = new FutureResultCallback<Document>();
76+
clientEncrypted.getDatabase("admin").runCommand(new Document("ping", 1), pingCallback);
77+
pingCallback.get();
78+
79+
assertFalse(pidFile.exists());
80+
} finally {
81+
clientEncrypted.close();
82+
}
83+
}
84+
85+
@Test
86+
public void shouldNotSpawnWhenBypassAutoEncryptionIsTrue() {
87+
assumeTrue(serverVersionAtLeast(4, 1));
88+
Map<String, Object> extraOptions = new HashMap<String, Object>();
89+
extraOptions.put("mongocryptdSpawnArgs", asList("--pidfilepath=" + pidFile.getAbsolutePath(), "--port=27099"));
90+
91+
AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder()
92+
.keyVaultNamespace(keyVaultNamespace.getFullName())
93+
.kmsProviders(kmsProviders)
94+
.extraOptions(extraOptions)
95+
.bypassAutoEncryption(true)
96+
.build();
97+
98+
MongoClientSettings clientSettings = Fixture.getMongoClientSettingsBuilder()
99+
.autoEncryptionSettings(autoEncryptionSettings)
100+
.build();
101+
MongoClient clientEncrypted = MongoClients.create(clientSettings);
102+
103+
try {
104+
FutureResultCallback<Document> pingCallback = new FutureResultCallback<Document>();
105+
clientEncrypted.getDatabase("admin").runCommand(new Document("ping", 1), pingCallback);
106+
pingCallback.get();
107+
108+
assertFalse(pidFile.exists());
109+
} finally {
110+
clientEncrypted.close();
111+
}
112+
}
113+
}

driver-async/src/test/functional/com/mongodb/async/client/Fixture.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ public static synchronized MongoClient getMongoClient() {
5959
return mongoClient;
6060
}
6161

62+
public static MongoClientSettings.Builder getMongoClientSettingsBuilder() {
63+
return getMongoClientBuilderFromConnectionString();
64+
}
65+
6266
public static MongoClientSettings getMongoClientSettings() {
6367
return getMongoClientBuilderFromConnectionString().build();
6468
}

0 commit comments

Comments
 (0)