UNPKG

mongodb

Version:
1,098 lines (996 loc) 34.4 kB
import type { ExplicitEncryptionContextOptions, MongoCrypt, MongoCryptConstructor, MongoCryptOptions } from 'mongodb-client-encryption'; import { type Binary, deserialize, type Document, type Int32, type Long, serialize, type UUID } from '../bson'; import { type AnyBulkWriteOperation, type BulkWriteResult } from '../bulk/common'; import { type ProxyOptions } from '../cmap/connection'; import { type Collection } from '../collection'; import { type FindCursor } from '../cursor/find_cursor'; import { type Db } from '../db'; import { getMongoDBClientEncryption } from '../deps'; import { type MongoClient, type MongoClientOptions } from '../mongo_client'; import { type Filter, type WithId } from '../mongo_types'; import { type CreateCollectionOptions } from '../operations/create_collection'; import { type DeleteResult } from '../operations/delete'; import { type CSOTTimeoutContext, TimeoutContext } from '../timeout'; import { MongoDBCollectionNamespace, resolveTimeoutOptions } from '../utils'; import * as cryptoCallbacks from './crypto_callbacks'; import { MongoCryptCreateDataKeyError, MongoCryptCreateEncryptedCollectionError, MongoCryptInvalidArgumentError } from './errors'; import { type ClientEncryptionDataKeyProvider, type CredentialProviders, isEmptyCredentials, type KMSProviders, refreshKMSCredentials } from './providers/index'; import { type ClientEncryptionSocketOptions, type CSFLEKMSTlsOptions, StateMachine } from './state_machine'; /** * @public * The schema for a DataKey in the key vault collection. */ export interface DataKey { _id: UUID; version?: number; keyAltNames?: string[]; keyMaterial: Binary; creationDate: Date; updateDate: Date; status: number; masterKey: Document; } /** * @public * The public interface for explicit in-use encryption */ export class ClientEncryption { /** @internal */ _client: MongoClient; /** @internal */ _keyVaultNamespace: string; /** @internal */ _keyVaultClient: MongoClient; /** @internal */ _proxyOptions: ProxyOptions; /** @internal */ _tlsOptions: CSFLEKMSTlsOptions; /** @internal */ _kmsProviders: KMSProviders; /** @internal */ _timeoutMS?: number; /** @internal */ _mongoCrypt: MongoCrypt; /** @internal */ _credentialProviders?: CredentialProviders; /** @internal */ static getMongoCrypt(): MongoCryptConstructor { const encryption = 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: MongoClient, options: ClientEncryptionOptions) { this._client = client; this._proxyOptions = options.proxyOptions ?? {}; this._tlsOptions = options.tlsOptions ?? {}; this._kmsProviders = options.kmsProviders || {}; const { timeoutMS } = resolveTimeoutOptions(client, options); this._timeoutMS = timeoutMS; this._credentialProviders = options.credentialProviders; if (options.credentialProviders?.aws && !isEmptyCredentials('aws', this._kmsProviders)) { throw new 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 MongoCryptInvalidArgumentError('Missing required option `keyVaultNamespace`'); } const mongoCryptOptions: MongoCryptOptions = { ...options, cryptoCallbacks, kmsProviders: !Buffer.isBuffer(this._kmsProviders) ? (serialize(this._kmsProviders) as Buffer) : this._kmsProviders }; 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: ClientEncryptionDataKeyProvider, options: ClientEncryptionCreateDataKeyProviderOptions = {} ): Promise<UUID> { if (options.keyAltNames && !Array.isArray(options.keyAltNames)) { throw new 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 MongoCryptInvalidArgumentError( `Option "keyAltNames" must be an array of strings, but item at index ${i} was of type ${typeof keyAltName}` ); } return serialize({ keyAltName }); }); } let keyMaterial = undefined; if (options.keyMaterial) { keyMaterial = serialize({ keyMaterial: options.keyMaterial }); } const dataKeyBson = serialize({ provider, ...options.masterKey }); const context = this._mongoCrypt.makeDataKeyContext(dataKeyBson, { keyAltNames, keyMaterial }); const stateMachine = new StateMachine({ proxyOptions: this._proxyOptions, tlsOptions: this._tlsOptions, socketOptions: autoSelectSocketOptions(this._client.s.options) }); const timeoutContext = options?.timeoutContext ?? TimeoutContext.create(resolveTimeoutOptions(this._client, { timeoutMS: this._timeoutMS })); const dataKey = deserialize( await stateMachine.execute(this, context, { timeoutContext }) ) as DataKey; const { db: dbName, collection: collectionName } = MongoDBCollectionNamespace.fromString( this._keyVaultNamespace ); const { insertedId } = await this._keyVaultClient .db(dbName) .collection<DataKey>(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: Filter<DataKey>, options: ClientEncryptionRewrapManyDataKeyProviderOptions ): Promise<{ bulkWriteResult?: BulkWriteResult }> { let keyEncryptionKeyBson = undefined; if (options) { const keyEncryptionKey = Object.assign({ provider: options.provider }, options.masterKey); keyEncryptionKeyBson = serialize(keyEncryptionKey); } const filterBson = serialize(filter); const context = this._mongoCrypt.makeRewrapManyDataKeyContext(filterBson, keyEncryptionKeyBson); const stateMachine = new StateMachine({ proxyOptions: this._proxyOptions, tlsOptions: this._tlsOptions, socketOptions: autoSelectSocketOptions(this._client.s.options) }); const timeoutContext = TimeoutContext.create( resolveTimeoutOptions(this._client, { timeoutMS: this._timeoutMS }) ); const { v: dataKeys } = deserialize( await stateMachine.execute(this, context, { timeoutContext }) ); if (dataKeys.length === 0) { return {}; } const { db: dbName, collection: collectionName } = MongoDBCollectionNamespace.fromString( this._keyVaultNamespace ); const replacements = dataKeys.map( (key: DataKey): AnyBulkWriteOperation<DataKey> => ({ updateOne: { filter: { _id: key._id }, update: { $set: { masterKey: key.masterKey, keyMaterial: key.keyMaterial }, $currentDate: { updateDate: true } } } }) ); const result = await this._keyVaultClient .db(dbName) .collection<DataKey>(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: Binary): Promise<DeleteResult> { const { db: dbName, collection: collectionName } = MongoDBCollectionNamespace.fromString( this._keyVaultNamespace ); return await this._keyVaultClient .db(dbName) .collection<DataKey>(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(): FindCursor<DataKey> { const { db: dbName, collection: collectionName } = MongoDBCollectionNamespace.fromString( this._keyVaultNamespace ); return this._keyVaultClient .db(dbName) .collection<DataKey>(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: Binary): Promise<DataKey | null> { const { db: dbName, collection: collectionName } = MongoDBCollectionNamespace.fromString( this._keyVaultNamespace ); return await this._keyVaultClient .db(dbName) .collection<DataKey>(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: string): Promise<WithId<DataKey> | null> { const { db: dbName, collection: collectionName } = MongoDBCollectionNamespace.fromString( this._keyVaultNamespace ); return await this._keyVaultClient .db(dbName) .collection<DataKey>(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: Binary, keyAltName: string): Promise<WithId<DataKey> | null> { const { db: dbName, collection: collectionName } = MongoDBCollectionNamespace.fromString( this._keyVaultNamespace ); const value = await this._keyVaultClient .db(dbName) .collection<DataKey>(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: Binary, keyAltName: string): Promise<WithId<DataKey> | null> { const { db: dbName, collection: collectionName } = 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<DataKey>(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<TSchema extends Document = Document>( db: Db, name: string, options: { provider: ClientEncryptionDataKeyProvider; createCollectionOptions: Omit<CreateCollectionOptions, 'encryptedFields'> & { encryptedFields: Document; }; masterKey?: AWSEncryptionKeyOptions | AzureEncryptionKeyOptions | GCPEncryptionKeyOptions; } ): Promise<{ collection: Collection<TSchema>; encryptedFields: Document }> { const { provider, masterKey, createCollectionOptions: { encryptedFields: { ...encryptedFields }, ...createCollectionOptions } } = options; const timeoutContext = this._timeoutMS != null ? TimeoutContext.create(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 is PromiseRejectedResult => result.status === 'rejected' ); if (rejection != null) { throw new MongoCryptCreateDataKeyError(encryptedFields, { cause: rejection.reason }); } } try { const collection = await db.createCollection<TSchema>(name, { ...createCollectionOptions, encryptedFields, timeoutMS: timeoutContext?.csotEnabled() ? timeoutContext?.getRemainingTimeMSOrThrow() : undefined }); return { collection, encryptedFields }; } catch (cause) { throw new 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: unknown, options: ClientEncryptionEncryptOptions): Promise<Binary> { 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: Document, options: ClientEncryptionEncryptOptions ): Promise<Binary> { 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<T = any>(value: Binary): Promise<T> { const valueBuffer = serialize({ v: value }); const context = this._mongoCrypt.makeExplicitDecryptionContext(valueBuffer); const stateMachine = new StateMachine({ proxyOptions: this._proxyOptions, tlsOptions: this._tlsOptions, socketOptions: autoSelectSocketOptions(this._client.s.options) }); const timeoutContext = this._timeoutMS != null ? TimeoutContext.create(resolveTimeoutOptions(this._client, { timeoutMS: this._timeoutMS })) : undefined; const { v } = 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(): Promise<KMSProviders> { return await 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. * */ private async _encrypt( value: unknown, expressionMode: boolean, options: ClientEncryptionEncryptOptions ): Promise<Binary> { const { algorithm, keyId, keyAltName, contentionFactor, queryType, rangeOptions } = options; const contextOptions: ExplicitEncryptionContextOptions = { expressionMode, algorithm }; if (keyId) { contextOptions.keyId = keyId.buffer; } if (keyAltName) { if (keyId) { throw new MongoCryptInvalidArgumentError( `"options" cannot contain both "keyId" and "keyAltName"` ); } if (typeof keyAltName !== 'string') { throw new MongoCryptInvalidArgumentError( `"options.keyAltName" must be of type string, but was of type ${typeof keyAltName}` ); } contextOptions.keyAltName = 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 = serialize(rangeOptions); } const valueBuffer = serialize({ v: value }); const stateMachine = new 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 ? TimeoutContext.create(resolveTimeoutOptions(this._client, { timeoutMS: this._timeoutMS })) : undefined; const { v } = deserialize(await stateMachine.execute(this, context, { timeoutContext })); return v; } } /** * @public * Options to provide when encrypting data. */ export interface ClientEncryptionEncryptOptions { /** * The algorithm to use for encryption. */ algorithm: | 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' | 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' | 'Indexed' | 'Unindexed' | 'Range'; /** * The id of the Binary dataKey to use for encryption */ keyId?: Binary; /** * A unique string name corresponding to an already existing dataKey. */ keyAltName?: string; /** The contention factor. */ contentionFactor?: bigint | number; /** * The query type. */ queryType?: 'equality' | 'range'; /** The index options for a Queryable Encryption field supporting "range" queries.*/ rangeOptions?: RangeOptions; } /** * @public * @experimental */ export interface ClientEncryptionRewrapManyDataKeyProviderOptions { provider: ClientEncryptionDataKeyProvider; masterKey?: | AWSEncryptionKeyOptions | AzureEncryptionKeyOptions | GCPEncryptionKeyOptions | KMIPEncryptionKeyOptions | undefined; } /** * @public * Additional settings to provide when creating a new `ClientEncryption` instance. */ export interface ClientEncryptionOptions { /** * The namespace of the key vault, used to store encryption keys */ keyVaultNamespace: string; /** * A MongoClient used to fetch keys from a key vault. Defaults to client. */ keyVaultClient?: MongoClient | undefined; /** * Options for specific KMS providers to use */ kmsProviders?: KMSProviders; /** * Options for user provided custom credential providers. */ credentialProviders?: CredentialProviders; /** * Options for specifying a Socks5 proxy to use for connecting to the KMS. */ proxyOptions?: ProxyOptions; /** * TLS options for kms providers to use. */ tlsOptions?: CSFLEKMSTlsOptions; /** * @experimental * * The timeout setting to be used for all the operations on ClientEncryption. * * When provided, `timeoutMS` is used as the timeout for each operation executed on * the ClientEncryption object. For example: * * ```typescript * const clientEncryption = new ClientEncryption(client, { * timeoutMS: 1_000 * kmsProviders: { local: { key: '<KEY>' } } * }); * * // `1_000` is used as the timeout for createDataKey call * await clientEncryption.createDataKey('local'); * ``` * * If `timeoutMS` is configured on the provided client, the client's `timeoutMS` value * will be used unless `timeoutMS` is also provided as a client encryption option. * * ```typescript * const client = new MongoClient('<uri>', { timeoutMS: 2_000 }); * * // timeoutMS is set to 1_000 on clientEncryption * const clientEncryption = new ClientEncryption(client, { * timeoutMS: 1_000 * kmsProviders: { local: { key: '<KEY>' } } * }); * ``` */ timeoutMS?: number; } /** * @public * Configuration options for making an AWS encryption key */ export interface AWSEncryptionKeyOptions { /** * The AWS region of the KMS */ region: string; /** * The Amazon Resource Name (ARN) to the AWS customer master key (CMK) */ key: string; /** * An alternate host to send KMS requests to. May include port number. */ endpoint?: string | undefined; } /** * @public * Configuration options for making an AWS encryption key */ export interface GCPEncryptionKeyOptions { /** * GCP project ID */ projectId: string; /** * Location name (e.g. "global") */ location: string; /** * Key ring name */ keyRing: string; /** * Key name */ keyName: string; /** * Key version */ keyVersion?: string | undefined; /** * KMS URL, defaults to `https://www.googleapis.com/auth/cloudkms` */ endpoint?: string | undefined; } /** * @public * Configuration options for making an Azure encryption key */ export interface AzureEncryptionKeyOptions { /** * Key name */ keyName: string; /** * Key vault URL, typically `<name>.vault.azure.net` */ keyVaultEndpoint: string; /** * Key version */ keyVersion?: string | undefined; } /** * @public * Configuration options for making a KMIP encryption key */ export interface KMIPEncryptionKeyOptions { /** * keyId is the KMIP Unique Identifier to a 96 byte KMIP Secret Data managed object. * * If keyId is omitted, a random 96 byte KMIP Secret Data managed object will be created. */ keyId?: string; /** * Host with optional port. */ endpoint?: string; /** * If true, this key should be decrypted by the KMIP server. * * Requires `mongodb-client-encryption>=6.0.1`. */ delegated?: boolean; } /** * @public * Options to provide when creating a new data key. */ export interface ClientEncryptionCreateDataKeyProviderOptions { /** * Identifies a new KMS-specific key used to encrypt the new data key */ masterKey?: | AWSEncryptionKeyOptions | AzureEncryptionKeyOptions | GCPEncryptionKeyOptions | KMIPEncryptionKeyOptions | undefined; /** * An optional list of string alternate names used to reference a key. * If a key is created with alternate names, then encryption may refer to the key by the unique alternate name instead of by _id. */ keyAltNames?: string[] | undefined; /** @experimental */ keyMaterial?: Buffer | Binary; /** @internal */ timeoutContext?: CSOTTimeoutContext; } /** * @public * @experimental */ export interface ClientEncryptionRewrapManyDataKeyResult { /** The result of rewrapping data keys. If unset, no keys matched the filter. */ bulkWriteResult?: BulkWriteResult; } /** * @public * RangeOptions specifies index options for a Queryable Encryption field supporting "range" queries. * min, max, sparsity, trimFactor and range must match the values set in the encryptedFields of the destination collection. * For double and decimal128, min/max/precision must all be set, or all be unset. */ export interface RangeOptions { /** min is the minimum value for the encrypted index. Required if precision is set. */ min?: any; /** max is the minimum value for the encrypted index. Required if precision is set. */ max?: any; /** sparsity may be used to tune performance. must be non-negative. When omitted, a default value is used. */ sparsity?: Long | bigint; /** trimFactor may be used to tune performance. must be non-negative. When omitted, a default value is used. */ trimFactor?: Int32 | number; /* precision determines the number of significant digits after the decimal point. May only be set for double or decimal128. */ precision?: number; } /** * Get the socket options from the client. * @param baseOptions - The mongo client options. * @returns ClientEncryptionSocketOptions */ export function autoSelectSocketOptions( baseOptions: MongoClientOptions ): ClientEncryptionSocketOptions { const options: ClientEncryptionSocketOptions = { autoSelectFamily: true }; if ('autoSelectFamily' in baseOptions) { options.autoSelectFamily = baseOptions.autoSelectFamily; } if ('autoSelectFamilyAttemptTimeout' in baseOptions) { options.autoSelectFamilyAttemptTimeout = baseOptions.autoSelectFamilyAttemptTimeout; } return options; }