This repository has been archived by the owner on Feb 4, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(auth): switch to MongoCredentials and cleanup
A major refactor to rewrite core to use MongoCredentials objects instead of individually passing around auth fields. Also removes significant amounts of duplicate code in auth providers. Also switches to the async version of crypto.randomBytes to resolve #307. Fixes NODE-1436 Fixes NODE-1442
- Loading branch information
1 parent
99abe7d
commit 0b9243d
Showing
22 changed files
with
981 additions
and
1,434 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
'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 = err; | ||
} else if (r.result && r.result['$err']) { | ||
errorObject = r.result; | ||
} else if (r.result && r.result['errmsg']) { | ||
errorObject = r.result; | ||
} 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 }; |
Oops, something went wrong.