Sindbad~EG File Manager
// Resolves the default auth mechanism according to
// Resolves the default auth mechanism according to
import type { Document } from '../../bson';
import {
MongoAPIError,
MongoAzureError,
MongoInvalidArgumentError,
MongoMissingCredentialsError
} from '../../error';
import { GSSAPICanonicalizationValue } from './gssapi';
import type { OIDCRefreshFunction, OIDCRequestFunction } from './mongodb_oidc';
import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from './providers';
// https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst
function getDefaultAuthMechanism(hello?: Document): AuthMechanism {
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(AuthMechanism.MONGODB_SCRAM_SHA256)
? AuthMechanism.MONGODB_SCRAM_SHA256
: AuthMechanism.MONGODB_SCRAM_SHA1;
}
// Fallback to legacy selection method. If wire version >= 3, use scram-sha-1
if (hello.maxWireVersion >= 3) {
return AuthMechanism.MONGODB_SCRAM_SHA1;
}
}
// Default for wireprotocol < 3
return AuthMechanism.MONGODB_CR;
}
const ALLOWED_PROVIDER_NAMES: AuthMechanismProperties['PROVIDER_NAME'][] = ['aws', 'azure'];
const ALLOWED_HOSTS_ERROR = 'Auth mechanism property ALLOWED_HOSTS must be an array of strings.';
/** @internal */
export const DEFAULT_ALLOWED_HOSTS = [
'*.mongodb.net',
'*.mongodb-dev.net',
'*.mongodbgov.net',
'localhost',
'127.0.0.1',
'::1'
];
/** Error for when the token audience is missing in the environment. */
const TOKEN_AUDIENCE_MISSING_ERROR =
'TOKEN_AUDIENCE must be set in the auth mechanism properties when PROVIDER_NAME is azure.';
/** @public */
export interface AuthMechanismProperties extends Document {
SERVICE_HOST?: string;
SERVICE_NAME?: string;
SERVICE_REALM?: string;
CANONICALIZE_HOST_NAME?: GSSAPICanonicalizationValue;
AWS_SESSION_TOKEN?: string;
/** @experimental */
REQUEST_TOKEN_CALLBACK?: OIDCRequestFunction;
/** @experimental */
REFRESH_TOKEN_CALLBACK?: OIDCRefreshFunction;
/** @experimental */
PROVIDER_NAME?: 'aws' | 'azure';
/** @experimental */
ALLOWED_HOSTS?: string[];
/** @experimental */
TOKEN_AUDIENCE?: string;
}
/** @public */
export interface MongoCredentialsOptions {
username?: string;
password: string;
source: string;
db?: string;
mechanism?: AuthMechanism;
mechanismProperties: AuthMechanismProperties;
}
/**
* A representation of the credentials used by MongoDB
* @public
*/
export class MongoCredentials {
/** The username used for authentication */
readonly username: string;
/** The password used for authentication */
readonly password: string;
/** The database that the user should authenticate against */
readonly source: string;
/** The method used to authenticate */
readonly mechanism: AuthMechanism;
/** Special properties used by some types of auth mechanisms */
readonly mechanismProperties: AuthMechanismProperties;
constructor(options: MongoCredentialsOptions) {
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 || 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 (this.mechanism === AuthMechanism.MONGODB_OIDC && !this.mechanismProperties.ALLOWED_HOSTS) {
this.mechanismProperties = {
...this.mechanismProperties,
ALLOWED_HOSTS: DEFAULT_ALLOWED_HOSTS
};
}
Object.freeze(this.mechanismProperties);
Object.freeze(this);
}
/** Determines if two MongoCredentials objects are equivalent */
equals(other: MongoCredentials): boolean {
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?: Document): MongoCredentials {
// 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(): void {
if (
(this.mechanism === AuthMechanism.MONGODB_GSSAPI ||
this.mechanism === AuthMechanism.MONGODB_CR ||
this.mechanism === AuthMechanism.MONGODB_PLAIN ||
this.mechanism === AuthMechanism.MONGODB_SCRAM_SHA1 ||
this.mechanism === AuthMechanism.MONGODB_SCRAM_SHA256) &&
!this.username
) {
throw new MongoMissingCredentialsError(`Username required for mechanism '${this.mechanism}'`);
}
if (this.mechanism === AuthMechanism.MONGODB_OIDC) {
if (this.username && this.mechanismProperties.PROVIDER_NAME) {
throw new MongoInvalidArgumentError(
`username and PROVIDER_NAME may not be used together for mechanism '${this.mechanism}'.`
);
}
if (
this.mechanismProperties.PROVIDER_NAME === 'azure' &&
!this.mechanismProperties.TOKEN_AUDIENCE
) {
throw new MongoAzureError(TOKEN_AUDIENCE_MISSING_ERROR);
}
if (
this.mechanismProperties.PROVIDER_NAME &&
!ALLOWED_PROVIDER_NAMES.includes(this.mechanismProperties.PROVIDER_NAME)
) {
throw new MongoInvalidArgumentError(
`Currently only a PROVIDER_NAME in ${ALLOWED_PROVIDER_NAMES.join(
','
)} is supported for mechanism '${this.mechanism}'.`
);
}
if (
this.mechanismProperties.REFRESH_TOKEN_CALLBACK &&
!this.mechanismProperties.REQUEST_TOKEN_CALLBACK
) {
throw new MongoInvalidArgumentError(
`A REQUEST_TOKEN_CALLBACK must be provided when using a REFRESH_TOKEN_CALLBACK for mechanism '${this.mechanism}'`
);
}
if (
!this.mechanismProperties.PROVIDER_NAME &&
!this.mechanismProperties.REQUEST_TOKEN_CALLBACK
) {
throw new MongoInvalidArgumentError(
`Either a PROVIDER_NAME or a REQUEST_TOKEN_CALLBACK must be specified for mechanism '${this.mechanism}'.`
);
}
if (this.mechanismProperties.ALLOWED_HOSTS) {
const hosts = this.mechanismProperties.ALLOWED_HOSTS;
if (!Array.isArray(hosts)) {
throw new MongoInvalidArgumentError(ALLOWED_HOSTS_ERROR);
}
for (const host of hosts) {
if (typeof host !== 'string') {
throw new MongoInvalidArgumentError(ALLOWED_HOSTS_ERROR);
}
}
}
}
if (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 MongoAPIError(
`Invalid source '${this.source}' for mechanism '${this.mechanism}' specified.`
);
}
}
if (this.mechanism === AuthMechanism.MONGODB_PLAIN && this.source == null) {
// TODO(NODE-3485): Replace this with a MongoAuthValidationError
throw new MongoAPIError('PLAIN Authentication Mechanism needs an auth source');
}
if (this.mechanism === 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 MongoAPIError(`Password not allowed for mechanism MONGODB-X509`);
}
const canonicalization = this.mechanismProperties.CANONICALIZE_HOST_NAME ?? false;
if (!Object.values(GSSAPICanonicalizationValue).includes(canonicalization)) {
throw new MongoAPIError(`Invalid CANONICALIZE_HOST_NAME value: ${canonicalization}`);
}
}
static merge(
creds: MongoCredentials | undefined,
options: Partial<MongoCredentialsOptions>
): MongoCredentials {
return new MongoCredentials({
username: options.username ?? creds?.username ?? '',
password: options.password ?? creds?.password ?? '',
mechanism: options.mechanism ?? creds?.mechanism ?? AuthMechanism.MONGODB_DEFAULT,
mechanismProperties: options.mechanismProperties ?? creds?.mechanismProperties ?? {},
source: options.source ?? options.db ?? creds?.source ?? 'admin'
});
}
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists