Commit
This commit is contained in:
commit
d1c8cae2c1
1417 changed files with 326736 additions and 0 deletions
272
node_modules/mongodb/lib/client-side-encryption/auto_encrypter.js
generated
vendored
Normal file
272
node_modules/mongodb/lib/client-side-encryption/auto_encrypter.js
generated
vendored
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
"use strict";
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AutoEncrypter = exports.AutoEncryptionLoggerLevel = void 0;
|
||||
const net = require("net");
|
||||
const bson_1 = require("../bson");
|
||||
const constants_1 = require("../constants");
|
||||
const deps_1 = require("../deps");
|
||||
const error_1 = require("../error");
|
||||
const mongo_client_1 = require("../mongo_client");
|
||||
const utils_1 = require("../utils");
|
||||
const client_encryption_1 = require("./client_encryption");
|
||||
const errors_1 = require("./errors");
|
||||
const mongocryptd_manager_1 = require("./mongocryptd_manager");
|
||||
const providers_1 = require("./providers");
|
||||
const state_machine_1 = require("./state_machine");
|
||||
/** @public */
|
||||
exports.AutoEncryptionLoggerLevel = Object.freeze({
|
||||
FatalError: 0,
|
||||
Error: 1,
|
||||
Warning: 2,
|
||||
Info: 3,
|
||||
Trace: 4
|
||||
});
|
||||
/**
|
||||
* @internal An internal class to be used by the driver for auto encryption
|
||||
* **NOTE**: Not meant to be instantiated directly, this is for internal use only.
|
||||
*/
|
||||
class AutoEncrypter {
|
||||
static { _a = constants_1.kDecorateResult; }
|
||||
/** @internal */
|
||||
static getMongoCrypt() {
|
||||
const encryption = (0, deps_1.getMongoDBClientEncryption)();
|
||||
if ('kModuleError' in encryption) {
|
||||
throw encryption.kModuleError;
|
||||
}
|
||||
return encryption.MongoCrypt;
|
||||
}
|
||||
/**
|
||||
* Create an AutoEncrypter
|
||||
*
|
||||
* **Note**: Do not instantiate this class directly. Rather, supply the relevant options to a MongoClient
|
||||
*
|
||||
* **Note**: Supplying `options.schemaMap` provides more security than relying on JSON Schemas obtained from the server.
|
||||
* It protects against a malicious server advertising a false JSON Schema, which could trick the client into sending unencrypted data that should be encrypted.
|
||||
* Schemas supplied in the schemaMap only apply to configuring automatic encryption for Client-Side Field Level Encryption.
|
||||
* Other validation rules in the JSON schema will not be enforced by the driver and will result in an error.
|
||||
*
|
||||
* @example <caption>Create an AutoEncrypter that makes use of mongocryptd</caption>
|
||||
* ```ts
|
||||
* // Enabling autoEncryption via a MongoClient using mongocryptd
|
||||
* const { MongoClient } = require('mongodb');
|
||||
* const client = new MongoClient(URL, {
|
||||
* autoEncryption: {
|
||||
* kmsProviders: {
|
||||
* aws: {
|
||||
* accessKeyId: AWS_ACCESS_KEY,
|
||||
* secretAccessKey: AWS_SECRET_KEY
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* await client.connect();
|
||||
* // From here on, the client will be encrypting / decrypting automatically
|
||||
* @example <caption>Create an AutoEncrypter that makes use of libmongocrypt's CSFLE shared library</caption>
|
||||
* ```ts
|
||||
* // Enabling autoEncryption via a MongoClient using CSFLE shared library
|
||||
* const { MongoClient } = require('mongodb');
|
||||
* const client = new MongoClient(URL, {
|
||||
* autoEncryption: {
|
||||
* kmsProviders: {
|
||||
* aws: {}
|
||||
* },
|
||||
* extraOptions: {
|
||||
* cryptSharedLibPath: '/path/to/local/crypt/shared/lib',
|
||||
* cryptSharedLibRequired: true
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* await client.connect();
|
||||
* // From here on, the client will be encrypting / decrypting automatically
|
||||
*/
|
||||
constructor(client, options) {
|
||||
/**
|
||||
* Used by devtools to enable decorating decryption results.
|
||||
*
|
||||
* When set and enabled, `decrypt` will automatically recursively
|
||||
* traverse a decrypted document and if a field has been decrypted,
|
||||
* it will mark it as decrypted. Compass uses this to determine which
|
||||
* fields were decrypted.
|
||||
*/
|
||||
this[_a] = false;
|
||||
this._client = client;
|
||||
this._bypassEncryption = options.bypassAutoEncryption === true;
|
||||
this._keyVaultNamespace = options.keyVaultNamespace || 'admin.datakeys';
|
||||
this._keyVaultClient = options.keyVaultClient || client;
|
||||
this._metaDataClient = options.metadataClient || client;
|
||||
this._proxyOptions = options.proxyOptions || {};
|
||||
this._tlsOptions = options.tlsOptions || {};
|
||||
this._kmsProviders = options.kmsProviders || {};
|
||||
this._credentialProviders = options.credentialProviders;
|
||||
if (options.credentialProviders?.aws && !(0, providers_1.isEmptyCredentials)('aws', this._kmsProviders)) {
|
||||
throw new errors_1.MongoCryptInvalidArgumentError('Can only provide a custom AWS credential provider when the state machine is configured for automatic AWS credential fetching');
|
||||
}
|
||||
const mongoCryptOptions = {
|
||||
errorWrapper: errors_1.defaultErrorWrapper
|
||||
};
|
||||
if (options.schemaMap) {
|
||||
mongoCryptOptions.schemaMap = Buffer.isBuffer(options.schemaMap)
|
||||
? options.schemaMap
|
||||
: (0, bson_1.serialize)(options.schemaMap);
|
||||
}
|
||||
if (options.encryptedFieldsMap) {
|
||||
mongoCryptOptions.encryptedFieldsMap = Buffer.isBuffer(options.encryptedFieldsMap)
|
||||
? options.encryptedFieldsMap
|
||||
: (0, bson_1.serialize)(options.encryptedFieldsMap);
|
||||
}
|
||||
mongoCryptOptions.kmsProviders = !Buffer.isBuffer(this._kmsProviders)
|
||||
? (0, bson_1.serialize)(this._kmsProviders)
|
||||
: this._kmsProviders;
|
||||
if (options.options?.logger) {
|
||||
mongoCryptOptions.logger = options.options.logger;
|
||||
}
|
||||
if (options.extraOptions && options.extraOptions.cryptSharedLibPath) {
|
||||
mongoCryptOptions.cryptSharedLibPath = options.extraOptions.cryptSharedLibPath;
|
||||
}
|
||||
if (options.bypassQueryAnalysis) {
|
||||
mongoCryptOptions.bypassQueryAnalysis = options.bypassQueryAnalysis;
|
||||
}
|
||||
if (options.keyExpirationMS != null) {
|
||||
mongoCryptOptions.keyExpirationMS = options.keyExpirationMS;
|
||||
}
|
||||
this._bypassMongocryptdAndCryptShared = this._bypassEncryption || !!options.bypassQueryAnalysis;
|
||||
if (options.extraOptions && options.extraOptions.cryptSharedLibSearchPaths) {
|
||||
// Only for driver testing
|
||||
mongoCryptOptions.cryptSharedLibSearchPaths = options.extraOptions.cryptSharedLibSearchPaths;
|
||||
}
|
||||
else if (!this._bypassMongocryptdAndCryptShared) {
|
||||
mongoCryptOptions.cryptSharedLibSearchPaths = ['$SYSTEM'];
|
||||
}
|
||||
const MongoCrypt = AutoEncrypter.getMongoCrypt();
|
||||
this._mongocrypt = new MongoCrypt(mongoCryptOptions);
|
||||
this._contextCounter = 0;
|
||||
if (options.extraOptions &&
|
||||
options.extraOptions.cryptSharedLibRequired &&
|
||||
!this.cryptSharedLibVersionInfo) {
|
||||
throw new errors_1.MongoCryptInvalidArgumentError('`cryptSharedLibRequired` set but no crypt_shared library loaded');
|
||||
}
|
||||
// Only instantiate mongocryptd manager/client once we know for sure
|
||||
// that we are not using the CSFLE shared library.
|
||||
if (!this._bypassMongocryptdAndCryptShared && !this.cryptSharedLibVersionInfo) {
|
||||
this._mongocryptdManager = new mongocryptd_manager_1.MongocryptdManager(options.extraOptions);
|
||||
const clientOptions = {
|
||||
serverSelectionTimeoutMS: 10000
|
||||
};
|
||||
if ((options.extraOptions == null || typeof options.extraOptions.mongocryptdURI !== 'string') &&
|
||||
!net.getDefaultAutoSelectFamily) {
|
||||
// Only set family if autoSelectFamily options are not supported.
|
||||
clientOptions.family = 4;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: TS complains as this always returns true on versions where it is present.
|
||||
if (net.getDefaultAutoSelectFamily) {
|
||||
// AutoEncrypter is made inside of MongoClient constructor while options are being parsed,
|
||||
// we do not have access to the options that are in progress.
|
||||
// TODO(NODE-6449): AutoEncrypter does not use client options for autoSelectFamily
|
||||
Object.assign(clientOptions, (0, client_encryption_1.autoSelectSocketOptions)(this._client.s?.options ?? {}));
|
||||
}
|
||||
this._mongocryptdClient = new mongo_client_1.MongoClient(this._mongocryptdManager.uri, clientOptions);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Initializes the auto encrypter by spawning a mongocryptd and connecting to it.
|
||||
*
|
||||
* This function is a no-op when bypassSpawn is set or the crypt shared library is used.
|
||||
*/
|
||||
async init() {
|
||||
if (this._bypassMongocryptdAndCryptShared || this.cryptSharedLibVersionInfo) {
|
||||
return;
|
||||
}
|
||||
if (!this._mongocryptdManager) {
|
||||
throw new error_1.MongoRuntimeError('Reached impossible state: mongocryptdManager is undefined when neither bypassSpawn nor the shared lib are specified.');
|
||||
}
|
||||
if (!this._mongocryptdClient) {
|
||||
throw new error_1.MongoRuntimeError('Reached impossible state: mongocryptdClient is undefined when neither bypassSpawn nor the shared lib are specified.');
|
||||
}
|
||||
if (!this._mongocryptdManager.bypassSpawn) {
|
||||
await this._mongocryptdManager.spawn();
|
||||
}
|
||||
try {
|
||||
const client = await this._mongocryptdClient.connect();
|
||||
return client;
|
||||
}
|
||||
catch (error) {
|
||||
throw new error_1.MongoRuntimeError('Unable to connect to `mongocryptd`, please make sure it is running or in your PATH for auto-spawn', { cause: error });
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Cleans up the `_mongocryptdClient`, if present.
|
||||
*/
|
||||
async close() {
|
||||
await this._mongocryptdClient?.close();
|
||||
}
|
||||
/**
|
||||
* Encrypt a command for a given namespace.
|
||||
*/
|
||||
async encrypt(ns, cmd, options = {}) {
|
||||
options.signal?.throwIfAborted();
|
||||
if (this._bypassEncryption) {
|
||||
// If `bypassAutoEncryption` has been specified, don't encrypt
|
||||
return cmd;
|
||||
}
|
||||
const commandBuffer = Buffer.isBuffer(cmd) ? cmd : (0, bson_1.serialize)(cmd, options);
|
||||
const context = this._mongocrypt.makeEncryptionContext(utils_1.MongoDBCollectionNamespace.fromString(ns).db, commandBuffer);
|
||||
context.id = this._contextCounter++;
|
||||
context.ns = ns;
|
||||
context.document = cmd;
|
||||
const stateMachine = new state_machine_1.StateMachine({
|
||||
promoteValues: false,
|
||||
promoteLongs: false,
|
||||
proxyOptions: this._proxyOptions,
|
||||
tlsOptions: this._tlsOptions,
|
||||
socketOptions: (0, client_encryption_1.autoSelectSocketOptions)(this._client.s.options)
|
||||
});
|
||||
return (0, bson_1.deserialize)(await stateMachine.execute(this, context, options), {
|
||||
promoteValues: false,
|
||||
promoteLongs: false
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Decrypt a command response
|
||||
*/
|
||||
async decrypt(response, options = {}) {
|
||||
options.signal?.throwIfAborted();
|
||||
const context = this._mongocrypt.makeDecryptionContext(response);
|
||||
context.id = this._contextCounter++;
|
||||
const stateMachine = new state_machine_1.StateMachine({
|
||||
...options,
|
||||
proxyOptions: this._proxyOptions,
|
||||
tlsOptions: this._tlsOptions,
|
||||
socketOptions: (0, client_encryption_1.autoSelectSocketOptions)(this._client.s.options)
|
||||
});
|
||||
return await stateMachine.execute(this, context, options);
|
||||
}
|
||||
/**
|
||||
* Ask the user for KMS credentials.
|
||||
*
|
||||
* This returns anything that looks like the kmsProviders original input
|
||||
* option. It can be empty, and any provider specified here will override
|
||||
* the original ones.
|
||||
*/
|
||||
async askForKMSCredentials() {
|
||||
return await (0, providers_1.refreshKMSCredentials)(this._kmsProviders, this._credentialProviders);
|
||||
}
|
||||
/**
|
||||
* Return the current libmongocrypt's CSFLE shared library version
|
||||
* as `{ version: bigint, versionStr: string }`, or `null` if no CSFLE
|
||||
* shared library was loaded.
|
||||
*/
|
||||
get cryptSharedLibVersionInfo() {
|
||||
return this._mongocrypt.cryptSharedLibVersionInfo;
|
||||
}
|
||||
static get libmongocryptVersion() {
|
||||
return AutoEncrypter.getMongoCrypt().libmongocryptVersion;
|
||||
}
|
||||
}
|
||||
exports.AutoEncrypter = AutoEncrypter;
|
||||
//# sourceMappingURL=auto_encrypter.js.map
|
||||
1
node_modules/mongodb/lib/client-side-encryption/auto_encrypter.js.map
generated
vendored
Normal file
1
node_modules/mongodb/lib/client-side-encryption/auto_encrypter.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
609
node_modules/mongodb/lib/client-side-encryption/client_encryption.js
generated
vendored
Normal file
609
node_modules/mongodb/lib/client-side-encryption/client_encryption.js
generated
vendored
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ClientEncryption = void 0;
|
||||
exports.autoSelectSocketOptions = autoSelectSocketOptions;
|
||||
const bson_1 = require("../bson");
|
||||
const deps_1 = require("../deps");
|
||||
const timeout_1 = require("../timeout");
|
||||
const utils_1 = require("../utils");
|
||||
const errors_1 = require("./errors");
|
||||
const index_1 = require("./providers/index");
|
||||
const state_machine_1 = require("./state_machine");
|
||||
/**
|
||||
* @public
|
||||
* The public interface for explicit in-use encryption
|
||||
*/
|
||||
class ClientEncryption {
|
||||
/** @internal */
|
||||
static getMongoCrypt() {
|
||||
const encryption = (0, deps_1.getMongoDBClientEncryption)();
|
||||
if ('kModuleError' in encryption) {
|
||||
throw encryption.kModuleError;
|
||||
}
|
||||
return encryption.MongoCrypt;
|
||||
}
|
||||
/**
|
||||
* Create a new encryption instance
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* new ClientEncryption(mongoClient, {
|
||||
* keyVaultNamespace: 'client.encryption',
|
||||
* kmsProviders: {
|
||||
* local: {
|
||||
* key: masterKey // The master key used for encryption/decryption. A 96-byte long Buffer
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* new ClientEncryption(mongoClient, {
|
||||
* keyVaultNamespace: 'client.encryption',
|
||||
* kmsProviders: {
|
||||
* aws: {
|
||||
* accessKeyId: AWS_ACCESS_KEY,
|
||||
* secretAccessKey: AWS_SECRET_KEY
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
constructor(client, options) {
|
||||
this._client = client;
|
||||
this._proxyOptions = options.proxyOptions ?? {};
|
||||
this._tlsOptions = options.tlsOptions ?? {};
|
||||
this._kmsProviders = options.kmsProviders || {};
|
||||
const { timeoutMS } = (0, utils_1.resolveTimeoutOptions)(client, options);
|
||||
this._timeoutMS = timeoutMS;
|
||||
this._credentialProviders = options.credentialProviders;
|
||||
if (options.credentialProviders?.aws && !(0, index_1.isEmptyCredentials)('aws', this._kmsProviders)) {
|
||||
throw new errors_1.MongoCryptInvalidArgumentError('Can only provide a custom AWS credential provider when the state machine is configured for automatic AWS credential fetching');
|
||||
}
|
||||
if (options.keyVaultNamespace == null) {
|
||||
throw new errors_1.MongoCryptInvalidArgumentError('Missing required option `keyVaultNamespace`');
|
||||
}
|
||||
const mongoCryptOptions = {
|
||||
...options,
|
||||
kmsProviders: !Buffer.isBuffer(this._kmsProviders)
|
||||
? (0, bson_1.serialize)(this._kmsProviders)
|
||||
: this._kmsProviders,
|
||||
errorWrapper: errors_1.defaultErrorWrapper
|
||||
};
|
||||
this._keyVaultNamespace = options.keyVaultNamespace;
|
||||
this._keyVaultClient = options.keyVaultClient || client;
|
||||
const MongoCrypt = ClientEncryption.getMongoCrypt();
|
||||
this._mongoCrypt = new MongoCrypt(mongoCryptOptions);
|
||||
}
|
||||
/**
|
||||
* Creates a data key used for explicit encryption and inserts it into the key vault namespace
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Using async/await to create a local key
|
||||
* const dataKeyId = await clientEncryption.createDataKey('local');
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Using async/await to create an aws key
|
||||
* const dataKeyId = await clientEncryption.createDataKey('aws', {
|
||||
* masterKey: {
|
||||
* region: 'us-east-1',
|
||||
* key: 'xxxxxxxxxxxxxx' // CMK ARN here
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Using async/await to create an aws key with a keyAltName
|
||||
* const dataKeyId = await clientEncryption.createDataKey('aws', {
|
||||
* masterKey: {
|
||||
* region: 'us-east-1',
|
||||
* key: 'xxxxxxxxxxxxxx' // CMK ARN here
|
||||
* },
|
||||
* keyAltNames: [ 'mySpecialKey' ]
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
async createDataKey(provider, options = {}) {
|
||||
if (options.keyAltNames && !Array.isArray(options.keyAltNames)) {
|
||||
throw new errors_1.MongoCryptInvalidArgumentError(`Option "keyAltNames" must be an array of strings, but was of type ${typeof options.keyAltNames}.`);
|
||||
}
|
||||
let keyAltNames = undefined;
|
||||
if (options.keyAltNames && options.keyAltNames.length > 0) {
|
||||
keyAltNames = options.keyAltNames.map((keyAltName, i) => {
|
||||
if (typeof keyAltName !== 'string') {
|
||||
throw new errors_1.MongoCryptInvalidArgumentError(`Option "keyAltNames" must be an array of strings, but item at index ${i} was of type ${typeof keyAltName}`);
|
||||
}
|
||||
return (0, bson_1.serialize)({ keyAltName });
|
||||
});
|
||||
}
|
||||
let keyMaterial = undefined;
|
||||
if (options.keyMaterial) {
|
||||
keyMaterial = (0, bson_1.serialize)({ keyMaterial: options.keyMaterial });
|
||||
}
|
||||
const dataKeyBson = (0, bson_1.serialize)({
|
||||
provider,
|
||||
...options.masterKey
|
||||
});
|
||||
const context = this._mongoCrypt.makeDataKeyContext(dataKeyBson, {
|
||||
keyAltNames,
|
||||
keyMaterial
|
||||
});
|
||||
const stateMachine = new state_machine_1.StateMachine({
|
||||
proxyOptions: this._proxyOptions,
|
||||
tlsOptions: this._tlsOptions,
|
||||
socketOptions: autoSelectSocketOptions(this._client.s.options)
|
||||
});
|
||||
const timeoutContext = options?.timeoutContext ??
|
||||
timeout_1.TimeoutContext.create((0, utils_1.resolveTimeoutOptions)(this._client, { timeoutMS: this._timeoutMS }));
|
||||
const dataKey = (0, bson_1.deserialize)(await stateMachine.execute(this, context, { timeoutContext }));
|
||||
const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
|
||||
const { insertedId } = await this._keyVaultClient
|
||||
.db(dbName)
|
||||
.collection(collectionName)
|
||||
.insertOne(dataKey, {
|
||||
writeConcern: { w: 'majority' },
|
||||
timeoutMS: timeoutContext?.csotEnabled()
|
||||
? timeoutContext?.getRemainingTimeMSOrThrow()
|
||||
: undefined
|
||||
});
|
||||
return insertedId;
|
||||
}
|
||||
/**
|
||||
* Searches the keyvault for any data keys matching the provided filter. If there are matches, rewrapManyDataKey then attempts to re-wrap the data keys using the provided options.
|
||||
*
|
||||
* If no matches are found, then no bulk write is performed.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // rewrapping all data data keys (using a filter that matches all documents)
|
||||
* const filter = {};
|
||||
*
|
||||
* const result = await clientEncryption.rewrapManyDataKey(filter);
|
||||
* if (result.bulkWriteResult != null) {
|
||||
* // keys were re-wrapped, results will be available in the bulkWrite object.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // attempting to rewrap all data keys with no matches
|
||||
* const filter = { _id: new Binary() } // assume _id matches no documents in the database
|
||||
* const result = await clientEncryption.rewrapManyDataKey(filter);
|
||||
*
|
||||
* if (result.bulkWriteResult == null) {
|
||||
* // no keys matched, `bulkWriteResult` does not exist on the result object
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
async rewrapManyDataKey(filter, options) {
|
||||
let keyEncryptionKeyBson = undefined;
|
||||
if (options) {
|
||||
const keyEncryptionKey = Object.assign({ provider: options.provider }, options.masterKey);
|
||||
keyEncryptionKeyBson = (0, bson_1.serialize)(keyEncryptionKey);
|
||||
}
|
||||
const filterBson = (0, bson_1.serialize)(filter);
|
||||
const context = this._mongoCrypt.makeRewrapManyDataKeyContext(filterBson, keyEncryptionKeyBson);
|
||||
const stateMachine = new state_machine_1.StateMachine({
|
||||
proxyOptions: this._proxyOptions,
|
||||
tlsOptions: this._tlsOptions,
|
||||
socketOptions: autoSelectSocketOptions(this._client.s.options)
|
||||
});
|
||||
const timeoutContext = timeout_1.TimeoutContext.create((0, utils_1.resolveTimeoutOptions)(this._client, { timeoutMS: this._timeoutMS }));
|
||||
const { v: dataKeys } = (0, bson_1.deserialize)(await stateMachine.execute(this, context, { timeoutContext }));
|
||||
if (dataKeys.length === 0) {
|
||||
return {};
|
||||
}
|
||||
const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
|
||||
const replacements = dataKeys.map((key) => ({
|
||||
updateOne: {
|
||||
filter: { _id: key._id },
|
||||
update: {
|
||||
$set: {
|
||||
masterKey: key.masterKey,
|
||||
keyMaterial: key.keyMaterial
|
||||
},
|
||||
$currentDate: {
|
||||
updateDate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
const result = await this._keyVaultClient
|
||||
.db(dbName)
|
||||
.collection(collectionName)
|
||||
.bulkWrite(replacements, {
|
||||
writeConcern: { w: 'majority' },
|
||||
timeoutMS: timeoutContext.csotEnabled() ? timeoutContext?.remainingTimeMS : undefined
|
||||
});
|
||||
return { bulkWriteResult: result };
|
||||
}
|
||||
/**
|
||||
* Deletes the key with the provided id from the keyvault, if it exists.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // delete a key by _id
|
||||
* const id = new Binary(); // id is a bson binary subtype 4 object
|
||||
* const { deletedCount } = await clientEncryption.deleteKey(id);
|
||||
*
|
||||
* if (deletedCount != null && deletedCount > 0) {
|
||||
* // successful deletion
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
async deleteKey(_id) {
|
||||
const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
|
||||
return await this._keyVaultClient
|
||||
.db(dbName)
|
||||
.collection(collectionName)
|
||||
.deleteOne({ _id }, { writeConcern: { w: 'majority' }, timeoutMS: this._timeoutMS });
|
||||
}
|
||||
/**
|
||||
* Finds all the keys currently stored in the keyvault.
|
||||
*
|
||||
* This method will not throw.
|
||||
*
|
||||
* @returns a FindCursor over all keys in the keyvault.
|
||||
* @example
|
||||
* ```ts
|
||||
* // fetching all keys
|
||||
* const keys = await clientEncryption.getKeys().toArray();
|
||||
* ```
|
||||
*/
|
||||
getKeys() {
|
||||
const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
|
||||
return this._keyVaultClient
|
||||
.db(dbName)
|
||||
.collection(collectionName)
|
||||
.find({}, { readConcern: { level: 'majority' }, timeoutMS: this._timeoutMS });
|
||||
}
|
||||
/**
|
||||
* Finds a key in the keyvault with the specified _id.
|
||||
*
|
||||
* Returns a promise that either resolves to a {@link DataKey} if a document matches the key or null if no documents
|
||||
* match the id. The promise rejects with an error if an error is thrown.
|
||||
* @example
|
||||
* ```ts
|
||||
* // getting a key by id
|
||||
* const id = new Binary(); // id is a bson binary subtype 4 object
|
||||
* const key = await clientEncryption.getKey(id);
|
||||
* if (!key) {
|
||||
* // key is null if there was no matching key
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
async getKey(_id) {
|
||||
const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
|
||||
return await this._keyVaultClient
|
||||
.db(dbName)
|
||||
.collection(collectionName)
|
||||
.findOne({ _id }, { readConcern: { level: 'majority' }, timeoutMS: this._timeoutMS });
|
||||
}
|
||||
/**
|
||||
* Finds a key in the keyvault which has the specified keyAltName.
|
||||
*
|
||||
* @param keyAltName - a keyAltName to search for a key
|
||||
* @returns Returns a promise that either resolves to a {@link DataKey} if a document matches the key or null if no documents
|
||||
* match the keyAltName. The promise rejects with an error if an error is thrown.
|
||||
* @example
|
||||
* ```ts
|
||||
* // get a key by alt name
|
||||
* const keyAltName = 'keyAltName';
|
||||
* const key = await clientEncryption.getKeyByAltName(keyAltName);
|
||||
* if (!key) {
|
||||
* // key is null if there is no matching key
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
async getKeyByAltName(keyAltName) {
|
||||
const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
|
||||
return await this._keyVaultClient
|
||||
.db(dbName)
|
||||
.collection(collectionName)
|
||||
.findOne({ keyAltNames: keyAltName }, { readConcern: { level: 'majority' }, timeoutMS: this._timeoutMS });
|
||||
}
|
||||
/**
|
||||
* Adds a keyAltName to a key identified by the provided _id.
|
||||
*
|
||||
* This method resolves to/returns the *old* key value (prior to adding the new altKeyName).
|
||||
*
|
||||
* @param _id - The id of the document to update.
|
||||
* @param keyAltName - a keyAltName to search for a key
|
||||
* @returns Returns a promise that either resolves to a {@link DataKey} if a document matches the key or null if no documents
|
||||
* match the id. The promise rejects with an error if an error is thrown.
|
||||
* @example
|
||||
* ```ts
|
||||
* // adding an keyAltName to a data key
|
||||
* const id = new Binary(); // id is a bson binary subtype 4 object
|
||||
* const keyAltName = 'keyAltName';
|
||||
* const oldKey = await clientEncryption.addKeyAltName(id, keyAltName);
|
||||
* if (!oldKey) {
|
||||
* // null is returned if there is no matching document with an id matching the supplied id
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
async addKeyAltName(_id, keyAltName) {
|
||||
const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
|
||||
const value = await this._keyVaultClient
|
||||
.db(dbName)
|
||||
.collection(collectionName)
|
||||
.findOneAndUpdate({ _id }, { $addToSet: { keyAltNames: keyAltName } }, { writeConcern: { w: 'majority' }, returnDocument: 'before', timeoutMS: this._timeoutMS });
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Adds a keyAltName to a key identified by the provided _id.
|
||||
*
|
||||
* This method resolves to/returns the *old* key value (prior to removing the new altKeyName).
|
||||
*
|
||||
* If the removed keyAltName is the last keyAltName for that key, the `altKeyNames` property is unset from the document.
|
||||
*
|
||||
* @param _id - The id of the document to update.
|
||||
* @param keyAltName - a keyAltName to search for a key
|
||||
* @returns Returns a promise that either resolves to a {@link DataKey} if a document matches the key or null if no documents
|
||||
* match the id. The promise rejects with an error if an error is thrown.
|
||||
* @example
|
||||
* ```ts
|
||||
* // removing a key alt name from a data key
|
||||
* const id = new Binary(); // id is a bson binary subtype 4 object
|
||||
* const keyAltName = 'keyAltName';
|
||||
* const oldKey = await clientEncryption.removeKeyAltName(id, keyAltName);
|
||||
*
|
||||
* if (!oldKey) {
|
||||
* // null is returned if there is no matching document with an id matching the supplied id
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
async removeKeyAltName(_id, keyAltName) {
|
||||
const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
|
||||
const pipeline = [
|
||||
{
|
||||
$set: {
|
||||
keyAltNames: {
|
||||
$cond: [
|
||||
{
|
||||
$eq: ['$keyAltNames', [keyAltName]]
|
||||
},
|
||||
'$$REMOVE',
|
||||
{
|
||||
$filter: {
|
||||
input: '$keyAltNames',
|
||||
cond: {
|
||||
$ne: ['$$this', keyAltName]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
const value = await this._keyVaultClient
|
||||
.db(dbName)
|
||||
.collection(collectionName)
|
||||
.findOneAndUpdate({ _id }, pipeline, {
|
||||
writeConcern: { w: 'majority' },
|
||||
returnDocument: 'before',
|
||||
timeoutMS: this._timeoutMS
|
||||
});
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* A convenience method for creating an encrypted collection.
|
||||
* This method will create data keys for any encryptedFields that do not have a `keyId` defined
|
||||
* and then create a new collection with the full set of encryptedFields.
|
||||
*
|
||||
* @param db - A Node.js driver Db object with which to create the collection
|
||||
* @param name - The name of the collection to be created
|
||||
* @param options - Options for createDataKey and for createCollection
|
||||
* @returns created collection and generated encryptedFields
|
||||
* @throws MongoCryptCreateDataKeyError - If part way through the process a createDataKey invocation fails, an error will be rejected that has the partial `encryptedFields` that were created.
|
||||
* @throws MongoCryptCreateEncryptedCollectionError - If creating the collection fails, an error will be rejected that has the entire `encryptedFields` that were created.
|
||||
*/
|
||||
async createEncryptedCollection(db, name, options) {
|
||||
const { provider, masterKey, createCollectionOptions: { encryptedFields: { ...encryptedFields }, ...createCollectionOptions } } = options;
|
||||
const timeoutContext = this._timeoutMS != null
|
||||
? timeout_1.TimeoutContext.create((0, utils_1.resolveTimeoutOptions)(this._client, { timeoutMS: this._timeoutMS }))
|
||||
: undefined;
|
||||
if (Array.isArray(encryptedFields.fields)) {
|
||||
const createDataKeyPromises = encryptedFields.fields.map(async (field) => field == null || typeof field !== 'object' || field.keyId != null
|
||||
? field
|
||||
: {
|
||||
...field,
|
||||
keyId: await this.createDataKey(provider, {
|
||||
masterKey,
|
||||
// clone the timeoutContext
|
||||
// in order to avoid sharing the same timeout for server selection and connection checkout across different concurrent operations
|
||||
timeoutContext: timeoutContext?.csotEnabled() ? timeoutContext?.clone() : undefined
|
||||
})
|
||||
});
|
||||
const createDataKeyResolutions = await Promise.allSettled(createDataKeyPromises);
|
||||
encryptedFields.fields = createDataKeyResolutions.map((resolution, index) => resolution.status === 'fulfilled' ? resolution.value : encryptedFields.fields[index]);
|
||||
const rejection = createDataKeyResolutions.find((result) => result.status === 'rejected');
|
||||
if (rejection != null) {
|
||||
throw new errors_1.MongoCryptCreateDataKeyError(encryptedFields, { cause: rejection.reason });
|
||||
}
|
||||
}
|
||||
try {
|
||||
const collection = await db.createCollection(name, {
|
||||
...createCollectionOptions,
|
||||
encryptedFields,
|
||||
timeoutMS: timeoutContext?.csotEnabled()
|
||||
? timeoutContext?.getRemainingTimeMSOrThrow()
|
||||
: undefined
|
||||
});
|
||||
return { collection, encryptedFields };
|
||||
}
|
||||
catch (cause) {
|
||||
throw new errors_1.MongoCryptCreateEncryptedCollectionError(encryptedFields, { cause });
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Explicitly encrypt a provided value. Note that either `options.keyId` or `options.keyAltName` must
|
||||
* be specified. Specifying both `options.keyId` and `options.keyAltName` is considered an error.
|
||||
*
|
||||
* @param value - The value that you wish to serialize. Must be of a type that can be serialized into BSON
|
||||
* @param options -
|
||||
* @returns a Promise that either resolves with the encrypted value, or rejects with an error.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Encryption with async/await api
|
||||
* async function encryptMyData(value) {
|
||||
* const keyId = await clientEncryption.createDataKey('local');
|
||||
* return clientEncryption.encrypt(value, { keyId, algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' });
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Encryption using a keyAltName
|
||||
* async function encryptMyData(value) {
|
||||
* await clientEncryption.createDataKey('local', { keyAltNames: 'mySpecialKey' });
|
||||
* return clientEncryption.encrypt(value, { keyAltName: 'mySpecialKey', algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' });
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
async encrypt(value, options) {
|
||||
return await this._encrypt(value, false, options);
|
||||
}
|
||||
/**
|
||||
* Encrypts a Match Expression or Aggregate Expression to query a range index.
|
||||
*
|
||||
* Only supported when queryType is "range" and algorithm is "Range".
|
||||
*
|
||||
* @param expression - a BSON document of one of the following forms:
|
||||
* 1. A Match Expression of this form:
|
||||
* `{$and: [{<field>: {$gt: <value1>}}, {<field>: {$lt: <value2> }}]}`
|
||||
* 2. An Aggregate Expression of this form:
|
||||
* `{$and: [{$gt: [<fieldpath>, <value1>]}, {$lt: [<fieldpath>, <value2>]}]}`
|
||||
*
|
||||
* `$gt` may also be `$gte`. `$lt` may also be `$lte`.
|
||||
*
|
||||
* @param options -
|
||||
* @returns Returns a Promise that either resolves with the encrypted value or rejects with an error.
|
||||
*/
|
||||
async encryptExpression(expression, options) {
|
||||
return await this._encrypt(expression, true, options);
|
||||
}
|
||||
/**
|
||||
* Explicitly decrypt a provided encrypted value
|
||||
*
|
||||
* @param value - An encrypted value
|
||||
* @returns a Promise that either resolves with the decrypted value, or rejects with an error
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Decrypting value with async/await API
|
||||
* async function decryptMyValue(value) {
|
||||
* return clientEncryption.decrypt(value);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
async decrypt(value) {
|
||||
const valueBuffer = (0, bson_1.serialize)({ v: value });
|
||||
const context = this._mongoCrypt.makeExplicitDecryptionContext(valueBuffer);
|
||||
const stateMachine = new state_machine_1.StateMachine({
|
||||
proxyOptions: this._proxyOptions,
|
||||
tlsOptions: this._tlsOptions,
|
||||
socketOptions: autoSelectSocketOptions(this._client.s.options)
|
||||
});
|
||||
const timeoutContext = this._timeoutMS != null
|
||||
? timeout_1.TimeoutContext.create((0, utils_1.resolveTimeoutOptions)(this._client, { timeoutMS: this._timeoutMS }))
|
||||
: undefined;
|
||||
const { v } = (0, bson_1.deserialize)(await stateMachine.execute(this, context, { timeoutContext }));
|
||||
return v;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
* Ask the user for KMS credentials.
|
||||
*
|
||||
* This returns anything that looks like the kmsProviders original input
|
||||
* option. It can be empty, and any provider specified here will override
|
||||
* the original ones.
|
||||
*/
|
||||
async askForKMSCredentials() {
|
||||
return await (0, index_1.refreshKMSCredentials)(this._kmsProviders, this._credentialProviders);
|
||||
}
|
||||
static get libmongocryptVersion() {
|
||||
return ClientEncryption.getMongoCrypt().libmongocryptVersion;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
* A helper that perform explicit encryption of values and expressions.
|
||||
* Explicitly encrypt a provided value. Note that either `options.keyId` or `options.keyAltName` must
|
||||
* be specified. Specifying both `options.keyId` and `options.keyAltName` is considered an error.
|
||||
*
|
||||
* @param value - The value that you wish to encrypt. Must be of a type that can be serialized into BSON
|
||||
* @param expressionMode - a boolean that indicates whether or not to encrypt the value as an expression
|
||||
* @param options - options to pass to encrypt
|
||||
* @returns the raw result of the call to stateMachine.execute(). When expressionMode is set to true, the return
|
||||
* value will be a bson document. When false, the value will be a BSON Binary.
|
||||
*
|
||||
*/
|
||||
async _encrypt(value, expressionMode, options) {
|
||||
const { algorithm, keyId, keyAltName, contentionFactor, queryType, rangeOptions, textOptions } = options;
|
||||
const contextOptions = {
|
||||
expressionMode,
|
||||
algorithm
|
||||
};
|
||||
if (keyId) {
|
||||
contextOptions.keyId = keyId.buffer;
|
||||
}
|
||||
if (keyAltName) {
|
||||
if (keyId) {
|
||||
throw new errors_1.MongoCryptInvalidArgumentError(`"options" cannot contain both "keyId" and "keyAltName"`);
|
||||
}
|
||||
if (typeof keyAltName !== 'string') {
|
||||
throw new errors_1.MongoCryptInvalidArgumentError(`"options.keyAltName" must be of type string, but was of type ${typeof keyAltName}`);
|
||||
}
|
||||
contextOptions.keyAltName = (0, bson_1.serialize)({ keyAltName });
|
||||
}
|
||||
if (typeof contentionFactor === 'number' || typeof contentionFactor === 'bigint') {
|
||||
contextOptions.contentionFactor = contentionFactor;
|
||||
}
|
||||
if (typeof queryType === 'string') {
|
||||
contextOptions.queryType = queryType;
|
||||
}
|
||||
if (typeof rangeOptions === 'object') {
|
||||
contextOptions.rangeOptions = (0, bson_1.serialize)(rangeOptions);
|
||||
}
|
||||
if (typeof textOptions === 'object') {
|
||||
contextOptions.textOptions = (0, bson_1.serialize)(textOptions);
|
||||
}
|
||||
const valueBuffer = (0, bson_1.serialize)({ v: value });
|
||||
const stateMachine = new state_machine_1.StateMachine({
|
||||
proxyOptions: this._proxyOptions,
|
||||
tlsOptions: this._tlsOptions,
|
||||
socketOptions: autoSelectSocketOptions(this._client.s.options)
|
||||
});
|
||||
const context = this._mongoCrypt.makeExplicitEncryptionContext(valueBuffer, contextOptions);
|
||||
const timeoutContext = this._timeoutMS != null
|
||||
? timeout_1.TimeoutContext.create((0, utils_1.resolveTimeoutOptions)(this._client, { timeoutMS: this._timeoutMS }))
|
||||
: undefined;
|
||||
const { v } = (0, bson_1.deserialize)(await stateMachine.execute(this, context, { timeoutContext }));
|
||||
return v;
|
||||
}
|
||||
}
|
||||
exports.ClientEncryption = ClientEncryption;
|
||||
/**
|
||||
* Get the socket options from the client.
|
||||
* @param baseOptions - The mongo client options.
|
||||
* @returns ClientEncryptionSocketOptions
|
||||
*/
|
||||
function autoSelectSocketOptions(baseOptions) {
|
||||
const options = { autoSelectFamily: true };
|
||||
if ('autoSelectFamily' in baseOptions) {
|
||||
options.autoSelectFamily = baseOptions.autoSelectFamily;
|
||||
}
|
||||
if ('autoSelectFamilyAttemptTimeout' in baseOptions) {
|
||||
options.autoSelectFamilyAttemptTimeout = baseOptions.autoSelectFamilyAttemptTimeout;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
//# sourceMappingURL=client_encryption.js.map
|
||||
1
node_modules/mongodb/lib/client-side-encryption/client_encryption.js.map
generated
vendored
Normal file
1
node_modules/mongodb/lib/client-side-encryption/client_encryption.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
138
node_modules/mongodb/lib/client-side-encryption/errors.js
generated
vendored
Normal file
138
node_modules/mongodb/lib/client-side-encryption/errors.js
generated
vendored
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MongoCryptKMSRequestNetworkTimeoutError = exports.MongoCryptAzureKMSRequestError = exports.MongoCryptCreateEncryptedCollectionError = exports.MongoCryptCreateDataKeyError = exports.MongoCryptInvalidArgumentError = exports.defaultErrorWrapper = exports.MongoCryptError = void 0;
|
||||
const error_1 = require("../error");
|
||||
/**
|
||||
* @public
|
||||
* An error indicating that something went wrong specifically with MongoDB Client Encryption
|
||||
*/
|
||||
class MongoCryptError extends error_1.MongoError {
|
||||
/**
|
||||
* **Do not use this constructor!**
|
||||
*
|
||||
* Meant for internal use only.
|
||||
*
|
||||
* @remarks
|
||||
* This class is only meant to be constructed within the driver. This constructor is
|
||||
* not subject to semantic versioning compatibility guarantees and may change at any time.
|
||||
*
|
||||
* @public
|
||||
**/
|
||||
constructor(message, options = {}) {
|
||||
super(message, options);
|
||||
}
|
||||
get name() {
|
||||
return 'MongoCryptError';
|
||||
}
|
||||
}
|
||||
exports.MongoCryptError = MongoCryptError;
|
||||
const defaultErrorWrapper = (error) => new MongoCryptError(error.message, { cause: error });
|
||||
exports.defaultErrorWrapper = defaultErrorWrapper;
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* An error indicating an invalid argument was provided to an encryption API.
|
||||
*/
|
||||
class MongoCryptInvalidArgumentError extends MongoCryptError {
|
||||
/**
|
||||
* **Do not use this constructor!**
|
||||
*
|
||||
* Meant for internal use only.
|
||||
*
|
||||
* @remarks
|
||||
* This class is only meant to be constructed within the driver. This constructor is
|
||||
* not subject to semantic versioning compatibility guarantees and may change at any time.
|
||||
*
|
||||
* @public
|
||||
**/
|
||||
constructor(message) {
|
||||
super(message);
|
||||
}
|
||||
get name() {
|
||||
return 'MongoCryptInvalidArgumentError';
|
||||
}
|
||||
}
|
||||
exports.MongoCryptInvalidArgumentError = MongoCryptInvalidArgumentError;
|
||||
/**
|
||||
* @public
|
||||
* An error indicating that `ClientEncryption.createEncryptedCollection()` failed to create data keys
|
||||
*/
|
||||
class MongoCryptCreateDataKeyError extends MongoCryptError {
|
||||
/**
|
||||
* **Do not use this constructor!**
|
||||
*
|
||||
* Meant for internal use only.
|
||||
*
|
||||
* @remarks
|
||||
* This class is only meant to be constructed within the driver. This constructor is
|
||||
* not subject to semantic versioning compatibility guarantees and may change at any time.
|
||||
*
|
||||
* @public
|
||||
**/
|
||||
constructor(encryptedFields, { cause }) {
|
||||
super(`Unable to complete creating data keys: ${cause.message}`, { cause });
|
||||
this.encryptedFields = encryptedFields;
|
||||
}
|
||||
get name() {
|
||||
return 'MongoCryptCreateDataKeyError';
|
||||
}
|
||||
}
|
||||
exports.MongoCryptCreateDataKeyError = MongoCryptCreateDataKeyError;
|
||||
/**
|
||||
* @public
|
||||
* An error indicating that `ClientEncryption.createEncryptedCollection()` failed to create a collection
|
||||
*/
|
||||
class MongoCryptCreateEncryptedCollectionError extends MongoCryptError {
|
||||
/**
|
||||
* **Do not use this constructor!**
|
||||
*
|
||||
* Meant for internal use only.
|
||||
*
|
||||
* @remarks
|
||||
* This class is only meant to be constructed within the driver. This constructor is
|
||||
* not subject to semantic versioning compatibility guarantees and may change at any time.
|
||||
*
|
||||
* @public
|
||||
**/
|
||||
constructor(encryptedFields, { cause }) {
|
||||
super(`Unable to create collection: ${cause.message}`, { cause });
|
||||
this.encryptedFields = encryptedFields;
|
||||
}
|
||||
get name() {
|
||||
return 'MongoCryptCreateEncryptedCollectionError';
|
||||
}
|
||||
}
|
||||
exports.MongoCryptCreateEncryptedCollectionError = MongoCryptCreateEncryptedCollectionError;
|
||||
/**
|
||||
* @public
|
||||
* An error indicating that mongodb-client-encryption failed to auto-refresh Azure KMS credentials.
|
||||
*/
|
||||
class MongoCryptAzureKMSRequestError extends MongoCryptError {
|
||||
/**
|
||||
* **Do not use this constructor!**
|
||||
*
|
||||
* Meant for internal use only.
|
||||
*
|
||||
* @remarks
|
||||
* This class is only meant to be constructed within the driver. This constructor is
|
||||
* not subject to semantic versioning compatibility guarantees and may change at any time.
|
||||
*
|
||||
* @public
|
||||
**/
|
||||
constructor(message, body) {
|
||||
super(message);
|
||||
this.body = body;
|
||||
}
|
||||
get name() {
|
||||
return 'MongoCryptAzureKMSRequestError';
|
||||
}
|
||||
}
|
||||
exports.MongoCryptAzureKMSRequestError = MongoCryptAzureKMSRequestError;
|
||||
/** @public */
|
||||
class MongoCryptKMSRequestNetworkTimeoutError extends MongoCryptError {
|
||||
get name() {
|
||||
return 'MongoCryptKMSRequestNetworkTimeoutError';
|
||||
}
|
||||
}
|
||||
exports.MongoCryptKMSRequestNetworkTimeoutError = MongoCryptKMSRequestNetworkTimeoutError;
|
||||
//# sourceMappingURL=errors.js.map
|
||||
1
node_modules/mongodb/lib/client-side-encryption/errors.js.map
generated
vendored
Normal file
1
node_modules/mongodb/lib/client-side-encryption/errors.js.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/client-side-encryption/errors.ts"],"names":[],"mappings":";;;AACA,oCAAsC;AAEtC;;;GAGG;AACH,MAAa,eAAgB,SAAQ,kBAAU;IAC7C;;;;;;;;;;QAUI;IACJ,YAAY,OAAe,EAAE,UAA6B,EAAE;QAC1D,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1B,CAAC;IAED,IAAa,IAAI;QACf,OAAO,iBAAiB,CAAC;IAC3B,CAAC;CACF;AAnBD,0CAmBC;AAEM,MAAM,mBAAmB,GAAG,CAAC,KAAY,EAAE,EAAE,CAClD,IAAI,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAD1C,QAAA,mBAAmB,uBACuB;AAEvD;;;;GAIG;AACH,MAAa,8BAA+B,SAAQ,eAAe;IACjE;;;;;;;;;;QAUI;IACJ,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;IACjB,CAAC;IAED,IAAa,IAAI;QACf,OAAO,gCAAgC,CAAC;IAC1C,CAAC;CACF;AAnBD,wEAmBC;AACD;;;GAGG;AACH,MAAa,4BAA6B,SAAQ,eAAe;IAE/D;;;;;;;;;;QAUI;IACJ,YAAY,eAAyB,EAAE,EAAE,KAAK,EAAoB;QAChE,KAAK,CAAC,0CAA0C,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5E,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IACzC,CAAC;IAED,IAAa,IAAI;QACf,OAAO,8BAA8B,CAAC;IACxC,CAAC;CACF;AArBD,oEAqBC;AAED;;;GAGG;AACH,MAAa,wCAAyC,SAAQ,eAAe;IAE3E;;;;;;;;;;QAUI;IACJ,YAAY,eAAyB,EAAE,EAAE,KAAK,EAAoB;QAChE,KAAK,CAAC,gCAAgC,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IACzC,CAAC;IAED,IAAa,IAAI;QACf,OAAO,0CAA0C,CAAC;IACpD,CAAC;CACF;AArBD,4FAqBC;AAED;;;GAGG;AACH,MAAa,8BAA+B,SAAQ,eAAe;IAGjE;;;;;;;;;;QAUI;IACJ,YAAY,OAAe,EAAE,IAAe;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,IAAa,IAAI;QACf,OAAO,gCAAgC,CAAC;IAC1C,CAAC;CACF;AAtBD,wEAsBC;AAED,cAAc;AACd,MAAa,uCAAwC,SAAQ,eAAe;IAC1E,IAAa,IAAI;QACf,OAAO,yCAAyC,CAAC;IACnD,CAAC;CACF;AAJD,0FAIC"}
|
||||
85
node_modules/mongodb/lib/client-side-encryption/mongocryptd_manager.js
generated
vendored
Normal file
85
node_modules/mongodb/lib/client-side-encryption/mongocryptd_manager.js
generated
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MongocryptdManager = void 0;
|
||||
const error_1 = require("../error");
|
||||
/**
|
||||
* @internal
|
||||
* An internal class that handles spawning a mongocryptd.
|
||||
*/
|
||||
class MongocryptdManager {
|
||||
static { this.DEFAULT_MONGOCRYPTD_URI = 'mongodb://localhost:27020'; }
|
||||
constructor(extraOptions = {}) {
|
||||
this.spawnPath = '';
|
||||
this.spawnArgs = [];
|
||||
this.uri =
|
||||
typeof extraOptions.mongocryptdURI === 'string' && extraOptions.mongocryptdURI.length > 0
|
||||
? extraOptions.mongocryptdURI
|
||||
: MongocryptdManager.DEFAULT_MONGOCRYPTD_URI;
|
||||
this.bypassSpawn = !!extraOptions.mongocryptdBypassSpawn;
|
||||
if (Object.hasOwn(extraOptions, 'mongocryptdSpawnPath') && extraOptions.mongocryptdSpawnPath) {
|
||||
this.spawnPath = extraOptions.mongocryptdSpawnPath;
|
||||
}
|
||||
if (Object.hasOwn(extraOptions, 'mongocryptdSpawnArgs') &&
|
||||
Array.isArray(extraOptions.mongocryptdSpawnArgs)) {
|
||||
this.spawnArgs = this.spawnArgs.concat(extraOptions.mongocryptdSpawnArgs);
|
||||
}
|
||||
if (this.spawnArgs
|
||||
.filter(arg => typeof arg === 'string')
|
||||
.every(arg => arg.indexOf('--idleShutdownTimeoutSecs') < 0)) {
|
||||
this.spawnArgs.push('--idleShutdownTimeoutSecs', '60');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Will check to see if a mongocryptd is up. If it is not up, it will attempt
|
||||
* to spawn a mongocryptd in a detached process, and then wait for it to be up.
|
||||
*/
|
||||
async spawn() {
|
||||
const cmdName = this.spawnPath || 'mongocryptd';
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const { spawn } = require('child_process');
|
||||
// Spawned with stdio: ignore and detached: true
|
||||
// to ensure child can outlive parent.
|
||||
this._child = spawn(cmdName, this.spawnArgs, {
|
||||
stdio: 'ignore',
|
||||
detached: true
|
||||
});
|
||||
this._child.on('error', () => {
|
||||
// From the FLE spec:
|
||||
// "The stdout and stderr of the spawned process MUST not be exposed in the driver
|
||||
// (e.g. redirect to /dev/null). Users can pass the argument --logpath to
|
||||
// extraOptions.mongocryptdSpawnArgs if they need to inspect mongocryptd logs.
|
||||
// If spawning is necessary, the driver MUST spawn mongocryptd whenever server
|
||||
// selection on the MongoClient to mongocryptd fails. If the MongoClient fails to
|
||||
// connect after spawning, the server selection error is propagated to the user."
|
||||
// The AutoEncrypter and MongoCryptdManager should work together to spawn
|
||||
// mongocryptd whenever necessary. Additionally, the `mongocryptd` intentionally
|
||||
// shuts down after 60s and gets respawned when necessary. We rely on server
|
||||
// selection timeouts when connecting to the `mongocryptd` to inform users that something
|
||||
// has been configured incorrectly. For those reasons, we suppress stderr from
|
||||
// the `mongocryptd` process and immediately unref the process.
|
||||
});
|
||||
// unref child to remove handle from event loop
|
||||
this._child.unref();
|
||||
}
|
||||
/**
|
||||
* @returns the result of `fn` or rejects with an error.
|
||||
*/
|
||||
async withRespawn(fn) {
|
||||
try {
|
||||
const result = await fn();
|
||||
return result;
|
||||
}
|
||||
catch (err) {
|
||||
// If we are not bypassing spawning, then we should retry once on a MongoTimeoutError (server selection error)
|
||||
const shouldSpawn = err instanceof error_1.MongoNetworkTimeoutError && !this.bypassSpawn;
|
||||
if (!shouldSpawn) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
await this.spawn();
|
||||
const result = await fn();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
exports.MongocryptdManager = MongocryptdManager;
|
||||
//# sourceMappingURL=mongocryptd_manager.js.map
|
||||
1
node_modules/mongodb/lib/client-side-encryption/mongocryptd_manager.js.map
generated
vendored
Normal file
1
node_modules/mongodb/lib/client-side-encryption/mongocryptd_manager.js.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"mongocryptd_manager.js","sourceRoot":"","sources":["../../src/client-side-encryption/mongocryptd_manager.ts"],"names":[],"mappings":"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"}
|
||||
23
node_modules/mongodb/lib/client-side-encryption/providers/aws.js
generated
vendored
Normal file
23
node_modules/mongodb/lib/client-side-encryption/providers/aws.js
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.loadAWSCredentials = loadAWSCredentials;
|
||||
const aws_temporary_credentials_1 = require("../../cmap/auth/aws_temporary_credentials");
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
async function loadAWSCredentials(kmsProviders, provider) {
|
||||
const credentialProvider = new aws_temporary_credentials_1.AWSSDKCredentialProvider(provider);
|
||||
// We shouldn't ever receive a response from the AWS SDK that doesn't have a `SecretAccessKey`
|
||||
// or `AccessKeyId`. However, TS says these fields are optional. We provide empty strings
|
||||
// and let libmongocrypt error if we're unable to fetch the required keys.
|
||||
const { SecretAccessKey = '', AccessKeyId = '', Token } = await credentialProvider.getCredentials();
|
||||
const aws = {
|
||||
secretAccessKey: SecretAccessKey,
|
||||
accessKeyId: AccessKeyId
|
||||
};
|
||||
// the AWS session token is only required for temporary credentials so only attach it to the
|
||||
// result if it's present in the response from the aws sdk
|
||||
Token != null && (aws.sessionToken = Token);
|
||||
return { ...kmsProviders, aws };
|
||||
}
|
||||
//# sourceMappingURL=aws.js.map
|
||||
1
node_modules/mongodb/lib/client-side-encryption/providers/aws.js.map
generated
vendored
Normal file
1
node_modules/mongodb/lib/client-side-encryption/providers/aws.js.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"aws.js","sourceRoot":"","sources":["../../../src/client-side-encryption/providers/aws.ts"],"names":[],"mappings":";;AASA,gDAuBC;AAhCD,yFAGmD;AAGnD;;GAEG;AACI,KAAK,UAAU,kBAAkB,CACtC,YAA0B,EAC1B,QAAgC;IAEhC,MAAM,kBAAkB,GAAG,IAAI,oDAAwB,CAAC,QAAQ,CAAC,CAAC;IAElE,8FAA8F;IAC9F,2FAA2F;IAC3F,0EAA0E;IAC1E,MAAM,EACJ,eAAe,GAAG,EAAE,EACpB,WAAW,GAAG,EAAE,EAChB,KAAK,EACN,GAAG,MAAM,kBAAkB,CAAC,cAAc,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAqC;QAC5C,eAAe,EAAE,eAAe;QAChC,WAAW,EAAE,WAAW;KACzB,CAAC;IACF,4FAA4F;IAC5F,0DAA0D;IAC1D,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,CAAC;IAE5C,OAAO,EAAE,GAAG,YAAY,EAAE,GAAG,EAAE,CAAC;AAClC,CAAC"}
|
||||
132
node_modules/mongodb/lib/client-side-encryption/providers/azure.js
generated
vendored
Normal file
132
node_modules/mongodb/lib/client-side-encryption/providers/azure.js
generated
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.tokenCache = exports.AzureCredentialCache = exports.AZURE_BASE_URL = void 0;
|
||||
exports.addAzureParams = addAzureParams;
|
||||
exports.prepareRequest = prepareRequest;
|
||||
exports.fetchAzureKMSToken = fetchAzureKMSToken;
|
||||
exports.loadAzureCredentials = loadAzureCredentials;
|
||||
const error_1 = require("../../error");
|
||||
const utils_1 = require("../../utils");
|
||||
const errors_1 = require("../errors");
|
||||
const MINIMUM_TOKEN_REFRESH_IN_MILLISECONDS = 6000;
|
||||
/** Base URL for getting Azure tokens. */
|
||||
exports.AZURE_BASE_URL = 'http://169.254.169.254/metadata/identity/oauth2/token?';
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class AzureCredentialCache {
|
||||
constructor() {
|
||||
this.cachedToken = null;
|
||||
}
|
||||
async getToken() {
|
||||
if (this.cachedToken == null || this.needsRefresh(this.cachedToken)) {
|
||||
this.cachedToken = await this._getToken();
|
||||
}
|
||||
return { accessToken: this.cachedToken.accessToken };
|
||||
}
|
||||
needsRefresh(token) {
|
||||
const timeUntilExpirationMS = token.expiresOnTimestamp - Date.now();
|
||||
return timeUntilExpirationMS <= MINIMUM_TOKEN_REFRESH_IN_MILLISECONDS;
|
||||
}
|
||||
/**
|
||||
* exposed for testing
|
||||
*/
|
||||
resetCache() {
|
||||
this.cachedToken = null;
|
||||
}
|
||||
/**
|
||||
* exposed for testing
|
||||
*/
|
||||
_getToken() {
|
||||
return fetchAzureKMSToken();
|
||||
}
|
||||
}
|
||||
exports.AzureCredentialCache = AzureCredentialCache;
|
||||
/** @internal */
|
||||
exports.tokenCache = new AzureCredentialCache();
|
||||
/** @internal */
|
||||
async function parseResponse(response) {
|
||||
const { status, body: rawBody } = response;
|
||||
const body = (() => {
|
||||
try {
|
||||
return JSON.parse(rawBody);
|
||||
}
|
||||
catch {
|
||||
throw new errors_1.MongoCryptAzureKMSRequestError('Malformed JSON body in GET request.');
|
||||
}
|
||||
})();
|
||||
if (status !== 200) {
|
||||
throw new errors_1.MongoCryptAzureKMSRequestError('Unable to complete request.', body);
|
||||
}
|
||||
if (!body.access_token) {
|
||||
throw new errors_1.MongoCryptAzureKMSRequestError('Malformed response body - missing field `access_token`.');
|
||||
}
|
||||
if (!body.expires_in) {
|
||||
throw new errors_1.MongoCryptAzureKMSRequestError('Malformed response body - missing field `expires_in`.');
|
||||
}
|
||||
const expiresInMS = Number(body.expires_in) * 1000;
|
||||
if (Number.isNaN(expiresInMS)) {
|
||||
throw new errors_1.MongoCryptAzureKMSRequestError('Malformed response body - unable to parse int from `expires_in` field.');
|
||||
}
|
||||
return {
|
||||
accessToken: body.access_token,
|
||||
expiresOnTimestamp: Date.now() + expiresInMS
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
* Get the Azure endpoint URL.
|
||||
*/
|
||||
function addAzureParams(url, resource, username) {
|
||||
url.searchParams.append('api-version', '2018-02-01');
|
||||
url.searchParams.append('resource', resource);
|
||||
if (username) {
|
||||
url.searchParams.append('client_id', username);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* parses any options provided by prose tests to `fetchAzureKMSToken` and merges them with
|
||||
* the default values for headers and the request url.
|
||||
*/
|
||||
function prepareRequest(options) {
|
||||
const url = new URL(options.url?.toString() ?? exports.AZURE_BASE_URL);
|
||||
addAzureParams(url, 'https://vault.azure.net');
|
||||
const headers = { ...options.headers, 'Content-Type': 'application/json', Metadata: true };
|
||||
return { headers, url };
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* `AzureKMSRequestOptions` allows prose tests to modify the http request sent to the idms
|
||||
* servers. This is required to simulate different server conditions. No options are expected to
|
||||
* be set outside of tests.
|
||||
*
|
||||
* exposed for CSFLE
|
||||
* [prose test 18](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#azure-imds-credentials)
|
||||
*/
|
||||
async function fetchAzureKMSToken(options = {}) {
|
||||
const { headers, url } = prepareRequest(options);
|
||||
try {
|
||||
const response = await (0, utils_1.get)(url, { headers });
|
||||
return await parseResponse(response);
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof error_1.MongoNetworkTimeoutError) {
|
||||
throw new errors_1.MongoCryptAzureKMSRequestError(`[Azure KMS] ${error.message}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @throws Will reject with a `MongoCryptError` if the http request fails or the http response is malformed.
|
||||
*/
|
||||
async function loadAzureCredentials(kmsProviders) {
|
||||
const azure = await exports.tokenCache.getToken();
|
||||
return { ...kmsProviders, azure };
|
||||
}
|
||||
//# sourceMappingURL=azure.js.map
|
||||
1
node_modules/mongodb/lib/client-side-encryption/providers/azure.js.map
generated
vendored
Normal file
1
node_modules/mongodb/lib/client-side-encryption/providers/azure.js.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"azure.js","sourceRoot":"","sources":["../../../src/client-side-encryption/providers/azure.ts"],"names":[],"mappings":"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"}
|
||||
16
node_modules/mongodb/lib/client-side-encryption/providers/gcp.js
generated
vendored
Normal file
16
node_modules/mongodb/lib/client-side-encryption/providers/gcp.js
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.loadGCPCredentials = loadGCPCredentials;
|
||||
const deps_1 = require("../../deps");
|
||||
/** @internal */
|
||||
async function loadGCPCredentials(kmsProviders) {
|
||||
const gcpMetadata = (0, deps_1.getGcpMetadata)();
|
||||
if ('kModuleError' in gcpMetadata) {
|
||||
return kmsProviders;
|
||||
}
|
||||
const { access_token: accessToken } = await gcpMetadata.instance({
|
||||
property: 'service-accounts/default/token'
|
||||
});
|
||||
return { ...kmsProviders, gcp: { accessToken } };
|
||||
}
|
||||
//# sourceMappingURL=gcp.js.map
|
||||
1
node_modules/mongodb/lib/client-side-encryption/providers/gcp.js.map
generated
vendored
Normal file
1
node_modules/mongodb/lib/client-side-encryption/providers/gcp.js.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"gcp.js","sourceRoot":"","sources":["../../../src/client-side-encryption/providers/gcp.ts"],"names":[],"mappings":";;AAIA,gDAWC;AAfD,qCAA4C;AAG5C,gBAAgB;AACT,KAAK,UAAU,kBAAkB,CAAC,YAA0B;IACjE,MAAM,WAAW,GAAG,IAAA,qBAAc,GAAE,CAAC;IAErC,IAAI,cAAc,IAAI,WAAW,EAAE,CAAC;QAClC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,WAAW,CAAC,QAAQ,CAA2B;QACzF,QAAQ,EAAE,gCAAgC;KAC3C,CAAC,CAAC;IACH,OAAO,EAAE,GAAG,YAAY,EAAE,GAAG,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC;AACnD,CAAC"}
|
||||
43
node_modules/mongodb/lib/client-side-encryption/providers/index.js
generated
vendored
Normal file
43
node_modules/mongodb/lib/client-side-encryption/providers/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isEmptyCredentials = isEmptyCredentials;
|
||||
exports.refreshKMSCredentials = refreshKMSCredentials;
|
||||
const aws_1 = require("./aws");
|
||||
const azure_1 = require("./azure");
|
||||
const gcp_1 = require("./gcp");
|
||||
/**
|
||||
* Auto credential fetching should only occur when the provider is defined on the kmsProviders map
|
||||
* and the settings are an empty object.
|
||||
*
|
||||
* This is distinct from a nullish provider key.
|
||||
*
|
||||
* @internal - exposed for testing purposes only
|
||||
*/
|
||||
function isEmptyCredentials(providerName, kmsProviders) {
|
||||
const provider = kmsProviders[providerName];
|
||||
if (provider == null) {
|
||||
return false;
|
||||
}
|
||||
return typeof provider === 'object' && Object.keys(provider).length === 0;
|
||||
}
|
||||
/**
|
||||
* Load cloud provider credentials for the user provided KMS providers.
|
||||
* Credentials will only attempt to get loaded if they do not exist
|
||||
* and no existing credentials will get overwritten.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
async function refreshKMSCredentials(kmsProviders, credentialProviders) {
|
||||
let finalKMSProviders = kmsProviders;
|
||||
if (isEmptyCredentials('aws', kmsProviders)) {
|
||||
finalKMSProviders = await (0, aws_1.loadAWSCredentials)(finalKMSProviders, credentialProviders?.aws);
|
||||
}
|
||||
if (isEmptyCredentials('gcp', kmsProviders)) {
|
||||
finalKMSProviders = await (0, gcp_1.loadGCPCredentials)(finalKMSProviders);
|
||||
}
|
||||
if (isEmptyCredentials('azure', kmsProviders)) {
|
||||
finalKMSProviders = await (0, azure_1.loadAzureCredentials)(finalKMSProviders);
|
||||
}
|
||||
return finalKMSProviders;
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
node_modules/mongodb/lib/client-side-encryption/providers/index.js.map
generated
vendored
Normal file
1
node_modules/mongodb/lib/client-side-encryption/providers/index.js.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client-side-encryption/providers/index.ts"],"names":[],"mappings":";;AA0KA,gDASC;AASD,sDAkBC;AA5MD,+BAA2C;AAC3C,mCAA+C;AAC/C,+BAA2C;AA8J3C;;;;;;;GAOG;AACH,SAAgB,kBAAkB,CAChC,YAA6C,EAC7C,YAA0B;IAE1B,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAC5E,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,qBAAqB,CACzC,YAA0B,EAC1B,mBAAyC;IAEzC,IAAI,iBAAiB,GAAG,YAAY,CAAC;IAErC,IAAI,kBAAkB,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,CAAC;QAC5C,iBAAiB,GAAG,MAAM,IAAA,wBAAkB,EAAC,iBAAiB,EAAE,mBAAmB,EAAE,GAAG,CAAC,CAAC;IAC5F,CAAC;IAED,IAAI,kBAAkB,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,CAAC;QAC5C,iBAAiB,GAAG,MAAM,IAAA,wBAAkB,EAAC,iBAAiB,CAAC,CAAC;IAClE,CAAC;IAED,IAAI,kBAAkB,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,CAAC;QAC9C,iBAAiB,GAAG,MAAM,IAAA,4BAAoB,EAAC,iBAAiB,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,iBAAiB,CAAC;AAC3B,CAAC"}
|
||||
426
node_modules/mongodb/lib/client-side-encryption/state_machine.js
generated
vendored
Normal file
426
node_modules/mongodb/lib/client-side-encryption/state_machine.js
generated
vendored
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.StateMachine = void 0;
|
||||
const fs = require("fs/promises");
|
||||
const net = require("net");
|
||||
const tls = require("tls");
|
||||
const bson_1 = require("../bson");
|
||||
const abstract_cursor_1 = require("../cursor/abstract_cursor");
|
||||
const deps_1 = require("../deps");
|
||||
const error_1 = require("../error");
|
||||
const timeout_1 = require("../timeout");
|
||||
const utils_1 = require("../utils");
|
||||
const client_encryption_1 = require("./client_encryption");
|
||||
const errors_1 = require("./errors");
|
||||
let socks = null;
|
||||
function loadSocks() {
|
||||
if (socks == null) {
|
||||
const socksImport = (0, deps_1.getSocks)();
|
||||
if ('kModuleError' in socksImport) {
|
||||
throw socksImport.kModuleError;
|
||||
}
|
||||
socks = socksImport;
|
||||
}
|
||||
return socks;
|
||||
}
|
||||
// libmongocrypt states
|
||||
const MONGOCRYPT_CTX_ERROR = 0;
|
||||
const MONGOCRYPT_CTX_NEED_MONGO_COLLINFO = 1;
|
||||
const MONGOCRYPT_CTX_NEED_MONGO_MARKINGS = 2;
|
||||
const MONGOCRYPT_CTX_NEED_MONGO_KEYS = 3;
|
||||
const MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS = 7;
|
||||
const MONGOCRYPT_CTX_NEED_KMS = 4;
|
||||
const MONGOCRYPT_CTX_READY = 5;
|
||||
const MONGOCRYPT_CTX_DONE = 6;
|
||||
const HTTPS_PORT = 443;
|
||||
const stateToString = new Map([
|
||||
[MONGOCRYPT_CTX_ERROR, 'MONGOCRYPT_CTX_ERROR'],
|
||||
[MONGOCRYPT_CTX_NEED_MONGO_COLLINFO, 'MONGOCRYPT_CTX_NEED_MONGO_COLLINFO'],
|
||||
[MONGOCRYPT_CTX_NEED_MONGO_MARKINGS, 'MONGOCRYPT_CTX_NEED_MONGO_MARKINGS'],
|
||||
[MONGOCRYPT_CTX_NEED_MONGO_KEYS, 'MONGOCRYPT_CTX_NEED_MONGO_KEYS'],
|
||||
[MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS, 'MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS'],
|
||||
[MONGOCRYPT_CTX_NEED_KMS, 'MONGOCRYPT_CTX_NEED_KMS'],
|
||||
[MONGOCRYPT_CTX_READY, 'MONGOCRYPT_CTX_READY'],
|
||||
[MONGOCRYPT_CTX_DONE, 'MONGOCRYPT_CTX_DONE']
|
||||
]);
|
||||
const INSECURE_TLS_OPTIONS = [
|
||||
'tlsInsecure',
|
||||
'tlsAllowInvalidCertificates',
|
||||
'tlsAllowInvalidHostnames'
|
||||
];
|
||||
/**
|
||||
* Helper function for logging. Enabled by setting the environment flag MONGODB_CRYPT_DEBUG.
|
||||
* @param msg - Anything you want to be logged.
|
||||
*/
|
||||
function debug(msg) {
|
||||
if (process.env.MONGODB_CRYPT_DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(msg);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This is kind of a hack. For `rewrapManyDataKey`, we have tests that
|
||||
* guarantee that when there are no matching keys, `rewrapManyDataKey` returns
|
||||
* nothing. We also have tests for auto encryption that guarantee for `encrypt`
|
||||
* we return an error when there are no matching keys. This error is generated in
|
||||
* subsequent iterations of the state machine.
|
||||
* Some apis (`encrypt`) throw if there are no filter matches and others (`rewrapManyDataKey`)
|
||||
* do not. We set the result manually here, and let the state machine continue. `libmongocrypt`
|
||||
* will inform us if we need to error by setting the state to `MONGOCRYPT_CTX_ERROR` but
|
||||
* otherwise we'll return `{ v: [] }`.
|
||||
*/
|
||||
let EMPTY_V;
|
||||
/**
|
||||
* @internal
|
||||
* An internal class that executes across a MongoCryptContext until either
|
||||
* a finishing state or an error is reached. Do not instantiate directly.
|
||||
*/
|
||||
// TODO(DRIVERS-2671): clarify CSOT behavior for FLE APIs
|
||||
class StateMachine {
|
||||
constructor(options, bsonOptions = (0, bson_1.pluckBSONSerializeOptions)(options)) {
|
||||
this.options = options;
|
||||
this.bsonOptions = bsonOptions;
|
||||
}
|
||||
/**
|
||||
* Executes the state machine according to the specification
|
||||
*/
|
||||
async execute(executor, context, options) {
|
||||
const keyVaultNamespace = executor._keyVaultNamespace;
|
||||
const keyVaultClient = executor._keyVaultClient;
|
||||
const metaDataClient = executor._metaDataClient;
|
||||
const mongocryptdClient = executor._mongocryptdClient;
|
||||
const mongocryptdManager = executor._mongocryptdManager;
|
||||
let result = null;
|
||||
// Typescript treats getters just like properties: Once you've tested it for equality
|
||||
// it cannot change. Which is exactly the opposite of what we use state and status for.
|
||||
// Every call to at least `addMongoOperationResponse` and `finalize` can change the state.
|
||||
// These wrappers let us write code more naturally and not add compiler exceptions
|
||||
// to conditions checks inside the state machine.
|
||||
const getStatus = () => context.status;
|
||||
const getState = () => context.state;
|
||||
while (getState() !== MONGOCRYPT_CTX_DONE && getState() !== MONGOCRYPT_CTX_ERROR) {
|
||||
options.signal?.throwIfAborted();
|
||||
debug(`[context#${context.id}] ${stateToString.get(getState()) || getState()}`);
|
||||
switch (getState()) {
|
||||
case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: {
|
||||
const filter = (0, bson_1.deserialize)(context.nextMongoOperation());
|
||||
if (!metaDataClient) {
|
||||
throw new errors_1.MongoCryptError('unreachable state machine state: entered MONGOCRYPT_CTX_NEED_MONGO_COLLINFO but metadata client is undefined');
|
||||
}
|
||||
const collInfoCursor = this.fetchCollectionInfo(metaDataClient, context.ns, filter, options);
|
||||
for await (const collInfo of collInfoCursor) {
|
||||
context.addMongoOperationResponse((0, bson_1.serialize)(collInfo));
|
||||
if (getState() === MONGOCRYPT_CTX_ERROR)
|
||||
break;
|
||||
}
|
||||
if (getState() === MONGOCRYPT_CTX_ERROR)
|
||||
break;
|
||||
context.finishMongoOperation();
|
||||
break;
|
||||
}
|
||||
case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: {
|
||||
const command = context.nextMongoOperation();
|
||||
if (getState() === MONGOCRYPT_CTX_ERROR)
|
||||
break;
|
||||
if (!mongocryptdClient) {
|
||||
throw new errors_1.MongoCryptError('unreachable state machine state: entered MONGOCRYPT_CTX_NEED_MONGO_MARKINGS but mongocryptdClient is undefined');
|
||||
}
|
||||
// When we are using the shared library, we don't have a mongocryptd manager.
|
||||
const markedCommand = mongocryptdManager
|
||||
? await mongocryptdManager.withRespawn(this.markCommand.bind(this, mongocryptdClient, context.ns, command, options))
|
||||
: await this.markCommand(mongocryptdClient, context.ns, command, options);
|
||||
context.addMongoOperationResponse(markedCommand);
|
||||
context.finishMongoOperation();
|
||||
break;
|
||||
}
|
||||
case MONGOCRYPT_CTX_NEED_MONGO_KEYS: {
|
||||
const filter = context.nextMongoOperation();
|
||||
const keys = await this.fetchKeys(keyVaultClient, keyVaultNamespace, filter, options);
|
||||
if (keys.length === 0) {
|
||||
// See docs on EMPTY_V
|
||||
result = EMPTY_V ??= (0, bson_1.serialize)({ v: [] });
|
||||
}
|
||||
for (const key of keys) {
|
||||
context.addMongoOperationResponse((0, bson_1.serialize)(key));
|
||||
}
|
||||
context.finishMongoOperation();
|
||||
break;
|
||||
}
|
||||
case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS: {
|
||||
const kmsProviders = await executor.askForKMSCredentials();
|
||||
context.provideKMSProviders((0, bson_1.serialize)(kmsProviders));
|
||||
break;
|
||||
}
|
||||
case MONGOCRYPT_CTX_NEED_KMS: {
|
||||
await Promise.all(this.requests(context, options));
|
||||
context.finishKMSRequests();
|
||||
break;
|
||||
}
|
||||
case MONGOCRYPT_CTX_READY: {
|
||||
const finalizedContext = context.finalize();
|
||||
if (getState() === MONGOCRYPT_CTX_ERROR) {
|
||||
const message = getStatus().message || 'Finalization error';
|
||||
throw new errors_1.MongoCryptError(message);
|
||||
}
|
||||
result = finalizedContext;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new errors_1.MongoCryptError(`Unknown state: ${getState()}`);
|
||||
}
|
||||
}
|
||||
if (getState() === MONGOCRYPT_CTX_ERROR || result == null) {
|
||||
const message = getStatus().message;
|
||||
if (!message) {
|
||||
debug(`unidentifiable error in MongoCrypt - received an error status from \`libmongocrypt\` but received no error message.`);
|
||||
}
|
||||
throw new errors_1.MongoCryptError(message ??
|
||||
'unidentifiable error in MongoCrypt - received an error status from `libmongocrypt` but received no error message.');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Handles the request to the KMS service. Exposed for testing purposes. Do not directly invoke.
|
||||
* @param kmsContext - A C++ KMS context returned from the bindings
|
||||
* @returns A promise that resolves when the KMS reply has be fully parsed
|
||||
*/
|
||||
async kmsRequest(request, options) {
|
||||
const parsedUrl = request.endpoint.split(':');
|
||||
const port = parsedUrl[1] != null ? Number.parseInt(parsedUrl[1], 10) : HTTPS_PORT;
|
||||
const socketOptions = {
|
||||
host: parsedUrl[0],
|
||||
servername: parsedUrl[0],
|
||||
port,
|
||||
...(0, client_encryption_1.autoSelectSocketOptions)(this.options.socketOptions || {})
|
||||
};
|
||||
const message = request.message;
|
||||
const buffer = new utils_1.BufferPool();
|
||||
let netSocket;
|
||||
let socket;
|
||||
function destroySockets() {
|
||||
for (const sock of [socket, netSocket]) {
|
||||
if (sock) {
|
||||
sock.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
function onerror(cause) {
|
||||
return new errors_1.MongoCryptError('KMS request failed', { cause });
|
||||
}
|
||||
function onclose() {
|
||||
return new errors_1.MongoCryptError('KMS request closed');
|
||||
}
|
||||
const tlsOptions = this.options.tlsOptions;
|
||||
if (tlsOptions) {
|
||||
const kmsProvider = request.kmsProvider;
|
||||
const providerTlsOptions = tlsOptions[kmsProvider];
|
||||
if (providerTlsOptions) {
|
||||
const error = this.validateTlsOptions(kmsProvider, providerTlsOptions);
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
await this.setTlsOptions(providerTlsOptions, socketOptions);
|
||||
}
|
||||
catch (err) {
|
||||
throw onerror(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
let abortListener;
|
||||
try {
|
||||
if (this.options.proxyOptions && this.options.proxyOptions.proxyHost) {
|
||||
netSocket = new net.Socket();
|
||||
const { promise: willConnect, reject: rejectOnNetSocketError, resolve: resolveOnNetSocketConnect } = (0, utils_1.promiseWithResolvers)();
|
||||
netSocket
|
||||
.once('error', err => rejectOnNetSocketError(onerror(err)))
|
||||
.once('close', () => rejectOnNetSocketError(onclose()))
|
||||
.once('connect', () => resolveOnNetSocketConnect());
|
||||
const netSocketOptions = {
|
||||
...socketOptions,
|
||||
host: this.options.proxyOptions.proxyHost,
|
||||
port: this.options.proxyOptions.proxyPort || 1080
|
||||
};
|
||||
netSocket.connect(netSocketOptions);
|
||||
await willConnect;
|
||||
try {
|
||||
socks ??= loadSocks();
|
||||
socketOptions.socket = (await socks.SocksClient.createConnection({
|
||||
existing_socket: netSocket,
|
||||
command: 'connect',
|
||||
destination: { host: socketOptions.host, port: socketOptions.port },
|
||||
proxy: {
|
||||
// host and port are ignored because we pass existing_socket
|
||||
host: 'iLoveJavaScript',
|
||||
port: 0,
|
||||
type: 5,
|
||||
userId: this.options.proxyOptions.proxyUsername,
|
||||
password: this.options.proxyOptions.proxyPassword
|
||||
}
|
||||
})).socket;
|
||||
}
|
||||
catch (err) {
|
||||
throw onerror(err);
|
||||
}
|
||||
}
|
||||
socket = tls.connect(socketOptions, () => {
|
||||
socket.write(message);
|
||||
});
|
||||
const { promise: willResolveKmsRequest, reject: rejectOnTlsSocketError, resolve } = (0, utils_1.promiseWithResolvers)();
|
||||
abortListener = (0, utils_1.addAbortListener)(options?.signal, function () {
|
||||
destroySockets();
|
||||
rejectOnTlsSocketError(this.reason);
|
||||
});
|
||||
socket
|
||||
.once('error', err => rejectOnTlsSocketError(onerror(err)))
|
||||
.once('close', () => rejectOnTlsSocketError(onclose()))
|
||||
.on('data', data => {
|
||||
buffer.append(data);
|
||||
while (request.bytesNeeded > 0 && buffer.length) {
|
||||
const bytesNeeded = Math.min(request.bytesNeeded, buffer.length);
|
||||
request.addResponse(buffer.read(bytesNeeded));
|
||||
}
|
||||
if (request.bytesNeeded <= 0) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
await (options?.timeoutContext?.csotEnabled()
|
||||
? Promise.all([
|
||||
willResolveKmsRequest,
|
||||
timeout_1.Timeout.expires(options.timeoutContext?.remainingTimeMS)
|
||||
])
|
||||
: willResolveKmsRequest);
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof timeout_1.TimeoutError)
|
||||
throw new error_1.MongoOperationTimeoutError('KMS request timed out');
|
||||
throw error;
|
||||
}
|
||||
finally {
|
||||
// There's no need for any more activity on this socket at this point.
|
||||
destroySockets();
|
||||
abortListener?.[utils_1.kDispose]();
|
||||
}
|
||||
}
|
||||
*requests(context, options) {
|
||||
for (let request = context.nextKMSRequest(); request != null; request = context.nextKMSRequest()) {
|
||||
yield this.kmsRequest(request, options);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Validates the provided TLS options are secure.
|
||||
*
|
||||
* @param kmsProvider - The KMS provider name.
|
||||
* @param tlsOptions - The client TLS options for the provider.
|
||||
*
|
||||
* @returns An error if any option is invalid.
|
||||
*/
|
||||
validateTlsOptions(kmsProvider, tlsOptions) {
|
||||
const tlsOptionNames = Object.keys(tlsOptions);
|
||||
for (const option of INSECURE_TLS_OPTIONS) {
|
||||
if (tlsOptionNames.includes(option)) {
|
||||
return new errors_1.MongoCryptError(`Insecure TLS options prohibited for ${kmsProvider}: ${option}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sets only the valid secure TLS options.
|
||||
*
|
||||
* @param tlsOptions - The client TLS options for the provider.
|
||||
* @param options - The existing connection options.
|
||||
*/
|
||||
async setTlsOptions(tlsOptions, options) {
|
||||
// If a secureContext is provided, ensure it is set.
|
||||
if (tlsOptions.secureContext) {
|
||||
options.secureContext = tlsOptions.secureContext;
|
||||
}
|
||||
if (tlsOptions.tlsCertificateKeyFile) {
|
||||
const cert = await fs.readFile(tlsOptions.tlsCertificateKeyFile);
|
||||
options.cert = options.key = cert;
|
||||
}
|
||||
if (tlsOptions.tlsCAFile) {
|
||||
options.ca = await fs.readFile(tlsOptions.tlsCAFile);
|
||||
}
|
||||
if (tlsOptions.tlsCertificateKeyFilePassword) {
|
||||
options.passphrase = tlsOptions.tlsCertificateKeyFilePassword;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Fetches collection info for a provided namespace, when libmongocrypt
|
||||
* enters the `MONGOCRYPT_CTX_NEED_MONGO_COLLINFO` state. The result is
|
||||
* used to inform libmongocrypt of the schema associated with this
|
||||
* namespace. Exposed for testing purposes. Do not directly invoke.
|
||||
*
|
||||
* @param client - A MongoClient connected to the topology
|
||||
* @param ns - The namespace to list collections from
|
||||
* @param filter - A filter for the listCollections command
|
||||
* @param callback - Invoked with the info of the requested collection, or with an error
|
||||
*/
|
||||
fetchCollectionInfo(client, ns, filter, options) {
|
||||
const { db } = utils_1.MongoDBCollectionNamespace.fromString(ns);
|
||||
const cursor = client.db(db).listCollections(filter, {
|
||||
promoteLongs: false,
|
||||
promoteValues: false,
|
||||
timeoutContext: options?.timeoutContext && new abstract_cursor_1.CursorTimeoutContext(options?.timeoutContext, Symbol()),
|
||||
signal: options?.signal,
|
||||
nameOnly: false
|
||||
});
|
||||
return cursor;
|
||||
}
|
||||
/**
|
||||
* Calls to the mongocryptd to provide markings for a command.
|
||||
* Exposed for testing purposes. Do not directly invoke.
|
||||
* @param client - A MongoClient connected to a mongocryptd
|
||||
* @param ns - The namespace (database.collection) the command is being executed on
|
||||
* @param command - The command to execute.
|
||||
* @param callback - Invoked with the serialized and marked bson command, or with an error
|
||||
*/
|
||||
async markCommand(client, ns, command, options) {
|
||||
const { db } = utils_1.MongoDBCollectionNamespace.fromString(ns);
|
||||
const bsonOptions = { promoteLongs: false, promoteValues: false };
|
||||
const rawCommand = (0, bson_1.deserialize)(command, bsonOptions);
|
||||
const commandOptions = {
|
||||
timeoutMS: undefined,
|
||||
signal: undefined
|
||||
};
|
||||
if (options?.timeoutContext?.csotEnabled()) {
|
||||
commandOptions.timeoutMS = options.timeoutContext.remainingTimeMS;
|
||||
}
|
||||
if (options?.signal) {
|
||||
commandOptions.signal = options.signal;
|
||||
}
|
||||
const response = await client.db(db).command(rawCommand, {
|
||||
...bsonOptions,
|
||||
...commandOptions
|
||||
});
|
||||
return (0, bson_1.serialize)(response, this.bsonOptions);
|
||||
}
|
||||
/**
|
||||
* Requests keys from the keyVault collection on the topology.
|
||||
* Exposed for testing purposes. Do not directly invoke.
|
||||
* @param client - A MongoClient connected to the topology
|
||||
* @param keyVaultNamespace - The namespace (database.collection) of the keyVault Collection
|
||||
* @param filter - The filter for the find query against the keyVault Collection
|
||||
* @param callback - Invoked with the found keys, or with an error
|
||||
*/
|
||||
fetchKeys(client, keyVaultNamespace, filter, options) {
|
||||
const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(keyVaultNamespace);
|
||||
const commandOptions = {
|
||||
timeoutContext: undefined,
|
||||
signal: undefined
|
||||
};
|
||||
if (options?.timeoutContext != null) {
|
||||
commandOptions.timeoutContext = new abstract_cursor_1.CursorTimeoutContext(options.timeoutContext, Symbol());
|
||||
}
|
||||
if (options?.signal != null) {
|
||||
commandOptions.signal = options.signal;
|
||||
}
|
||||
return client
|
||||
.db(dbName)
|
||||
.collection(collectionName, { readConcern: { level: 'majority' } })
|
||||
.find((0, bson_1.deserialize)(filter), commandOptions)
|
||||
.toArray();
|
||||
}
|
||||
}
|
||||
exports.StateMachine = StateMachine;
|
||||
//# sourceMappingURL=state_machine.js.map
|
||||
1
node_modules/mongodb/lib/client-side-encryption/state_machine.js.map
generated
vendored
Normal file
1
node_modules/mongodb/lib/client-side-encryption/state_machine.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue