-
Notifications
You must be signed in to change notification settings - Fork 1.8k
test(NODE-7623): fix explicit encryption prose tests for MongoDB 9.0+ #4982
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
seanrmilligan
wants to merge
1
commit into
mongodb:main
Choose a base branch
from
seanrmilligan:sean.milligan/explicit-encryption-test
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+327
−301
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
293 changes: 293 additions & 0 deletions
293
...ration/client-side-encryption/client_side_encryption.prose.12.explicit_encryption.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,293 @@ | ||
| import { BSON, EJSON } from 'bson'; | ||
| import { expect } from 'chai'; | ||
| import * as fs from 'fs/promises'; | ||
| import * as path from 'path'; | ||
|
|
||
| import { ClientEncryption } from '../../mongodb'; | ||
| import { getEncryptExtraOptions } from '../../tools/utils'; | ||
| import { dropCollection } from '../shared'; | ||
| import { getKmsProviders } from './client_side_encryption.prose.test'; | ||
|
|
||
| const LOCAL_KEY = Buffer.from( | ||
| 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', | ||
| 'base64' | ||
| ); | ||
|
|
||
| const eeMetadata: MongoDBMetadataUI = { | ||
| requires: { | ||
| clientSideEncryption: true, | ||
| mongodb: '>=7.0.0', | ||
| topology: ['replicaset', 'sharded'] | ||
| } | ||
| }; | ||
|
|
||
| describe('12. Explicit Encryption', eeMetadata, function () { | ||
| const data = path.join(__dirname, '..', '..', 'spec', 'client-side-encryption', 'etc', 'data'); | ||
| let encryptedFields; | ||
| let encryptedFieldsC10; | ||
| let key1Document; | ||
| let key1Id; | ||
| let setupClient; | ||
| let keyVaultClient; | ||
| let clientEncryption; | ||
| let encryptedClient; | ||
|
|
||
| beforeEach(async function () { | ||
| // Load the file encryptedFields.json as encryptedFields. | ||
| encryptedFields = EJSON.parse( | ||
| await fs.readFile(path.join(data, 'encryptedFields.json'), 'utf8'), | ||
| { relaxed: false } | ||
| ); | ||
| // Load the file encryptedFields-c10.json as encryptedFields_c10. | ||
| encryptedFieldsC10 = EJSON.parse( | ||
| await fs.readFile(path.join(data, 'encryptedFields-c10.json'), 'utf8'), | ||
| { relaxed: false } | ||
| ); | ||
| // Load the file key1-document.json as key1Document. | ||
| key1Document = EJSON.parse( | ||
| await fs.readFile(path.join(data, 'keys', 'key1-document.json'), 'utf8'), | ||
| { relaxed: false } | ||
| ); | ||
| // Read the "_id" field of key1Document as key1ID. | ||
| key1Id = key1Document._id; | ||
| setupClient = this.configuration.newClient(); | ||
| // Drop and create the collection db.explicit_encryption using encryptedFields as an option. | ||
| const db = setupClient.db('db'); | ||
| await dropCollection(db, 'explicit_encryption', { encryptedFields }); | ||
| await db.createCollection('explicit_encryption', { encryptedFields }); | ||
| // Drop and create the collection db.explicit_encryption_c10 using encryptedFields_c10 as an option. | ||
| await dropCollection(db, 'explicit_encryption_c10', { encryptedFields: encryptedFieldsC10 }); | ||
| await db.createCollection('explicit_encryption_c10', { encryptedFields: encryptedFieldsC10 }); | ||
| // Drop and create the collection keyvault.datakeys. | ||
| const kdb = setupClient.db('keyvault'); | ||
| await dropCollection(kdb, 'datakeys'); | ||
| await kdb.createCollection('datakeys'); | ||
| // Insert key1Document in keyvault.datakeys with majority write concern. | ||
| await kdb.collection('datakeys').insertOne(key1Document, { writeConcern: { w: 'majority' } }); | ||
| // Create a MongoClient named keyVaultClient. | ||
| keyVaultClient = this.configuration.newClient(); | ||
| // Create a ClientEncryption object named clientEncryption with these options: | ||
| // ClientEncryptionOpts { | ||
| // keyVaultClient: <keyVaultClient>; | ||
| // keyVaultNamespace: "keyvault.datakeys"; | ||
| // kmsProviders: { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } } | ||
| // } | ||
| clientEncryption = new ClientEncryption(keyVaultClient, { | ||
| keyVaultNamespace: 'keyvault.datakeys', | ||
| kmsProviders: getKmsProviders(LOCAL_KEY), | ||
| bson: BSON, | ||
| extraOptions: getEncryptExtraOptions() | ||
| }); | ||
| // Create a MongoClient named ``encryptedClient`` with these ``AutoEncryptionOpts``: | ||
| // AutoEncryptionOpts { | ||
| // keyVaultNamespace: "keyvault.datakeys"; | ||
| // kmsProviders: { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }, | ||
| // bypassQueryAnalysis: true | ||
| // } | ||
| encryptedClient = this.configuration.newClient( | ||
| {}, | ||
| { | ||
| autoEncryption: { | ||
| bypassQueryAnalysis: true, | ||
| keyVaultNamespace: 'keyvault.datakeys', | ||
| kmsProviders: getKmsProviders(LOCAL_KEY), | ||
| extraOptions: getEncryptExtraOptions() | ||
| } | ||
| } | ||
| ); | ||
| }); | ||
|
|
||
| afterEach(async function () { | ||
| await setupClient.close(); | ||
| await keyVaultClient.close(); | ||
| await encryptedClient.close(); | ||
| }); | ||
|
|
||
| context('Case 1: can insert encrypted indexed and find', eeMetadata, function () { | ||
| let insertPayload; | ||
| let findPayload; | ||
|
|
||
| beforeEach(async function () { | ||
| // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts: | ||
| // class EncryptOpts { | ||
| // keyId : <key1ID> | ||
| // algorithm: "Indexed", | ||
| // } | ||
| // Store the result in insertPayload. | ||
| insertPayload = await clientEncryption.encrypt('encrypted indexed value', { | ||
| keyId: key1Id, | ||
| algorithm: 'Indexed', | ||
| contentionFactor: 0 | ||
| }); | ||
| // Use encryptedClient to insert the document { "encryptedIndexed": <insertPayload> } | ||
| // into db.explicit_encryption. | ||
| await encryptedClient.db('db').collection('explicit_encryption').insertOne({ | ||
| encryptedIndexed: insertPayload | ||
| }); | ||
| // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts: | ||
| // class EncryptOpts { | ||
| // keyId : <key1ID> | ||
| // algorithm: "Indexed", | ||
| // queryType: Equality | ||
| // } | ||
| // Store the result in findPayload. | ||
| findPayload = await clientEncryption.encrypt('encrypted indexed value', { | ||
| keyId: key1Id, | ||
| algorithm: 'Indexed', | ||
| queryType: 'equality', | ||
| contentionFactor: 0 | ||
| }); | ||
| }); | ||
|
|
||
| it('returns the decrypted value', async function () { | ||
| // Use encryptedClient to run a "find" operation on the db.explicit_encryption | ||
| // collection with the filter { "encryptedIndexed": <findPayload> }. | ||
| // Assert one document is returned containing the field | ||
| // { "encryptedIndexed": "encrypted indexed value" }. | ||
| const collection = encryptedClient.db('db').collection('explicit_encryption'); | ||
| const result = await collection.findOne({ encryptedIndexed: findPayload }); | ||
| expect(result).to.have.property('encryptedIndexed', 'encrypted indexed value'); | ||
| }); | ||
| }); | ||
|
|
||
| context( | ||
| 'Case 2: can insert encrypted indexed and find with non-zero contention', | ||
| eeMetadata, | ||
| function () { | ||
| let findPayload; | ||
|
|
||
| beforeEach(async function () { | ||
| for (let i = 0; i < 10; i++) { | ||
| // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts: | ||
| // class EncryptOpts { | ||
| // keyId : <key1ID> | ||
| // algorithm: "Indexed", | ||
| // contentionFactor: 10 | ||
| // } | ||
| // Store the result in insertPayload. | ||
| const insertPayload = await clientEncryption.encrypt('encrypted indexed value', { | ||
| keyId: key1Id, | ||
| algorithm: 'Indexed', | ||
| contentionFactor: 10 | ||
| }); | ||
| // Use encryptedClient to insert the document { "encryptedIndexed": <insertPayload> } | ||
| // into db.explicit_encryption_c10. | ||
| await encryptedClient.db('db').collection('explicit_encryption_c10').insertOne({ | ||
| encryptedIndexed: insertPayload | ||
| }); | ||
| // Repeat the above steps 10 times to insert 10 total documents. | ||
| // The insertPayload must be regenerated each iteration. | ||
| } | ||
| // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts: | ||
| // class EncryptOpts { | ||
| // keyId : <key1ID> | ||
| // algorithm: "Indexed", | ||
| // queryType: Equality, | ||
| // contentionFactor: 10 | ||
| // } | ||
| // Store the result in findPayload. | ||
| findPayload = await clientEncryption.encrypt('encrypted indexed value', { | ||
| keyId: key1Id, | ||
| algorithm: 'Indexed', | ||
| queryType: 'equality', | ||
| contentionFactor: 10 | ||
| }); | ||
| }); | ||
|
|
||
| it('returns all documents with contention', async function () { | ||
| // Use encryptedClient to run a "find" operation on the db.explicit_encryption_c10 | ||
| // collection with the filter { "encryptedIndexed": <findPayload> }. | ||
| // Assert 10 documents are returned. Assert each returned document contains the | ||
| // field { "encryptedIndexed": "encrypted indexed value" }. | ||
| const collection = encryptedClient.db('db').collection('explicit_encryption_c10'); | ||
| const result = await collection.find({ encryptedIndexed: findPayload }).toArray(); | ||
| expect(result.length).to.equal(10); | ||
| for (const doc of result) { | ||
| expect(doc).to.have.property('encryptedIndexed', 'encrypted indexed value'); | ||
| } | ||
| }); | ||
| } | ||
| ); | ||
|
|
||
| context('Case 3: can insert encrypted unindexed', eeMetadata, function () { | ||
| let insertPayload; | ||
|
|
||
| beforeEach(async function () { | ||
| // Use clientEncryption to encrypt the value "encrypted unindexed value" with these EncryptOpts: | ||
| // class EncryptOpts { | ||
| // keyId : <key1ID> | ||
| // algorithm: "Unindexed" | ||
| // } | ||
| // Store the result in insertPayload. | ||
| insertPayload = await clientEncryption.encrypt('encrypted unindexed value', { | ||
| keyId: key1Id, | ||
| algorithm: 'Unindexed' | ||
| }); | ||
| // Use encryptedClient to insert the document { "_id": 1, "encryptedUnindexed": <insertPayload> } | ||
| // into db.explicit_encryption. | ||
| await encryptedClient.db('db').collection('explicit_encryption').insertOne({ | ||
| _id: 1, | ||
| encryptedUnindexed: insertPayload | ||
| }); | ||
| }); | ||
|
|
||
| it('returns unindexed documents', async function () { | ||
| // Use encryptedClient to run a "find" operation on the db.explicit_encryption | ||
| // collection with the filter { "_id": 1 }. | ||
| // Assert one document is returned containing the field | ||
| // { "encryptedUnindexed": "encrypted unindexed value" }. | ||
| const collection = encryptedClient.db('db').collection('explicit_encryption'); | ||
| const result = await collection.findOne({ _id: 1 }); | ||
| expect(result).to.have.property('encryptedUnindexed', 'encrypted unindexed value'); | ||
| }); | ||
| }); | ||
|
|
||
| context('Case 4: can roundtrip encrypted indexed', eeMetadata, function () { | ||
| let payload; | ||
|
|
||
| beforeEach(async function () { | ||
| // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts: | ||
| // class EncryptOpts { | ||
| // keyId : <key1ID> | ||
| // algorithm: "Indexed", | ||
| // } | ||
| // Store the result in payload. | ||
| payload = await clientEncryption.encrypt('encrypted indexed value', { | ||
| keyId: key1Id, | ||
| algorithm: 'Indexed', | ||
| contentionFactor: 0 | ||
| }); | ||
| }); | ||
|
|
||
| it('decrypts the value', async function () { | ||
| // Use clientEncryption to decrypt payload. Assert the returned value | ||
| // equals "encrypted indexed value". | ||
| const result = await clientEncryption.decrypt(payload); | ||
| expect(result).equals('encrypted indexed value'); | ||
| }); | ||
| }); | ||
|
|
||
| context('Case 5: can roundtrip encrypted unindexed', eeMetadata, function () { | ||
| let payload; | ||
|
|
||
| beforeEach(async function () { | ||
| // Use clientEncryption to encrypt the value "encrypted unindexed value" with these EncryptOpts: | ||
| // class EncryptOpts { | ||
| // keyId : <key1ID> | ||
| // algorithm: "Unindexed", | ||
| // } | ||
| // Store the result in payload. | ||
| payload = await clientEncryption.encrypt('encrypted unindexed value', { | ||
| keyId: key1Id, | ||
| algorithm: 'Unindexed' | ||
| }); | ||
| }); | ||
|
|
||
| it('decrypts the value', async function () { | ||
| // Use clientEncryption to decrypt payload. Assert the returned value | ||
| // equals "encrypted unindexed value". | ||
| const result = await clientEncryption.decrypt(payload); | ||
| expect(result).equals('encrypted unindexed value'); | ||
| }); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do seem to define the various test interfaces/functions/etc., instead of importing them: currently, no prose file imports anything from another prose test file. This test would be the first to break that pattern. Let's follow the pattern and re-define whatever we need, instead of importing.