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