add_user.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import * as crypto from 'crypto';
  2. import type { Document } from '../bson';
  3. import type { Db } from '../db';
  4. import { MongoInvalidArgumentError } from '../error';
  5. import type { Server } from '../sdam/server';
  6. import type { ClientSession } from '../sessions';
  7. import { type Callback, emitWarningOnce, getTopology } from '../utils';
  8. import { CommandCallbackOperation, type CommandOperationOptions } from './command';
  9. import { Aspect, defineAspects } from './operation';
  10. /**
  11. * @public
  12. * @deprecated Use the createUser command directly instead.
  13. */
  14. export interface RoleSpecification {
  15. /**
  16. * A role grants privileges to perform sets of actions on defined resources.
  17. * A given role applies to the database on which it is defined and can grant access down to a collection level of granularity.
  18. */
  19. role: string;
  20. /** The database this user's role should effect. */
  21. db: string;
  22. }
  23. /**
  24. * @public
  25. * @deprecated Use the createUser command directly instead.
  26. */
  27. export interface AddUserOptions extends CommandOperationOptions {
  28. /** Roles associated with the created user */
  29. roles?: string | string[] | RoleSpecification | RoleSpecification[];
  30. /** Custom data associated with the user (only Mongodb 2.6 or higher) */
  31. customData?: Document;
  32. }
  33. /** @internal */
  34. export class AddUserOperation extends CommandCallbackOperation<Document> {
  35. override options: AddUserOptions;
  36. db: Db;
  37. username: string;
  38. password?: string;
  39. constructor(db: Db, username: string, password: string | undefined, options?: AddUserOptions) {
  40. super(db, options);
  41. this.db = db;
  42. this.username = username;
  43. this.password = password;
  44. this.options = options ?? {};
  45. }
  46. override executeCallback(
  47. server: Server,
  48. session: ClientSession | undefined,
  49. callback: Callback<Document>
  50. ): void {
  51. const db = this.db;
  52. const username = this.username;
  53. const password = this.password;
  54. const options = this.options;
  55. // Error out if digestPassword set
  56. // v5 removed the digestPassword option from AddUserOptions but we still want to throw
  57. // an error when digestPassword is provided.
  58. if ('digestPassword' in options && options.digestPassword != null) {
  59. return callback(
  60. new MongoInvalidArgumentError(
  61. 'Option "digestPassword" not supported via addUser, use db.command(...) instead'
  62. )
  63. );
  64. }
  65. let roles;
  66. if (!options.roles || (Array.isArray(options.roles) && options.roles.length === 0)) {
  67. emitWarningOnce(
  68. 'Creating a user without roles is deprecated. Defaults to "root" if db is "admin" or "dbOwner" otherwise'
  69. );
  70. if (db.databaseName.toLowerCase() === 'admin') {
  71. roles = ['root'];
  72. } else {
  73. roles = ['dbOwner'];
  74. }
  75. } else {
  76. roles = Array.isArray(options.roles) ? options.roles : [options.roles];
  77. }
  78. let topology;
  79. try {
  80. topology = getTopology(db);
  81. } catch (error) {
  82. return callback(error);
  83. }
  84. const digestPassword = topology.lastHello().maxWireVersion >= 7;
  85. let userPassword = password;
  86. if (!digestPassword) {
  87. // Use node md5 generator
  88. const md5 = crypto.createHash('md5');
  89. // Generate keys used for authentication
  90. md5.update(`${username}:mongo:${password}`);
  91. userPassword = md5.digest('hex');
  92. }
  93. // Build the command to execute
  94. const command: Document = {
  95. createUser: username,
  96. customData: options.customData || {},
  97. roles: roles,
  98. digestPassword
  99. };
  100. // No password
  101. if (typeof password === 'string') {
  102. command.pwd = userPassword;
  103. }
  104. super.executeCommandCallback(server, session, command, callback);
  105. }
  106. }
  107. defineAspects(AddUserOperation, [Aspect.WRITE_OPERATION]);