mongo_credentials.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.MongoCredentials = exports.DEFAULT_ALLOWED_HOSTS = void 0;
  4. const error_1 = require("../../error");
  5. const gssapi_1 = require("./gssapi");
  6. const providers_1 = require("./providers");
  7. // https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst
  8. function getDefaultAuthMechanism(hello) {
  9. if (hello) {
  10. // If hello contains saslSupportedMechs, use scram-sha-256
  11. // if it is available, else scram-sha-1
  12. if (Array.isArray(hello.saslSupportedMechs)) {
  13. return hello.saslSupportedMechs.includes(providers_1.AuthMechanism.MONGODB_SCRAM_SHA256)
  14. ? providers_1.AuthMechanism.MONGODB_SCRAM_SHA256
  15. : providers_1.AuthMechanism.MONGODB_SCRAM_SHA1;
  16. }
  17. // Fallback to legacy selection method. If wire version >= 3, use scram-sha-1
  18. if (hello.maxWireVersion >= 3) {
  19. return providers_1.AuthMechanism.MONGODB_SCRAM_SHA1;
  20. }
  21. }
  22. // Default for wireprotocol < 3
  23. return providers_1.AuthMechanism.MONGODB_CR;
  24. }
  25. const ALLOWED_PROVIDER_NAMES = ['aws', 'azure'];
  26. const ALLOWED_HOSTS_ERROR = 'Auth mechanism property ALLOWED_HOSTS must be an array of strings.';
  27. /** @internal */
  28. exports.DEFAULT_ALLOWED_HOSTS = [
  29. '*.mongodb.net',
  30. '*.mongodb-dev.net',
  31. '*.mongodbgov.net',
  32. 'localhost',
  33. '127.0.0.1',
  34. '::1'
  35. ];
  36. /** Error for when the token audience is missing in the environment. */
  37. const TOKEN_AUDIENCE_MISSING_ERROR = 'TOKEN_AUDIENCE must be set in the auth mechanism properties when PROVIDER_NAME is azure.';
  38. /**
  39. * A representation of the credentials used by MongoDB
  40. * @public
  41. */
  42. class MongoCredentials {
  43. constructor(options) {
  44. this.username = options.username ?? '';
  45. this.password = options.password;
  46. this.source = options.source;
  47. if (!this.source && options.db) {
  48. this.source = options.db;
  49. }
  50. this.mechanism = options.mechanism || providers_1.AuthMechanism.MONGODB_DEFAULT;
  51. this.mechanismProperties = options.mechanismProperties || {};
  52. if (this.mechanism.match(/MONGODB-AWS/i)) {
  53. if (!this.username && process.env.AWS_ACCESS_KEY_ID) {
  54. this.username = process.env.AWS_ACCESS_KEY_ID;
  55. }
  56. if (!this.password && process.env.AWS_SECRET_ACCESS_KEY) {
  57. this.password = process.env.AWS_SECRET_ACCESS_KEY;
  58. }
  59. if (this.mechanismProperties.AWS_SESSION_TOKEN == null &&
  60. process.env.AWS_SESSION_TOKEN != null) {
  61. this.mechanismProperties = {
  62. ...this.mechanismProperties,
  63. AWS_SESSION_TOKEN: process.env.AWS_SESSION_TOKEN
  64. };
  65. }
  66. }
  67. if (this.mechanism === providers_1.AuthMechanism.MONGODB_OIDC && !this.mechanismProperties.ALLOWED_HOSTS) {
  68. this.mechanismProperties = {
  69. ...this.mechanismProperties,
  70. ALLOWED_HOSTS: exports.DEFAULT_ALLOWED_HOSTS
  71. };
  72. }
  73. Object.freeze(this.mechanismProperties);
  74. Object.freeze(this);
  75. }
  76. /** Determines if two MongoCredentials objects are equivalent */
  77. equals(other) {
  78. return (this.mechanism === other.mechanism &&
  79. this.username === other.username &&
  80. this.password === other.password &&
  81. this.source === other.source);
  82. }
  83. /**
  84. * If the authentication mechanism is set to "default", resolves the authMechanism
  85. * based on the server version and server supported sasl mechanisms.
  86. *
  87. * @param hello - A hello response from the server
  88. */
  89. resolveAuthMechanism(hello) {
  90. // If the mechanism is not "default", then it does not need to be resolved
  91. if (this.mechanism.match(/DEFAULT/i)) {
  92. return new MongoCredentials({
  93. username: this.username,
  94. password: this.password,
  95. source: this.source,
  96. mechanism: getDefaultAuthMechanism(hello),
  97. mechanismProperties: this.mechanismProperties
  98. });
  99. }
  100. return this;
  101. }
  102. validate() {
  103. if ((this.mechanism === providers_1.AuthMechanism.MONGODB_GSSAPI ||
  104. this.mechanism === providers_1.AuthMechanism.MONGODB_CR ||
  105. this.mechanism === providers_1.AuthMechanism.MONGODB_PLAIN ||
  106. this.mechanism === providers_1.AuthMechanism.MONGODB_SCRAM_SHA1 ||
  107. this.mechanism === providers_1.AuthMechanism.MONGODB_SCRAM_SHA256) &&
  108. !this.username) {
  109. throw new error_1.MongoMissingCredentialsError(`Username required for mechanism '${this.mechanism}'`);
  110. }
  111. if (this.mechanism === providers_1.AuthMechanism.MONGODB_OIDC) {
  112. if (this.username && this.mechanismProperties.PROVIDER_NAME) {
  113. throw new error_1.MongoInvalidArgumentError(`username and PROVIDER_NAME may not be used together for mechanism '${this.mechanism}'.`);
  114. }
  115. if (this.mechanismProperties.PROVIDER_NAME === 'azure' &&
  116. !this.mechanismProperties.TOKEN_AUDIENCE) {
  117. throw new error_1.MongoAzureError(TOKEN_AUDIENCE_MISSING_ERROR);
  118. }
  119. if (this.mechanismProperties.PROVIDER_NAME &&
  120. !ALLOWED_PROVIDER_NAMES.includes(this.mechanismProperties.PROVIDER_NAME)) {
  121. throw new error_1.MongoInvalidArgumentError(`Currently only a PROVIDER_NAME in ${ALLOWED_PROVIDER_NAMES.join(',')} is supported for mechanism '${this.mechanism}'.`);
  122. }
  123. if (this.mechanismProperties.REFRESH_TOKEN_CALLBACK &&
  124. !this.mechanismProperties.REQUEST_TOKEN_CALLBACK) {
  125. throw new error_1.MongoInvalidArgumentError(`A REQUEST_TOKEN_CALLBACK must be provided when using a REFRESH_TOKEN_CALLBACK for mechanism '${this.mechanism}'`);
  126. }
  127. if (!this.mechanismProperties.PROVIDER_NAME &&
  128. !this.mechanismProperties.REQUEST_TOKEN_CALLBACK) {
  129. throw new error_1.MongoInvalidArgumentError(`Either a PROVIDER_NAME or a REQUEST_TOKEN_CALLBACK must be specified for mechanism '${this.mechanism}'.`);
  130. }
  131. if (this.mechanismProperties.ALLOWED_HOSTS) {
  132. const hosts = this.mechanismProperties.ALLOWED_HOSTS;
  133. if (!Array.isArray(hosts)) {
  134. throw new error_1.MongoInvalidArgumentError(ALLOWED_HOSTS_ERROR);
  135. }
  136. for (const host of hosts) {
  137. if (typeof host !== 'string') {
  138. throw new error_1.MongoInvalidArgumentError(ALLOWED_HOSTS_ERROR);
  139. }
  140. }
  141. }
  142. }
  143. if (providers_1.AUTH_MECHS_AUTH_SRC_EXTERNAL.has(this.mechanism)) {
  144. if (this.source != null && this.source !== '$external') {
  145. // TODO(NODE-3485): Replace this with a MongoAuthValidationError
  146. throw new error_1.MongoAPIError(`Invalid source '${this.source}' for mechanism '${this.mechanism}' specified.`);
  147. }
  148. }
  149. if (this.mechanism === providers_1.AuthMechanism.MONGODB_PLAIN && this.source == null) {
  150. // TODO(NODE-3485): Replace this with a MongoAuthValidationError
  151. throw new error_1.MongoAPIError('PLAIN Authentication Mechanism needs an auth source');
  152. }
  153. if (this.mechanism === providers_1.AuthMechanism.MONGODB_X509 && this.password != null) {
  154. if (this.password === '') {
  155. Reflect.set(this, 'password', undefined);
  156. return;
  157. }
  158. // TODO(NODE-3485): Replace this with a MongoAuthValidationError
  159. throw new error_1.MongoAPIError(`Password not allowed for mechanism MONGODB-X509`);
  160. }
  161. const canonicalization = this.mechanismProperties.CANONICALIZE_HOST_NAME ?? false;
  162. if (!Object.values(gssapi_1.GSSAPICanonicalizationValue).includes(canonicalization)) {
  163. throw new error_1.MongoAPIError(`Invalid CANONICALIZE_HOST_NAME value: ${canonicalization}`);
  164. }
  165. }
  166. static merge(creds, options) {
  167. return new MongoCredentials({
  168. username: options.username ?? creds?.username ?? '',
  169. password: options.password ?? creds?.password ?? '',
  170. mechanism: options.mechanism ?? creds?.mechanism ?? providers_1.AuthMechanism.MONGODB_DEFAULT,
  171. mechanismProperties: options.mechanismProperties ?? creds?.mechanismProperties ?? {},
  172. source: options.source ?? options.db ?? creds?.source ?? 'admin'
  173. });
  174. }
  175. }
  176. exports.MongoCredentials = MongoCredentials;
  177. //# sourceMappingURL=mongo_credentials.js.map