something is working
This commit is contained in:
+36
@@ -0,0 +1,36 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AuthProvider = exports.AuthContext = void 0;
|
||||
const error_1 = require("../../error");
|
||||
/** Context used during authentication */
|
||||
class AuthContext {
|
||||
constructor(connection, credentials, options) {
|
||||
this.connection = connection;
|
||||
this.credentials = credentials;
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
exports.AuthContext = AuthContext;
|
||||
class AuthProvider {
|
||||
/**
|
||||
* Prepare the handshake document before the initial handshake.
|
||||
*
|
||||
* @param handshakeDoc - The document used for the initial handshake on a connection
|
||||
* @param authContext - Context for authentication flow
|
||||
*/
|
||||
prepare(handshakeDoc, authContext, callback) {
|
||||
callback(undefined, handshakeDoc);
|
||||
}
|
||||
/**
|
||||
* Authenticate
|
||||
*
|
||||
* @param context - A shared context for authentication flow
|
||||
* @param callback - The callback to return the result from the authentication
|
||||
*/
|
||||
auth(context, callback) {
|
||||
// TODO(NODE-3483): Replace this with MongoMethodOverrideError
|
||||
callback(new error_1.MongoRuntimeError('`auth` method must be overridden by subclass'));
|
||||
}
|
||||
}
|
||||
exports.AuthProvider = AuthProvider;
|
||||
//# sourceMappingURL=auth_provider.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"auth_provider.js","sourceRoot":"","sources":["../../../src/cmap/auth/auth_provider.ts"],"names":[],"mappings":";;;AACA,uCAAgD;AAQhD,yCAAyC;AACzC,MAAa,WAAW;IAatB,YACE,UAAsB,EACtB,WAAyC,EACzC,OAA2B;QAE3B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAtBD,kCAsBC;AAED,MAAa,YAAY;IACvB;;;;;OAKG;IACH,OAAO,CACL,YAA+B,EAC/B,WAAwB,EACxB,QAAqC;QAErC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACpC,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAAC,OAAoB,EAAE,QAAkB;QAC3C,8DAA8D;QAC9D,QAAQ,CAAC,IAAI,yBAAiB,CAAC,8CAA8C,CAAC,CAAC,CAAC;IAClF,CAAC;CACF;AAzBD,oCAyBC"}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.resolveCname = exports.performGSSAPICanonicalizeHostName = exports.GSSAPI = exports.GSSAPICanonicalizationValue = void 0;
|
||||
const dns = require("dns");
|
||||
const deps_1 = require("../../deps");
|
||||
const error_1 = require("../../error");
|
||||
const utils_1 = require("../../utils");
|
||||
const auth_provider_1 = require("./auth_provider");
|
||||
/** @public */
|
||||
exports.GSSAPICanonicalizationValue = Object.freeze({
|
||||
on: true,
|
||||
off: false,
|
||||
none: 'none',
|
||||
forward: 'forward',
|
||||
forwardAndReverse: 'forwardAndReverse'
|
||||
});
|
||||
class GSSAPI extends auth_provider_1.AuthProvider {
|
||||
auth(authContext, callback) {
|
||||
const { connection, credentials } = authContext;
|
||||
if (credentials == null)
|
||||
return callback(new error_1.MongoMissingCredentialsError('Credentials required for GSSAPI authentication'));
|
||||
const { username } = credentials;
|
||||
function externalCommand(command, cb) {
|
||||
return connection.command((0, utils_1.ns)('$external.$cmd'), command, undefined, cb);
|
||||
}
|
||||
makeKerberosClient(authContext, (err, client) => {
|
||||
if (err)
|
||||
return callback(err);
|
||||
if (client == null)
|
||||
return callback(new error_1.MongoMissingDependencyError('GSSAPI client missing'));
|
||||
client.step('', (err, payload) => {
|
||||
if (err)
|
||||
return callback(err);
|
||||
externalCommand(saslStart(payload), (err, result) => {
|
||||
if (err)
|
||||
return callback(err);
|
||||
if (result == null)
|
||||
return callback();
|
||||
negotiate(client, 10, result.payload, (err, payload) => {
|
||||
if (err)
|
||||
return callback(err);
|
||||
externalCommand(saslContinue(payload, result.conversationId), (err, result) => {
|
||||
if (err)
|
||||
return callback(err);
|
||||
if (result == null)
|
||||
return callback();
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.GSSAPI = GSSAPI;
|
||||
function makeKerberosClient(authContext, callback) {
|
||||
var _a;
|
||||
const { hostAddress } = authContext.options;
|
||||
const { credentials } = authContext;
|
||||
if (!hostAddress || typeof hostAddress.host !== 'string' || !credentials) {
|
||||
return callback(new error_1.MongoInvalidArgumentError('Connection must have host and port and credentials defined.'));
|
||||
}
|
||||
if ('kModuleError' in deps_1.Kerberos) {
|
||||
return callback(deps_1.Kerberos['kModuleError']);
|
||||
}
|
||||
const { initializeClient } = deps_1.Kerberos;
|
||||
const { username, password } = credentials;
|
||||
const mechanismProperties = credentials.mechanismProperties;
|
||||
const serviceName = (_a = mechanismProperties.SERVICE_NAME) !== null && _a !== void 0 ? _a : 'mongodb';
|
||||
performGSSAPICanonicalizeHostName(hostAddress.host, mechanismProperties, (err, host) => {
|
||||
var _a;
|
||||
if (err)
|
||||
return callback(err);
|
||||
const initOptions = {};
|
||||
if (password != null) {
|
||||
Object.assign(initOptions, { user: username, password: password });
|
||||
}
|
||||
const spnHost = (_a = mechanismProperties.SERVICE_HOST) !== null && _a !== void 0 ? _a : host;
|
||||
let spn = `${serviceName}${process.platform === 'win32' ? '/' : '@'}${spnHost}`;
|
||||
if ('SERVICE_REALM' in mechanismProperties) {
|
||||
spn = `${spn}@${mechanismProperties.SERVICE_REALM}`;
|
||||
}
|
||||
initializeClient(spn, initOptions, (err, client) => {
|
||||
// TODO(NODE-3483)
|
||||
if (err)
|
||||
return callback(new error_1.MongoRuntimeError(err));
|
||||
callback(undefined, client);
|
||||
});
|
||||
});
|
||||
}
|
||||
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 mode = mechanismProperties.CANONICALIZE_HOST_NAME;
|
||||
if (!mode || mode === exports.GSSAPICanonicalizationValue.none) {
|
||||
return callback(undefined, host);
|
||||
}
|
||||
// If forward and reverse or true
|
||||
if (mode === exports.GSSAPICanonicalizationValue.on ||
|
||||
mode === exports.GSSAPICanonicalizationValue.forwardAndReverse) {
|
||||
// Perform the lookup of the ip address.
|
||||
dns.lookup(host, (error, address) => {
|
||||
// No ip found, return the error.
|
||||
if (error)
|
||||
return callback(error);
|
||||
// Perform a reverse ptr lookup on the ip address.
|
||||
dns.resolvePtr(address, (err, results) => {
|
||||
// This can error as ptr records may not exist for all ips. In this case
|
||||
// fallback to a cname lookup as dns.lookup() does not return the
|
||||
// cname.
|
||||
if (err) {
|
||||
return resolveCname(host, callback);
|
||||
}
|
||||
// If the ptr did not error but had no results, return the host.
|
||||
callback(undefined, results.length > 0 ? results[0] : host);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
// The case for forward is just to resolve the cname as dns.lookup()
|
||||
// will not return it.
|
||||
resolveCname(host, callback);
|
||||
}
|
||||
}
|
||||
exports.performGSSAPICanonicalizeHostName = performGSSAPICanonicalizeHostName;
|
||||
function resolveCname(host, callback) {
|
||||
// Attempt to resolve the host name
|
||||
dns.resolveCname(host, (err, r) => {
|
||||
if (err)
|
||||
return callback(undefined, host);
|
||||
// Get the first resolve host id
|
||||
if (r.length > 0) {
|
||||
return callback(undefined, r[0]);
|
||||
}
|
||||
callback(undefined, host);
|
||||
});
|
||||
}
|
||||
exports.resolveCname = resolveCname;
|
||||
//# sourceMappingURL=gssapi.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+134
@@ -0,0 +1,134 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MongoCredentials = void 0;
|
||||
const error_1 = require("../../error");
|
||||
const utils_1 = require("../../utils");
|
||||
const gssapi_1 = require("./gssapi");
|
||||
const providers_1 = require("./providers");
|
||||
// https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst
|
||||
function getDefaultAuthMechanism(hello) {
|
||||
if (hello) {
|
||||
// If hello contains saslSupportedMechs, use scram-sha-256
|
||||
// if it is available, else scram-sha-1
|
||||
if (Array.isArray(hello.saslSupportedMechs)) {
|
||||
return hello.saslSupportedMechs.includes(providers_1.AuthMechanism.MONGODB_SCRAM_SHA256)
|
||||
? providers_1.AuthMechanism.MONGODB_SCRAM_SHA256
|
||||
: providers_1.AuthMechanism.MONGODB_SCRAM_SHA1;
|
||||
}
|
||||
// Fallback to legacy selection method. If wire version >= 3, use scram-sha-1
|
||||
if (hello.maxWireVersion >= 3) {
|
||||
return providers_1.AuthMechanism.MONGODB_SCRAM_SHA1;
|
||||
}
|
||||
}
|
||||
// Default for wireprotocol < 3
|
||||
return providers_1.AuthMechanism.MONGODB_CR;
|
||||
}
|
||||
/**
|
||||
* A representation of the credentials used by MongoDB
|
||||
* @public
|
||||
*/
|
||||
class MongoCredentials {
|
||||
constructor(options) {
|
||||
this.username = options.username;
|
||||
this.password = options.password;
|
||||
this.source = options.source;
|
||||
if (!this.source && options.db) {
|
||||
this.source = options.db;
|
||||
}
|
||||
this.mechanism = options.mechanism || providers_1.AuthMechanism.MONGODB_DEFAULT;
|
||||
this.mechanismProperties = options.mechanismProperties || {};
|
||||
if (this.mechanism.match(/MONGODB-AWS/i)) {
|
||||
if (!this.username && process.env.AWS_ACCESS_KEY_ID) {
|
||||
this.username = process.env.AWS_ACCESS_KEY_ID;
|
||||
}
|
||||
if (!this.password && 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 != null) {
|
||||
this.mechanismProperties = {
|
||||
...this.mechanismProperties,
|
||||
AWS_SESSION_TOKEN: process.env.AWS_SESSION_TOKEN
|
||||
};
|
||||
}
|
||||
}
|
||||
if ('gssapiCanonicalizeHostName' in this.mechanismProperties) {
|
||||
(0, utils_1.emitWarningOnce)('gssapiCanonicalizeHostName is deprecated. Please use CANONICALIZE_HOST_NAME instead.');
|
||||
this.mechanismProperties.CANONICALIZE_HOST_NAME =
|
||||
this.mechanismProperties.gssapiCanonicalizeHostName;
|
||||
}
|
||||
Object.freeze(this.mechanismProperties);
|
||||
Object.freeze(this);
|
||||
}
|
||||
/** Determines if two MongoCredentials objects are equivalent */
|
||||
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 hello - A hello response from the server
|
||||
*/
|
||||
resolveAuthMechanism(hello) {
|
||||
// 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(hello),
|
||||
mechanismProperties: this.mechanismProperties
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
validate() {
|
||||
var _a;
|
||||
if ((this.mechanism === providers_1.AuthMechanism.MONGODB_GSSAPI ||
|
||||
this.mechanism === providers_1.AuthMechanism.MONGODB_CR ||
|
||||
this.mechanism === providers_1.AuthMechanism.MONGODB_PLAIN ||
|
||||
this.mechanism === providers_1.AuthMechanism.MONGODB_SCRAM_SHA1 ||
|
||||
this.mechanism === providers_1.AuthMechanism.MONGODB_SCRAM_SHA256) &&
|
||||
!this.username) {
|
||||
throw new error_1.MongoMissingCredentialsError(`Username required for mechanism '${this.mechanism}'`);
|
||||
}
|
||||
if (providers_1.AUTH_MECHS_AUTH_SRC_EXTERNAL.has(this.mechanism)) {
|
||||
if (this.source != null && this.source !== '$external') {
|
||||
// TODO(NODE-3485): Replace this with a MongoAuthValidationError
|
||||
throw new error_1.MongoAPIError(`Invalid source '${this.source}' for mechanism '${this.mechanism}' specified.`);
|
||||
}
|
||||
}
|
||||
if (this.mechanism === providers_1.AuthMechanism.MONGODB_PLAIN && this.source == null) {
|
||||
// TODO(NODE-3485): Replace this with a MongoAuthValidationError
|
||||
throw new error_1.MongoAPIError('PLAIN Authentication Mechanism needs an auth source');
|
||||
}
|
||||
if (this.mechanism === providers_1.AuthMechanism.MONGODB_X509 && this.password != null) {
|
||||
if (this.password === '') {
|
||||
Reflect.set(this, 'password', undefined);
|
||||
return;
|
||||
}
|
||||
// TODO(NODE-3485): Replace this with a MongoAuthValidationError
|
||||
throw new error_1.MongoAPIError(`Password not allowed for mechanism MONGODB-X509`);
|
||||
}
|
||||
const canonicalization = (_a = this.mechanismProperties.CANONICALIZE_HOST_NAME) !== null && _a !== void 0 ? _a : false;
|
||||
if (!Object.values(gssapi_1.GSSAPICanonicalizationValue).includes(canonicalization)) {
|
||||
throw new error_1.MongoAPIError(`Invalid CANONICALIZE_HOST_NAME value: ${canonicalization}`);
|
||||
}
|
||||
}
|
||||
static merge(creds, options) {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
||||
return new MongoCredentials({
|
||||
username: (_b = (_a = options.username) !== null && _a !== void 0 ? _a : creds === null || creds === void 0 ? void 0 : creds.username) !== null && _b !== void 0 ? _b : '',
|
||||
password: (_d = (_c = options.password) !== null && _c !== void 0 ? _c : creds === null || creds === void 0 ? void 0 : creds.password) !== null && _d !== void 0 ? _d : '',
|
||||
mechanism: (_f = (_e = options.mechanism) !== null && _e !== void 0 ? _e : creds === null || creds === void 0 ? void 0 : creds.mechanism) !== null && _f !== void 0 ? _f : providers_1.AuthMechanism.MONGODB_DEFAULT,
|
||||
mechanismProperties: (_h = (_g = options.mechanismProperties) !== null && _g !== void 0 ? _g : creds === null || creds === void 0 ? void 0 : creds.mechanismProperties) !== null && _h !== void 0 ? _h : {},
|
||||
source: (_l = (_k = (_j = options.source) !== null && _j !== void 0 ? _j : options.db) !== null && _k !== void 0 ? _k : creds === null || creds === void 0 ? void 0 : creds.source) !== null && _l !== void 0 ? _l : 'admin'
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.MongoCredentials = MongoCredentials;
|
||||
//# sourceMappingURL=mongo_credentials.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"mongo_credentials.js","sourceRoot":"","sources":["../../../src/cmap/auth/mongo_credentials.ts"],"names":[],"mappings":"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"}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MongoCR = void 0;
|
||||
const crypto = require("crypto");
|
||||
const error_1 = require("../../error");
|
||||
const utils_1 = require("../../utils");
|
||||
const auth_provider_1 = require("./auth_provider");
|
||||
class MongoCR extends auth_provider_1.AuthProvider {
|
||||
auth(authContext, callback) {
|
||||
const { connection, credentials } = authContext;
|
||||
if (!credentials) {
|
||||
return callback(new error_1.MongoMissingCredentialsError('AuthContext must provide credentials.'));
|
||||
}
|
||||
const username = credentials.username;
|
||||
const password = credentials.password;
|
||||
const source = credentials.source;
|
||||
connection.command((0, utils_1.ns)(`${source}.$cmd`), { getnonce: 1 }, undefined, (err, r) => {
|
||||
let nonce = null;
|
||||
let key = null;
|
||||
// Get nonce
|
||||
if (err == null) {
|
||||
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((0, utils_1.ns)(`${source}.$cmd`), authenticateCommand, undefined, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.MongoCR = MongoCR;
|
||||
//# sourceMappingURL=mongocr.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"mongocr.js","sourceRoot":"","sources":["../../../src/cmap/auth/mongocr.ts"],"names":[],"mappings":";;;AAAA,iCAAiC;AAEjC,uCAA2D;AAC3D,uCAA2C;AAC3C,mDAA4D;AAE5D,MAAa,OAAQ,SAAQ,4BAAY;IAC9B,IAAI,CAAC,WAAwB,EAAE,QAAkB;QACxD,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC;QAChD,IAAI,CAAC,WAAW,EAAE;YAChB,OAAO,QAAQ,CAAC,IAAI,oCAA4B,CAAC,uCAAuC,CAAC,CAAC,CAAC;SAC5F;QACD,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC;QACtC,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC;QACtC,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC;QAClC,UAAU,CAAC,OAAO,CAAC,IAAA,UAAE,EAAC,GAAG,MAAM,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YAC9E,IAAI,KAAK,GAAG,IAAI,CAAC;YACjB,IAAI,GAAG,GAAG,IAAI,CAAC;YAEf,YAAY;YACZ,IAAI,GAAG,IAAI,IAAI,EAAE;gBACf,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;gBAEhB,yBAAyB;gBACzB,IAAI,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;gBAEnC,wCAAwC;gBACxC,GAAG,CAAC,MAAM,CAAC,GAAG,QAAQ,UAAU,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;gBACpD,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAExC,YAAY;gBACZ,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;gBAC/B,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,CAAC;gBACrD,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;aACzB;YAED,MAAM,mBAAmB,GAAG;gBAC1B,YAAY,EAAE,CAAC;gBACf,IAAI,EAAE,QAAQ;gBACd,KAAK;gBACL,GAAG;aACJ,CAAC;YAEF,UAAU,CAAC,OAAO,CAAC,IAAA,UAAE,EAAC,GAAG,MAAM,OAAO,CAAC,EAAE,mBAAmB,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACrF,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAxCD,0BAwCC"}
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MongoDBAWS = void 0;
|
||||
const crypto = require("crypto");
|
||||
const http = require("http");
|
||||
const url = require("url");
|
||||
const BSON = require("../../bson");
|
||||
const deps_1 = require("../../deps");
|
||||
const error_1 = require("../../error");
|
||||
const utils_1 = require("../../utils");
|
||||
const auth_provider_1 = require("./auth_provider");
|
||||
const mongo_credentials_1 = require("./mongo_credentials");
|
||||
const providers_1 = require("./providers");
|
||||
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';
|
||||
const bsonOptions = {
|
||||
promoteLongs: true,
|
||||
promoteValues: true,
|
||||
promoteBuffers: false,
|
||||
bsonRegExp: false
|
||||
};
|
||||
class MongoDBAWS extends auth_provider_1.AuthProvider {
|
||||
auth(authContext, callback) {
|
||||
const { connection, credentials } = authContext;
|
||||
if (!credentials) {
|
||||
return callback(new error_1.MongoMissingCredentialsError('AuthContext must provide credentials.'));
|
||||
}
|
||||
if ('kModuleError' in deps_1.aws4) {
|
||||
return callback(deps_1.aws4['kModuleError']);
|
||||
}
|
||||
const { sign } = deps_1.aws4;
|
||||
if ((0, utils_1.maxWireVersion)(connection) < 9) {
|
||||
callback(new error_1.MongoCompatibilityError('MONGODB-AWS authentication requires MongoDB version 4.4 or later'));
|
||||
return;
|
||||
}
|
||||
if (!credentials.username) {
|
||||
makeTempCredentials(credentials, (err, tempCredentials) => {
|
||||
if (err || !tempCredentials)
|
||||
return callback(err);
|
||||
authContext.credentials = tempCredentials;
|
||||
this.auth(authContext, callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
const accessKeyId = credentials.username;
|
||||
const secretAccessKey = credentials.password;
|
||||
const sessionToken = credentials.mechanismProperties.AWS_SESSION_TOKEN;
|
||||
// If all three defined, include sessionToken, else include username and pass, else no credentials
|
||||
const awsCredentials = accessKeyId && secretAccessKey && sessionToken
|
||||
? { accessKeyId, secretAccessKey, sessionToken }
|
||||
: accessKeyId && secretAccessKey
|
||||
? { accessKeyId, secretAccessKey }
|
||||
: undefined;
|
||||
const db = credentials.source;
|
||||
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 }, bsonOptions)
|
||||
};
|
||||
connection.command((0, utils_1.ns)(`${db}.$cmd`), saslStart, undefined, (err, res) => {
|
||||
if (err)
|
||||
return callback(err);
|
||||
const serverResponse = BSON.deserialize(res.payload.buffer, bsonOptions);
|
||||
const host = serverResponse.h;
|
||||
const serverNonce = serverResponse.s.buffer;
|
||||
if (serverNonce.length !== 64) {
|
||||
callback(
|
||||
// TODO(NODE-3483)
|
||||
new error_1.MongoRuntimeError(`Invalid server nonce length ${serverNonce.length}, expected 64`));
|
||||
return;
|
||||
}
|
||||
if (serverNonce.compare(nonce, 0, nonce.length, 0, nonce.length) !== 0) {
|
||||
// TODO(NODE-3483)
|
||||
callback(new error_1.MongoRuntimeError('Server nonce does not begin with client nonce'));
|
||||
return;
|
||||
}
|
||||
if (host.length < 1 || host.length > 255 || host.indexOf('..') !== -1) {
|
||||
// TODO(NODE-3483)
|
||||
callback(new error_1.MongoRuntimeError(`Server returned an invalid host: "${host}"`));
|
||||
return;
|
||||
}
|
||||
const body = 'Action=GetCallerIdentity&Version=2011-06-15';
|
||||
const options = 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
|
||||
}, awsCredentials);
|
||||
const payload = {
|
||||
a: options.headers.Authorization,
|
||||
d: options.headers['X-Amz-Date']
|
||||
};
|
||||
if (sessionToken) {
|
||||
payload.t = sessionToken;
|
||||
}
|
||||
const saslContinue = {
|
||||
saslContinue: 1,
|
||||
conversationId: 1,
|
||||
payload: BSON.serialize(payload, bsonOptions)
|
||||
};
|
||||
connection.command((0, utils_1.ns)(`${db}.$cmd`), saslContinue, undefined, callback);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.MongoDBAWS = MongoDBAWS;
|
||||
function makeTempCredentials(credentials, callback) {
|
||||
function done(creds) {
|
||||
if (!creds.AccessKeyId || !creds.SecretAccessKey || !creds.Token) {
|
||||
callback(new error_1.MongoMissingCredentialsError('Could not obtain temporary MONGODB-AWS credentials'));
|
||||
return;
|
||||
}
|
||||
callback(undefined, new mongo_credentials_1.MongoCredentials({
|
||||
username: creds.AccessKeyId,
|
||||
password: creds.SecretAccessKey,
|
||||
source: credentials.source,
|
||||
mechanism: providers_1.AuthMechanism.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}`, undefined, (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) {
|
||||
const 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) {
|
||||
// TODO(NODE-3483)
|
||||
callback(new error_1.MongoRuntimeError(`Invalid JSON response: "${data}"`));
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('timeout', () => {
|
||||
req.destroy(new error_1.MongoAWSError(`AWS request to ${uri} timed out after ${options.timeout} ms`));
|
||||
});
|
||||
req.on('error', err => callback(err));
|
||||
req.end();
|
||||
}
|
||||
//# sourceMappingURL=mongodb_aws.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+27
@@ -0,0 +1,27 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Plain = void 0;
|
||||
const bson_1 = require("../../bson");
|
||||
const error_1 = require("../../error");
|
||||
const utils_1 = require("../../utils");
|
||||
const auth_provider_1 = require("./auth_provider");
|
||||
class Plain extends auth_provider_1.AuthProvider {
|
||||
auth(authContext, callback) {
|
||||
const { connection, credentials } = authContext;
|
||||
if (!credentials) {
|
||||
return callback(new error_1.MongoMissingCredentialsError('AuthContext must provide credentials.'));
|
||||
}
|
||||
const username = credentials.username;
|
||||
const password = credentials.password;
|
||||
const payload = new bson_1.Binary(Buffer.from(`\x00${username}\x00${password}`));
|
||||
const command = {
|
||||
saslStart: 1,
|
||||
mechanism: 'PLAIN',
|
||||
payload: payload,
|
||||
autoAuthorize: 1
|
||||
};
|
||||
connection.command((0, utils_1.ns)('$external.$cmd'), command, undefined, callback);
|
||||
}
|
||||
}
|
||||
exports.Plain = Plain;
|
||||
//# sourceMappingURL=plain.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"plain.js","sourceRoot":"","sources":["../../../src/cmap/auth/plain.ts"],"names":[],"mappings":";;;AAAA,qCAAoC;AACpC,uCAA2D;AAC3D,uCAA2C;AAC3C,mDAA4D;AAE5D,MAAa,KAAM,SAAQ,4BAAY;IAC5B,IAAI,CAAC,WAAwB,EAAE,QAAkB;QACxD,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC;QAChD,IAAI,CAAC,WAAW,EAAE;YAChB,OAAO,QAAQ,CAAC,IAAI,oCAA4B,CAAC,uCAAuC,CAAC,CAAC,CAAC;SAC5F;QACD,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC;QACtC,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC;QAEtC,MAAM,OAAO,GAAG,IAAI,aAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,QAAQ,OAAO,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC1E,MAAM,OAAO,GAAG;YACd,SAAS,EAAE,CAAC;YACZ,SAAS,EAAE,OAAO;YAClB,OAAO,EAAE,OAAO;YAChB,aAAa,EAAE,CAAC;SACjB,CAAC;QAEF,UAAU,CAAC,OAAO,CAAC,IAAA,UAAE,EAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IACzE,CAAC;CACF;AAnBD,sBAmBC"}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AUTH_MECHS_AUTH_SRC_EXTERNAL = exports.AuthMechanism = void 0;
|
||||
/** @public */
|
||||
exports.AuthMechanism = Object.freeze({
|
||||
MONGODB_AWS: 'MONGODB-AWS',
|
||||
MONGODB_CR: 'MONGODB-CR',
|
||||
MONGODB_DEFAULT: 'DEFAULT',
|
||||
MONGODB_GSSAPI: 'GSSAPI',
|
||||
MONGODB_PLAIN: 'PLAIN',
|
||||
MONGODB_SCRAM_SHA1: 'SCRAM-SHA-1',
|
||||
MONGODB_SCRAM_SHA256: 'SCRAM-SHA-256',
|
||||
MONGODB_X509: 'MONGODB-X509'
|
||||
});
|
||||
/** @internal */
|
||||
exports.AUTH_MECHS_AUTH_SRC_EXTERNAL = new Set([
|
||||
exports.AuthMechanism.MONGODB_GSSAPI,
|
||||
exports.AuthMechanism.MONGODB_AWS,
|
||||
exports.AuthMechanism.MONGODB_X509
|
||||
]);
|
||||
//# sourceMappingURL=providers.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../../src/cmap/auth/providers.ts"],"names":[],"mappings":";;;AAAA,cAAc;AACD,QAAA,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;IACzC,WAAW,EAAE,aAAa;IAC1B,UAAU,EAAE,YAAY;IACxB,eAAe,EAAE,SAAS;IAC1B,cAAc,EAAE,QAAQ;IACxB,aAAa,EAAE,OAAO;IACtB,kBAAkB,EAAE,aAAa;IACjC,oBAAoB,EAAE,eAAe;IACrC,YAAY,EAAE,cAAc;CACpB,CAAC,CAAC;AAKZ,gBAAgB;AACH,QAAA,4BAA4B,GAAG,IAAI,GAAG,CAAgB;IACjE,qBAAa,CAAC,cAAc;IAC5B,qBAAa,CAAC,WAAW;IACzB,qBAAa,CAAC,YAAY;CAC3B,CAAC,CAAC"}
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ScramSHA256 = exports.ScramSHA1 = void 0;
|
||||
const crypto = require("crypto");
|
||||
const bson_1 = require("../../bson");
|
||||
const deps_1 = require("../../deps");
|
||||
const error_1 = require("../../error");
|
||||
const utils_1 = require("../../utils");
|
||||
const auth_provider_1 = require("./auth_provider");
|
||||
const providers_1 = require("./providers");
|
||||
class ScramSHA extends auth_provider_1.AuthProvider {
|
||||
constructor(cryptoMethod) {
|
||||
super();
|
||||
this.cryptoMethod = cryptoMethod || 'sha1';
|
||||
}
|
||||
prepare(handshakeDoc, authContext, callback) {
|
||||
const cryptoMethod = this.cryptoMethod;
|
||||
const credentials = authContext.credentials;
|
||||
if (!credentials) {
|
||||
return callback(new error_1.MongoMissingCredentialsError('AuthContext must provide credentials.'));
|
||||
}
|
||||
if (cryptoMethod === 'sha256' && deps_1.saslprep == null) {
|
||||
(0, utils_1.emitWarning)('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 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' ? providers_1.AuthMechanism.MONGODB_SCRAM_SHA1 : providers_1.AuthMechanism.MONGODB_SCRAM_SHA256;
|
||||
// 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 bson_1.Binary(Buffer.concat([Buffer.from('n,,', 'utf8'), clientFirstMessageBare(username, nonce)])),
|
||||
autoAuthorize: 1,
|
||||
options: { skipEmptyExchange: true }
|
||||
};
|
||||
}
|
||||
function executeScram(cryptoMethod, authContext, callback) {
|
||||
const { connection, credentials } = authContext;
|
||||
if (!credentials) {
|
||||
return callback(new error_1.MongoMissingCredentialsError('AuthContext must provide credentials.'));
|
||||
}
|
||||
if (!authContext.nonce) {
|
||||
return callback(new error_1.MongoInvalidArgumentError('AuthContext must contain a valid nonce property'));
|
||||
}
|
||||
const nonce = authContext.nonce;
|
||||
const db = credentials.source;
|
||||
const saslStartCmd = makeFirstMessage(cryptoMethod, credentials, nonce);
|
||||
connection.command((0, utils_1.ns)(`${db}.$cmd`), saslStartCmd, undefined, (_err, result) => {
|
||||
const err = resolveError(_err, result);
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
continueScramConversation(cryptoMethod, result, authContext, callback);
|
||||
});
|
||||
}
|
||||
function continueScramConversation(cryptoMethod, response, authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
if (!credentials) {
|
||||
return callback(new error_1.MongoMissingCredentialsError('AuthContext must provide credentials.'));
|
||||
}
|
||||
if (!authContext.nonce) {
|
||||
return callback(new error_1.MongoInvalidArgumentError('Unable to continue SCRAM without valid nonce'));
|
||||
}
|
||||
const nonce = authContext.nonce;
|
||||
const db = credentials.source;
|
||||
const username = cleanUsername(credentials.username);
|
||||
const password = credentials.password;
|
||||
let processedPassword;
|
||||
if (cryptoMethod === 'sha256') {
|
||||
processedPassword = 'kModuleError' in deps_1.saslprep ? password : (0, deps_1.saslprep)(password);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
processedPassword = passwordDigest(username, password);
|
||||
}
|
||||
catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
}
|
||||
const payload = Buffer.isBuffer(response.payload)
|
||||
? new bson_1.Binary(response.payload)
|
||||
: response.payload;
|
||||
const dict = parsePayload(payload.value());
|
||||
const iterations = parseInt(dict.i, 10);
|
||||
if (iterations && iterations < 4096) {
|
||||
callback(
|
||||
// TODO(NODE-3483)
|
||||
new error_1.MongoRuntimeError(`Server returned an invalid iteration count ${iterations}`), false);
|
||||
return;
|
||||
}
|
||||
const salt = dict.s;
|
||||
const rnonce = dict.r;
|
||||
if (rnonce.startsWith('nonce')) {
|
||||
// TODO(NODE-3483)
|
||||
callback(new error_1.MongoRuntimeError(`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(), 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 bson_1.Binary(Buffer.from(clientFinal))
|
||||
};
|
||||
connection.command((0, utils_1.ns)(`${db}.$cmd`), saslContinueCmd, undefined, (_err, r) => {
|
||||
const err = resolveError(_err, r);
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
const parsedResponse = parsePayload(r.payload.value());
|
||||
if (!compareDigest(Buffer.from(parsedResponse.v, 'base64'), serverSignature)) {
|
||||
callback(new error_1.MongoRuntimeError('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((0, utils_1.ns)(`${db}.$cmd`), retrySaslContinueCmd, undefined, 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 error_1.MongoInvalidArgumentError('Username must be a string');
|
||||
}
|
||||
if (typeof password !== 'string') {
|
||||
throw new error_1.MongoInvalidArgumentError('Password must be a string');
|
||||
}
|
||||
if (password.length === 0) {
|
||||
throw new error_1.MongoInvalidArgumentError('Password cannot be empty');
|
||||
}
|
||||
let md5;
|
||||
try {
|
||||
md5 = crypto.createHash('md5');
|
||||
}
|
||||
catch (err) {
|
||||
if (crypto.getFips()) {
|
||||
// This error is (slightly) more helpful than what comes from OpenSSL directly, e.g.
|
||||
// 'Error: error:060800C8:digital envelope routines:EVP_DigestInit_ex:disabled for FIPS'
|
||||
throw new Error('Auth mechanism SCRAM-SHA-1 is not supported in FIPS mode');
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
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] != null) {
|
||||
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;
|
||||
if (result) {
|
||||
if (result.$err || result.errmsg)
|
||||
return new error_1.MongoServerError(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
class ScramSHA1 extends ScramSHA {
|
||||
constructor() {
|
||||
super('sha1');
|
||||
}
|
||||
}
|
||||
exports.ScramSHA1 = ScramSHA1;
|
||||
class ScramSHA256 extends ScramSHA {
|
||||
constructor() {
|
||||
super('sha256');
|
||||
}
|
||||
}
|
||||
exports.ScramSHA256 = ScramSHA256;
|
||||
//# sourceMappingURL=scram.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+39
@@ -0,0 +1,39 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.X509 = void 0;
|
||||
const error_1 = require("../../error");
|
||||
const utils_1 = require("../../utils");
|
||||
const auth_provider_1 = require("./auth_provider");
|
||||
class X509 extends auth_provider_1.AuthProvider {
|
||||
prepare(handshakeDoc, authContext, callback) {
|
||||
const { credentials } = authContext;
|
||||
if (!credentials) {
|
||||
return callback(new error_1.MongoMissingCredentialsError('AuthContext must provide credentials.'));
|
||||
}
|
||||
Object.assign(handshakeDoc, {
|
||||
speculativeAuthenticate: x509AuthenticateCommand(credentials)
|
||||
});
|
||||
callback(undefined, handshakeDoc);
|
||||
}
|
||||
auth(authContext, callback) {
|
||||
const connection = authContext.connection;
|
||||
const credentials = authContext.credentials;
|
||||
if (!credentials) {
|
||||
return callback(new error_1.MongoMissingCredentialsError('AuthContext must provide credentials.'));
|
||||
}
|
||||
const response = authContext.response;
|
||||
if (response && response.speculativeAuthenticate) {
|
||||
return callback();
|
||||
}
|
||||
connection.command((0, utils_1.ns)('$external.$cmd'), x509AuthenticateCommand(credentials), undefined, callback);
|
||||
}
|
||||
}
|
||||
exports.X509 = X509;
|
||||
function x509AuthenticateCommand(credentials) {
|
||||
const command = { authenticate: 1, mechanism: 'MONGODB-X509' };
|
||||
if (credentials.username) {
|
||||
command.user = credentials.username;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
//# sourceMappingURL=x509.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"x509.js","sourceRoot":"","sources":["../../../src/cmap/auth/x509.ts"],"names":[],"mappings":";;;AACA,uCAA2D;AAC3D,uCAA2C;AAE3C,mDAA4D;AAG5D,MAAa,IAAK,SAAQ,4BAAY;IAC3B,OAAO,CACd,YAA+B,EAC/B,WAAwB,EACxB,QAAkB;QAElB,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC;QACpC,IAAI,CAAC,WAAW,EAAE;YAChB,OAAO,QAAQ,CAAC,IAAI,oCAA4B,CAAC,uCAAuC,CAAC,CAAC,CAAC;SAC5F;QACD,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE;YAC1B,uBAAuB,EAAE,uBAAuB,CAAC,WAAW,CAAC;SAC9D,CAAC,CAAC;QAEH,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACpC,CAAC;IAEQ,IAAI,CAAC,WAAwB,EAAE,QAAkB;QACxD,MAAM,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC;QAC1C,MAAM,WAAW,GAAG,WAAW,CAAC,WAAW,CAAC;QAC5C,IAAI,CAAC,WAAW,EAAE;YAChB,OAAO,QAAQ,CAAC,IAAI,oCAA4B,CAAC,uCAAuC,CAAC,CAAC,CAAC;SAC5F;QACD,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC;QAEtC,IAAI,QAAQ,IAAI,QAAQ,CAAC,uBAAuB,EAAE;YAChD,OAAO,QAAQ,EAAE,CAAC;SACnB;QAED,UAAU,CAAC,OAAO,CAChB,IAAA,UAAE,EAAC,gBAAgB,CAAC,EACpB,uBAAuB,CAAC,WAAW,CAAC,EACpC,SAAS,EACT,QAAQ,CACT,CAAC;IACJ,CAAC;CACF;AApCD,oBAoCC;AAED,SAAS,uBAAuB,CAAC,WAA6B;IAC5D,MAAM,OAAO,GAAa,EAAE,YAAY,EAAE,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC;IACzE,IAAI,WAAW,CAAC,QAAQ,EAAE;QACxB,OAAO,CAAC,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC;KACrC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
||||
Reference in New Issue
Block a user