'use strict';
|
|
const MongoError = require('../error').MongoError;
|
|
/**
|
* Creates a new AuthProvider, which dictates how to authenticate for a given
|
* mechanism.
|
* @class
|
*/
|
class AuthProvider {
|
constructor(bson) {
|
this.bson = bson;
|
this.authStore = [];
|
}
|
|
/**
|
* Authenticate
|
* @method
|
* @param {SendAuthCommand} sendAuthCommand Writes an auth command directly to a specific connection
|
* @param {Connection[]} connections Connections to authenticate using this authenticator
|
* @param {MongoCredentials} credentials Authentication credentials
|
* @param {authResultCallback} callback The callback to return the result from the authentication
|
*/
|
auth(sendAuthCommand, connections, credentials, callback) {
|
// Total connections
|
let count = connections.length;
|
|
if (count === 0) {
|
callback(null, null);
|
return;
|
}
|
|
// Valid connections
|
let numberOfValidConnections = 0;
|
let errorObject = null;
|
|
const execute = connection => {
|
this._authenticateSingleConnection(sendAuthCommand, connection, credentials, (err, r) => {
|
// Adjust count
|
count = count - 1;
|
|
// If we have an error
|
if (err) {
|
errorObject = new MongoError(err);
|
} else if (r && (r.$err || r.errmsg)) {
|
errorObject = new MongoError(r);
|
} else {
|
numberOfValidConnections = numberOfValidConnections + 1;
|
}
|
|
// Still authenticating against other connections.
|
if (count !== 0) {
|
return;
|
}
|
|
// We have authenticated all connections
|
if (numberOfValidConnections > 0) {
|
// Store the auth details
|
this.addCredentials(credentials);
|
// Return correct authentication
|
callback(null, true);
|
} else {
|
if (errorObject == null) {
|
errorObject = new MongoError(`failed to authenticate using ${credentials.mechanism}`);
|
}
|
callback(errorObject, false);
|
}
|
});
|
};
|
|
const executeInNextTick = _connection => process.nextTick(() => execute(_connection));
|
|
// For each connection we need to authenticate
|
while (connections.length > 0) {
|
executeInNextTick(connections.shift());
|
}
|
}
|
|
/**
|
* Implementation of a single connection authenticating. Is meant to be overridden.
|
* Will error if called directly
|
* @ignore
|
*/
|
_authenticateSingleConnection(/*sendAuthCommand, connection, credentials, callback*/) {
|
throw new Error('_authenticateSingleConnection must be overridden');
|
}
|
|
/**
|
* Adds credentials to store only if it does not exist
|
* @param {MongoCredentials} credentials credentials to add to store
|
*/
|
addCredentials(credentials) {
|
const found = this.authStore.some(cred => cred.equals(credentials));
|
|
if (!found) {
|
this.authStore.push(credentials);
|
}
|
}
|
|
/**
|
* Re authenticate pool
|
* @method
|
* @param {SendAuthCommand} sendAuthCommand Writes an auth command directly to a specific connection
|
* @param {Connection[]} connections Connections to authenticate using this authenticator
|
* @param {authResultCallback} callback The callback to return the result from the authentication
|
*/
|
reauthenticate(sendAuthCommand, connections, callback) {
|
const authStore = this.authStore.slice(0);
|
let count = authStore.length;
|
if (count === 0) {
|
return callback(null, null);
|
}
|
|
for (let i = 0; i < authStore.length; i++) {
|
this.auth(sendAuthCommand, connections, authStore[i], function(err) {
|
count = count - 1;
|
if (count === 0) {
|
callback(err, null);
|
}
|
});
|
}
|
}
|
|
/**
|
* Remove credentials that have been previously stored in the auth provider
|
* @method
|
* @param {string} source Name of database we are removing authStore details about
|
* @return {object}
|
*/
|
logout(source) {
|
this.authStore = this.authStore.filter(credentials => credentials.source !== source);
|
}
|
}
|
|
/**
|
* A function that writes authentication commands to a specific connection
|
* @callback SendAuthCommand
|
* @param {Connection} connection The connection to write to
|
* @param {Command} command A command with a toBin method that can be written to a connection
|
* @param {AuthWriteCallback} callback Callback called when command response is received
|
*/
|
|
/**
|
* A callback for a specific auth command
|
* @callback AuthWriteCallback
|
* @param {Error} err If command failed, an error from the server
|
* @param {object} r The response from the server
|
*/
|
|
/**
|
* This is a result from an authentication strategy
|
*
|
* @callback authResultCallback
|
* @param {error} error An error object. Set to null if no error present
|
* @param {boolean} result The result of the authentication process
|
*/
|
|
module.exports = { AuthProvider };
|