encrypter.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. 'use strict';
  2. const MongoClient = require('./mongo_client');
  3. const BSON = require('./core/connection/utils').retrieveBSON();
  4. const MongoError = require('./core/error').MongoError;
  5. try {
  6. require.resolve('mongodb-client-encryption');
  7. } catch (err) {
  8. throw new MongoError(
  9. 'Auto-encryption requested, but the module is not installed. ' +
  10. 'Please add `mongodb-client-encryption` as a dependency of your project'
  11. );
  12. }
  13. const mongodbClientEncryption = require('mongodb-client-encryption');
  14. if (typeof mongodbClientEncryption.extension !== 'function') {
  15. throw new MongoError(
  16. 'loaded version of `mongodb-client-encryption` does not have property `extension`. ' +
  17. 'Please make sure you are loading the correct version of `mongodb-client-encryption`'
  18. );
  19. }
  20. const AutoEncrypter = mongodbClientEncryption.extension(require('../index')).AutoEncrypter;
  21. const kInternalClient = Symbol('internalClient');
  22. class Encrypter {
  23. /**
  24. * @param {MongoClient} client
  25. * @param {{autoEncryption: import('./mongo_client').AutoEncryptionOptions, bson: object}} options
  26. */
  27. constructor(client, options) {
  28. this.bypassAutoEncryption = !!options.autoEncryption.bypassAutoEncryption;
  29. this.needsConnecting = false;
  30. if (options.maxPoolSize === 0 && options.autoEncryption.keyVaultClient == null) {
  31. options.autoEncryption.keyVaultClient = client;
  32. } else if (options.autoEncryption.keyVaultClient == null) {
  33. options.autoEncryption.keyVaultClient = this.getInternalClient(client);
  34. }
  35. if (this.bypassAutoEncryption) {
  36. options.autoEncryption.metadataClient = undefined;
  37. } else if (options.maxPoolSize === 0) {
  38. options.autoEncryption.metadataClient = client;
  39. } else {
  40. options.autoEncryption.metadataClient = this.getInternalClient(client);
  41. }
  42. options.autoEncryption.bson = Encrypter.makeBSON(options);
  43. this.autoEncrypter = new AutoEncrypter(client, options.autoEncryption);
  44. }
  45. getInternalClient(client) {
  46. if (!this[kInternalClient]) {
  47. const clonedOptions = {};
  48. for (const key of Object.keys(client.s.options)) {
  49. if (
  50. ['autoEncryption', 'minPoolSize', 'servers', 'caseTranslate', 'dbName'].indexOf(key) !==
  51. -1
  52. )
  53. continue;
  54. clonedOptions[key] = client.s.options[key];
  55. }
  56. clonedOptions.minPoolSize = 0;
  57. const allEvents = [
  58. // APM
  59. 'commandStarted',
  60. 'commandSucceeded',
  61. 'commandFailed',
  62. // SDAM
  63. 'serverOpening',
  64. 'serverClosed',
  65. 'serverDescriptionChanged',
  66. 'serverHeartbeatStarted',
  67. 'serverHeartbeatSucceeded',
  68. 'serverHeartbeatFailed',
  69. 'topologyOpening',
  70. 'topologyClosed',
  71. 'topologyDescriptionChanged',
  72. // Legacy
  73. 'joined',
  74. 'left',
  75. 'ping',
  76. 'ha',
  77. // CMAP
  78. 'connectionPoolCreated',
  79. 'connectionPoolClosed',
  80. 'connectionCreated',
  81. 'connectionReady',
  82. 'connectionClosed',
  83. 'connectionCheckOutStarted',
  84. 'connectionCheckOutFailed',
  85. 'connectionCheckedOut',
  86. 'connectionCheckedIn',
  87. 'connectionPoolCleared'
  88. ];
  89. this[kInternalClient] = new MongoClient(client.s.url, clonedOptions);
  90. for (const eventName of allEvents) {
  91. for (const listener of client.listeners(eventName)) {
  92. this[kInternalClient].on(eventName, listener);
  93. }
  94. }
  95. client.on('newListener', (eventName, listener) => {
  96. this[kInternalClient].on(eventName, listener);
  97. });
  98. this.needsConnecting = true;
  99. }
  100. return this[kInternalClient];
  101. }
  102. connectInternalClient(callback) {
  103. if (this.needsConnecting) {
  104. this.needsConnecting = false;
  105. return this[kInternalClient].connect(callback);
  106. }
  107. return callback();
  108. }
  109. close(client, force, callback) {
  110. this.autoEncrypter.teardown(e => {
  111. if (this[kInternalClient] && client !== this[kInternalClient]) {
  112. return this[kInternalClient].close(force, callback);
  113. }
  114. callback(e);
  115. });
  116. }
  117. static makeBSON(options) {
  118. return (
  119. (options || {}).bson ||
  120. new BSON([
  121. BSON.Binary,
  122. BSON.Code,
  123. BSON.DBRef,
  124. BSON.Decimal128,
  125. BSON.Double,
  126. BSON.Int32,
  127. BSON.Long,
  128. BSON.Map,
  129. BSON.MaxKey,
  130. BSON.MinKey,
  131. BSON.ObjectId,
  132. BSON.BSONRegExp,
  133. BSON.Symbol,
  134. BSON.Timestamp
  135. ])
  136. );
  137. }
  138. }
  139. module.exports = { Encrypter };