123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.ClientEncryption = void 0;
- const bson_1 = require("../bson");
- const deps_1 = require("../deps");
- const utils_1 = require("../utils");
- const cryptoCallbacks = require("./crypto_callbacks");
- const errors_1 = require("./errors");
- const index_1 = require("./providers/index");
- const state_machine_1 = require("./state_machine");
- class ClientEncryption {
-
- static getMongoCrypt() {
- const encryption = (0, deps_1.getMongoDBClientEncryption)();
- if ('kModuleError' in encryption) {
- throw encryption.kModuleError;
- }
- return encryption.MongoCrypt;
- }
-
- constructor(client, options) {
- this._client = client;
- this._proxyOptions = options.proxyOptions ?? {};
- this._tlsOptions = options.tlsOptions ?? {};
- this._kmsProviders = options.kmsProviders || {};
- if (options.keyVaultNamespace == null) {
- throw new errors_1.MongoCryptInvalidArgumentError('Missing required option `keyVaultNamespace`');
- }
- const mongoCryptOptions = {
- ...options,
- cryptoCallbacks,
- kmsProviders: !Buffer.isBuffer(this._kmsProviders)
- ? (0, bson_1.serialize)(this._kmsProviders)
- : this._kmsProviders
- };
- this._keyVaultNamespace = options.keyVaultNamespace;
- this._keyVaultClient = options.keyVaultClient || client;
- const MongoCrypt = ClientEncryption.getMongoCrypt();
- this._mongoCrypt = new MongoCrypt(mongoCryptOptions);
- }
-
- 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
- });
- const dataKey = await stateMachine.execute(this, context);
- 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' } });
- return insertedId;
- }
-
- 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
- });
- const { v: dataKeys } = await stateMachine.execute(this, context);
- 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' }
- });
- return { bulkWriteResult: result };
- }
-
- async deleteKey(_id) {
- const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
- return this._keyVaultClient
- .db(dbName)
- .collection(collectionName)
- .deleteOne({ _id }, { writeConcern: { w: 'majority' } });
- }
-
- getKeys() {
- const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
- return this._keyVaultClient
- .db(dbName)
- .collection(collectionName)
- .find({}, { readConcern: { level: 'majority' } });
- }
-
- async getKey(_id) {
- const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
- return this._keyVaultClient
- .db(dbName)
- .collection(collectionName)
- .findOne({ _id }, { readConcern: { level: 'majority' } });
- }
-
- async getKeyByAltName(keyAltName) {
- const { db: dbName, collection: collectionName } = utils_1.MongoDBCollectionNamespace.fromString(this._keyVaultNamespace);
- return this._keyVaultClient
- .db(dbName)
- .collection(collectionName)
- .findOne({ keyAltNames: keyAltName }, { readConcern: { level: 'majority' } });
- }
-
- 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' });
- return value;
- }
-
- 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'
- });
- return value;
- }
-
- async createEncryptedCollection(db, name, options) {
- const { provider, masterKey, createCollectionOptions: { encryptedFields: { ...encryptedFields }, ...createCollectionOptions } } = options;
- 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 })
- });
- 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
- });
- return { collection, encryptedFields };
- }
- catch (cause) {
- throw new errors_1.MongoCryptCreateEncryptedCollectionError(encryptedFields, { cause });
- }
- }
-
- async encrypt(value, options) {
- return this._encrypt(value, false, options);
- }
-
- async encryptExpression(expression, options) {
- return this._encrypt(expression, true, options);
- }
-
- 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
- });
- const { v } = await stateMachine.execute(this, context);
- return v;
- }
-
- async askForKMSCredentials() {
- return (0, index_1.refreshKMSCredentials)(this._kmsProviders);
- }
- static get libmongocryptVersion() {
- return ClientEncryption.getMongoCrypt().libmongocryptVersion;
- }
-
- async _encrypt(value, expressionMode, options) {
- const { algorithm, keyId, keyAltName, contentionFactor, queryType, rangeOptions } = 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);
- }
- const valueBuffer = (0, bson_1.serialize)({ v: value });
- const stateMachine = new state_machine_1.StateMachine({
- proxyOptions: this._proxyOptions,
- tlsOptions: this._tlsOptions
- });
- const context = this._mongoCrypt.makeExplicitEncryptionContext(valueBuffer, contextOptions);
- const result = await stateMachine.execute(this, context);
- return result.v;
- }
- }
- exports.ClientEncryption = ClientEncryption;
|