mongo_credentials.js 8.6 KB

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