auth_provider.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. 'use strict';
  2. const MongoError = require('../error').MongoError;
  3. /**
  4. * Creates a new AuthProvider, which dictates how to authenticate for a given
  5. * mechanism.
  6. * @class
  7. */
  8. class AuthProvider {
  9. constructor(bson) {
  10. this.bson = bson;
  11. this.authStore = [];
  12. }
  13. /**
  14. * Authenticate
  15. * @method
  16. * @param {SendAuthCommand} sendAuthCommand Writes an auth command directly to a specific connection
  17. * @param {Connection[]} connections Connections to authenticate using this authenticator
  18. * @param {MongoCredentials} credentials Authentication credentials
  19. * @param {authResultCallback} callback The callback to return the result from the authentication
  20. */
  21. auth(sendAuthCommand, connections, credentials, callback) {
  22. // Total connections
  23. let count = connections.length;
  24. if (count === 0) {
  25. callback(null, null);
  26. return;
  27. }
  28. // Valid connections
  29. let numberOfValidConnections = 0;
  30. let errorObject = null;
  31. const execute = connection => {
  32. this._authenticateSingleConnection(sendAuthCommand, connection, credentials, (err, r) => {
  33. // Adjust count
  34. count = count - 1;
  35. // If we have an error
  36. if (err) {
  37. errorObject = new MongoError(err);
  38. } else if (r && (r.$err || r.errmsg)) {
  39. errorObject = new MongoError(r);
  40. } else {
  41. numberOfValidConnections = numberOfValidConnections + 1;
  42. }
  43. // Still authenticating against other connections.
  44. if (count !== 0) {
  45. return;
  46. }
  47. // We have authenticated all connections
  48. if (numberOfValidConnections > 0) {
  49. // Store the auth details
  50. this.addCredentials(credentials);
  51. // Return correct authentication
  52. callback(null, true);
  53. } else {
  54. if (errorObject == null) {
  55. errorObject = new MongoError(`failed to authenticate using ${credentials.mechanism}`);
  56. }
  57. callback(errorObject, false);
  58. }
  59. });
  60. };
  61. const executeInNextTick = _connection => process.nextTick(() => execute(_connection));
  62. // For each connection we need to authenticate
  63. while (connections.length > 0) {
  64. executeInNextTick(connections.shift());
  65. }
  66. }
  67. /**
  68. * Implementation of a single connection authenticating. Is meant to be overridden.
  69. * Will error if called directly
  70. * @ignore
  71. */
  72. _authenticateSingleConnection(/*sendAuthCommand, connection, credentials, callback*/) {
  73. throw new Error('_authenticateSingleConnection must be overridden');
  74. }
  75. /**
  76. * Adds credentials to store only if it does not exist
  77. * @param {MongoCredentials} credentials credentials to add to store
  78. */
  79. addCredentials(credentials) {
  80. const found = this.authStore.some(cred => cred.equals(credentials));
  81. if (!found) {
  82. this.authStore.push(credentials);
  83. }
  84. }
  85. /**
  86. * Re authenticate pool
  87. * @method
  88. * @param {SendAuthCommand} sendAuthCommand Writes an auth command directly to a specific connection
  89. * @param {Connection[]} connections Connections to authenticate using this authenticator
  90. * @param {authResultCallback} callback The callback to return the result from the authentication
  91. */
  92. reauthenticate(sendAuthCommand, connections, callback) {
  93. const authStore = this.authStore.slice(0);
  94. let count = authStore.length;
  95. if (count === 0) {
  96. return callback(null, null);
  97. }
  98. for (let i = 0; i < authStore.length; i++) {
  99. this.auth(sendAuthCommand, connections, authStore[i], function(err) {
  100. count = count - 1;
  101. if (count === 0) {
  102. callback(err, null);
  103. }
  104. });
  105. }
  106. }
  107. /**
  108. * Remove credentials that have been previously stored in the auth provider
  109. * @method
  110. * @param {string} source Name of database we are removing authStore details about
  111. * @return {object}
  112. */
  113. logout(source) {
  114. this.authStore = this.authStore.filter(credentials => credentials.source !== source);
  115. }
  116. }
  117. /**
  118. * A function that writes authentication commands to a specific connection
  119. * @callback SendAuthCommand
  120. * @param {Connection} connection The connection to write to
  121. * @param {Command} command A command with a toBin method that can be written to a connection
  122. * @param {AuthWriteCallback} callback Callback called when command response is received
  123. */
  124. /**
  125. * A callback for a specific auth command
  126. * @callback AuthWriteCallback
  127. * @param {Error} err If command failed, an error from the server
  128. * @param {object} r The response from the server
  129. */
  130. /**
  131. * This is a result from an authentication strategy
  132. *
  133. * @callback authResultCallback
  134. * @param {error} error An error object. Set to null if no error present
  135. * @param {boolean} result The result of the authentication process
  136. */
  137. module.exports = { AuthProvider };