Skip to content

Commit e31607a

Browse files
committed
Add client-side encryption support (POC)
JAVA-3071
1 parent 8af8178 commit e31607a

File tree

73 files changed

+13951
-55
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+13951
-55
lines changed

.evergreen/.evg.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ functions:
237237
working_dir: "src"
238238
script: |
239239
${PREPARE_SHELL}
240-
AUTH="${AUTH}" SSL="${SSL}" MONGODB_URI="${MONGODB_URI}" SAFE_FOR_MULTI_MONGOS="${SAFE_FOR_MULTI_MONGOS}" TOPOLOGY="${TOPOLOGY}" COMPRESSOR="${COMPRESSOR}" JDK="${JDK}" .evergreen/run-tests.sh
240+
AUTH="${AUTH}" SSL="${SSL}" MONGODB_URI="${MONGODB_URI}" SAFE_FOR_MULTI_MONGOS="${SAFE_FOR_MULTI_MONGOS}" TOPOLOGY="${TOPOLOGY}" COMPRESSOR="${COMPRESSOR}" JDK="${JDK}" AWS_ACCESS_KEY_ID=${aws_access_key_id} AWS_SECRET_ACCESS_KEY=${aws_secret_access_key} .evergreen/run-tests.sh
241241
242242
"run slow tests":
243243
- command: shell.exec

.evergreen/run-tests.sh

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ set -o errexit # Exit the script with error if any of the commands fail
1313
# JDK Set the version of java to be used. Java versions can be set from the java toolchain /opt/java
1414
# "jdk5", "jdk6", "jdk7", "jdk8", "jdk9"
1515
# SLOW_TESTS_ONLY Set to true to only run the slow tests
16+
# AWS_ACCESS_KEY_ID The AWS access key identifier for client-side encryption
17+
# AWS_SECRET_ACCESS_KEY The AWS secret access key for client-side encryption
18+
1619
AUTH=${AUTH:-noauth}
1720
SSL=${SSL:-nossl}
1821
MONGODB_URI=${MONGODB_URI:-}
@@ -43,12 +46,12 @@ provision_ssl () {
4346
if [ ! -f client.pkc ]; then
4447
openssl pkcs12 -CAfile ${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem -export -in ${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem -out client.pkc -password pass:bithere
4548
fi
46-
if [ ! -f mongo-truststore ]; then
47-
${JAVA_HOME}/bin/keytool -importcert -trustcacerts -file ${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem -keystore mongo-truststore -storepass hithere -storetype JKS -noprompt
48-
fi
49+
50+
cp ${JAVA_HOME}/lib/security/cacerts mongo-truststore
51+
${JAVA_HOME}/bin/keytool -importcert -trustcacerts -file ${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem -keystore mongo-truststore -storepass changeit -storetype JKS -noprompt
4952

5053
# We add extra gradle arguments for SSL
51-
export GRADLE_EXTRA_VARS="-Pssl.enabled=true -Pssl.keyStoreType=pkcs12 -Pssl.keyStore=`pwd`/client.pkc -Pssl.keyStorePassword=bithere -Pssl.trustStoreType=jks -Pssl.trustStore=`pwd`/mongo-truststore -Pssl.trustStorePassword=hithere"
54+
export GRADLE_EXTRA_VARS="-Pssl.enabled=true -Pssl.keyStoreType=pkcs12 -Pssl.keyStore=`pwd`/client.pkc -Pssl.keyStorePassword=bithere -Pssl.trustStoreType=jks -Pssl.trustStore=`pwd`/mongo-truststore -Pssl.trustStorePassword=changeit"
5255
# Arguments for auth + SSL
5356
if [ "$AUTH" != "noauth" ] || [ "$TOPOLOGY" == "replica_set" ]; then
5457
export MONGODB_URI="${MONGODB_URI}&ssl=true&sslInvalidHostNameAllowed=true"
@@ -118,5 +121,6 @@ if [ "$SLOW_TESTS_ONLY" == "true" ]; then
118121
${TRANSACTION_URI} ${GRADLE_EXTRA_VARS} ${ASYNC_TYPE} --stacktrace --info testSlowOnly
119122
else
120123
./gradlew -PjdkHome=/opt/java/${JDK} -Dorg.mongodb.test.uri=${MONGODB_URI} \
124+
-Dorg.mongodb.test.awsAccessKeyId=${AWS_ACCESS_KEY_ID} -Dorg.mongodb.test.awsSecretAccessKey=${AWS_SECRET_ACCESS_KEY} \
121125
${TRANSACTION_URI} ${GRADLE_EXTRA_VARS} ${ASYNC_TYPE} --stacktrace --info test
122126
fi

build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ ext {
4242
nettyVersion = '4.1.17.Final'
4343
snappyVersion = '1.1.4'
4444
zstdVersion = '1.3.8-3'
45+
mongoCryptVersion = '1.0.0-SNAPSHOT'
4546
gitVersion = getGitVersion()
4647
}
4748

@@ -134,6 +135,9 @@ configure(javaCodeCheckedProjects) {
134135
'org.mongodb.useSocket': System.getProperty('org.mongodb.useSocket', 'false'),
135136
'org.mongodb.disableAsync': System.getProperty('org.mongodb.disableAsync', 'false'),
136137
'org.mongodb.async.type': System.getProperty('org.mongodb.async.type', 'nio2'),
138+
'org.mongodb.test.awsAccessKeyId': System.getProperty('org.mongodb.test.awsAccessKeyId'),
139+
'org.mongodb.test.awsSecretAccessKey': System.getProperty('org.mongodb.test.awsSecretAccessKey'),
140+
'jna.library.path': System.getProperty('jna.library.path')
137141
)
138142

139143
project.ext.buildingWith = { propertyName ->

config/codenarc/codenarc.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
<ruleset-ref path='rulesets/formatting.xml'>
8181
<rule-config name='LineLength'>
8282
<property name='length' value='140'/>
83+
<property name='doNotApplyToFileNames' value='ClientSideEncryptionProseTestSpecification.groovy'/>
8384
</rule-config>
8485
<!-- this check is failing '})' when it shouldn't -->
8586
<exclude name='SpaceAfterClosingBrace'/>

config/findbugs-exclude.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,10 @@
147147
<Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"/>
148148
</Match>
149149

150+
<Match>
151+
<Class name="com.mongodb.client.internal.CryptConnection"/>
152+
<Method name="retain"/>
153+
<Bug pattern="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT"/>
154+
</Match>
155+
150156
</FindBugsFilter>

driver-async/src/test/unit/com/mongodb/async/client/MongoClientSettingsSpecification.groovy

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,9 @@ class MongoClientSettingsSpecification extends Specification {
490490
def extras = ['credentialList', 'clusterSettings', 'connectionPoolSettings', 'heartbeatSocketSettings', 'serverSettings',
491491
'socketSettings' , 'sslSettings']
492492
def actual = MongoClientSettings.Builder.declaredMethods.grep { !it.synthetic } *.name.sort() - extras
493-
def expected = com.mongodb.MongoClientSettings.Builder.declaredMethods.grep { !it.synthetic } *.name.sort() - 'commandListenerList'
493+
def expected = com.mongodb.MongoClientSettings.Builder.declaredMethods.grep { !it.synthetic } *.name.sort()
494+
expected -= 'commandListenerList'
495+
expected -= 'autoEncryptionSettings'
494496

495497
then:
496498
actual == expected

driver-core/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ dependencies {
4242
compile "io.netty:netty-handler:$nettyVersion", optional
4343
compile "org.xerial.snappy:snappy-java:$snappyVersion", optional
4444
compile "com.github.luben:zstd-jni:$zstdVersion", optional
45-
45+
compile "org.mongodb:mongocrypt:$mongoCryptVersion", optional
46+
4647
testCompile project(':bson').sourceSets.test.output
4748
}
4849

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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;
18+
19+
import com.mongodb.annotations.NotThreadSafe;
20+
import com.mongodb.lang.Nullable;
21+
import org.bson.BsonDocument;
22+
23+
import java.util.Collections;
24+
import java.util.Map;
25+
26+
import static com.mongodb.assertions.Assertions.notNull;
27+
28+
/**
29+
* The auto-encryption options.
30+
*
31+
* @since 3.11
32+
*/
33+
public final class AutoEncryptionSettings {
34+
private final MongoClientSettings keyVaultMongoClientSettings;
35+
private final String keyVaultNamespace;
36+
private final Map<String, Map<String, Object>> kmsProviders;
37+
private final Map<String, BsonDocument> namespaceToLocalSchemaDocumentMap;
38+
private final Map<String, Object> extraOptions;
39+
private final boolean bypassAutoEncryption;
40+
41+
/**
42+
* A builder for {@code AutoEncryptionSettings} so that {@code AutoEncryptionSettings} can be immutable, and to support easier
43+
* construction through chaining.
44+
*/
45+
@NotThreadSafe
46+
public static final class Builder {
47+
private MongoClientSettings keyVaultMongoClientSettings;
48+
private String keyVaultNamespace;
49+
private Map<String, Map<String, Object>> kmsProviders;
50+
private Map<String, BsonDocument> namespaceToLocalSchemaDocumentMap = Collections.emptyMap();
51+
private Map<String, Object> extraOptions = Collections.emptyMap();
52+
private boolean bypassAutoEncryption;
53+
54+
/**
55+
* Sets the key vault settings.
56+
*
57+
* @param keyVaultMongoClientSettings the key vault mongo client settings, which may be null.
58+
* @return this
59+
*/
60+
public Builder keyVaultMongoClientSettings(final MongoClientSettings keyVaultMongoClientSettings) {
61+
this.keyVaultMongoClientSettings = keyVaultMongoClientSettings;
62+
return this;
63+
}
64+
65+
/**
66+
* Sets the key vault namespace
67+
*
68+
* @param keyVaultNamespace the key vault namespace, which may not be null
69+
* @return this
70+
*/
71+
public Builder keyVaultNamespace(final String keyVaultNamespace) {
72+
this.keyVaultNamespace = notNull("keyVaultNamespace", keyVaultNamespace);
73+
return this;
74+
}
75+
76+
/**
77+
* Sets the KMS providers map.
78+
*
79+
* @param kmsProviders the KMS providers map, which may not be null
80+
* @return this
81+
*/
82+
public Builder kmsProviders(final Map<String, Map<String, Object>> kmsProviders) {
83+
this.kmsProviders = notNull("kmsProviders", kmsProviders);
84+
return this;
85+
}
86+
87+
/**
88+
* Sets the map from namespace to local schema document
89+
*
90+
* @param namespaceToLocalSchemaDocumentMap the map from namespace to local schema document
91+
* @return this
92+
*/
93+
public Builder namespaceToLocalSchemaDocumentMap(final Map<String, BsonDocument> namespaceToLocalSchemaDocumentMap) {
94+
this.namespaceToLocalSchemaDocumentMap = notNull("namespaceToLocalSchemaDocumentMap", namespaceToLocalSchemaDocumentMap);
95+
return this;
96+
}
97+
98+
/**
99+
* Sets the extra options.
100+
*
101+
* @param extraOptions the extra options, which may not be null
102+
* @return this
103+
*/
104+
public Builder extraOptions(final Map<String, Object> extraOptions) {
105+
this.extraOptions = notNull("extraOptions", extraOptions);
106+
return this;
107+
}
108+
109+
/**
110+
* Sets whether auto-encryption should be bypassed.
111+
*
112+
* @param bypassAutoEncryption whether auto-encryption should be bypassed
113+
* @return this
114+
*/
115+
public Builder bypassAutoEncryption(final boolean bypassAutoEncryption) {
116+
this.bypassAutoEncryption = bypassAutoEncryption;
117+
return this;
118+
}
119+
120+
/**
121+
* Build an instance of {@code AutoEncryptionSettings}.
122+
*
123+
* @return the settings from this builder
124+
*/
125+
public AutoEncryptionSettings build() {
126+
return new AutoEncryptionSettings(this);
127+
}
128+
129+
private Builder() {
130+
}
131+
}
132+
133+
/**
134+
* Convenience method to create a Builder.
135+
*
136+
* @return a builder
137+
*/
138+
public static Builder builder() {
139+
return new Builder();
140+
}
141+
142+
/**
143+
* Gets the key vault settings.
144+
*
145+
* @return the key vault settings, which may be null to indicate that the same {@code MongoClient} should be used to access the key
146+
* vault collection as is used for the rest of the application.
147+
*/
148+
@Nullable
149+
public MongoClientSettings getKeyVaultMongoClientSettings() {
150+
return keyVaultMongoClientSettings;
151+
}
152+
153+
/**
154+
* Gets the key vault namespace.
155+
*
156+
* @return the key vault namespace, which may not be null
157+
*/
158+
public String getKeyVaultNamespace() {
159+
return keyVaultNamespace;
160+
}
161+
162+
/**
163+
* Gets the map of KMS provider properties
164+
*
165+
* @return map of KMS provider properties
166+
*/
167+
public Map<String, Map<String, Object>> getKmsProviders() {
168+
return kmsProviders;
169+
}
170+
171+
/**
172+
* Gets the map of namespace to local JSON schema
173+
*
174+
* @return map of namespace to local JSON schema
175+
*/
176+
public Map<String, BsonDocument> getNamespaceToLocalSchemaDocumentMap() {
177+
return namespaceToLocalSchemaDocumentMap;
178+
}
179+
180+
/**
181+
* Gets the extra options that control the behavior of auto-encryption components.
182+
*
183+
* @return the extra options
184+
*/
185+
public Map<String, Object> getExtraOptions() {
186+
return extraOptions;
187+
}
188+
189+
/**
190+
* Gets whether auto-encryption should be bypassed. Even when this option is true, auto-decryption is still enabled.
191+
*
192+
* @return true if auto-encryption should be bypassed
193+
*/
194+
public boolean isBypassAutoEncryption() {
195+
return bypassAutoEncryption;
196+
}
197+
198+
private AutoEncryptionSettings(final Builder builder) {
199+
this.keyVaultMongoClientSettings = builder.keyVaultMongoClientSettings;
200+
this.keyVaultNamespace = notNull("keyVaultNamespace", builder.keyVaultNamespace);
201+
this.kmsProviders = notNull("kmsProviders", builder.kmsProviders);
202+
this.namespaceToLocalSchemaDocumentMap = notNull("namespaceToLocalSchemaDocumentMap", builder.namespaceToLocalSchemaDocumentMap);
203+
this.extraOptions = notNull("extraOptions", builder.extraOptions);
204+
this.bypassAutoEncryption = builder.bypassAutoEncryption;
205+
}
206+
}

0 commit comments

Comments
 (0)