You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
3.5 KiB
119 lines
3.5 KiB
3 years ago
|
import * as crypto from 'crypto';
|
||
|
|
||
|
import type { Document } from '../bson';
|
||
|
import type { Db } from '../db';
|
||
|
import { MongoInvalidArgumentError } from '../error';
|
||
|
import type { Server } from '../sdam/server';
|
||
|
import type { ClientSession } from '../sessions';
|
||
|
import { Callback, emitWarningOnce, getTopology } from '../utils';
|
||
|
import { CommandOperation, CommandOperationOptions } from './command';
|
||
|
import { Aspect, defineAspects } from './operation';
|
||
|
|
||
|
/** @public */
|
||
|
export interface RoleSpecification {
|
||
|
/**
|
||
|
* A role grants privileges to perform sets of actions on defined resources.
|
||
|
* A given role applies to the database on which it is defined and can grant access down to a collection level of granularity.
|
||
|
*/
|
||
|
role: string;
|
||
|
/** The database this user's role should effect. */
|
||
|
db: string;
|
||
|
}
|
||
|
|
||
|
/** @public */
|
||
|
export interface AddUserOptions extends CommandOperationOptions {
|
||
|
/** Roles associated with the created user */
|
||
|
roles?: string | string[] | RoleSpecification | RoleSpecification[];
|
||
|
/** Custom data associated with the user (only Mongodb 2.6 or higher) */
|
||
|
customData?: Document;
|
||
|
}
|
||
|
|
||
|
/** @internal */
|
||
|
export class AddUserOperation extends CommandOperation<Document> {
|
||
|
override options: AddUserOptions;
|
||
|
db: Db;
|
||
|
username: string;
|
||
|
password?: string;
|
||
|
|
||
|
constructor(db: Db, username: string, password: string | undefined, options?: AddUserOptions) {
|
||
|
super(db, options);
|
||
|
|
||
|
this.db = db;
|
||
|
this.username = username;
|
||
|
this.password = password;
|
||
|
this.options = options ?? {};
|
||
|
}
|
||
|
|
||
|
override execute(
|
||
|
server: Server,
|
||
|
session: ClientSession | undefined,
|
||
|
callback: Callback<Document>
|
||
|
): void {
|
||
|
const db = this.db;
|
||
|
const username = this.username;
|
||
|
const password = this.password;
|
||
|
const options = this.options;
|
||
|
|
||
|
// Error out if digestPassword set
|
||
|
// v5 removed the digestPassword option from AddUserOptions but we still want to throw
|
||
|
// an error when digestPassword is provided.
|
||
|
if ('digestPassword' in options && options.digestPassword != null) {
|
||
|
return callback(
|
||
|
new MongoInvalidArgumentError(
|
||
|
'Option "digestPassword" not supported via addUser, use db.command(...) instead'
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
let roles;
|
||
|
if (!options.roles || (Array.isArray(options.roles) && options.roles.length === 0)) {
|
||
|
emitWarningOnce(
|
||
|
'Creating a user without roles is deprecated. Defaults to "root" if db is "admin" or "dbOwner" otherwise'
|
||
|
);
|
||
|
if (db.databaseName.toLowerCase() === 'admin') {
|
||
|
roles = ['root'];
|
||
|
} else {
|
||
|
roles = ['dbOwner'];
|
||
|
}
|
||
|
} else {
|
||
|
roles = Array.isArray(options.roles) ? options.roles : [options.roles];
|
||
|
}
|
||
|
|
||
|
let topology;
|
||
|
try {
|
||
|
topology = getTopology(db);
|
||
|
} catch (error) {
|
||
|
return callback(error);
|
||
|
}
|
||
|
|
||
|
const digestPassword = topology.lastHello().maxWireVersion >= 7;
|
||
|
|
||
|
let userPassword = password;
|
||
|
|
||
|
if (!digestPassword) {
|
||
|
// Use node md5 generator
|
||
|
const md5 = crypto.createHash('md5');
|
||
|
// Generate keys used for authentication
|
||
|
md5.update(`${username}:mongo:${password}`);
|
||
|
userPassword = md5.digest('hex');
|
||
|
}
|
||
|
|
||
|
// Build the command to execute
|
||
|
const command: Document = {
|
||
|
createUser: username,
|
||
|
customData: options.customData || {},
|
||
|
roles: roles,
|
||
|
digestPassword
|
||
|
};
|
||
|
|
||
|
// No password
|
||
|
if (typeof password === 'string') {
|
||
|
command.pwd = userPassword;
|
||
|
}
|
||
|
|
||
|
super.executeCommand(server, session, command, callback);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
defineAspects(AddUserOperation, [Aspect.WRITE_OPERATION]);
|