diff --git a/.evergreen/config.in.yml b/.evergreen/config.in.yml index bae7dede80..c9eb51a755 100644 --- a/.evergreen/config.in.yml +++ b/.evergreen/config.in.yml @@ -422,7 +422,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -440,7 +439,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -458,7 +456,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -477,7 +474,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -495,7 +491,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -513,7 +508,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -532,7 +526,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -549,7 +542,6 @@ functions: params: include_expansions_in_env: - DRIVERS_TOOLS - - MONGODB_AWS_SDK - PROJECT_DIRECTORY - MONGODB_BINARIES - AWS_ACCESS_KEY_ID @@ -597,7 +589,6 @@ functions: - AWS_SESSION_TOKEN env: AWS_CREDENTIAL_TYPE: env-creds - MONGODB_AWS_SDK: "true" working_dir: "src" binary: bash args: diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 7fafbd1d63..5908cde138 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -377,7 +377,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -394,7 +393,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -411,7 +409,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -429,7 +426,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -446,7 +442,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -463,7 +458,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -481,7 +475,6 @@ functions: include_expansions_in_env: - MONGODB_URI - DRIVERS_TOOLS - - MONGODB_AWS_SDK - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN @@ -497,7 +490,6 @@ functions: params: include_expansions_in_env: - DRIVERS_TOOLS - - MONGODB_AWS_SDK - PROJECT_DIRECTORY - MONGODB_BINARIES - AWS_ACCESS_KEY_ID @@ -542,7 +534,6 @@ functions: - AWS_SESSION_TOKEN env: AWS_CREDENTIAL_TYPE: env-creds - MONGODB_AWS_SDK: 'true' working_dir: src binary: bash args: @@ -1660,7 +1651,6 @@ tasks: - {key: AUTH, value: auth} - {key: ORCHESTRATION_FILE, value: auth-aws.json} - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'true'} - func: install dependencies - func: bootstrap mongo-orchestration - func: assume secrets manager role @@ -1675,7 +1665,6 @@ tasks: - {key: AUTH, value: auth} - {key: ORCHESTRATION_FILE, value: auth-aws.json} - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'true'} - func: install dependencies - func: bootstrap mongo-orchestration - func: assume secrets manager role @@ -1690,7 +1679,6 @@ tasks: - {key: AUTH, value: auth} - {key: ORCHESTRATION_FILE, value: auth-aws.json} - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'true'} - func: install dependencies - func: bootstrap mongo-orchestration - func: assume secrets manager role @@ -1705,7 +1693,6 @@ tasks: - {key: AUTH, value: auth} - {key: ORCHESTRATION_FILE, value: auth-aws.json} - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'true'} - func: install dependencies - func: bootstrap mongo-orchestration - func: assume secrets manager role @@ -1720,7 +1707,6 @@ tasks: - {key: AUTH, value: auth} - {key: ORCHESTRATION_FILE, value: auth-aws.json} - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'true'} - func: install dependencies - func: bootstrap mongo-orchestration - func: assume secrets manager role @@ -1735,7 +1721,6 @@ tasks: - {key: AUTH, value: auth} - {key: ORCHESTRATION_FILE, value: auth-aws.json} - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'true'} - func: install dependencies - func: bootstrap mongo-orchestration - func: assume secrets manager role @@ -1750,7 +1735,6 @@ tasks: - {key: AUTH, value: auth} - {key: ORCHESTRATION_FILE, value: auth-aws.json} - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'true'} - func: install dependencies - func: bootstrap mongo-orchestration - func: assume secrets manager role @@ -1765,87 +1749,10 @@ tasks: - {key: AUTH, value: auth} - {key: ORCHESTRATION_FILE, value: auth-aws.json} - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'true'} - func: install dependencies - func: bootstrap mongo-orchestration - func: assume secrets manager role - func: run aws auth test AssumeRoleWithWebIdentity with AWS_ROLE_SESSION_NAME set - - name: aws-latest-auth-test-run-aws-auth-test-with-regular-aws-credentials-no-peer-dependencies - commands: - - command: expansions.update - type: setup - params: - updates: - - {key: VERSION, value: latest} - - {key: AUTH, value: auth} - - {key: ORCHESTRATION_FILE, value: auth-aws.json} - - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'false'} - - func: install dependencies - - func: bootstrap mongo-orchestration - - func: assume secrets manager role - - func: run aws auth test with regular aws credentials - - name: aws-latest-auth-test-run-aws-auth-test-with-assume-role-credentials-no-peer-dependencies - commands: - - command: expansions.update - type: setup - params: - updates: - - {key: VERSION, value: latest} - - {key: AUTH, value: auth} - - {key: ORCHESTRATION_FILE, value: auth-aws.json} - - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'false'} - - func: install dependencies - - func: bootstrap mongo-orchestration - - func: assume secrets manager role - - func: run aws auth test with assume role credentials - - name: aws-latest-auth-test-run-aws-auth-test-with-aws-credentials-as-environment-variables-no-peer-dependencies - commands: - - command: expansions.update - type: setup - params: - updates: - - {key: VERSION, value: latest} - - {key: AUTH, value: auth} - - {key: ORCHESTRATION_FILE, value: auth-aws.json} - - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'false'} - - func: install dependencies - - func: bootstrap mongo-orchestration - - func: assume secrets manager role - - func: run aws auth test with aws credentials as environment variables - - name: >- - aws-latest-auth-test-run-aws-auth-test-with-aws-credentials-and-session-token-as-environment-variables-no-peer-dependencies - commands: - - command: expansions.update - type: setup - params: - updates: - - {key: VERSION, value: latest} - - {key: AUTH, value: auth} - - {key: ORCHESTRATION_FILE, value: auth-aws.json} - - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'false'} - - func: install dependencies - - func: bootstrap mongo-orchestration - - func: assume secrets manager role - - func: run aws auth test with aws credentials and session token as environment variables - - name: aws-latest-auth-test-run-aws-ECS-auth-test-no-peer-dependencies - commands: - - command: expansions.update - type: setup - params: - updates: - - {key: VERSION, value: latest} - - {key: AUTH, value: auth} - - {key: ORCHESTRATION_FILE, value: auth-aws.json} - - {key: TOPOLOGY, value: server} - - {key: MONGODB_AWS_SDK, value: 'false'} - - func: install dependencies - - func: bootstrap mongo-orchestration - - func: assume secrets manager role - - func: run aws ECS auth test - name: run-spec-benchmark-tests-node-server tags: - run-spec-benchmark-tests @@ -3567,12 +3474,6 @@ buildvariants: - aws-latest-auth-test-run-aws-ECS-auth-test - aws-latest-auth-test-run-aws-auth-test-AssumeRoleWithWebIdentity-with-AWS_ROLE_SESSION_NAME-unset - aws-latest-auth-test-run-aws-auth-test-AssumeRoleWithWebIdentity-with-AWS_ROLE_SESSION_NAME-set - - aws-latest-auth-test-run-aws-auth-test-with-regular-aws-credentials-no-peer-dependencies - - aws-latest-auth-test-run-aws-auth-test-with-assume-role-credentials-no-peer-dependencies - - aws-latest-auth-test-run-aws-auth-test-with-aws-credentials-as-environment-variables-no-peer-dependencies - - >- - aws-latest-auth-test-run-aws-auth-test-with-aws-credentials-and-session-token-as-environment-variables-no-peer-dependencies - - aws-latest-auth-test-run-aws-ECS-auth-test-no-peer-dependencies - name: ubuntu2204-test-atlas-data-lake display_name: Atlas Data Lake Tests run_on: ubuntu2204-large diff --git a/.evergreen/generate_evergreen_tasks.js b/.evergreen/generate_evergreen_tasks.js index a26908a8ee..6341ff1267 100644 --- a/.evergreen/generate_evergreen_tasks.js +++ b/.evergreen/generate_evergreen_tasks.js @@ -342,14 +342,8 @@ for (const VERSION of AWS_AUTH_VERSIONS) { { func: 'run aws auth test with aws credentials as environment variables' }, { func: 'run aws auth test with aws credentials and session token as environment variables' }, { func: 'run aws ECS auth test' }, - { - func: 'run aws auth test AssumeRoleWithWebIdentity with AWS_ROLE_SESSION_NAME unset', - onlySdk: true - }, - { - func: 'run aws auth test AssumeRoleWithWebIdentity with AWS_ROLE_SESSION_NAME set', - onlySdk: true - } + { func: 'run aws auth test AssumeRoleWithWebIdentity with AWS_ROLE_SESSION_NAME unset' }, + { func: 'run aws auth test AssumeRoleWithWebIdentity with AWS_ROLE_SESSION_NAME set' } ]; const awsTasks = awsFuncs.map(fn => ({ @@ -359,8 +353,7 @@ for (const VERSION of AWS_AUTH_VERSIONS) { VERSION, AUTH: 'auth', ORCHESTRATION_FILE: 'auth-aws.json', - TOPOLOGY: 'server', - MONGODB_AWS_SDK: 'true' + TOPOLOGY: 'server' }), { func: 'install dependencies' }, { func: 'bootstrap mongo-orchestration' }, @@ -369,29 +362,8 @@ for (const VERSION of AWS_AUTH_VERSIONS) { ] })); - const awsNoPeerDependenciesTasks = awsFuncs - .filter(fn => fn.onlySdk !== true) - .map(fn => ({ - name: `${name(fn.func)}-no-peer-dependencies`, - commands: [ - updateExpansions({ - VERSION: VERSION, - AUTH: 'auth', - ORCHESTRATION_FILE: 'auth-aws.json', - TOPOLOGY: 'server', - MONGODB_AWS_SDK: 'false' - }), - { func: 'install dependencies' }, - { func: 'bootstrap mongo-orchestration' }, - { func: 'assume secrets manager role' }, - { func: fn.func } - ] - })); - - const allAwsTasks = awsTasks.concat(awsNoPeerDependenciesTasks); - - TASKS.push(...allAwsTasks); - AWS_AUTH_TASKS.push(...allAwsTasks.map(t => t.name)); + TASKS.push(...awsTasks); + AWS_AUTH_TASKS.push(...awsTasks.map(t => t.name)); } const BUILD_VARIANTS = []; diff --git a/.evergreen/prepare-mongodb-aws-ecs-auth.sh b/.evergreen/prepare-mongodb-aws-ecs-auth.sh index 2de4215c1a..e9ef2bb0c7 100755 --- a/.evergreen/prepare-mongodb-aws-ecs-auth.sh +++ b/.evergreen/prepare-mongodb-aws-ecs-auth.sh @@ -10,8 +10,6 @@ mkdir -p $ECS_SRC_DIR/.evergreen set -ex # write test file -echo "export MONGODB_AWS_SDK=$MONGODB_AWS_SDK" >>$PROJECT_DIRECTORY/.evergreen/run-mongodb-aws-ecs-test.sh -echo "if [ $MONGODB_AWS_SDK = 'false' ]; then rm -rf ./node_modules/@aws-sdk/credential-providers; fi" >>$PROJECT_DIRECTORY/.evergreen/run-mongodb-aws-ecs-test.sh echo "npm run check:aws" >>$PROJECT_DIRECTORY/.evergreen/run-mongodb-aws-ecs-test.sh # copy test file to AWS ecs test directory diff --git a/.evergreen/setup-mongodb-aws-auth-tests.sh b/.evergreen/setup-mongodb-aws-auth-tests.sh index 79ab66e55b..0d91583d04 100644 --- a/.evergreen/setup-mongodb-aws-auth-tests.sh +++ b/.evergreen/setup-mongodb-aws-auth-tests.sh @@ -8,7 +8,6 @@ set +x if [ -z ${MONGODB_URI+omitted} ]; then echo "MONGODB_URI is unset" && exit 1; fi if [ -z ${DRIVERS_TOOLS+omitted} ]; then echo "DRIVERS_TOOLS is unset" && exit 1; fi if [ -z ${AWS_CREDENTIAL_TYPE+omitted} ]; then echo "AWS_CREDENTIAL_TYPE is unset" && exit 1; fi -if [ -z ${MONGODB_AWS_SDK+omitted} ]; then echo "MONGODB_AWS_SDK is unset" && exit 1; fi bash $DRIVERS_TOOLS/.evergreen/auth_aws/setup-secrets.sh @@ -25,7 +24,5 @@ cd $BEFORE npm install --no-save aws4 -if [ $MONGODB_AWS_SDK = 'false' ]; then rm -rf ./node_modules/@aws-sdk/credential-providers; fi - # revert to show test output set -x diff --git a/src/cmap/auth/aws_temporary_credentials.ts b/src/cmap/auth/aws_temporary_credentials.ts index baa1a64fc8..8c1c9ff0eb 100644 --- a/src/cmap/auth/aws_temporary_credentials.ts +++ b/src/cmap/auth/aws_temporary_credentials.ts @@ -1,10 +1,5 @@ import { type AWSCredentials, getAwsCredentialProvider } from '../../deps'; import { MongoAWSError } from '../../error'; -import { request } from '../../utils'; - -const AWS_RELATIVE_URI = 'http://169.254.170.2'; -const AWS_EC2_URI = 'http://169.254.169.254'; -const AWS_EC2_PATH = '/latest/meta-data/iam/security-credentials'; /** * @internal @@ -24,26 +19,9 @@ export interface AWSTempCredentials { /** @public **/ export type AWSCredentialProvider = () => Promise; -/** - * @internal - * - * Fetches temporary AWS credentials. - */ -export abstract class AWSTemporaryCredentialProvider { - abstract getCredentials(): Promise; - private static _awsSDK: ReturnType; - protected static get awsSDK() { - AWSTemporaryCredentialProvider._awsSDK ??= getAwsCredentialProvider(); - return AWSTemporaryCredentialProvider._awsSDK; - } - - static get isAWSSDKInstalled(): boolean { - return !('kModuleError' in AWSTemporaryCredentialProvider.awsSDK); - } -} - /** @internal */ -export class AWSSDKCredentialProvider extends AWSTemporaryCredentialProvider { +export class AWSSDKCredentialProvider { + private static _awsSDK: ReturnType; private _provider?: AWSCredentialProvider; /** @@ -51,20 +29,23 @@ export class AWSSDKCredentialProvider extends AWSTemporaryCredentialProvider { * @param credentialsProvider - The credentials provider. */ constructor(credentialsProvider?: AWSCredentialProvider) { - super(); - if (credentialsProvider) { this._provider = credentialsProvider; } } + static get awsSDK() { + AWSSDKCredentialProvider._awsSDK ??= getAwsCredentialProvider(); + return AWSSDKCredentialProvider._awsSDK; + } + /** * The AWS SDK caches credentials automatically and handles refresh when the credentials have expired. * To ensure this occurs, we need to cache the `provider` returned by the AWS sdk and re-use it when fetching credentials. */ private get provider(): () => Promise { - if ('kModuleError' in AWSTemporaryCredentialProvider.awsSDK) { - throw AWSTemporaryCredentialProvider.awsSDK.kModuleError; + if ('kModuleError' in AWSSDKCredentialProvider.awsSDK) { + throw AWSSDKCredentialProvider.awsSDK.kModuleError; } if (this._provider) { return this._provider; @@ -112,15 +93,15 @@ export class AWSSDKCredentialProvider extends AWSTemporaryCredentialProvider { this._provider = awsRegionSettingsExist && useRegionalSts - ? AWSTemporaryCredentialProvider.awsSDK.fromNodeProviderChain({ + ? AWSSDKCredentialProvider.awsSDK.fromNodeProviderChain({ clientConfig: { region: AWS_REGION } }) - : AWSTemporaryCredentialProvider.awsSDK.fromNodeProviderChain(); + : AWSSDKCredentialProvider.awsSDK.fromNodeProviderChain(); return this._provider; } - override async getCredentials(): Promise { + async getCredentials(): Promise { /* * Creates a credential provider that will attempt to find credentials from the * following sources (listed in order of precedence): @@ -144,42 +125,3 @@ export class AWSSDKCredentialProvider extends AWSTemporaryCredentialProvider { } } } - -/** - * @internal - * Fetches credentials manually (without the AWS SDK), as outlined in the [Obtaining Credentials](https://github.com/mongodb/specifications/blob/master/source/auth/auth.md#obtaining-credentials) - * section of the Auth spec. - */ -export class LegacyAWSTemporaryCredentialProvider extends AWSTemporaryCredentialProvider { - override async getCredentials(): Promise { - // If the environment variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI - // is set then drivers MUST assume that it was set by an AWS ECS agent - if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) { - return await request( - `${AWS_RELATIVE_URI}${process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}` - ); - } - - // Otherwise assume we are on an EC2 instance - - // get a token - const token = await request(`${AWS_EC2_URI}/latest/api/token`, { - method: 'PUT', - json: false, - headers: { 'X-aws-ec2-metadata-token-ttl-seconds': 30 } - }); - - // get role name - const roleName = await request(`${AWS_EC2_URI}/${AWS_EC2_PATH}`, { - json: false, - headers: { 'X-aws-ec2-metadata-token': token } - }); - - // get temp credentials - const creds = await request(`${AWS_EC2_URI}/${AWS_EC2_PATH}/${roleName}`, { - headers: { 'X-aws-ec2-metadata-token': token } - }); - - return creds; - } -} diff --git a/src/cmap/auth/mongodb_aws.ts b/src/cmap/auth/mongodb_aws.ts index 9cb22c82ca..d8bb29886d 100644 --- a/src/cmap/auth/mongodb_aws.ts +++ b/src/cmap/auth/mongodb_aws.ts @@ -11,9 +11,7 @@ import { type AuthContext, AuthProvider } from './auth_provider'; import { type AWSCredentialProvider, AWSSDKCredentialProvider, - type AWSTempCredentials, - AWSTemporaryCredentialProvider, - LegacyAWSTemporaryCredentialProvider + type AWSTempCredentials } from './aws_temporary_credentials'; import { MongoCredentials } from './mongo_credentials'; import { AuthMechanism } from './providers'; @@ -34,16 +32,11 @@ interface AWSSaslContinuePayload { } export class MongoDBAWS extends AuthProvider { - private credentialFetcher: AWSTemporaryCredentialProvider; - private credentialProvider?: AWSCredentialProvider; + private credentialFetcher: AWSSDKCredentialProvider; constructor(credentialProvider?: AWSCredentialProvider) { super(); - - this.credentialProvider = credentialProvider; - this.credentialFetcher = AWSTemporaryCredentialProvider.isAWSSDKInstalled - ? new AWSSDKCredentialProvider(credentialProvider) - : new LegacyAWSTemporaryCredentialProvider(); + this.credentialFetcher = new AWSSDKCredentialProvider(credentialProvider); } override async auth(authContext: AuthContext): Promise { @@ -162,7 +155,7 @@ export class MongoDBAWS extends AuthProvider { async function makeTempCredentials( credentials: MongoCredentials, - awsCredentialFetcher: AWSTemporaryCredentialProvider + awsCredentialFetcher: AWSSDKCredentialProvider ): Promise { function makeMongoCredentialsFromAWSTemp(creds: AWSTempCredentials) { // The AWS session token (creds.Token) may or may not be set. diff --git a/test/integration/auth/mongodb_aws.test.ts b/test/integration/auth/mongodb_aws.test.ts index e65b9c60bd..d7153cc1d8 100644 --- a/test/integration/auth/mongodb_aws.test.ts +++ b/test/integration/auth/mongodb_aws.test.ts @@ -8,7 +8,7 @@ import * as sinon from 'sinon'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { refreshKMSCredentials } from '../../../src/client-side-encryption/providers'; import { - AWSTemporaryCredentialProvider, + AWSSDKCredentialProvider, type CommandOptions, Connection, type Document, @@ -18,6 +18,7 @@ import { type MongoDBNamespace, type MongoDBResponseConstructor, MongoMissingCredentialsError, + MongoMissingDependencyError, MongoServerError, setDifference } from '../../mongodb'; @@ -25,7 +26,6 @@ import { const isMongoDBAWSAuthEnvironment = (process.env.MONGODB_URI ?? '').includes('MONGODB-AWS'); describe('MONGODB-AWS', function () { - let awsSdkPresent; let client: MongoClient; beforeEach(function () { @@ -33,249 +33,272 @@ describe('MONGODB-AWS', function () { this.currentTest.skipReason = 'requires MONGODB_URI to contain MONGODB-AWS auth mechanism'; return this.skip(); } - - const { MONGODB_AWS_SDK = 'unset' } = process.env; - expect( - ['true', 'false'], - `Always inform the AWS tests if they run with or without the SDK (MONGODB_AWS_SDK=${MONGODB_AWS_SDK})` - ).to.include(MONGODB_AWS_SDK); - - awsSdkPresent = AWSTemporaryCredentialProvider.isAWSSDKInstalled; - expect( - awsSdkPresent, - MONGODB_AWS_SDK === 'true' - ? 'expected aws sdk to be installed' - : 'expected aws sdk to not be installed' - ).to.be[MONGODB_AWS_SDK]; }); afterEach(async () => { await client?.close(); }); - it('should authorize when successfully authenticated', async function () { - client = this.configuration.newClient(process.env.MONGODB_URI); // use the URI built by the test environment - - const result = await client - .db('aws') - .collection('aws_test') - .estimatedDocumentCount() - .catch(error => error); - - expect(result).to.not.be.instanceOf(MongoServerError); - expect(result).to.be.a('number'); - }); - - describe('ConversationId', function () { - let commandStub: sinon.SinonStub< - [ - ns: MongoDBNamespace, - command: Document, - options?: CommandOptions, - responseType?: MongoDBResponseConstructor - ], - Promise - >; - - let saslStartResult, saslContinue; - + context('when the AWS SDK is not present', function () { beforeEach(function () { - // spy on connection.command, filter for saslStart and saslContinue commands - commandStub = sinon.stub(Connection.prototype, 'command').callsFake(async function ( - ns: MongoDBNamespace, - command: Document, - options: CommandOptions, - responseType?: MongoDBResponseConstructor - ) { - if (command.saslContinue != null) { - saslContinue = { ...command }; + AWSSDKCredentialProvider.awsSDK['kModuleError'] = new MongoMissingDependencyError( + 'Missing dependency @aws-sdk/credential-providers', + { + cause: new Error(), + dependencyName: '@aws-sdk/credential-providers' } + ); + }); - const result = await commandStub.wrappedMethod.call( - this, - ns, - command, - options, - responseType - ); + afterEach(function () { + delete AWSSDKCredentialProvider.awsSDK['kModuleError']; + }); - if (command.saslStart != null) { - // Modify the result of the saslStart to check if the saslContinue uses it - result.conversationId = 999; - saslStartResult = { ...result }; + describe('when attempting AWS auth', function () { + it('throws an error', async function () { + client = this.configuration.newClient(process.env.MONGODB_URI); // use the URI built by the test environment + + const result = await client + .db('aws') + .collection('aws_test') + .estimatedDocumentCount() + .catch(e => e); + + // TODO(NODE-7046): Remove branch when removing support for AWS credentials in URI. + // The drivers tools scripts put the credentials in the URI currently for some environments, + // this will need to change when doing the DRIVERS-3131 work. + if (!client.options.credentials.username) { + expect(result).to.be.instanceof(MongoAWSError); + expect(result.message).to.match(/credential-providers/); + } else { + expect(result).to.equal(0); } - - return result; }); }); + }); - afterEach(function () { - commandStub.restore(); - sinon.restore(); - }); - - it('should use conversationId returned by saslStart in saslContinue', async function () { + context('when the AWS SDK is present', function () { + it('should authorize when successfully authenticated', async function () { client = this.configuration.newClient(process.env.MONGODB_URI); // use the URI built by the test environment - const err = await client + const result = await client .db('aws') .collection('aws_test') .estimatedDocumentCount() - .catch(e => e); + .catch(error => error); - // Expecting the saslContinue to fail since we changed the conversationId - expect(err).to.be.instanceof(MongoServerError); - expect(err.message).to.match(/Mismatched conversation id/); + expect(result).to.not.be.instanceOf(MongoServerError); + expect(result).to.be.a('number'); + }); - expect(saslStartResult).to.not.be.undefined; - expect(saslContinue).to.not.be.undefined; + describe('ConversationId', function () { + let commandStub: sinon.SinonStub< + [ + ns: MongoDBNamespace, + command: Document, + options?: CommandOptions, + responseType?: MongoDBResponseConstructor + ], + Promise + >; - expect(saslStartResult).to.have.property('conversationId', 999); + let saslStartResult, saslContinue; - expect(saslContinue).to.have.property('conversationId').equal(saslStartResult.conversationId); - }); - }); + beforeEach(function () { + // spy on connection.command, filter for saslStart and saslContinue commands + commandStub = sinon.stub(Connection.prototype, 'command').callsFake(async function ( + ns: MongoDBNamespace, + command: Document, + options: CommandOptions, + responseType?: MongoDBResponseConstructor + ) { + if (command.saslContinue != null) { + saslContinue = { ...command }; + } - context('when user supplies a credentials provider', function () { - let providerCount = 0; + const result = await commandStub.wrappedMethod.call( + this, + ns, + command, + options, + responseType + ); - beforeEach(function () { - if (!awsSdkPresent) { - this.skipReason = 'only relevant to AssumeRoleWithWebIdentity with SDK installed'; - return this.skip(); - } - // If we have a username the credentials have been set from the URI, options, or environment - // variables per the auth spec stated order. - if (client.options.credentials.username) { - this.skipReason = 'Credentials in the URI on env variables will not use custom provider.'; - return this.skip(); - } - }); + if (command.saslStart != null) { + // Modify the result of the saslStart to check if the saslContinue uses it + result.conversationId = 999; + saslStartResult = { ...result }; + } - it('authenticates with a user provided credentials provider', async function () { - // @ts-expect-error We intentionally access a protected variable. - const credentialProvider = AWSTemporaryCredentialProvider.awsSDK; - const provider = async () => { - providerCount++; - return await credentialProvider.fromNodeProviderChain().apply(); - }; - client = this.configuration.newClient(process.env.MONGODB_URI, { - authMechanismProperties: { - AWS_CREDENTIAL_PROVIDER: provider - } + return result; + }); }); - const result = await client - .db('aws') - .collection('aws_test') - .estimatedDocumentCount() - .catch(error => error); + afterEach(function () { + commandStub.restore(); + sinon.restore(); + }); - expect(result).to.not.be.instanceOf(MongoServerError); - expect(result).to.be.a('number'); - expect(providerCount).to.be.greaterThan(0); - }); - }); + it('should use conversationId returned by saslStart in saslContinue', async function () { + client = this.configuration.newClient(process.env.MONGODB_URI); // use the URI built by the test environment + + const err = await client + .db('aws') + .collection('aws_test') + .estimatedDocumentCount() + .catch(e => e); + + // Expecting the saslContinue to fail since we changed the conversationId + expect(err).to.be.instanceof(MongoServerError); + expect(err.message).to.match(/Mismatched conversation id/); + + expect(saslStartResult).to.not.be.undefined; + expect(saslContinue).to.not.be.undefined; - it('should allow empty string in authMechanismProperties.AWS_SESSION_TOKEN to override AWS_SESSION_TOKEN environment variable', function () { - client = this.configuration.newClient(this.configuration.url(), { - authMechanismProperties: { AWS_SESSION_TOKEN: '' } + expect(saslStartResult).to.have.property('conversationId', 999); + + expect(saslContinue) + .to.have.property('conversationId') + .equal(saslStartResult.conversationId); + }); }); - expect(client) - .to.have.nested.property('options.credentials.mechanismProperties.AWS_SESSION_TOKEN') - .that.equals(''); - }); - it('should store a MongoDBAWS provider instance per client', async function () { - client = this.configuration.newClient(process.env.MONGODB_URI); + context('when user supplies a credentials provider', function () { + let providerCount = 0; - await client - .db('aws') - .collection('aws_test') - .estimatedDocumentCount() - .catch(error => error); + beforeEach(function () { + // If we have a username the credentials have been set from the URI, options, or environment + // variables per the auth spec stated order. + if (client.options.credentials.username) { + this.skipReason = 'Credentials in the URI on env variables will not use custom provider.'; + return this.skip(); + } + }); - expect(client).to.have.nested.property('s.authProviders'); - const provider = client.s.authProviders.getOrCreateProvider('MONGODB-AWS'); - expect(provider).to.be.instanceOf(MongoDBAWS); - }); + it('authenticates with a user provided credentials provider', async function () { + const credentialProvider = AWSSDKCredentialProvider.awsSDK; + const provider = async () => { + providerCount++; + return await credentialProvider.fromNodeProviderChain().apply(); + }; + client = this.configuration.newClient(process.env.MONGODB_URI, { + authMechanismProperties: { + AWS_CREDENTIAL_PROVIDER: provider + } + }); - describe('with missing aws token', () => { - let awsSessionToken: string | undefined; + const result = await client + .db('aws') + .collection('aws_test') + .estimatedDocumentCount() + .catch(error => error); - beforeEach(() => { - awsSessionToken = process.env.AWS_SESSION_TOKEN; - delete process.env.AWS_SESSION_TOKEN; + expect(result).to.not.be.instanceOf(MongoServerError); + expect(result).to.be.a('number'); + expect(providerCount).to.be.greaterThan(0); + }); }); - afterEach(() => { - if (awsSessionToken != null) { - process.env.AWS_SESSION_TOKEN = awsSessionToken; - } + it('should allow empty string in authMechanismProperties.AWS_SESSION_TOKEN to override AWS_SESSION_TOKEN environment variable', function () { + client = this.configuration.newClient(this.configuration.url(), { + authMechanismProperties: { AWS_SESSION_TOKEN: '' } + }); + expect(client) + .to.have.nested.property('options.credentials.mechanismProperties.AWS_SESSION_TOKEN') + .that.equals(''); }); - it('should not throw an exception when aws token is missing', async function () { + it('should store a MongoDBAWS provider instance per client', async function () { client = this.configuration.newClient(process.env.MONGODB_URI); - const result = await client + await client .db('aws') .collection('aws_test') .estimatedDocumentCount() .catch(error => error); - // We check only for the MongoMissingCredentialsError - // and do check for the MongoServerError as the error or numeric result - // that can be returned depending on different types of environments - // getting credentials from different sources. - expect(result).to.not.be.instanceOf(MongoMissingCredentialsError); + expect(client).to.have.nested.property('s.authProviders'); + const provider = client.s.authProviders.getOrCreateProvider('MONGODB-AWS'); + expect(provider).to.be.instanceOf(MongoDBAWS); }); - }); - describe('EC2 with missing credentials', () => { - let client; + describe('with missing aws token', () => { + let awsSessionToken: string | undefined; - beforeEach(function () { - if (!process.env.IS_EC2) { - this.currentTest.skipReason = 'requires an AWS EC2 environment'; - this.skip(); - } - sinon.stub(http, 'request').callsFake(function (...args) { - // We pass in a legacy object that has the same properties as a URL - // but it is not an instanceof URL. - expect(args[0]).to.be.an('object'); - if (typeof args[0] === 'object') { - args[0].hostname = 'www.example.com'; - args[0].port = '81'; + beforeEach(() => { + awsSessionToken = process.env.AWS_SESSION_TOKEN; + delete process.env.AWS_SESSION_TOKEN; + }); + + afterEach(() => { + if (awsSessionToken != null) { + process.env.AWS_SESSION_TOKEN = awsSessionToken; } - return http.request.wrappedMethod.apply(this, args); }); - }); - afterEach(async () => { - sinon.restore(); - await client?.close(); + it('should not throw an exception when aws token is missing', async function () { + client = this.configuration.newClient(process.env.MONGODB_URI); + + const result = await client + .db('aws') + .collection('aws_test') + .estimatedDocumentCount() + .catch(error => error); + + // We check only for the MongoMissingCredentialsError + // and do check for the MongoServerError as the error or numeric result + // that can be returned depending on different types of environments + // getting credentials from different sources. + expect(result).to.not.be.instanceOf(MongoMissingCredentialsError); + }); }); - it('should respect the default timeout of 10000ms', async function () { - const config = this.configuration; - client = config.newClient(process.env.MONGODB_URI, { authMechanism: 'MONGODB-AWS' }); // use the URI built by the test environment - const startTime = performance.now(); + describe('EC2 with missing credentials', () => { + let client; - const caughtError = await client - .db() - .command({ ping: 1 }) - .catch(error => error); + beforeEach(function () { + if (!process.env.IS_EC2) { + this.currentTest.skipReason = 'requires an AWS EC2 environment'; + this.skip(); + } + sinon.stub(http, 'request').callsFake(function (...args) { + // We pass in a legacy object that has the same properties as a URL + // but it is not an instanceof URL. + expect(args[0]).to.be.an('object'); + if (typeof args[0] === 'object') { + args[0].hostname = 'www.example.com'; + args[0].port = '81'; + } + return http.request.wrappedMethod.apply(this, args); + }); + }); + + afterEach(async () => { + sinon.restore(); + await client?.close(); + }); - const endTime = performance.now(); - const timeTaken = endTime - startTime; - expect(caughtError).to.be.instanceOf(MongoAWSError); - expect(caughtError) - .property('message') - .match(/(timed out after)|(Could not load credentials)/); - // Credentials provider from the SDK does not allow to configure the timeout - // and defaults to 2 seconds - so we ensure this timeout happens below 12s - // instead of the 10s-12s range previously. - expect(timeTaken).to.be.below(12000); + it('should respect the default timeout of 10000ms', async function () { + const config = this.configuration; + client = config.newClient(process.env.MONGODB_URI, { authMechanism: 'MONGODB-AWS' }); // use the URI built by the test environment + const startTime = performance.now(); + + const caughtError = await client + .db() + .command({ ping: 1 }) + .catch(error => error); + + const endTime = performance.now(); + const timeTaken = endTime - startTime; + expect(caughtError).to.be.instanceOf(MongoAWSError); + expect(caughtError) + .property('message') + .match(/(timed out after)|(Could not load credentials)/); + // Credentials provider from the SDK does not allow to configure the timeout + // and defaults to 2 seconds - so we ensure this timeout happens below 12s + // instead of the 10s-12s range previously. + expect(timeTaken).to.be.below(12000); + }); }); }); @@ -356,10 +379,7 @@ describe('MONGODB-AWS', function () { const envCheck = () => { const { AWS_WEB_IDENTITY_TOKEN_FILE = '' } = process.env; - return ( - AWS_WEB_IDENTITY_TOKEN_FILE.length === 0 || - !AWSTemporaryCredentialProvider.isAWSSDKInstalled - ); + return AWS_WEB_IDENTITY_TOKEN_FILE.length === 0; }; beforeEach(function () { @@ -369,8 +389,7 @@ describe('MONGODB-AWS', function () { return this.skip(); } - // @ts-expect-error We intentionally access a protected variable. - credentialProvider = AWSTemporaryCredentialProvider.awsSDK; + credentialProvider = AWSSDKCredentialProvider.awsSDK; storedEnv = process.env; if (test.env.AWS_STS_REGIONAL_ENDPOINTS === undefined) { @@ -387,7 +406,7 @@ describe('MONGODB-AWS', function () { numberOfFromNodeProviderChainCalls = 0; // @ts-expect-error We intentionally access a protected variable. - AWSTemporaryCredentialProvider._awsSDK = { + AWSSDKCredentialProvider._awsSDK = { fromNodeProviderChain(...args) { calledArguments = args; numberOfFromNodeProviderChainCalls += 1; @@ -409,7 +428,7 @@ describe('MONGODB-AWS', function () { process.env.AWS_REGION = storedEnv.AWS_REGION; } // @ts-expect-error We intentionally access a protected variable. - AWSTemporaryCredentialProvider._awsSDK = credentialProvider; + AWSSDKCredentialProvider._awsSDK = credentialProvider; calledArguments = []; }); @@ -440,75 +459,73 @@ describe('MONGODB-AWS', function () { }); } }); -}); -describe('AWS KMS Credential Fetching', function () { - context('when the AWS SDK is not installed', function () { - beforeEach(function () { - this.currentTest.skipReason = !isMongoDBAWSAuthEnvironment - ? 'Test must run in an AWS auth testing environment' - : AWSTemporaryCredentialProvider.isAWSSDKInstalled - ? 'This test must run in an environment where the AWS SDK is not installed.' - : undefined; - this.currentTest?.skipReason && this.skip(); - }); - it('fetching AWS KMS credentials throws an error', async function () { - const error = await refreshKMSCredentials({ aws: {} }).catch(e => e); - expect(error).to.be.instanceOf(MongoAWSError); - }); - }); + describe('AWS KMS Credential Fetching', function () { + context('when the AWS SDK is not installed', function () { + beforeEach(function () { + AWSSDKCredentialProvider.awsSDK['kModuleError'] = new MongoMissingDependencyError( + 'Missing dependency @aws-sdk/credential-providers', + { + cause: new Error(), + dependencyName: '@aws-sdk/credential-providers' + } + ); + }); - context('when the AWS SDK is installed', function () { - beforeEach(function () { - this.currentTest.skipReason = !isMongoDBAWSAuthEnvironment - ? 'Test must run in an AWS auth testing environment' - : !AWSTemporaryCredentialProvider.isAWSSDKInstalled - ? 'This test must run in an environment where the AWS SDK is installed.' - : undefined; - this.currentTest?.skipReason && this.skip(); - }); + afterEach(function () { + delete AWSSDKCredentialProvider.awsSDK['kModuleError']; + }); - context('when a credential provider is not provided', function () { - it('KMS credentials are successfully fetched.', async function () { - const { aws } = await refreshKMSCredentials({ aws: {} }); + it('fetching AWS KMS credentials throws an error', async function () { + const result = await refreshKMSCredentials({ aws: {} }).catch(e => e); - expect(aws).to.have.property('accessKeyId'); - expect(aws).to.have.property('secretAccessKey'); + expect(result).to.be.instanceof(MongoAWSError); + expect(result.message).to.match(/credential-providers/); }); }); - context('when a credential provider is provided', function () { - let credentialProvider; - let providerCount = 0; + context('when the AWS SDK is installed', function () { + context('when a credential provider is not provided', function () { + it('KMS credentials are successfully fetched.', async function () { + const { aws } = await refreshKMSCredentials({ aws: {} }); - beforeEach(function () { - // @ts-expect-error We intentionally access a protected variable. - const provider = AWSTemporaryCredentialProvider.awsSDK; - credentialProvider = async () => { - providerCount++; - return await provider.fromNodeProviderChain().apply(); - }; + expect(aws).to.have.property('accessKeyId'); + expect(aws).to.have.property('secretAccessKey'); + }); }); - it('KMS credentials are successfully fetched.', async function () { - const { aws } = await refreshKMSCredentials({ aws: {} }, { aws: credentialProvider }); + context('when a credential provider is provided', function () { + let credentialProvider; + let providerCount = 0; - expect(aws).to.have.property('accessKeyId'); - expect(aws).to.have.property('secretAccessKey'); - expect(providerCount).to.be.greaterThan(0); + beforeEach(function () { + const provider = AWSSDKCredentialProvider.awsSDK; + credentialProvider = async () => { + providerCount++; + return await provider.fromNodeProviderChain().apply(); + }; + }); + + it('KMS credentials are successfully fetched.', async function () { + const { aws } = await refreshKMSCredentials({ aws: {} }, { aws: credentialProvider }); + + expect(aws).to.have.property('accessKeyId'); + expect(aws).to.have.property('secretAccessKey'); + expect(providerCount).to.be.greaterThan(0); + }); }); - }); - it('does not return any extra keys for the `aws` credential provider', async function () { - const { aws } = await refreshKMSCredentials({ aws: {} }); + it('does not return any extra keys for the `aws` credential provider', async function () { + const { aws } = await refreshKMSCredentials({ aws: {} }); - const keys = new Set(Object.keys(aws ?? {})); - const allowedKeys = ['accessKeyId', 'secretAccessKey', 'sessionToken']; + const keys = new Set(Object.keys(aws ?? {})); + const allowedKeys = ['accessKeyId', 'secretAccessKey', 'sessionToken']; - expect( - Array.from(setDifference(keys, allowedKeys)), - 'received an unexpected key in the response refreshing KMS credentials' - ).to.deep.equal([]); + expect( + Array.from(setDifference(keys, allowedKeys)), + 'received an unexpected key in the response refreshing KMS credentials' + ).to.deep.equal([]); + }); }); }); }); diff --git a/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts b/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts index 59e32946ba..078705d7b4 100644 --- a/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts +++ b/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts @@ -2,7 +2,7 @@ import { expect } from 'chai'; /* eslint-disable @typescript-eslint/no-restricted-imports */ import { ClientEncryption } from '../../../src/client-side-encryption/client_encryption'; -import { AWSTemporaryCredentialProvider, Binary, MongoClient } from '../../mongodb'; +import { AWSSDKCredentialProvider, Binary, MongoClient } from '../../mongodb'; import { getEncryptExtraOptions } from '../../tools/utils'; const metadata: MongoDBMetadataUI = { @@ -22,14 +22,8 @@ describe('26. Custom AWS Credential Providers', metadata, () => { let credentialProvider; beforeEach(async function () { - this.currentTest.skipReason = !AWSTemporaryCredentialProvider.isAWSSDKInstalled - ? 'This test must run in an environment where the AWS SDK is installed.' - : undefined; - this.currentTest?.skipReason && this.skip(); - keyVaultClient = this.configuration.newClient(process.env.MONGODB_UR); - // @ts-expect-error We intentionally access a protected variable. - credentialProvider = AWSTemporaryCredentialProvider.awsSDK; + credentialProvider = AWSSDKCredentialProvider.awsSDK; }); afterEach(async () => {