From 13077572464eac63c9ff2ceccd9b4226be9cb445 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Fri, 10 Feb 2023 15:41:46 +0100 Subject: [PATCH] chore(shell-api): simplify key-mangement helpers by using driver helpers MONGOSH-1205 (#1404) This PR updates the below mentioned methods to start using helpers provided by [libmongocrypt](https://github.com/mongodb/libmongocrypt/blob/f38c549/bindings/node/lib/clientEncryption.js) - KeyVault.getKeys - KeyVault.deleteKey - KeyVault.addKeyAlternateName - KeyVault.removeKeyAlternateName --- .../src/field-level-encryption.spec.ts | 99 +++++++------------ .../shell-api/src/field-level-encryption.ts | 31 ++---- 2 files changed, 44 insertions(+), 86 deletions(-) diff --git a/packages/shell-api/src/field-level-encryption.spec.ts b/packages/shell-api/src/field-level-encryption.spec.ts index f98b6ad35..5bcfecaa0 100644 --- a/packages/shell-api/src/field-level-encryption.spec.ts +++ b/packages/shell-api/src/field-level-encryption.spec.ts @@ -11,7 +11,6 @@ import { signatures, toShellResult } from './decorators'; import { ALL_PLATFORMS, ALL_SERVER_VERSIONS, ALL_TOPOLOGIES, ALL_API_VERSIONS } from './enums'; import { ClientEncryption, ClientSideFieldLevelEncryptionOptions, ClientSideFieldLevelEncryptionKmsProvider as KMSProvider, KeyVault } from './field-level-encryption'; import Mongo from './mongo'; -import { DeleteResult } from './result'; import ShellInstanceState from './shell-instance-state'; import { CliServiceProvider } from '../../service-provider-server'; import { startTestServer } from '../../../testing/integration-testing-hooks'; @@ -340,6 +339,18 @@ describe('Field Level Encryption', () => { } expect.fail('Expected error'); }); + it('reads keyAltNames and keyMaterial from DataKeyEncryptionKeyOptions', async() => { + const rawResult = { result: 1 }; + const keyVault = await mongo.getKeyVault(); + const options = { + keyAltNames: ['b'], + keyMaterial: new bson.Binary(Buffer.from('12345678123498761234123456789012', 'hex'), 4) + }; + + libmongoc.createDataKey.resolves(rawResult); + await keyVault.createKey('local', options); + expect(libmongoc.createDataKey).calledOnceWithExactly('local', options); + }); }); describe('getKey', () => { it('calls find on key coll', async() => { @@ -401,87 +412,45 @@ describe('Field Level Encryption', () => { }); }); describe('getKeys', () => { - it('calls find on key coll', async() => { - const c = { cursor: 1 } as any; - sp.find.returns(c); + it('calls getKeys on libmongocrypt', async() => { + const c = { count: 1 } as any; + libmongoc.getKeys.returns(c); const result = await keyVault.getKeys(); - expect(sp.find).to.have.been.calledOnceWithExactly(DB, COLL, {}, {}); + expect(libmongoc.getKeys).to.have.been.called; expect(result._cursor).to.deep.equal(c); }); }); describe('deleteKey', () => { - it('calls deleteOne on key coll', async() => { - const r = { acknowledged: 1, deletedCount: 1 } as any; - sp.deleteOne.resolves(r); + it('calls deleteKey on libmongocrypt', async() => { + const r = { acknowledged: true, deletedCount: 1 } as any; + libmongoc.deleteKey.resolves(r); const result = await keyVault.deleteKey(KEY_ID); - expect(sp.deleteOne).to.have.been.calledOnceWithExactly(DB, COLL, { _id: KEY_ID }, {}); - expect(result).to.deep.equal(new DeleteResult(true, 1)); + expect(libmongoc.deleteKey).to.have.been.calledOnceWithExactly(KEY_ID); + expect(result).to.deep.eq(r); }); }); describe('addKeyAlternateName', () => { - it('calls findOneAndUpdate on key coll', async() => { + it('calls addKeyAltName on libmongocrypt', async() => { const r = { value: { ok: 1 } } as any; - sp.findOneAndUpdate.resolves(r); + libmongoc.addKeyAltName.resolves(r.value); const result = await keyVault.addKeyAlternateName(KEY_ID, 'altname'); - expect(sp.findOneAndUpdate).to.have.been.calledOnceWithExactly( - DB, - COLL, - { _id: KEY_ID }, - { $addToSet: { 'keyAltNames': 'altname' }, $currentDate: { 'updateDate': true } }, - { returnDocument: 'before' } + expect(libmongoc.addKeyAltName).to.have.been.calledOnceWithExactly( + KEY_ID, + 'altname' ); - expect(result).to.deep.equal({ ok: 1 }); + expect(result).to.deep.equal(r.value); }); }); describe('removeKeyAlternateName', () => { - it('calls findOneAndUpdate on key coll without empty result', async() => { - const r = { value: { ok: 1, keyAltNames: ['other'] } } as any; - sp.findOneAndUpdate.resolves(r); + it('calls removeKeyAltName on libmongocrypt', async() => { + const r = { value: { ok: 1 } } as any; + libmongoc.removeKeyAltName.resolves(r.value); const result = await keyVault.removeKeyAlternateName(KEY_ID, 'altname'); - expect(sp.findOneAndUpdate).to.have.been.calledOnceWithExactly( - DB, - COLL, - { _id: KEY_ID }, - { $pull: { 'keyAltNames': 'altname' }, $currentDate: { 'updateDate': true } }, - { returnDocument: 'before' } + expect(libmongoc.removeKeyAltName).to.have.been.calledOnceWithExactly( + KEY_ID, + 'altname' ); - expect(result).to.deep.equal({ ok: 1, keyAltNames: ['other'] }); - }); - it('calls findOneAndUpdate on key coll with empty result', async() => { - const r = { value: { ok: 1, keyAltNames: ['altname'] } } as any; - const r2 = { value: { ok: 2 } } as any; - sp.findOneAndUpdate.onFirstCall().resolves(r); - sp.findOneAndUpdate.onSecondCall().resolves(r2); - const result = await keyVault.removeKeyAlternateName(KEY_ID, 'altname'); - const calls = sp.findOneAndUpdate.getCalls(); - expect(calls.length).to.equal(2); - expect(calls[0].args).to.deep.equal([ - DB, - COLL, - { _id: KEY_ID }, - { $pull: { 'keyAltNames': 'altname' }, $currentDate: { 'updateDate': true } }, - { returnDocument: 'before' } - ]); - expect(calls[1].args).to.deep.equal([ - DB, - COLL, - { _id: KEY_ID, keyAltNames: { $size: 0 } }, - { $unset: { 'keyAltNames': '' }, $currentDate: { 'updateDate': true } }, - { returnDocument: 'before' } - ]); - expect(result).to.deep.equal(r2.value); - }); - it('reads keyAltNames and keyMaterial from DataKeyEncryptionKeyOptions', async() => { - const rawResult = { result: 1 }; - const keyVault = await mongo.getKeyVault(); - const options = { - keyAltNames: ['b'], - keyMaterial: new bson.Binary(Buffer.from('12345678123498761234123456789012', 'hex'), 4) - }; - - libmongoc.createDataKey.resolves(rawResult); - await keyVault.createKey('local', options); - expect(libmongoc.createDataKey).calledOnceWithExactly('local', options); + expect(result).to.deep.equal(r.value); }); }); describe('rewrapManyDataKey', () => { diff --git a/packages/shell-api/src/field-level-encryption.ts b/packages/shell-api/src/field-level-encryption.ts index 85fc7d4ba..29b1e6101 100644 --- a/packages/shell-api/src/field-level-encryption.ts +++ b/packages/shell-api/src/field-level-encryption.ts @@ -364,47 +364,36 @@ export class KeyVault extends ShellApiWithMongoClass { return await makeSingleDocReturnValue(() => this._keyColl.find({ 'keyAltNames': keyAltName }), 'KeyVault.getKeyByAltName', this._instanceState); } + // eslint-disable-next-line @typescript-eslint/require-await @returnType('Cursor') @apiVersions([1]) @returnsPromise async getKeys(): Promise { - return this._keyColl.find({}); + return new Cursor( + this._mongo, + this._clientEncryption._libmongocrypt.getKeys() + ); } @returnsPromise @apiVersions([1]) async deleteKey(keyId: BinaryType): Promise { assertArgsDefinedType([keyId], [true], 'KeyVault.deleteKey'); - return this._keyColl.deleteOne({ '_id': keyId }); + return this._clientEncryption._libmongocrypt.deleteKey(keyId); } @returnsPromise @apiVersions([1]) - async addKeyAlternateName(keyId: BinaryType, keyAltName: string): Promise { + async addKeyAlternateName(keyId: BinaryType, keyAltName: string): Promise { assertArgsDefinedType([keyId, keyAltName], [true, 'string'], 'KeyVault.addKeyAlternateName'); - return this._keyColl.findAndModify({ - query: { '_id': keyId }, - update: { $addToSet: { 'keyAltNames': keyAltName }, $currentDate: { 'updateDate': true } }, - }); + return this._clientEncryption._libmongocrypt.addKeyAltName(keyId, keyAltName); } @returnsPromise @apiVersions([1]) - async removeKeyAlternateName(keyId: BinaryType, keyAltName: string): Promise { + async removeKeyAlternateName(keyId: BinaryType, keyAltName: string): Promise { assertArgsDefinedType([keyId, keyAltName], [true, 'string'], 'KeyVault.removeKeyAlternateName'); - const ret = await this._keyColl.findAndModify({ - query: { '_id': keyId }, - update: { $pull: { 'keyAltNames': keyAltName }, $currentDate: { 'updateDate': true } } - }); - - if (ret !== null && ret.keyAltNames !== undefined && ret.keyAltNames.length === 1 && ret.keyAltNames[0] === keyAltName) { - // Remove the empty array to prevent duplicate key violations - return this._keyColl.findAndModify({ - query: { '_id': keyId, 'keyAltNames': { $size: 0 } }, - update: { $unset: { 'keyAltNames': '' }, $currentDate: { 'updateDate': true } } - }); - } - return ret; + return this._clientEncryption._libmongocrypt.removeKeyAltName(keyId, keyAltName); } @returnsPromise