Skip to content

Commit 81f74cc

Browse files
rozzavbabanin
andauthored
CSFLE auto encryption tests improvements (#1788)
* Support auto encryption in unified tests Added support for schema 1.23 JAVA-5792 * Support CSFLE unified tests - Added scala UnifiedTest support. - Added scala unified tests for ClientEncryption and Crud JAVA-5674 --------- Co-authored-by: Viacheslav Babanin <[email protected]>
1 parent 63f94fb commit 81f74cc

File tree

17 files changed

+346
-46
lines changed

17 files changed

+346
-46
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import java.util.HashMap;
8989
import java.util.List;
9090
import java.util.Map;
91+
import java.util.Optional;
9192

9293
import static com.mongodb.assertions.Assertions.assertNotNull;
9394
import static com.mongodb.connection.ClusterConnectionMode.LOAD_BALANCED;
@@ -278,6 +279,11 @@ public static String getEnv(final String name, final String defaultValue) {
278279
return value == null ? defaultValue : value;
279280
}
280281

282+
public static Optional<String> cryptSharedLibPathSysPropValue() {
283+
String value = getEnv("CRYPT_SHARED_LIB_PATH", "");
284+
return value.isEmpty() ? Optional.empty() : Optional.of(value);
285+
}
286+
281287
@Nullable
282288
public static String getEnv(final String name) {
283289
return System.getenv(name);

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,14 @@ public void create(final WriteConcern writeConcern, final BsonDocument createOpt
176176
case "size":
177177
createCollectionOptions.sizeInBytes(createOptions.getNumber("size").longValue());
178178
break;
179+
case "encryptedFields":
180+
createCollectionOptions.encryptedFields(createOptions.getDocument("encryptedFields"));
181+
break;
182+
case "validator":
183+
ValidationOptions validationOptions = new ValidationOptions();
184+
validationOptions.validator(createOptions.getDocument("validator"));
185+
createCollectionOptions.validationOptions(validationOptions);
186+
break;
179187
default:
180188
throw new UnsupportedOperationException("Unsupported create collection option: " + option);
181189
}
@@ -195,6 +203,10 @@ public void create(final String collectionName, final CreateCollectionOptions op
195203
if (indexOptionDefaults.getStorageEngine() != null) {
196204
operation.indexOptionDefaults(new BsonDocument("storageEngine", toBsonDocument(indexOptionDefaults.getStorageEngine())));
197205
}
206+
Bson encryptedFields = options.getEncryptedFields();
207+
if (encryptedFields != null) {
208+
operation.encryptedFields(encryptedFields.toBsonDocument());
209+
}
198210
ValidationOptions validationOptions = options.getValidationOptions();
199211
if (validationOptions.getValidator() != null) {
200212
operation.validator(toBsonDocument(validationOptions.getValidator()));

driver-core/src/test/unit/com/mongodb/internal/connection/TestClusterListener.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.util.concurrent.locks.ReentrantLock;
3333
import java.util.function.Predicate;
3434

35-
import static com.mongodb.assertions.Assertions.isTrue;
3635
import static com.mongodb.assertions.Assertions.notNull;
3736
import static com.mongodb.internal.Locks.withLock;
3837

@@ -48,15 +47,17 @@ public final class TestClusterListener implements ClusterListener {
4847

4948
@Override
5049
public void clusterOpening(final ClusterOpeningEvent event) {
51-
isTrue("clusterOpeningEvent is null", clusterOpeningEvent == null);
52-
clusterOpeningEvent = event;
50+
if (clusterOpeningEvent == null) {
51+
clusterOpeningEvent = event;
52+
}
5353
}
5454

5555
@Override
5656
public void clusterClosed(final ClusterClosedEvent event) {
57-
isTrue("clusterClosingEvent is null", clusterClosingEvent == null);
58-
closedLatch.countDown();
59-
clusterClosingEvent = event;
57+
if (clusterClosingEvent == null) {
58+
closedLatch.countDown();
59+
clusterClosingEvent = event;
60+
}
6061
}
6162

6263
@Override
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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 org.mongodb.scala.syncadapter
18+
19+
import com.mongodb.ClusterFixture.TIMEOUT_DURATION
20+
import com.mongodb.client.model.{ CreateCollectionOptions, CreateEncryptedCollectionParams }
21+
import com.mongodb.client.model.vault.{
22+
DataKeyOptions,
23+
EncryptOptions,
24+
RewrapManyDataKeyOptions,
25+
RewrapManyDataKeyResult
26+
}
27+
import com.mongodb.client.result.DeleteResult
28+
import com.mongodb.client.vault.{ ClientEncryption => JClientEncryption }
29+
import com.mongodb.client.{ MongoDatabase => JMongoDatabase }
30+
import org.bson.{ BsonBinary, BsonDocument, BsonValue }
31+
import org.bson.conversions.Bson
32+
import org.mongodb.scala.vault.ClientEncryption
33+
import reactor.core.publisher.Mono
34+
35+
import java.util.Objects.requireNonNull
36+
37+
case class SyncClientEncryption(wrapped: ClientEncryption) extends JClientEncryption {
38+
39+
override def createDataKey(kmsProvider: String): BsonBinary =
40+
requireNonNull(Mono.from(wrapped.createDataKey(kmsProvider, new DataKeyOptions)).block(TIMEOUT_DURATION))
41+
42+
override def createDataKey(kmsProvider: String, dataKeyOptions: DataKeyOptions): BsonBinary =
43+
requireNonNull(Mono.from(wrapped.createDataKey(kmsProvider, dataKeyOptions)).block(TIMEOUT_DURATION))
44+
45+
override def encrypt(value: BsonValue, options: EncryptOptions): BsonBinary =
46+
requireNonNull(Mono.from(wrapped.encrypt(value, options)).block(TIMEOUT_DURATION))
47+
48+
override def encryptExpression(expression: Bson, options: EncryptOptions): BsonDocument =
49+
requireNonNull(Mono.from(wrapped
50+
.encryptExpression(expression.toBsonDocument, options)).block(TIMEOUT_DURATION).toBsonDocument)
51+
52+
override def decrypt(value: BsonBinary): BsonValue =
53+
requireNonNull(Mono.from(wrapped.decrypt(value)).block(TIMEOUT_DURATION))
54+
55+
override def deleteKey(id: BsonBinary): DeleteResult =
56+
requireNonNull(Mono.from(wrapped.deleteKey(id)).block(TIMEOUT_DURATION))
57+
58+
override def getKey(id: BsonBinary): BsonDocument = Mono.from(wrapped.getKey(id)).block(TIMEOUT_DURATION)
59+
60+
override def getKeys = new SyncFindIterable[BsonDocument](wrapped.keys)
61+
62+
override def addKeyAltName(id: BsonBinary, keyAltName: String): BsonDocument =
63+
Mono.from(wrapped.addKeyAltName(id, keyAltName)).block(TIMEOUT_DURATION)
64+
65+
override def removeKeyAltName(id: BsonBinary, keyAltName: String): BsonDocument =
66+
Mono.from(wrapped.removeKeyAltName(id, keyAltName)).block(TIMEOUT_DURATION)
67+
68+
override def getKeyByAltName(keyAltName: String): BsonDocument =
69+
Mono.from(wrapped.getKeyByAltName(keyAltName)).block(TIMEOUT_DURATION)
70+
71+
override def rewrapManyDataKey(filter: Bson): RewrapManyDataKeyResult =
72+
requireNonNull(Mono.from(wrapped.rewrapManyDataKey(filter)).block(TIMEOUT_DURATION))
73+
74+
override def rewrapManyDataKey(filter: Bson, options: RewrapManyDataKeyOptions): RewrapManyDataKeyResult =
75+
requireNonNull(Mono.from(wrapped.rewrapManyDataKey(filter, options)).block(TIMEOUT_DURATION))
76+
77+
override def createEncryptedCollection(
78+
database: JMongoDatabase,
79+
collectionName: String,
80+
createCollectionOptions: CreateCollectionOptions,
81+
createEncryptedCollectionParams: CreateEncryptedCollectionParams
82+
): BsonDocument = {
83+
database match {
84+
case syncMongoDatabase: SyncMongoDatabase =>
85+
requireNonNull(Mono.from(wrapped.createEncryptedCollection(
86+
syncMongoDatabase.wrapped,
87+
collectionName,
88+
createCollectionOptions,
89+
createEncryptedCollectionParams
90+
)).block(TIMEOUT_DURATION))
91+
case _ => throw new AssertionError(s"Unexpected database type: ${database.getClass}")
92+
}
93+
}
94+
95+
override def close(): Unit = {
96+
wrapped.close()
97+
}
98+
}

driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoCollection.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import org.mongodb.scala.MongoCollection
3333
import org.mongodb.scala.bson.DefaultHelper.DefaultsTo
3434
import org.mongodb.scala.result.{ InsertManyResult, InsertOneResult }
3535

36-
import java.util
3736
import java.util.concurrent.TimeUnit
3837
import scala.collection.JavaConverters._
3938
import scala.concurrent.duration.{ Duration, MILLISECONDS }
@@ -588,25 +587,25 @@ case class SyncMongoCollection[T](wrapped: MongoCollection[T]) extends JMongoCol
588587
)
589588

590589
override def renameCollection(newCollectionNamespace: MongoNamespace): Unit = {
591-
throw new UnsupportedOperationException
590+
wrapped.renameCollection(newCollectionNamespace).toFuture().get()
592591
}
593592

594593
override def renameCollection(
595594
newCollectionNamespace: MongoNamespace,
596595
renameCollectionOptions: RenameCollectionOptions
597596
): Unit = {
598-
throw new UnsupportedOperationException
597+
wrapped.renameCollection(newCollectionNamespace, renameCollectionOptions).toFuture().get()
599598
}
600599

601600
override def renameCollection(clientSession: ClientSession, newCollectionNamespace: MongoNamespace): Unit = {
602-
throw new UnsupportedOperationException
601+
wrapped.renameCollection(unwrap(clientSession), newCollectionNamespace).toFuture().get()
603602
}
604603

605604
override def renameCollection(
606605
clientSession: ClientSession,
607606
newCollectionNamespace: MongoNamespace,
608607
renameCollectionOptions: RenameCollectionOptions
609608
): Unit = {
610-
throw new UnsupportedOperationException
609+
wrapped.renameCollection(unwrap(clientSession), newCollectionNamespace, renameCollectionOptions).toFuture().get()
611610
}
612611
}

driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoDatabase.scala

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,15 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase {
5151
override def withCodecRegistry(codecRegistry: CodecRegistry) =
5252
SyncMongoDatabase(wrapped.withCodecRegistry(codecRegistry))
5353

54-
override def withReadPreference(readPreference: ReadPreference) = throw new UnsupportedOperationException
54+
override def withReadPreference(readPreference: ReadPreference) =
55+
SyncMongoDatabase(wrapped.withReadPreference(readPreference))
5556

56-
override def withWriteConcern(writeConcern: WriteConcern) = throw new UnsupportedOperationException
57+
override def withWriteConcern(writeConcern: WriteConcern) = SyncMongoDatabase(wrapped.withWriteConcern(writeConcern))
5758

58-
override def withReadConcern(readConcern: ReadConcern) = throw new UnsupportedOperationException
59+
override def withReadConcern(readConcern: ReadConcern) = SyncMongoDatabase(wrapped.withReadConcern(readConcern))
5960

60-
override def withTimeout(timeout: Long, timeUnit: TimeUnit) = throw new UnsupportedOperationException
61+
override def withTimeout(timeout: Long, timeUnit: TimeUnit) =
62+
SyncMongoDatabase(wrapped.withTimeout(timeout, timeUnit))
6163

6264
override def getCollection(collectionName: String) =
6365
SyncMongoCollection[Document](wrapped.getCollection(collectionName))
@@ -170,7 +172,7 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase {
170172
}
171173

172174
override def createView(viewName: String, viewOn: String, pipeline: java.util.List[_ <: Bson]): Unit = {
173-
throw new UnsupportedOperationException
175+
wrapped.createView(viewName, viewOn, pipeline.asScala.toList).toFuture().get()
174176
}
175177

176178
override def createView(
@@ -179,7 +181,7 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase {
179181
pipeline: java.util.List[_ <: Bson],
180182
createViewOptions: CreateViewOptions
181183
): Unit = {
182-
throw new UnsupportedOperationException
184+
wrapped.createView(viewName, viewOn, pipeline.asScala.toList, createViewOptions).toFuture().get()
183185
}
184186

185187
override def createView(
@@ -188,7 +190,7 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase {
188190
viewOn: String,
189191
pipeline: java.util.List[_ <: Bson]
190192
): Unit = {
191-
throw new UnsupportedOperationException
193+
wrapped.createView(unwrap(clientSession), viewName, viewOn, pipeline.asScala.toList).toFuture().get()
192194
}
193195

194196
override def createView(
@@ -198,7 +200,13 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase {
198200
pipeline: java.util.List[_ <: Bson],
199201
createViewOptions: CreateViewOptions
200202
): Unit = {
201-
throw new UnsupportedOperationException
203+
wrapped.createView(
204+
unwrap(clientSession),
205+
viewName,
206+
viewOn,
207+
pipeline.asScala.toList,
208+
createViewOptions
209+
).toFuture().get()
202210
}
203211

204212
override def watch = new SyncChangeStreamIterable[Document](wrapped.watch[Document]())
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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 org.mongodb.scala.unified
18+
19+
object ClientEncryptionTest extends UnifiedTest {
20+
val directory = "client-side-encryption/tests/unified"
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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 org.mongodb.scala.unified
18+
19+
object UnifiedCrudTest extends UnifiedTest {
20+
val directory = "crud"
21+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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 org.mongodb.scala.unified
18+
19+
import com.mongodb.client.gridfs.{ GridFSBucket => JGridFSBucket }
20+
import com.mongodb.client.unified.UnifiedTest.Language
21+
import com.mongodb.client.unified.{ UnifiedTest, UnifiedTest => JUnifiedTest }
22+
import com.mongodb.client.vault.{ ClientEncryption => JClientEncryption }
23+
import com.mongodb.client.{ MongoClient => JMongoClient, MongoDatabase => JMongoDatabase }
24+
import com.mongodb.reactivestreams.client.internal.vault.ClientEncryptionImpl
25+
import com.mongodb.{ ClientEncryptionSettings => JClientEncryptionSettings, MongoClientSettings }
26+
import org.junit.jupiter.api.TestInstance
27+
import org.junit.jupiter.api.TestInstance.Lifecycle
28+
import org.junit.jupiter.params.provider.Arguments
29+
import org.mongodb.scala.MongoClient
30+
import org.mongodb.scala.MongoClient.DEFAULT_CODEC_REGISTRY
31+
import org.mongodb.scala.syncadapter.{ SyncClientEncryption, SyncMongoClient }
32+
import org.mongodb.scala.vault.ClientEncryption
33+
34+
import java.util
35+
36+
@TestInstance(Lifecycle.PER_CLASS)
37+
abstract class UnifiedTest extends JUnifiedTest {
38+
39+
val directory: String
40+
41+
def data(): util.Collection[Arguments] = JUnifiedTest.getTestData(directory, true, Language.SCALA)
42+
43+
override def createMongoClient(settings: MongoClientSettings): JMongoClient =
44+
SyncMongoClient(MongoClient(MongoClientSettings.builder(settings).codecRegistry(DEFAULT_CODEC_REGISTRY).build()))
45+
46+
override def createGridFSBucket(database: JMongoDatabase): JGridFSBucket =
47+
throw new NotImplementedError("Not implemented")
48+
49+
override def createClientEncryption(
50+
keyVaultClient: JMongoClient,
51+
clientEncryptionSettings: JClientEncryptionSettings
52+
): JClientEncryption = {
53+
keyVaultClient match {
54+
case client: SyncMongoClient =>
55+
SyncClientEncryption(ClientEncryption(new ClientEncryptionImpl(
56+
client.wrapped.wrapped,
57+
clientEncryptionSettings
58+
)))
59+
case _ => throw new IllegalArgumentException(s"Invalid keyVaultClient type: ${keyVaultClient.getClass}")
60+
}
61+
}
62+
63+
override protected def isReactive: Boolean = true
64+
65+
override protected def getLanguage: Language = Language.SCALA
66+
}

driver-scala/src/main/scala/org/mongodb/scala/MongoClient.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ object MongoClient {
111111
* @param wrapped the underlying java MongoClient
112112
* @since 1.0
113113
*/
114-
case class MongoClient(private val wrapped: JMongoClient) extends MongoCluster(wrapped) with Closeable {
114+
case class MongoClient(protected[scala] val wrapped: JMongoClient) extends MongoCluster(wrapped) with Closeable {
115115

116116
/**
117117
* Close the client, which will close all underlying cached resources, including, for example,

0 commit comments

Comments
 (0)