Skip to content

Commit 8fce5f0

Browse files
committed
Read from data key collection with majority read concern
JAVA-3464
1 parent 9d0cac6 commit 8fce5f0

27 files changed

+280
-122
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.mongodb.async.client.internal;
1818

1919
import com.mongodb.MongoNamespace;
20+
import com.mongodb.ReadConcern;
2021
import com.mongodb.async.SingleResultCallback;
2122
import com.mongodb.async.client.MongoClient;
2223
import org.bson.BsonDocument;
@@ -40,6 +41,7 @@ class KeyRetriever implements Closeable {
4041

4142
public void find(final BsonDocument keyFilter, final SingleResultCallback<List<BsonDocument>> callback) {
4243
client.getDatabase(namespace.getDatabaseName()).getCollection(namespace.getCollectionName(), BsonDocument.class)
44+
.withReadConcern(ReadConcern.MAJORITY)
4345
.find(keyFilter).into(new ArrayList<BsonDocument>(), callback);
4446
}
4547

driver-core/src/test/resources/client-side-encryption/README.rst

Lines changed: 127 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -71,26 +71,28 @@ Each YAML file has the following keys:
7171

7272
- ``clientOptions``: Optional, parameters to pass to MongoClient().
7373

74-
- ``auto_encrypt_opts``: Optional
74+
- ``autoEncryptOpts``: Optional
7575

76-
- ``kms_providers`` A dictionary of KMS providers to set on the key vault ("aws" or "local")
76+
- ``kmsProviders`` A dictionary of KMS providers to set on the key vault ("aws" or "local")
7777

7878
- ``aws`` The AWS KMS provider. An empty object. Drivers MUST fill in AWS credentials from the environment.
7979

8080
- ``local`` The local KMS provider.
8181

8282
- ``key`` A 96 byte local key.
8383

84-
- ``schema_map``: Optional, a map from namespaces to local JSON schemas.
84+
- ``schemaMap``: Optional, a map from namespaces to local JSON schemas.
8585

8686
- ``keyVaultNamespace``: Optional, a namespace to the key vault collection. Defaults to "admin.datakeys".
8787

88+
- ``bypassAutoEncryption``: Optional, a boolean to indicate whether or not auto encryption should be bypassed. Defaults to ``false``.
89+
8890
- ``operations``: Array of documents, each describing an operation to be
8991
executed. Each document has the following fields:
9092

9193
- ``name``: |txn|
9294

93-
- ``object``: |txn|
95+
- ``object``: |txn|. Defaults to "collection" if omitted.
9496

9597
- ``collectionOptions``: |txn|
9698

@@ -125,18 +127,25 @@ Then for each element in ``tests``:
125127
#. Drop the ``admin.datakeys`` collection using writeConcern "majority".
126128
#. Insert the data specified into the ``admin.datakeys`` with write concern "majority".
127129

128-
#. Create a MongoClient using ``clientOptions``.
130+
#. Create a MongoClient.
129131

130-
#. If ``autoEncryptOpts`` includes ``aws`` as a KMS provider, pass in AWS credentials from the environment.
131-
#. If ``autoEncryptOpts`` does not include ``keyVaultNamespace``, default it to ``admin.datakeys``
132-
133132
#. Create a collection object from the MongoClient, using the ``database_name``
134-
and ``collection_name`` fields from the YAML file.
135-
#. Drop the test collection, using writeConcern "majority".
133+
and ``collection_name`` fields from the YAML file. Drop the collection
134+
with writeConcern "majority". If a ``json_schema`` is defined in the test,
135+
use the ``createCollection`` command to explicitly create the collection:
136+
137+
.. code:: typescript
138+
139+
{"create": <collection>, "validator": {"$jsonSchema": <json_schema>}}
140+
136141
#. If the YAML file contains a ``data`` array, insert the documents in ``data``
137142
into the test collection, using writeConcern "majority".
138143

139-
#. Set Command Monitoring listeners on the MongoClient.
144+
#. Create a **new** MongoClient using ``clientOptions``.
145+
146+
#. If ``autoEncryptOpts`` includes ``aws`` as a KMS provider, pass in AWS credentials from the environment.
147+
#. If ``autoEncryptOpts`` does not include ``keyVaultNamespace``, default it to ``admin.datakeys``.
148+
140149
#. For each element in ``operations``:
141150

142151
- Enter a "try" block or your programming language's closest equivalent.
@@ -204,7 +213,7 @@ Data key and double encryption
204213

205214
First, perform the setup.
206215

207-
#. Create a MongoClient without encryption enabled (referred to as ``client``).
216+
#. Create a MongoClient without encryption enabled (referred to as ``client``). Enable command monitoring to listen for command_started events.
208217

209218
#. Using ``client``, drop the collections ``admin.datakeys`` and ``db.coll``.
210219

@@ -243,13 +252,16 @@ First, perform the setup.
243252
}
244253
}
245254
255+
Configure ``client_encryption`` with the ``keyVaultClient`` of the previously created ``client``.
256+
246257
Then, test creating and using data keys from a ``local`` KMS provider:
247258

248259
#. Call ``client_encryption.createDataKey()`` with the ``local`` KMS provider and keyAltNames set to ``["local_altname"]``.
249260

250261
- Expect a BSON binary with subtype 4 to be returned, referred to as ``local_datakey_id``.
251262
- Use ``client`` to run a ``find`` on ``admin.datakeys`` by querying with the ``_id`` set to the ``local_datakey_id``.
252263
- Expect that exactly one document is returned with the "masterKey.provider" equal to "local".
264+
- Check that ``client`` captured a command_started event for the ``insert`` command containing a majority writeConcern.
253265

254266
#. Call ``client_encryption.encrypt()`` with the value "hello local", the algorithm ``AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic``, and the ``key_id`` of ``local_datakey_id``.
255267

@@ -276,6 +288,7 @@ Then, repeat the above tests with the ``aws`` KMS provider:
276288
- Expect a BSON binary with subtype 4 to be returned, referred to as ``aws_datakey_id``.
277289
- Use ``client`` to run a ``find`` on ``admin.datakeys`` by querying with the ``_id`` set to the ``aws_datakey_id``.
278290
- Expect that exactly one document is returned with the "masterKey.provider" equal to "aws".
291+
- Check that ``client`` captured a command_started event for the ``insert`` command containing a majority writeConcern.
279292

280293
#. Call ``client_encryption.encrypt()`` with the value "hello aws", the algorithm ``AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic``, and the ``key_id`` of ``aws_datakey_id``.
281294

@@ -355,13 +368,9 @@ First, perform the setup.
355368

356369
Using ``client_encrypted`` perform the following operations:
357370

358-
#. Insert ``{ "_id": "no_encryption_under_2mib", "unencrypted": <the string "a" repeated (2097152 - 1000) times> }``. (Note 2097152 is 2^21 bytes, or 2 MiB).
371+
#. Insert ``{ "_id": "over_2mib_under_16mib", "unencrypted": <the string "a" repeated 2097152 times> }``.
359372

360-
Expect this to succeed.
361-
362-
#. Insert ``{ "_id": "no_encryption_over_2mib", "unencrypted": <the string "a" repeated 2097152 times> }``.
363-
364-
Expect this to throw an exception due to exceeding the reduced maximum BSON document size.
373+
Expect this to succeed since this is still under the ``maxBsonObjectSize`` limit.
365374

366375
#. Insert the document `limits/limits-doc.json <../limits/limits-doc.json>`_ concatenated with ``{ "_id": "encryption_exceeds_2mib", "unencrypted": < the string "a" repeated (2097152 - 2000) times > }``
367376
Note: limits-doc.json is a 1005 byte BSON document that encrypts to a ~10,000 byte document.
@@ -371,9 +380,9 @@ Using ``client_encrypted`` perform the following operations:
371380

372381
#. Bulk insert the following:
373382

374-
- ``{ "_id": "no_encryption_under_2mib_1", "unencrypted": <the string "a" repeated (2097152 - 1000) times> }``
383+
- ``{ "_id": "over_2mib_1", "unencrypted": <the string "a" repeated (2097152) times> }``
375384

376-
- ``{ "_id": "no_encryption_under_2mib_2", "unencrypted": <the string "a" repeated (2097152 - 1000) times> }``
385+
- ``{ "_id": "over_2mib_2", "unencrypted": <the string "a" repeated (2097152) times> }``
377386

378387
Expect the bulk write to succeed and split after first doc (i.e. two inserts occur). This may be verified using `command monitoring <https://github.com/mongodb/specifications/tree/master/source/command-monitoring/command-monitoring.rst>`_.
379388

@@ -383,7 +392,15 @@ Using ``client_encrypted`` perform the following operations:
383392

384393
- The document `limits/limits-doc.json <../limits/limits-doc.json>`_ concatenated with ``{ "_id": "encryption_exceeds_2mib_2", "unencrypted": < the string "a" repeated (2097152 - 2000) times > }``
385394

386-
Expect the bulk write to succeed and split after first doc (i.e. two inserts occur).
395+
Expect the bulk write to succeed and split after first doc (i.e. two inserts occur). This may be verified using `command monitoring <https://github.com/mongodb/specifications/tree/master/source/command-monitoring/command-monitoring.rst>`_.
396+
397+
#. Insert ``{ "_id": "under_16mib", "unencrypted": <the string "a" repeated 16777216 - 2000 times>``.
398+
399+
Expect this to succeed since this is still (just) under the ``maxBsonObjectSize`` limit.
400+
401+
#. Insert the document `limits/limits-doc.json <../limits/limits-doc.json>`_ concatenated with ``{ "_id": "encryption_exceeds_16mib", "unencrypted": < the string "a" repeated (16777216 - 2000) times > }``
402+
403+
Expect this to fail since encryption results in a document exceeding the ``maxBsonObjectSize`` limit.
387404

388405
Optionally, if it is possible to mock the maxWriteBatchSize (i.e. the maximum number of documents in a batch) test that setting maxWriteBatchSize=1 and inserting the two documents ``{ "_id": "a" }, { "_id": "b" }`` with ``client_encrypted`` splits the operation into two inserts.
389406

@@ -487,5 +504,93 @@ The corpus test exhaustively enumerates all ways to encrypt all BSON value types
487504
- If ``allowed`` is true, decrypt the value with ``client_encryption``. Decrypt the value of the corresponding field of ``corpus_encrypted`` and validate that they are both equal.
488505
- If ``allowed`` is false, validate the value exactly equals the value of the corresponding field of ``corpus`` (neither was encrypted).
489506

490-
9. Repeat steps 1-8 with a local JSON schema. I.e. amend step 4 to configure the schema on ``client_encrypted`` and ``client_encryption`` with the ``schema_map`` option.
507+
9. Repeat steps 1-8 with a local JSON schema. I.e. amend step 4 to configure the schema on ``client_encrypted`` with the ``schema_map`` option.
508+
509+
Custom Endpoint Test
510+
====================
511+
512+
Data keys created with AWS KMS may specify a custom endpoint to contact (instead of the default endpoint derived from the AWS region).
513+
514+
1. Create a ``ClientEncryption`` object (referred to as ``client_encryption``)
515+
516+
Configure with ``aws`` KMS providers as follows:
517+
518+
.. code:: javascript
519+
520+
{
521+
"aws": { <AWS credentials> }
522+
}
523+
524+
Configure with ``keyVaultNamespace`` set to ``admin.datakeys``, and a default MongoClient as the ``keyVaultClient``.
525+
526+
2. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
527+
528+
.. code:: javascript
529+
530+
{
531+
region: "us-east-1",
532+
key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0"
533+
}
534+
535+
Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to validate it works.
536+
537+
3. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
538+
539+
.. code:: javascript
540+
541+
{
542+
region: "us-east-1",
543+
key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
544+
endpoint: "kms.us-east-1.amazonaws.com"
545+
}
546+
547+
Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to validate it works.
548+
549+
4. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
550+
551+
.. code:: javascript
552+
553+
{
554+
region: "us-east-1",
555+
key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
556+
endpoint: "kms.us-east-1.amazonaws.com:443"
557+
}
558+
559+
Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to validate it works.
560+
561+
5. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
562+
563+
.. code:: javascript
564+
565+
{
566+
region: "us-east-1",
567+
key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
568+
endpoint: "kms.us-east-1.amazonaws.com:12345"
569+
}
570+
571+
Expect this to fail with a socket connection error.
572+
573+
6. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
574+
575+
.. code:: javascript
576+
577+
{
578+
region: "us-east-1",
579+
key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
580+
endpoint: "kms.us-east-2.amazonaws.com"
581+
}
582+
583+
Expect this to fail with an exception with a message containing the string: "us-east-1"
584+
585+
7. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
586+
587+
.. code:: javascript
588+
589+
{
590+
region: "us-east-1",
591+
key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
592+
endpoint: "example.com"
593+
}
594+
595+
Expect this to fail with an exception with a message containing the string: "parse error"
491596

driver-core/src/test/resources/client-side-encryption/aggregate.json

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@
143143
"command_started_event": {
144144
"command": {
145145
"listCollections": 1,
146-
"cursor": {},
147146
"filter": {
148147
"name": "default"
149148
}
@@ -155,7 +154,6 @@
155154
"command_started_event": {
156155
"command": {
157156
"listCollections": 1,
158-
"cursor": {},
159157
"filter": {
160158
"name": "datakeys"
161159
},
@@ -189,7 +187,10 @@
189187
}
190188
]
191189
},
192-
"$db": "admin"
190+
"$db": "admin",
191+
"readConcern": {
192+
"level": "majority"
193+
}
193194
},
194195
"command_name": "find"
195196
}
@@ -255,7 +256,6 @@
255256
"command_started_event": {
256257
"command": {
257258
"listCollections": 1,
258-
"cursor": {},
259259
"filter": {
260260
"name": "default"
261261
}
@@ -277,7 +277,6 @@
277277
"command_started_event": {
278278
"command": {
279279
"listCollections": 1,
280-
"cursor": {},
281280
"filter": {
282281
"name": "datakeys"
283282
},
@@ -311,7 +310,10 @@
311310
}
312311
]
313312
},
314-
"$db": "admin"
313+
"$db": "admin",
314+
"readConcern": {
315+
"level": "majority"
316+
}
315317
},
316318
"command_name": "find"
317319
}

driver-core/src/test/resources/client-side-encryption/basic.json

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@
137137
"command_started_event": {
138138
"command": {
139139
"listCollections": 1,
140-
"cursor": {},
141140
"filter": {
142141
"name": "default"
143142
}
@@ -149,7 +148,6 @@
149148
"command_started_event": {
150149
"command": {
151150
"listCollections": 1,
152-
"cursor": {},
153151
"filter": {
154152
"name": "datakeys"
155153
},
@@ -183,7 +181,10 @@
183181
}
184182
]
185183
},
186-
"$db": "admin"
184+
"$db": "admin",
185+
"readConcern": {
186+
"level": "majority"
187+
}
187188
},
188189
"command_name": "find"
189190
}
@@ -275,7 +276,6 @@
275276
"command_started_event": {
276277
"command": {
277278
"listCollections": 1,
278-
"cursor": {},
279279
"filter": {
280280
"name": "default"
281281
}
@@ -287,7 +287,6 @@
287287
"command_started_event": {
288288
"command": {
289289
"listCollections": 1,
290-
"cursor": {},
291290
"filter": {
292291
"name": "datakeys"
293292
},
@@ -321,7 +320,10 @@
321320
}
322321
]
323322
},
324-
"$db": "admin"
323+
"$db": "admin",
324+
"readConcern": {
325+
"level": "majority"
326+
}
325327
},
326328
"command_name": "find"
327329
}

0 commit comments

Comments
 (0)