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