3 var f = require('util').format
4 , require_optional = require('require_optional')
5 , Query = require('../connection/commands').Query
6 , MongoError = require('../error');
8 var AuthSession = function(db, username, password, options) {
10 this.username = username;
11 this.password = password;
12 this.options = options;
15 AuthSession.prototype.equal = function(session) {
16 return session.db == this.db
17 && session.username == this.username
18 && session.password == this.password;
23 var MongoAuthProcess = null;
25 // Try to grab the Kerberos class
27 Kerberos = require_optional('kerberos').Kerberos;
28 // Authentication process for Mongo
29 MongoAuthProcess = require_optional('kerberos').processes.MongoAuthProcess;
34 * Creates a new GSSAPI authentication mechanism
36 * @return {GSSAPI} A cursor instance
38 var GSSAPI = function(bson) {
46 * @param {{Server}|{ReplSet}|{Mongos}} server Topology the authentication method is being called on
47 * @param {[]Connections} connections Connections to authenticate using this authenticator
48 * @param {string} db Name of the database
49 * @param {string} username Username
50 * @param {string} password Password
51 * @param {authResultCallback} callback The callback to return the result from the authentication
54 GSSAPI.prototype.auth = function(server, connections, db, username, password, options, callback) {
56 // We don't have the Kerberos library
57 if(Kerberos == null) return callback(new Error("Kerberos library is not installed"));
58 var gssapiServiceName = options['gssapiServiceName'] || 'mongodb';
60 var count = connections.length;
61 if(count == 0) return callback(null, null);
64 var numberOfValidConnections = 0;
65 var errorObject = null;
67 // For each connection we need to authenticate
68 while(connections.length > 0) {
70 var execute = function(connection) {
71 // Start Auth process for a connection
72 GSSAPIInitialize(self, db, username, password, db, gssapiServiceName, server, connection, options, function(err, r) {
76 // If we have an error
79 } else if(r.result['$err']) {
80 errorObject = r.result;
81 } else if(r.result['errmsg']) {
82 errorObject = r.result;
84 numberOfValidConnections = numberOfValidConnections + 1;
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, options));
91 // Return correct authentication
93 } else if(count == 0) {
94 if(errorObject == null) errorObject = new MongoError(f("failed to authenticate using mongocr"));
95 callback(errorObject, false);
100 var _execute = function(_connection) {
101 process.nextTick(function() {
102 execute(_connection);
106 _execute(connections.shift());
112 var GSSAPIInitialize = function(self, db, username, password, authdb, gssapiServiceName, server, connection, options, callback) {
113 // Create authenticator
114 var mongo_auth_process = new MongoAuthProcess(connection.host, connection.port, gssapiServiceName, options);
116 // Perform initialization
117 mongo_auth_process.init(username, password, function(err) {
118 if(err) return callback(err, false);
120 // Perform the first step
121 mongo_auth_process.transition('', function(err, payload) {
122 if(err) return callback(err, false);
124 // Call the next db step
125 MongoDBGSSAPIFirstStep(self, mongo_auth_process, payload, db, username, password, authdb, server, connection, callback);
131 // Perform first step against mongodb
132 var MongoDBGSSAPIFirstStep = function(self, mongo_auth_process, payload, db, username, password, authdb, server, connection, callback) {
133 // Build the sasl start command
136 , mechanism: 'GSSAPI'
141 // Write the commmand on the connection
142 server(connection, new Query(self.bson, "$external.$cmd", command, {
143 numberToSkip: 0, numberToReturn: 1
144 }), function(err, r) {
145 if(err) return callback(err, false);
147 // Execute mongodb transition
148 mongo_auth_process.transition(r.result.payload, function(err, payload) {
149 if(err) return callback(err, false);
151 // MongoDB API Second Step
152 MongoDBGSSAPISecondStep(self, mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback);
158 // Perform first step against mongodb
159 var MongoDBGSSAPISecondStep = function(self, mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback) {
160 // Build Authentication command to send to MongoDB
163 , conversationId: doc.conversationId
167 // Execute the command
168 // Write the commmand on the connection
169 server(connection, new Query(self.bson, "$external.$cmd", command, {
170 numberToSkip: 0, numberToReturn: 1
171 }), function(err, r) {
172 if(err) return callback(err, false);
174 // Call next transition for kerberos
175 mongo_auth_process.transition(doc.payload, function(err, payload) {
176 if(err) return callback(err, false);
178 // Call the last and third step
179 MongoDBGSSAPIThirdStep(self, mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback);
184 var MongoDBGSSAPIThirdStep = function(self, mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback) {
185 // Build final command
188 , conversationId: doc.conversationId
192 // Execute the command
193 server(connection, new Query(self.bson, "$external.$cmd", command, {
194 numberToSkip: 0, numberToReturn: 1
195 }), function(err, r) {
196 if(err) return callback(err, false);
197 mongo_auth_process.transition(null, function(err) {
198 if(err) return callback(err, null);
204 // Add to store only if it does not exist
205 var addAuthSession = function(authStore, session) {
208 for(var i = 0; i < authStore.length; i++) {
209 if(authStore[i].equal(session)) {
215 if(!found) authStore.push(session);
219 * Remove authStore credentials
221 * @param {string} db Name of database we are removing authStore details about
224 GSSAPI.prototype.logout = function(dbName) {
225 this.authStore = this.authStore.filter(function(x) {
226 return x.db != dbName;
231 * Re authenticate pool
233 * @param {{Server}|{ReplSet}|{Mongos}} server Topology the authentication method is being called on
234 * @param {[]Connections} connections Connections to authenticate using this authenticator
235 * @param {authResultCallback} callback The callback to return the result from the authentication
238 GSSAPI.prototype.reauthenticate = function(server, connections, callback) {
239 var authStore = this.authStore.slice(0);
240 var count = authStore.length;
241 if(count == 0) return callback(null, null);
242 // Iterate over all the auth details stored
243 for(var i = 0; i < authStore.length; i++) {
244 this.auth(server, connections, authStore[i].db, authStore[i].username, authStore[i].password, authStore[i].options, function(err) {
246 // Done re-authenticating
255 * This is a result from a authentication strategy
257 * @callback authResultCallback
258 * @param {error} error An error object. Set to null if no error present
259 * @param {boolean} result The result of the authentication process
262 module.exports = GSSAPI;