You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
4.6 KiB

2 months ago
'use strict';
const MongoClient = require('./mongo_client');
const BSON = require('./core/connection/utils').retrieveBSON();
const MongoError = require('./core/error').MongoError;
let mongodbClientEncryption = undefined;
try {
// Ensure you always wrap an optional require in the try block NODE-3199
mongodbClientEncryption = require('mongodb-client-encryption');
} catch (err) {
throw new MongoError(
'Auto-encryption requested, but the module is not installed. ' +
'Please add `mongodb-client-encryption` as a dependency of your project'
);
}
if (
mongodbClientEncryption === undefined ||
typeof mongodbClientEncryption.extension !== 'function'
) {
throw new MongoError(
'loaded version of `mongodb-client-encryption` does not have property `extension`. ' +
'Please make sure you are loading the correct version of `mongodb-client-encryption`'
);
}
const AutoEncrypter = mongodbClientEncryption.extension(require('../index')).AutoEncrypter;
const kInternalClient = Symbol('internalClient');
class Encrypter {
/**
* @param {MongoClient} client
* @param {{autoEncryption: import('./mongo_client').AutoEncryptionOptions, bson: object}} options
*/
constructor(client, options) {
this.bypassAutoEncryption = !!options.autoEncryption.bypassAutoEncryption;
this.needsConnecting = false;
if (options.maxPoolSize === 0 && options.autoEncryption.keyVaultClient == null) {
options.autoEncryption.keyVaultClient = client;
} else if (options.autoEncryption.keyVaultClient == null) {
options.autoEncryption.keyVaultClient = this.getInternalClient(client);
}
if (this.bypassAutoEncryption) {
options.autoEncryption.metadataClient = undefined;
} else if (options.maxPoolSize === 0) {
options.autoEncryption.metadataClient = client;
} else {
options.autoEncryption.metadataClient = this.getInternalClient(client);
}
options.autoEncryption.bson = Encrypter.makeBSON(options);
this.autoEncrypter = new AutoEncrypter(client, options.autoEncryption);
}
getInternalClient(client) {
if (!this[kInternalClient]) {
const clonedOptions = {};
for (const key of Object.keys(client.s.options)) {
if (
['autoEncryption', 'minPoolSize', 'servers', 'caseTranslate', 'dbName'].indexOf(key) !==
-1
)
continue;
clonedOptions[key] = client.s.options[key];
}
clonedOptions.minPoolSize = 0;
const allEvents = [
// APM
'commandStarted',
'commandSucceeded',
'commandFailed',
// SDAM
'serverOpening',
'serverClosed',
'serverDescriptionChanged',
'serverHeartbeatStarted',
'serverHeartbeatSucceeded',
'serverHeartbeatFailed',
'topologyOpening',
'topologyClosed',
'topologyDescriptionChanged',
// Legacy
'joined',
'left',
'ping',
'ha',
// CMAP
'connectionPoolCreated',
'connectionPoolClosed',
'connectionCreated',
'connectionReady',
'connectionClosed',
'connectionCheckOutStarted',
'connectionCheckOutFailed',
'connectionCheckedOut',
'connectionCheckedIn',
'connectionPoolCleared'
];
this[kInternalClient] = new MongoClient(client.s.url, clonedOptions);
for (const eventName of allEvents) {
for (const listener of client.listeners(eventName)) {
this[kInternalClient].on(eventName, listener);
}
}
client.on('newListener', (eventName, listener) => {
this[kInternalClient].on(eventName, listener);
});
this.needsConnecting = true;
}
return this[kInternalClient];
}
connectInternalClient(callback) {
if (this.needsConnecting) {
this.needsConnecting = false;
return this[kInternalClient].connect(callback);
}
return callback();
}
close(client, force, callback) {
this.autoEncrypter.teardown(e => {
if (this[kInternalClient] && client !== this[kInternalClient]) {
return this[kInternalClient].close(force, callback);
}
callback(e);
});
}
static makeBSON(options) {
return (
(options || {}).bson ||
new BSON([
BSON.Binary,
BSON.Code,
BSON.DBRef,
BSON.Decimal128,
BSON.Double,
BSON.Int32,
BSON.Long,
BSON.Map,
BSON.MaxKey,
BSON.MinKey,
BSON.ObjectId,
BSON.BSONRegExp,
BSON.Symbol,
BSON.Timestamp
])
);
}
}
module.exports = { Encrypter };