encrypter.js 4.6 KB

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