diff --git a/bindings/node/lib/autoEncrypter.js b/bindings/node/lib/autoEncrypter.js index f827109c7..5dbce92e7 100644 --- a/bindings/node/lib/autoEncrypter.js +++ b/bindings/node/lib/autoEncrypter.js @@ -108,6 +108,12 @@ module.exports = function (modules) { : this._bson.serialize(options.schemaMap); } + if (options.encryptedFieldsMap) { + mongoCryptOptions.encryptedFieldsMap = Buffer.isBuffer(options.encryptedFieldsMap) + ? options.encryptedFieldsMap + : this._bson.serialize(options.encryptedFieldsMap); + } + if (options.kmsProviders) { mongoCryptOptions.kmsProviders = !Buffer.isBuffer(options.kmsProviders) ? this._bson.serialize(options.kmsProviders) @@ -125,16 +131,27 @@ module.exports = function (modules) { } if (options.extraOptions && options.extraOptions.csfleSearchPaths) { + // Only for driver testing mongoCryptOptions.csfleSearchPaths = options.extraOptions.csfleSearchPaths; + } else if (!this._bypassEncryption) { + mongoCryptOptions.csfleSearchPaths = ['$SYSTEM']; + } + + if (options.bypassQueryAnalysis) { + mongoCryptOptions.bypassQueryAnalysis = options.bypassQueryAnalysis; } Object.assign(mongoCryptOptions, { cryptoCallbacks }); this._mongocrypt = new mc.MongoCrypt(mongoCryptOptions); this._contextCounter = 0; + if (options.extraOptions && options.extraOptions.csfleRequired && !this.csfleVersionInfo) { + throw new MongoError('`csfleRequired` set but no csfle shared library loaded'); + } + // Only instantiate mongocryptd manager/client once we know for sure // that we are not using the CSFLE shared library. - if (!this._bypassAutoEncryption && !this.csfleVersionInfo) { + if (!this._bypassEncryption && !this.csfleVersionInfo) { this._mongocryptdManager = new MongocryptdManager(options.extraOptions); this._mongocryptdClient = new MongoClient(this._mongocryptdManager.uri, { useNewUrlParser: true, @@ -181,7 +198,11 @@ module.exports = function (modules) { * @param {Function} callback Invoked when the mongocryptd client either successfully disconnects or errors */ teardown(force, callback) { - this._mongocryptdClient.close(force, callback); + if (this._mongocryptdClient) { + this._mongocryptdClient.close(force, callback); + } else { + callback(); + } } /** diff --git a/bindings/node/src/mongocrypt.cc b/bindings/node/src/mongocrypt.cc index caa8681a5..d614ac941 100644 --- a/bindings/node/src/mongocrypt.cc +++ b/bindings/node/src/mongocrypt.cc @@ -395,6 +395,20 @@ MongoCrypt::MongoCrypt(const CallbackInfo& info) } } + if (options.Has("encryptedFieldsMap")) { + Napi::Value encryptedFieldsMapBuffer = options["encryptedFieldsMap"]; + + if (!encryptedFieldsMapBuffer.IsBuffer()) { + throw TypeError::New(Env(), "Option `encryptedFieldsMap` must be a Buffer"); + } + + std::unique_ptr encryptedFieldsMapBinary( + BufferToBinary(encryptedFieldsMapBuffer.As())); + if (!mongocrypt_setopt_encrypted_field_config_map(_mongo_crypt.get(), encryptedFieldsMapBinary.get())) { + throw TypeError::New(Env(), errorStringFromStatus(_mongo_crypt.get())); + } + } + if (options.Has("logger")) { SetCallback("logger", options["logger"]); if (!mongocrypt_setopt_log_handler( @@ -438,6 +452,10 @@ MongoCrypt::MongoCrypt(const CallbackInfo& info) options.Get("csflePath").ToString().Utf8Value().c_str()); } + if (options.Get("bypassQueryAnalysis").ToBoolean()) { + mongocrypt_setopt_bypass_query_analysis(_mongo_crypt.get()); + } + mongocrypt_setopt_use_need_kms_credentials_state(_mongo_crypt.get()); // Initialize after all options are set. diff --git a/bindings/node/test/autoEncrypter.test.js b/bindings/node/test/autoEncrypter.test.js index 9710eb1f8..3713628fc 100644 --- a/bindings/node/test/autoEncrypter.test.js +++ b/bindings/node/test/autoEncrypter.test.js @@ -585,7 +585,7 @@ describe('AutoEncrypter', function () { const client = new MockClient(); this.mc = new AutoEncrypter(client, encryptionOptions); - const localMcdm = this.mc._mongocryptdManager; + const localMcdm = this.mc._mongocryptdManager || { spawn: () => {} }; sandbox.spy(localMcdm, 'spawn'); this.mc.init(err => { @@ -640,7 +640,30 @@ describe('AutoEncrypter', function () { }); describe('CSFLE shared library', function () { - it('should load a shared library by specifying its path', function () { + it('should fail if no library can be found in the search path and csfleRequired is set', function () { + // NB: This test has to be run before the tests/without having previously + // loaded a CSFLE shared library below to get the right error path. + const client = new MockClient(); + try { + new AutoEncrypter(client, { + keyVaultNamespace: 'admin.datakeys', + logger: () => {}, + kmsProviders: { + aws: { accessKeyId: 'example', secretAccessKey: 'example' }, + local: { key: Buffer.alloc(96) } + }, + extraOptions: { + csfleSearchPaths: ['/nonexistent'], + csfleRequired: true + } + }); + expect.fail('missed exception'); + } catch (err) { + expect(err.message).to.include('`csfleRequired` set but no csfle shared library loaded'); + } + }); + + it('should load a shared library by specifying its path', function (done) { const client = new MockClient(); this.mc = new AutoEncrypter(client, { keyVaultNamespace: 'admin.datakeys', @@ -661,9 +684,11 @@ describe('AutoEncrypter', function () { version: BigInt(0x000600020001000), versionStr: 'stubbed-mongo_csfle' }); + + this.mc.teardown(true, done); }); - it('should load a shared library by specifying a search path', function () { + it('should load a shared library by specifying a search path', function (done) { const client = new MockClient(); this.mc = new AutoEncrypter(client, { keyVaultNamespace: 'admin.datakeys', @@ -684,6 +709,8 @@ describe('AutoEncrypter', function () { version: BigInt(0x000600020001000), versionStr: 'stubbed-mongo_csfle' }); + + this.mc.teardown(true, done); }); }); });