plain.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. "use strict";
  2. var BSON = require('bson');
  3. var f = require('util').format
  4. , Binary = BSON.Binary
  5. , retrieveBSON = require('../connection/utils').retrieveBSON
  6. , Query = require('../connection/commands').Query
  7. , MongoError = require('../error');
  8. var BSON = retrieveBSON();
  9. var AuthSession = function(db, username, password) {
  10. this.db = db;
  11. this.username = username;
  12. this.password = password;
  13. }
  14. AuthSession.prototype.equal = function(session) {
  15. return session.db == this.db
  16. && session.username == this.username
  17. && session.password == this.password;
  18. }
  19. /**
  20. * Creates a new Plain authentication mechanism
  21. * @class
  22. * @return {Plain} A cursor instance
  23. */
  24. var Plain = function(bson) {
  25. this.bson = bson;
  26. this.authStore = [];
  27. }
  28. /**
  29. * Authenticate
  30. * @method
  31. * @param {{Server}|{ReplSet}|{Mongos}} server Topology the authentication method is being called on
  32. * @param {[]Connections} connections Connections to authenticate using this authenticator
  33. * @param {string} db Name of the database
  34. * @param {string} username Username
  35. * @param {string} password Password
  36. * @param {authResultCallback} callback The callback to return the result from the authentication
  37. * @return {object}
  38. */
  39. Plain.prototype.auth = function(server, connections, db, username, password, callback) {
  40. var self = this;
  41. // Total connections
  42. var count = connections.length;
  43. if(count == 0) return callback(null, null);
  44. // Valid connections
  45. var numberOfValidConnections = 0;
  46. var errorObject = null;
  47. // For each connection we need to authenticate
  48. while(connections.length > 0) {
  49. // Execute MongoCR
  50. var execute = function(connection) {
  51. // Create payload
  52. var payload = new Binary(f("\x00%s\x00%s", username, password));
  53. // Let's start the sasl process
  54. var command = {
  55. saslStart: 1
  56. , mechanism: 'PLAIN'
  57. , payload: payload
  58. , autoAuthorize: 1
  59. };
  60. // Let's start the process
  61. server(connection, new Query(self.bson, "$external.$cmd", command, {
  62. numberToSkip: 0, numberToReturn: 1
  63. }), function(err, r) {
  64. // Adjust count
  65. count = count - 1;
  66. // If we have an error
  67. if(err) {
  68. errorObject = err;
  69. } else if(r.result['$err']) {
  70. errorObject = r.result;
  71. } else if(r.result['errmsg']) {
  72. errorObject = r.result;
  73. } else {
  74. numberOfValidConnections = numberOfValidConnections + 1;
  75. }
  76. // We have authenticated all connections
  77. if(count == 0 && numberOfValidConnections > 0) {
  78. // Store the auth details
  79. addAuthSession(self.authStore, new AuthSession(db, username, password));
  80. // Return correct authentication
  81. callback(null, true);
  82. } else if(count == 0) {
  83. if(errorObject == null) errorObject = new MongoError(f("failed to authenticate using mongocr"));
  84. callback(errorObject, false);
  85. }
  86. });
  87. }
  88. var _execute = function(_connection) {
  89. process.nextTick(function() {
  90. execute(_connection);
  91. });
  92. }
  93. _execute(connections.shift());
  94. }
  95. }
  96. // Add to store only if it does not exist
  97. var addAuthSession = function(authStore, session) {
  98. var found = false;
  99. for(var i = 0; i < authStore.length; i++) {
  100. if(authStore[i].equal(session)) {
  101. found = true;
  102. break;
  103. }
  104. }
  105. if(!found) authStore.push(session);
  106. }
  107. /**
  108. * Remove authStore credentials
  109. * @method
  110. * @param {string} db Name of database we are removing authStore details about
  111. * @return {object}
  112. */
  113. Plain.prototype.logout = function(dbName) {
  114. this.authStore = this.authStore.filter(function(x) {
  115. return x.db != dbName;
  116. });
  117. }
  118. /**
  119. * Re authenticate pool
  120. * @method
  121. * @param {{Server}|{ReplSet}|{Mongos}} server Topology the authentication method is being called on
  122. * @param {[]Connections} connections Connections to authenticate using this authenticator
  123. * @param {authResultCallback} callback The callback to return the result from the authentication
  124. * @return {object}
  125. */
  126. Plain.prototype.reauthenticate = function(server, connections, callback) {
  127. var authStore = this.authStore.slice(0);
  128. var count = authStore.length;
  129. if(count == 0) return callback(null, null);
  130. // Iterate over all the auth details stored
  131. for(var i = 0; i < authStore.length; i++) {
  132. this.auth(server, connections, authStore[i].db, authStore[i].username, authStore[i].password, function(err) {
  133. count = count - 1;
  134. // Done re-authenticating
  135. if(count == 0) {
  136. callback(err, null);
  137. }
  138. });
  139. }
  140. }
  141. /**
  142. * This is a result from a authentication strategy
  143. *
  144. * @callback authResultCallback
  145. * @param {error} error An error object. Set to null if no error present
  146. * @param {boolean} result The result of the authentication process
  147. */
  148. module.exports = Plain;