Initial commit
This commit is contained in:
55
node_modules/mongodb/lib/core/auth/auth_provider.js
generated
vendored
Normal file
55
node_modules/mongodb/lib/core/auth/auth_provider.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Context used during authentication
|
||||
*
|
||||
* @property {Connection} connection The connection to authenticate
|
||||
* @property {MongoCredentials} credentials The credentials to use for authentication
|
||||
* @property {object} options The options passed to the `connect` method
|
||||
* @property {object?} response The response of the initial handshake
|
||||
* @property {Buffer?} nonce A random nonce generated for use in an authentication conversation
|
||||
*/
|
||||
class AuthContext {
|
||||
constructor(connection, credentials, options) {
|
||||
this.connection = connection;
|
||||
this.credentials = credentials;
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
class AuthProvider {
|
||||
constructor(bson) {
|
||||
this.bson = bson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the handshake document before the initial handshake.
|
||||
*
|
||||
* @param {object} handshakeDoc The document used for the initial handshake on a connection
|
||||
* @param {AuthContext} authContext Context for authentication flow
|
||||
* @param {function} callback
|
||||
*/
|
||||
prepare(handshakeDoc, context, callback) {
|
||||
callback(undefined, handshakeDoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate
|
||||
*
|
||||
* @param {AuthContext} context A shared context for authentication flow
|
||||
* @param {authResultCallback} callback The callback to return the result from the authentication
|
||||
*/
|
||||
auth(context, callback) {
|
||||
callback(new TypeError('`auth` method must be overridden by subclass'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a result from an authentication provider
|
||||
*
|
||||
* @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 = { AuthContext, AuthProvider };
|
||||
29
node_modules/mongodb/lib/core/auth/defaultAuthProviders.js
generated
vendored
Normal file
29
node_modules/mongodb/lib/core/auth/defaultAuthProviders.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const MongoCR = require('./mongocr');
|
||||
const X509 = require('./x509');
|
||||
const Plain = require('./plain');
|
||||
const GSSAPI = require('./gssapi');
|
||||
const ScramSHA1 = require('./scram').ScramSHA1;
|
||||
const ScramSHA256 = require('./scram').ScramSHA256;
|
||||
const MongoDBAWS = require('./mongodb_aws');
|
||||
|
||||
/**
|
||||
* Returns the default authentication providers.
|
||||
*
|
||||
* @param {BSON} bson Bson definition
|
||||
* @returns {Object} a mapping of auth names to auth types
|
||||
*/
|
||||
function defaultAuthProviders(bson) {
|
||||
return {
|
||||
'mongodb-aws': new MongoDBAWS(bson),
|
||||
mongocr: new MongoCR(bson),
|
||||
x509: new X509(bson),
|
||||
plain: new Plain(bson),
|
||||
gssapi: new GSSAPI(bson),
|
||||
'scram-sha-1': new ScramSHA1(bson),
|
||||
'scram-sha-256': new ScramSHA256(bson)
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { defaultAuthProviders };
|
||||
151
node_modules/mongodb/lib/core/auth/gssapi.js
generated
vendored
Normal file
151
node_modules/mongodb/lib/core/auth/gssapi.js
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
'use strict';
|
||||
const dns = require('dns');
|
||||
|
||||
const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
const retrieveKerberos = require('../utils').retrieveKerberos;
|
||||
const MongoError = require('../error').MongoError;
|
||||
|
||||
const kGssapiClient = Symbol('GSSAPI_CLIENT');
|
||||
let kerberos;
|
||||
|
||||
class GSSAPI extends AuthProvider {
|
||||
prepare(handshakeDoc, authContext, callback) {
|
||||
const host = authContext.options.host;
|
||||
const port = authContext.options.port;
|
||||
const credentials = authContext.credentials;
|
||||
if (!host || !port || !credentials) {
|
||||
return callback(
|
||||
new MongoError(
|
||||
`Connection must specify: ${host ? 'host' : ''}, ${port ? 'port' : ''}, ${
|
||||
credentials ? 'host' : 'credentials'
|
||||
}.`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (kerberos == null) {
|
||||
try {
|
||||
kerberos = retrieveKerberos();
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
}
|
||||
const username = credentials.username;
|
||||
const password = credentials.password;
|
||||
const mechanismProperties = credentials.mechanismProperties;
|
||||
const serviceName =
|
||||
mechanismProperties['gssapiservicename'] ||
|
||||
mechanismProperties['gssapiServiceName'] ||
|
||||
'mongodb';
|
||||
performGssapiCanonicalizeHostName(host, mechanismProperties, (err, host) => {
|
||||
if (err) return callback(err);
|
||||
const initOptions = {};
|
||||
if (password != null) {
|
||||
Object.assign(initOptions, { user: username, password: password });
|
||||
}
|
||||
kerberos.initializeClient(
|
||||
`${serviceName}${process.platform === 'win32' ? '/' : '@'}${host}`,
|
||||
initOptions,
|
||||
(err, client) => {
|
||||
if (err) return callback(new MongoError(err));
|
||||
if (client == null) return callback();
|
||||
this[kGssapiClient] = client;
|
||||
callback(undefined, handshakeDoc);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
auth(authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
if (credentials == null) return callback(new MongoError('credentials required'));
|
||||
const username = credentials.username;
|
||||
const client = this[kGssapiClient];
|
||||
if (client == null) return callback(new MongoError('gssapi client missing'));
|
||||
function externalCommand(command, cb) {
|
||||
return connection.command('$external.$cmd', command, cb);
|
||||
}
|
||||
client.step('', (err, payload) => {
|
||||
if (err) return callback(err);
|
||||
externalCommand(saslStart(payload), (err, response) => {
|
||||
const result = response.result;
|
||||
if (err) return callback(err);
|
||||
negotiate(client, 10, result.payload, (err, payload) => {
|
||||
if (err) return callback(err);
|
||||
externalCommand(saslContinue(payload, result.conversationId), (err, response) => {
|
||||
const result = response.result;
|
||||
if (err) return callback(err);
|
||||
finalize(client, username, result.payload, (err, payload) => {
|
||||
if (err) return callback(err);
|
||||
externalCommand(
|
||||
{
|
||||
saslContinue: 1,
|
||||
conversationId: result.conversationId,
|
||||
payload
|
||||
},
|
||||
(err, result) => {
|
||||
if (err) return callback(err);
|
||||
callback(undefined, result);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = GSSAPI;
|
||||
|
||||
function saslStart(payload) {
|
||||
return {
|
||||
saslStart: 1,
|
||||
mechanism: 'GSSAPI',
|
||||
payload,
|
||||
autoAuthorize: 1
|
||||
};
|
||||
}
|
||||
function saslContinue(payload, conversationId) {
|
||||
return {
|
||||
saslContinue: 1,
|
||||
conversationId,
|
||||
payload
|
||||
};
|
||||
}
|
||||
function negotiate(client, retries, payload, callback) {
|
||||
client.step(payload, (err, response) => {
|
||||
// Retries exhausted, raise error
|
||||
if (err && retries === 0) return callback(err);
|
||||
// Adjust number of retries and call step again
|
||||
if (err) return negotiate(client, retries - 1, payload, callback);
|
||||
// Return the payload
|
||||
callback(undefined, response || '');
|
||||
});
|
||||
}
|
||||
function finalize(client, user, payload, callback) {
|
||||
// GSS Client Unwrap
|
||||
client.unwrap(payload, (err, response) => {
|
||||
if (err) return callback(err);
|
||||
// Wrap the response
|
||||
client.wrap(response || '', { user }, (err, wrapped) => {
|
||||
if (err) return callback(err);
|
||||
// Return the payload
|
||||
callback(undefined, wrapped);
|
||||
});
|
||||
});
|
||||
}
|
||||
function performGssapiCanonicalizeHostName(host, mechanismProperties, callback) {
|
||||
const canonicalizeHostName =
|
||||
typeof mechanismProperties.gssapiCanonicalizeHostName === 'boolean'
|
||||
? mechanismProperties.gssapiCanonicalizeHostName
|
||||
: false;
|
||||
if (!canonicalizeHostName) return callback(undefined, host);
|
||||
// Attempt to resolve the host name
|
||||
dns.resolveCname(host, (err, r) => {
|
||||
if (err) return callback(err);
|
||||
// Get the first resolve host id
|
||||
if (Array.isArray(r) && r.length > 0) {
|
||||
return callback(undefined, r[0]);
|
||||
}
|
||||
callback(undefined, host);
|
||||
});
|
||||
}
|
||||
107
node_modules/mongodb/lib/core/auth/mongo_credentials.js
generated
vendored
Normal file
107
node_modules/mongodb/lib/core/auth/mongo_credentials.js
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
'use strict';
|
||||
|
||||
// Resolves the default auth mechanism according to
|
||||
// https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst
|
||||
function getDefaultAuthMechanism(ismaster) {
|
||||
if (ismaster) {
|
||||
// If ismaster contains saslSupportedMechs, use scram-sha-256
|
||||
// if it is available, else scram-sha-1
|
||||
if (Array.isArray(ismaster.saslSupportedMechs)) {
|
||||
return ismaster.saslSupportedMechs.indexOf('SCRAM-SHA-256') >= 0
|
||||
? 'scram-sha-256'
|
||||
: 'scram-sha-1';
|
||||
}
|
||||
|
||||
// Fallback to legacy selection method. If wire version >= 3, use scram-sha-1
|
||||
if (ismaster.maxWireVersion >= 3) {
|
||||
return 'scram-sha-1';
|
||||
}
|
||||
}
|
||||
|
||||
// Default for wireprotocol < 3
|
||||
return 'mongocr';
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of the credentials used by MongoDB
|
||||
* @class
|
||||
* @property {string} mechanism The method used to authenticate
|
||||
* @property {string} [username] The username used for authentication
|
||||
* @property {string} [password] The password used for authentication
|
||||
* @property {string} [source] The database that the user should authenticate against
|
||||
* @property {object} [mechanismProperties] Special properties used by some types of auth mechanisms
|
||||
*/
|
||||
class MongoCredentials {
|
||||
/**
|
||||
* Creates a new MongoCredentials object
|
||||
* @param {object} [options]
|
||||
* @param {string} [options.username] The username used for authentication
|
||||
* @param {string} [options.password] The password used for authentication
|
||||
* @param {string} [options.source] The database that the user should authenticate against
|
||||
* @param {string} [options.mechanism] The method used to authenticate
|
||||
* @param {object} [options.mechanismProperties] Special properties used by some types of auth mechanisms
|
||||
*/
|
||||
constructor(options) {
|
||||
options = options || {};
|
||||
this.username = options.username;
|
||||
this.password = options.password;
|
||||
this.source = options.source || options.db;
|
||||
this.mechanism = options.mechanism || 'default';
|
||||
this.mechanismProperties = options.mechanismProperties || {};
|
||||
|
||||
if (this.mechanism.match(/MONGODB-AWS/i)) {
|
||||
if (this.username == null && process.env.AWS_ACCESS_KEY_ID) {
|
||||
this.username = process.env.AWS_ACCESS_KEY_ID;
|
||||
}
|
||||
|
||||
if (this.password == null && process.env.AWS_SECRET_ACCESS_KEY) {
|
||||
this.password = process.env.AWS_SECRET_ACCESS_KEY;
|
||||
}
|
||||
|
||||
if (this.mechanismProperties.AWS_SESSION_TOKEN == null && process.env.AWS_SESSION_TOKEN) {
|
||||
this.mechanismProperties.AWS_SESSION_TOKEN = process.env.AWS_SESSION_TOKEN;
|
||||
}
|
||||
}
|
||||
|
||||
Object.freeze(this.mechanismProperties);
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if two MongoCredentials objects are equivalent
|
||||
* @param {MongoCredentials} other another MongoCredentials object
|
||||
* @returns {boolean} true if the two objects are equal.
|
||||
*/
|
||||
equals(other) {
|
||||
return (
|
||||
this.mechanism === other.mechanism &&
|
||||
this.username === other.username &&
|
||||
this.password === other.password &&
|
||||
this.source === other.source
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the authentication mechanism is set to "default", resolves the authMechanism
|
||||
* based on the server version and server supported sasl mechanisms.
|
||||
*
|
||||
* @param {Object} [ismaster] An ismaster response from the server
|
||||
* @returns {MongoCredentials}
|
||||
*/
|
||||
resolveAuthMechanism(ismaster) {
|
||||
// If the mechanism is not "default", then it does not need to be resolved
|
||||
if (this.mechanism.match(/DEFAULT/i)) {
|
||||
return new MongoCredentials({
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
source: this.source,
|
||||
mechanism: getDefaultAuthMechanism(ismaster),
|
||||
mechanismProperties: this.mechanismProperties
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { MongoCredentials };
|
||||
45
node_modules/mongodb/lib/core/auth/mongocr.js
generated
vendored
Normal file
45
node_modules/mongodb/lib/core/auth/mongocr.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
|
||||
class MongoCR extends AuthProvider {
|
||||
auth(authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
const username = credentials.username;
|
||||
const password = credentials.password;
|
||||
const source = credentials.source;
|
||||
|
||||
connection.command(`${source}.$cmd`, { getnonce: 1 }, (err, result) => {
|
||||
let nonce = null;
|
||||
let key = null;
|
||||
|
||||
// Get nonce
|
||||
if (err == null) {
|
||||
const r = result.result;
|
||||
nonce = r.nonce;
|
||||
// Use node md5 generator
|
||||
let md5 = crypto.createHash('md5');
|
||||
// Generate keys used for authentication
|
||||
md5.update(username + ':mongo:' + password, 'utf8');
|
||||
const hash_password = md5.digest('hex');
|
||||
// Final key
|
||||
md5 = crypto.createHash('md5');
|
||||
md5.update(nonce + username + hash_password, 'utf8');
|
||||
key = md5.digest('hex');
|
||||
}
|
||||
|
||||
const authenticateCommand = {
|
||||
authenticate: 1,
|
||||
user: username,
|
||||
nonce,
|
||||
key
|
||||
};
|
||||
|
||||
connection.command(`${source}.$cmd`, authenticateCommand, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MongoCR;
|
||||
256
node_modules/mongodb/lib/core/auth/mongodb_aws.js
generated
vendored
Normal file
256
node_modules/mongodb/lib/core/auth/mongodb_aws.js
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
'use strict';
|
||||
const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
const MongoCredentials = require('./mongo_credentials').MongoCredentials;
|
||||
const MongoError = require('../error').MongoError;
|
||||
const crypto = require('crypto');
|
||||
const http = require('http');
|
||||
const maxWireVersion = require('../utils').maxWireVersion;
|
||||
const url = require('url');
|
||||
|
||||
let aws4;
|
||||
try {
|
||||
aws4 = require('aws4');
|
||||
} catch (e) {
|
||||
// don't do anything;
|
||||
}
|
||||
|
||||
const ASCII_N = 110;
|
||||
const AWS_RELATIVE_URI = 'http://169.254.170.2';
|
||||
const AWS_EC2_URI = 'http://169.254.169.254';
|
||||
const AWS_EC2_PATH = '/latest/meta-data/iam/security-credentials';
|
||||
|
||||
class MongoDBAWS extends AuthProvider {
|
||||
auth(authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
|
||||
if (maxWireVersion(connection) < 9) {
|
||||
callback(new MongoError('MONGODB-AWS authentication requires MongoDB version 4.4 or later'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (aws4 == null) {
|
||||
callback(
|
||||
new MongoError(
|
||||
'MONGODB-AWS authentication requires the `aws4` module, please install it as a dependency of your project'
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (credentials.username == null) {
|
||||
makeTempCredentials(credentials, (err, tempCredentials) => {
|
||||
if (err) return callback(err);
|
||||
|
||||
authContext.credentials = tempCredentials;
|
||||
this.auth(authContext, callback);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const username = credentials.username;
|
||||
const password = credentials.password;
|
||||
const db = credentials.source;
|
||||
const token = credentials.mechanismProperties.AWS_SESSION_TOKEN;
|
||||
const bson = this.bson;
|
||||
|
||||
crypto.randomBytes(32, (err, nonce) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const saslStart = {
|
||||
saslStart: 1,
|
||||
mechanism: 'MONGODB-AWS',
|
||||
payload: bson.serialize({ r: nonce, p: ASCII_N })
|
||||
};
|
||||
|
||||
connection.command(`${db}.$cmd`, saslStart, (err, result) => {
|
||||
if (err) return callback(err);
|
||||
|
||||
const res = result.result;
|
||||
const serverResponse = bson.deserialize(res.payload.buffer);
|
||||
const host = serverResponse.h;
|
||||
const serverNonce = serverResponse.s.buffer;
|
||||
if (serverNonce.length !== 64) {
|
||||
callback(
|
||||
new MongoError(`Invalid server nonce length ${serverNonce.length}, expected 64`)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (serverNonce.compare(nonce, 0, nonce.length, 0, nonce.length) !== 0) {
|
||||
callback(new MongoError('Server nonce does not begin with client nonce'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (host.length < 1 || host.length > 255 || host.indexOf('..') !== -1) {
|
||||
callback(new MongoError(`Server returned an invalid host: "${host}"`));
|
||||
return;
|
||||
}
|
||||
|
||||
const body = 'Action=GetCallerIdentity&Version=2011-06-15';
|
||||
const options = aws4.sign(
|
||||
{
|
||||
method: 'POST',
|
||||
host,
|
||||
region: deriveRegion(serverResponse.h),
|
||||
service: 'sts',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': body.length,
|
||||
'X-MongoDB-Server-Nonce': serverNonce.toString('base64'),
|
||||
'X-MongoDB-GS2-CB-Flag': 'n'
|
||||
},
|
||||
path: '/',
|
||||
body
|
||||
},
|
||||
{
|
||||
accessKeyId: username,
|
||||
secretAccessKey: password,
|
||||
token
|
||||
}
|
||||
);
|
||||
|
||||
const authorization = options.headers.Authorization;
|
||||
const date = options.headers['X-Amz-Date'];
|
||||
const payload = { a: authorization, d: date };
|
||||
if (token) {
|
||||
payload.t = token;
|
||||
}
|
||||
|
||||
const saslContinue = {
|
||||
saslContinue: 1,
|
||||
conversationId: 1,
|
||||
payload: bson.serialize(payload)
|
||||
};
|
||||
|
||||
connection.command(`${db}.$cmd`, saslContinue, err => {
|
||||
if (err) return callback(err);
|
||||
callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function makeTempCredentials(credentials, callback) {
|
||||
function done(creds) {
|
||||
if (creds.AccessKeyId == null || creds.SecretAccessKey == null || creds.Token == null) {
|
||||
callback(new MongoError('Could not obtain temporary MONGODB-AWS credentials'));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(
|
||||
undefined,
|
||||
new MongoCredentials({
|
||||
username: creds.AccessKeyId,
|
||||
password: creds.SecretAccessKey,
|
||||
source: credentials.source,
|
||||
mechanism: 'MONGODB-AWS',
|
||||
mechanismProperties: {
|
||||
AWS_SESSION_TOKEN: creds.Token
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// If the environment variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
|
||||
// is set then drivers MUST assume that it was set by an AWS ECS agent
|
||||
if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
|
||||
request(
|
||||
`${AWS_RELATIVE_URI}${process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}`,
|
||||
(err, res) => {
|
||||
if (err) return callback(err);
|
||||
done(res);
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise assume we are on an EC2 instance
|
||||
|
||||
// get a token
|
||||
|
||||
request(
|
||||
`${AWS_EC2_URI}/latest/api/token`,
|
||||
{ method: 'PUT', json: false, headers: { 'X-aws-ec2-metadata-token-ttl-seconds': 30 } },
|
||||
(err, token) => {
|
||||
if (err) return callback(err);
|
||||
|
||||
// get role name
|
||||
request(
|
||||
`${AWS_EC2_URI}/${AWS_EC2_PATH}`,
|
||||
{ json: false, headers: { 'X-aws-ec2-metadata-token': token } },
|
||||
(err, roleName) => {
|
||||
if (err) return callback(err);
|
||||
|
||||
// get temp credentials
|
||||
request(
|
||||
`${AWS_EC2_URI}/${AWS_EC2_PATH}/${roleName}`,
|
||||
{ headers: { 'X-aws-ec2-metadata-token': token } },
|
||||
(err, creds) => {
|
||||
if (err) return callback(err);
|
||||
done(creds);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function deriveRegion(host) {
|
||||
const parts = host.split('.');
|
||||
if (parts.length === 1 || parts[1] === 'amazonaws') {
|
||||
return 'us-east-1';
|
||||
}
|
||||
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
function request(uri, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
options = Object.assign(
|
||||
{
|
||||
method: 'GET',
|
||||
timeout: 10000,
|
||||
json: true
|
||||
},
|
||||
url.parse(uri),
|
||||
options
|
||||
);
|
||||
|
||||
const req = http.request(options, res => {
|
||||
res.setEncoding('utf8');
|
||||
|
||||
let data = '';
|
||||
res.on('data', d => (data += d));
|
||||
res.on('end', () => {
|
||||
if (options.json === false) {
|
||||
callback(undefined, data);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
callback(undefined, parsed);
|
||||
} catch (err) {
|
||||
callback(new MongoError(`Invalid JSON response: "${data}"`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', err => callback(err));
|
||||
req.end();
|
||||
}
|
||||
|
||||
module.exports = MongoDBAWS;
|
||||
28
node_modules/mongodb/lib/core/auth/plain.js
generated
vendored
Normal file
28
node_modules/mongodb/lib/core/auth/plain.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
const retrieveBSON = require('../connection/utils').retrieveBSON;
|
||||
const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
|
||||
// TODO: can we get the Binary type from this.bson instead?
|
||||
const BSON = retrieveBSON();
|
||||
const Binary = BSON.Binary;
|
||||
|
||||
class Plain extends AuthProvider {
|
||||
auth(authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
const username = credentials.username;
|
||||
const password = credentials.password;
|
||||
|
||||
const payload = new Binary(`\x00${username}\x00${password}`);
|
||||
const command = {
|
||||
saslStart: 1,
|
||||
mechanism: 'PLAIN',
|
||||
payload: payload,
|
||||
autoAuthorize: 1
|
||||
};
|
||||
|
||||
connection.command('$external.$cmd', command, callback);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Plain;
|
||||
346
node_modules/mongodb/lib/core/auth/scram.js
generated
vendored
Normal file
346
node_modules/mongodb/lib/core/auth/scram.js
generated
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
'use strict';
|
||||
const crypto = require('crypto');
|
||||
const Buffer = require('safe-buffer').Buffer;
|
||||
const retrieveBSON = require('../connection/utils').retrieveBSON;
|
||||
const MongoError = require('../error').MongoError;
|
||||
const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
|
||||
const BSON = retrieveBSON();
|
||||
const Binary = BSON.Binary;
|
||||
|
||||
let saslprep;
|
||||
try {
|
||||
saslprep = require('saslprep');
|
||||
} catch (e) {
|
||||
// don't do anything;
|
||||
}
|
||||
|
||||
class ScramSHA extends AuthProvider {
|
||||
constructor(bson, cryptoMethod) {
|
||||
super(bson);
|
||||
this.cryptoMethod = cryptoMethod || 'sha1';
|
||||
}
|
||||
|
||||
prepare(handshakeDoc, authContext, callback) {
|
||||
const cryptoMethod = this.cryptoMethod;
|
||||
if (cryptoMethod === 'sha256' && saslprep == null) {
|
||||
console.warn('Warning: no saslprep library specified. Passwords will not be sanitized');
|
||||
}
|
||||
|
||||
crypto.randomBytes(24, (err, nonce) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// store the nonce for later use
|
||||
Object.assign(authContext, { nonce });
|
||||
|
||||
const credentials = authContext.credentials;
|
||||
const request = Object.assign({}, handshakeDoc, {
|
||||
speculativeAuthenticate: Object.assign(makeFirstMessage(cryptoMethod, credentials, nonce), {
|
||||
db: credentials.source
|
||||
})
|
||||
});
|
||||
|
||||
callback(undefined, request);
|
||||
});
|
||||
}
|
||||
|
||||
auth(authContext, callback) {
|
||||
const response = authContext.response;
|
||||
if (response && response.speculativeAuthenticate) {
|
||||
continueScramConversation(
|
||||
this.cryptoMethod,
|
||||
response.speculativeAuthenticate,
|
||||
authContext,
|
||||
callback
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
executeScram(this.cryptoMethod, authContext, callback);
|
||||
}
|
||||
}
|
||||
|
||||
function cleanUsername(username) {
|
||||
return username.replace('=', '=3D').replace(',', '=2C');
|
||||
}
|
||||
|
||||
function clientFirstMessageBare(username, nonce) {
|
||||
// NOTE: This is done b/c Javascript uses UTF-16, but the server is hashing in UTF-8.
|
||||
// Since the username is not sasl-prep-d, we need to do this here.
|
||||
return Buffer.concat([
|
||||
Buffer.from('n=', 'utf8'),
|
||||
Buffer.from(username, 'utf8'),
|
||||
Buffer.from(',r=', 'utf8'),
|
||||
Buffer.from(nonce.toString('base64'), 'utf8')
|
||||
]);
|
||||
}
|
||||
|
||||
function makeFirstMessage(cryptoMethod, credentials, nonce) {
|
||||
const username = cleanUsername(credentials.username);
|
||||
const mechanism = cryptoMethod === 'sha1' ? 'SCRAM-SHA-1' : 'SCRAM-SHA-256';
|
||||
|
||||
// NOTE: This is done b/c Javascript uses UTF-16, but the server is hashing in UTF-8.
|
||||
// Since the username is not sasl-prep-d, we need to do this here.
|
||||
return {
|
||||
saslStart: 1,
|
||||
mechanism,
|
||||
payload: new Binary(
|
||||
Buffer.concat([Buffer.from('n,,', 'utf8'), clientFirstMessageBare(username, nonce)])
|
||||
),
|
||||
autoAuthorize: 1,
|
||||
options: { skipEmptyExchange: true }
|
||||
};
|
||||
}
|
||||
|
||||
function executeScram(cryptoMethod, authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
const nonce = authContext.nonce;
|
||||
const db = credentials.source;
|
||||
|
||||
const saslStartCmd = makeFirstMessage(cryptoMethod, credentials, nonce);
|
||||
connection.command(`${db}.$cmd`, saslStartCmd, (_err, result) => {
|
||||
const err = resolveError(_err, result);
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
continueScramConversation(cryptoMethod, result.result, authContext, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function continueScramConversation(cryptoMethod, response, authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
const nonce = authContext.nonce;
|
||||
|
||||
const db = credentials.source;
|
||||
const username = cleanUsername(credentials.username);
|
||||
const password = credentials.password;
|
||||
|
||||
let processedPassword;
|
||||
if (cryptoMethod === 'sha256') {
|
||||
processedPassword = saslprep ? saslprep(password) : password;
|
||||
} else {
|
||||
try {
|
||||
processedPassword = passwordDigest(username, password);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
}
|
||||
|
||||
const payload = Buffer.isBuffer(response.payload)
|
||||
? new Binary(response.payload)
|
||||
: response.payload;
|
||||
const dict = parsePayload(payload.value());
|
||||
|
||||
const iterations = parseInt(dict.i, 10);
|
||||
if (iterations && iterations < 4096) {
|
||||
callback(new MongoError(`Server returned an invalid iteration count ${iterations}`), false);
|
||||
return;
|
||||
}
|
||||
|
||||
const salt = dict.s;
|
||||
const rnonce = dict.r;
|
||||
if (rnonce.startsWith('nonce')) {
|
||||
callback(new MongoError(`Server returned an invalid nonce: ${rnonce}`), false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up start of proof
|
||||
const withoutProof = `c=biws,r=${rnonce}`;
|
||||
const saltedPassword = HI(
|
||||
processedPassword,
|
||||
Buffer.from(salt, 'base64'),
|
||||
iterations,
|
||||
cryptoMethod
|
||||
);
|
||||
|
||||
const clientKey = HMAC(cryptoMethod, saltedPassword, 'Client Key');
|
||||
const serverKey = HMAC(cryptoMethod, saltedPassword, 'Server Key');
|
||||
const storedKey = H(cryptoMethod, clientKey);
|
||||
const authMessage = [
|
||||
clientFirstMessageBare(username, nonce),
|
||||
payload.value().toString('base64'),
|
||||
withoutProof
|
||||
].join(',');
|
||||
|
||||
const clientSignature = HMAC(cryptoMethod, storedKey, authMessage);
|
||||
const clientProof = `p=${xor(clientKey, clientSignature)}`;
|
||||
const clientFinal = [withoutProof, clientProof].join(',');
|
||||
|
||||
const serverSignature = HMAC(cryptoMethod, serverKey, authMessage);
|
||||
const saslContinueCmd = {
|
||||
saslContinue: 1,
|
||||
conversationId: response.conversationId,
|
||||
payload: new Binary(Buffer.from(clientFinal))
|
||||
};
|
||||
|
||||
connection.command(`${db}.$cmd`, saslContinueCmd, (_err, result) => {
|
||||
const err = resolveError(_err, result);
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
const r = result.result;
|
||||
const parsedResponse = parsePayload(r.payload.value());
|
||||
if (!compareDigest(Buffer.from(parsedResponse.v, 'base64'), serverSignature)) {
|
||||
callback(new MongoError('Server returned an invalid signature'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!r || r.done !== false) {
|
||||
return callback(err, r);
|
||||
}
|
||||
|
||||
const retrySaslContinueCmd = {
|
||||
saslContinue: 1,
|
||||
conversationId: r.conversationId,
|
||||
payload: Buffer.alloc(0)
|
||||
};
|
||||
|
||||
connection.command(`${db}.$cmd`, retrySaslContinueCmd, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function parsePayload(payload) {
|
||||
const dict = {};
|
||||
const parts = payload.split(',');
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const valueParts = parts[i].split('=');
|
||||
dict[valueParts[0]] = valueParts[1];
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
function passwordDigest(username, password) {
|
||||
if (typeof username !== 'string') {
|
||||
throw new MongoError('username must be a string');
|
||||
}
|
||||
|
||||
if (typeof password !== 'string') {
|
||||
throw new MongoError('password must be a string');
|
||||
}
|
||||
|
||||
if (password.length === 0) {
|
||||
throw new MongoError('password cannot be empty');
|
||||
}
|
||||
|
||||
const md5 = crypto.createHash('md5');
|
||||
md5.update(`${username}:mongo:${password}`, 'utf8');
|
||||
return md5.digest('hex');
|
||||
}
|
||||
|
||||
// XOR two buffers
|
||||
function xor(a, b) {
|
||||
if (!Buffer.isBuffer(a)) {
|
||||
a = Buffer.from(a);
|
||||
}
|
||||
|
||||
if (!Buffer.isBuffer(b)) {
|
||||
b = Buffer.from(b);
|
||||
}
|
||||
|
||||
const length = Math.max(a.length, b.length);
|
||||
const res = [];
|
||||
|
||||
for (let i = 0; i < length; i += 1) {
|
||||
res.push(a[i] ^ b[i]);
|
||||
}
|
||||
|
||||
return Buffer.from(res).toString('base64');
|
||||
}
|
||||
|
||||
function H(method, text) {
|
||||
return crypto
|
||||
.createHash(method)
|
||||
.update(text)
|
||||
.digest();
|
||||
}
|
||||
|
||||
function HMAC(method, key, text) {
|
||||
return crypto
|
||||
.createHmac(method, key)
|
||||
.update(text)
|
||||
.digest();
|
||||
}
|
||||
|
||||
let _hiCache = {};
|
||||
let _hiCacheCount = 0;
|
||||
function _hiCachePurge() {
|
||||
_hiCache = {};
|
||||
_hiCacheCount = 0;
|
||||
}
|
||||
|
||||
const hiLengthMap = {
|
||||
sha256: 32,
|
||||
sha1: 20
|
||||
};
|
||||
|
||||
function HI(data, salt, iterations, cryptoMethod) {
|
||||
// omit the work if already generated
|
||||
const key = [data, salt.toString('base64'), iterations].join('_');
|
||||
if (_hiCache[key] !== undefined) {
|
||||
return _hiCache[key];
|
||||
}
|
||||
|
||||
// generate the salt
|
||||
const saltedData = crypto.pbkdf2Sync(
|
||||
data,
|
||||
salt,
|
||||
iterations,
|
||||
hiLengthMap[cryptoMethod],
|
||||
cryptoMethod
|
||||
);
|
||||
|
||||
// cache a copy to speed up the next lookup, but prevent unbounded cache growth
|
||||
if (_hiCacheCount >= 200) {
|
||||
_hiCachePurge();
|
||||
}
|
||||
|
||||
_hiCache[key] = saltedData;
|
||||
_hiCacheCount += 1;
|
||||
return saltedData;
|
||||
}
|
||||
|
||||
function compareDigest(lhs, rhs) {
|
||||
if (lhs.length !== rhs.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof crypto.timingSafeEqual === 'function') {
|
||||
return crypto.timingSafeEqual(lhs, rhs);
|
||||
}
|
||||
|
||||
let result = 0;
|
||||
for (let i = 0; i < lhs.length; i++) {
|
||||
result |= lhs[i] ^ rhs[i];
|
||||
}
|
||||
|
||||
return result === 0;
|
||||
}
|
||||
|
||||
function resolveError(err, result) {
|
||||
if (err) return err;
|
||||
|
||||
const r = result.result;
|
||||
if (r.$err || r.errmsg) return new MongoError(r);
|
||||
}
|
||||
|
||||
class ScramSHA1 extends ScramSHA {
|
||||
constructor(bson) {
|
||||
super(bson, 'sha1');
|
||||
}
|
||||
}
|
||||
|
||||
class ScramSHA256 extends ScramSHA {
|
||||
constructor(bson) {
|
||||
super(bson, 'sha256');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { ScramSHA1, ScramSHA256 };
|
||||
35
node_modules/mongodb/lib/core/auth/x509.js
generated
vendored
Normal file
35
node_modules/mongodb/lib/core/auth/x509.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
const AuthProvider = require('./auth_provider').AuthProvider;
|
||||
|
||||
class X509 extends AuthProvider {
|
||||
prepare(handshakeDoc, authContext, callback) {
|
||||
const credentials = authContext.credentials;
|
||||
Object.assign(handshakeDoc, {
|
||||
speculativeAuthenticate: x509AuthenticateCommand(credentials)
|
||||
});
|
||||
|
||||
callback(undefined, handshakeDoc);
|
||||
}
|
||||
|
||||
auth(authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
const response = authContext.response;
|
||||
if (response.speculativeAuthenticate) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
connection.command('$external.$cmd', x509AuthenticateCommand(credentials), callback);
|
||||
}
|
||||
}
|
||||
|
||||
function x509AuthenticateCommand(credentials) {
|
||||
const command = { authenticate: 1, mechanism: 'MONGODB-X509' };
|
||||
if (credentials.username) {
|
||||
Object.apply(command, { user: credentials.username });
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
module.exports = X509;
|
||||
Reference in New Issue
Block a user