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
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 };
|