mongodb_aws.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.MongoDBAWS = void 0;
  4. const BSON = require("../../bson");
  5. const deps_1 = require("../../deps");
  6. const error_1 = require("../../error");
  7. const utils_1 = require("../../utils");
  8. const auth_provider_1 = require("./auth_provider");
  9. const aws_temporary_credentials_1 = require("./aws_temporary_credentials");
  10. const mongo_credentials_1 = require("./mongo_credentials");
  11. const providers_1 = require("./providers");
  12. const ASCII_N = 110;
  13. const bsonOptions = {
  14. useBigInt64: false,
  15. promoteLongs: true,
  16. promoteValues: true,
  17. promoteBuffers: false,
  18. bsonRegExp: false
  19. };
  20. class MongoDBAWS extends auth_provider_1.AuthProvider {
  21. constructor(credentialProvider) {
  22. super();
  23. this.credentialProvider = credentialProvider;
  24. this.credentialFetcher = aws_temporary_credentials_1.AWSTemporaryCredentialProvider.isAWSSDKInstalled
  25. ? new aws_temporary_credentials_1.AWSSDKCredentialProvider(credentialProvider)
  26. : new aws_temporary_credentials_1.LegacyAWSTemporaryCredentialProvider();
  27. }
  28. async auth(authContext) {
  29. const { connection } = authContext;
  30. if (!authContext.credentials) {
  31. throw new error_1.MongoMissingCredentialsError('AuthContext must provide credentials.');
  32. }
  33. if ('kModuleError' in deps_1.aws4) {
  34. throw deps_1.aws4['kModuleError'];
  35. }
  36. const { sign } = deps_1.aws4;
  37. if ((0, utils_1.maxWireVersion)(connection) < 9) {
  38. throw new error_1.MongoCompatibilityError('MONGODB-AWS authentication requires MongoDB version 4.4 or later');
  39. }
  40. if (!authContext.credentials.username) {
  41. authContext.credentials = await makeTempCredentials(authContext.credentials, this.credentialFetcher);
  42. }
  43. const { credentials } = authContext;
  44. const accessKeyId = credentials.username;
  45. const secretAccessKey = credentials.password;
  46. // Allow the user to specify an AWS session token for authentication with temporary credentials.
  47. const sessionToken = credentials.mechanismProperties.AWS_SESSION_TOKEN;
  48. // If all three defined, include sessionToken, else include username and pass, else no credentials
  49. const awsCredentials = accessKeyId && secretAccessKey && sessionToken
  50. ? { accessKeyId, secretAccessKey, sessionToken }
  51. : accessKeyId && secretAccessKey
  52. ? { accessKeyId, secretAccessKey }
  53. : undefined;
  54. const db = credentials.source;
  55. const nonce = await (0, utils_1.randomBytes)(32);
  56. // All messages between MongoDB clients and servers are sent as BSON objects
  57. // in the payload field of saslStart and saslContinue.
  58. const saslStart = {
  59. saslStart: 1,
  60. mechanism: 'MONGODB-AWS',
  61. payload: BSON.serialize({ r: nonce, p: ASCII_N }, bsonOptions)
  62. };
  63. const saslStartResponse = await connection.command((0, utils_1.ns)(`${db}.$cmd`), saslStart, undefined);
  64. const serverResponse = BSON.deserialize(saslStartResponse.payload.buffer, bsonOptions);
  65. const host = serverResponse.h;
  66. const serverNonce = serverResponse.s.buffer;
  67. if (serverNonce.length !== 64) {
  68. // TODO(NODE-3483)
  69. throw new error_1.MongoRuntimeError(`Invalid server nonce length ${serverNonce.length}, expected 64`);
  70. }
  71. if (!utils_1.ByteUtils.equals(serverNonce.subarray(0, nonce.byteLength), nonce)) {
  72. // throw because the serverNonce's leading 32 bytes must equal the client nonce's 32 bytes
  73. // https://github.com/mongodb/specifications/blob/master/source/auth/auth.md#conversation-5
  74. // TODO(NODE-3483)
  75. throw new error_1.MongoRuntimeError('Server nonce does not begin with client nonce');
  76. }
  77. if (host.length < 1 || host.length > 255 || host.indexOf('..') !== -1) {
  78. // TODO(NODE-3483)
  79. throw new error_1.MongoRuntimeError(`Server returned an invalid host: "${host}"`);
  80. }
  81. const body = 'Action=GetCallerIdentity&Version=2011-06-15';
  82. const options = sign({
  83. method: 'POST',
  84. host,
  85. region: deriveRegion(serverResponse.h),
  86. service: 'sts',
  87. headers: {
  88. 'Content-Type': 'application/x-www-form-urlencoded',
  89. 'Content-Length': body.length,
  90. 'X-MongoDB-Server-Nonce': utils_1.ByteUtils.toBase64(serverNonce),
  91. 'X-MongoDB-GS2-CB-Flag': 'n'
  92. },
  93. path: '/',
  94. body
  95. }, awsCredentials);
  96. const payload = {
  97. a: options.headers.Authorization,
  98. d: options.headers['X-Amz-Date']
  99. };
  100. if (sessionToken) {
  101. payload.t = sessionToken;
  102. }
  103. const saslContinue = {
  104. saslContinue: 1,
  105. conversationId: saslStartResponse.conversationId,
  106. payload: BSON.serialize(payload, bsonOptions)
  107. };
  108. await connection.command((0, utils_1.ns)(`${db}.$cmd`), saslContinue, undefined);
  109. }
  110. }
  111. exports.MongoDBAWS = MongoDBAWS;
  112. async function makeTempCredentials(credentials, awsCredentialFetcher) {
  113. function makeMongoCredentialsFromAWSTemp(creds) {
  114. // The AWS session token (creds.Token) may or may not be set.
  115. if (!creds.AccessKeyId || !creds.SecretAccessKey) {
  116. throw new error_1.MongoMissingCredentialsError('Could not obtain temporary MONGODB-AWS credentials');
  117. }
  118. return new mongo_credentials_1.MongoCredentials({
  119. username: creds.AccessKeyId,
  120. password: creds.SecretAccessKey,
  121. source: credentials.source,
  122. mechanism: providers_1.AuthMechanism.MONGODB_AWS,
  123. mechanismProperties: {
  124. AWS_SESSION_TOKEN: creds.Token
  125. }
  126. });
  127. }
  128. const temporaryCredentials = await awsCredentialFetcher.getCredentials();
  129. return makeMongoCredentialsFromAWSTemp(temporaryCredentials);
  130. }
  131. function deriveRegion(host) {
  132. const parts = host.split('.');
  133. if (parts.length === 1 || parts[1] === 'amazonaws') {
  134. return 'us-east-1';
  135. }
  136. return parts[1];
  137. }
  138. //# sourceMappingURL=mongodb_aws.js.map