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