Skip to content

Commit

Permalink
feat: Add MongoOption builder logic (#2623)
Browse files Browse the repository at this point in the history
Parse options from uri and object using a unified registry creating 
frozen MongoOptions interface object.

NODE-2699 NODE-2698
  • Loading branch information
nbbeeken committed Dec 8, 2020
1 parent f6d9b81 commit cb9ee9e
Show file tree
Hide file tree
Showing 29 changed files with 1,434 additions and 292 deletions.
23 changes: 13 additions & 10 deletions src/cmap/auth/defaultAuthProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ import { MongoDBAWS } from './mongodb_aws';
import type { AuthProvider } from './auth_provider';

/** @public */
export enum AuthMechanism {
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'
}
export const AuthMechanism = {
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'
} as const;

/** @public */
export type AuthMechanismId = typeof AuthMechanism[keyof typeof AuthMechanism];

export const AUTH_PROVIDERS = {
[AuthMechanism.MONGODB_AWS]: new MongoDBAWS(),
Expand Down
60 changes: 55 additions & 5 deletions src/cmap/auth/mongo_credentials.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Resolves the default auth mechanism according to

import type { Document } from '../../bson';
import { AuthMechanism } from './defaultAuthProviders';
import { AuthMechanismId, AuthMechanism } from './defaultAuthProviders';

// https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst
function getDefaultAuthMechanism(ismaster?: Document): AuthMechanism {
function getDefaultAuthMechanism(ismaster?: Document): AuthMechanismId {
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
return ismaster.saslSupportedMechs.includes(AuthMechanism.MONGODB_SCRAM_SHA256)
? AuthMechanism.MONGODB_SCRAM_SHA256
: AuthMechanism.MONGODB_SCRAM_SHA1;
}
Expand All @@ -30,7 +30,7 @@ export interface MongoCredentialsOptions {
password: string;
source: string;
db?: string;
mechanism?: AuthMechanism;
mechanism?: AuthMechanismId;
mechanismProperties: Document;
}

Expand All @@ -46,7 +46,7 @@ export class MongoCredentials {
/** The database that the user should authenticate against */
readonly source: string;
/** The method used to authenticate */
readonly mechanism: AuthMechanism;
readonly mechanism: AuthMechanismId;
/** Special properties used by some types of auth mechanisms */
readonly mechanismProperties: Document;

Expand Down Expand Up @@ -108,4 +108,54 @@ export class MongoCredentials {

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 TypeError(`Username required for mechanism '${this.mechanism}'`);
}

if (
this.mechanism === AuthMechanism.MONGODB_GSSAPI ||
this.mechanism === AuthMechanism.MONGODB_AWS ||
this.mechanism === AuthMechanism.MONGODB_X509
) {
if (this.source != null && this.source !== '$external') {
throw new TypeError(
`Invalid source '${this.source}' for mechanism '${this.mechanism}' specified.`
);
}
}

if (this.mechanism === AuthMechanism.MONGODB_PLAIN && this.source == null) {
throw new TypeError('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;
}
throw new TypeError(`Password not allowed for mechanism MONGODB-X509`);
}
}

static merge(
creds: MongoCredentials,
options: Partial<MongoCredentialsOptions>
): MongoCredentials {
return new MongoCredentials({
username: options.username ?? creds.username,
password: options.password ?? creds.password,
mechanism: options.mechanism ?? creds.mechanism,
mechanismProperties: options.mechanismProperties ?? creds.mechanismProperties,
source: options.source ?? creds.source ?? options.db
});
}
}
4 changes: 3 additions & 1 deletion src/cmap/auth/scram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { MongoCredentials } from './mongo_credentials';
import type { HandshakeDocument } from '../connect';

import { saslprep } from '../../deps';
import { AuthMechanism } from './defaultAuthProviders';

type CryptoMethod = 'sha1' | 'sha256';

Expand Down Expand Up @@ -83,7 +84,8 @@ function makeFirstMessage(
nonce: Buffer
) {
const username = cleanUsername(credentials.username);
const mechanism = cryptoMethod === 'sha1' ? 'SCRAM-SHA-1' : 'SCRAM-SHA-256';
const mechanism =
cryptoMethod === 'sha1' ? AuthMechanism.MONGODB_SCRAM_SHA1 : 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.
Expand Down
8 changes: 4 additions & 4 deletions src/cmap/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ import type { Server } from '../sdam/server';
import type { MongoCredentials } from './auth/mongo_credentials';
import type { CommandOptions } from './wire_protocol/command';
import type { GetMoreOptions } from './wire_protocol/get_more';
import type { InsertOptions, UpdateOptions, RemoveOptions } from './wire_protocol/index';
import type { Stream } from './connect';
import type { LoggerOptions } from '../logger';
import type { QueryOptions } from './wire_protocol/query';
import type { WriteCommandOptions } from './wire_protocol/write_command';

const kStream = Symbol('stream');
const kQueue = Symbol('queue');
Expand Down Expand Up @@ -280,17 +280,17 @@ export class Connection extends EventEmitter {
}

/** @internal */
insert(ns: string, ops: Document[], options: InsertOptions, callback: Callback): void {
insert(ns: string, ops: Document[], options: WriteCommandOptions, callback: Callback): void {
wp.insert(makeServerTrampoline(this), ns, ops, options, callback);
}

/** @internal */
update(ns: string, ops: Document[], options: UpdateOptions, callback: Callback): void {
update(ns: string, ops: Document[], options: WriteCommandOptions, callback: Callback): void {
wp.update(makeServerTrampoline(this), ns, ops, options, callback);
}

/** @internal */
remove(ns: string, ops: Document[], options: RemoveOptions, callback: Callback): void {
remove(ns: string, ops: Document[], options: WriteCommandOptions, callback: Callback): void {
wp.remove(makeServerTrampoline(this), ns, ops, options, callback);
}
}
Expand Down
15 changes: 3 additions & 12 deletions src/cmap/wire_protocol/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,31 @@ import type { Callback } from '../../utils';

export { writeCommand };

/** @internal */
export type InsertOptions = WriteCommandOptions;

export function insert(
server: Server,
ns: string,
ops: Document[],
options: InsertOptions,
options: WriteCommandOptions,
callback: Callback
): void {
writeCommand(server, 'insert', 'documents', ns, ops, options, callback);
}

/** @internal */
export type UpdateOptions = WriteCommandOptions;

export function update(
server: Server,
ns: string,
ops: Document[],
options: UpdateOptions,
options: WriteCommandOptions,
callback: Callback
): void {
writeCommand(server, 'update', 'updates', ns, ops, options, callback);
}

/** @internal */
export type RemoveOptions = WriteCommandOptions;

export function remove(
server: Server,
ns: string,
ops: Document[],
options: RemoveOptions,
options: WriteCommandOptions,
callback: Callback
): void {
writeCommand(server, 'delete', 'deletes', ns, ops, options, callback);
Expand Down
Loading

0 comments on commit cb9ee9e

Please sign in to comment.