diff --git a/src/cmap/auth/mongo_credentials.ts b/src/cmap/auth/mongo_credentials.ts index 259147d5b07..9abe1ebbc5b 100644 --- a/src/cmap/auth/mongo_credentials.ts +++ b/src/cmap/auth/mongo_credentials.ts @@ -134,26 +134,6 @@ export class MongoCredentials { this.mechanism = options.mechanism || AuthMechanism.MONGODB_DEFAULT; this.mechanismProperties = options.mechanismProperties || {}; - if (this.mechanism.match(/MONGODB-AWS/i)) { - if (!this.username && process.env.AWS_ACCESS_KEY_ID) { - this.username = process.env.AWS_ACCESS_KEY_ID; - } - - if (!this.password && process.env.AWS_SECRET_ACCESS_KEY) { - this.password = process.env.AWS_SECRET_ACCESS_KEY; - } - - if ( - this.mechanismProperties.AWS_SESSION_TOKEN == null && - process.env.AWS_SESSION_TOKEN != null - ) { - this.mechanismProperties = { - ...this.mechanismProperties, - AWS_SESSION_TOKEN: process.env.AWS_SESSION_TOKEN - }; - } - } - if (this.mechanism === AuthMechanism.MONGODB_OIDC && !this.mechanismProperties.ALLOWED_HOSTS) { this.mechanismProperties = { ...this.mechanismProperties, diff --git a/test/integration/auth/mongodb_aws.test.ts b/test/integration/auth/mongodb_aws.test.ts index d7153cc1d86..8190acdd574 100644 --- a/test/integration/auth/mongodb_aws.test.ts +++ b/test/integration/auth/mongodb_aws.test.ts @@ -163,39 +163,132 @@ describe('MONGODB-AWS', function () { }); }); - context('when user supplies a credentials provider', function () { - let providerCount = 0; - - 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(); - } - }); + context('when using a custom credential provider', function () { + // NOTE: Logic for scenarios 1-6 is handled via the evergreen variant configs. + // Scenarios 1-6 from the previous section with a user provided AWS_CREDENTIAL_PROVIDER auth mechanism + // property. This credentials MAY be obtained from the default credential provider from the AWS SDK. + // If the default provider does not cover all scenarios above, those not covered MAY be skipped. + // In these tests the driver MUST also assert that the user provided credential provider was called + // in each test. This may be via a custom function or object that wraps the calls to the custom provider + // and asserts that it was called at least once. For test scenarios where the drivers tools scripts put + // the credentials in the MONGODB_URI, drivers MAY extract the credentials from the URI and return the AWS + // credentials directly from the custom provider instead of using the AWS SDK default provider. + context('1. Custom Credential Provider Authenticates', function () { + let providerCount = 0; - 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 + 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 will not use custom provider.'; + return this.skip(); } }); - const result = await client - .db('aws') - .collection('aws_test') - .estimatedDocumentCount() - .catch(error => error); + 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 + } + }); - expect(result).to.not.be.instanceOf(MongoServerError); - expect(result).to.be.a('number'); - expect(providerCount).to.be.greaterThan(0); + 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'); + expect(providerCount).to.be.greaterThan(0); + }); + }); + + context('2. Custom Credential Provider Authentication Precedence', function () { + // Create a MongoClient configured with AWS auth and credentials in the URI. + // Example: mongodb://:@localhost:27017/?authMechanism=MONGODB-AWS + // Configure a custom credential provider to pass valid AWS credentials. The provider must + // track if it was called. + // Expect authentication to succeed and the custom credential provider was not called. + context('Case 1: Credentials in URI Take Precedence', function () { + let providerCount = 0; + let provider; + + beforeEach(function () { + if (!client?.options.credentials.username) { + this.skipReason = 'Test only runs when credentials are present in the URI'; + return this.skip(); + } + const credentialProvider = AWSSDKCredentialProvider.awsSDK; + provider = async () => { + providerCount++; + return await credentialProvider.fromNodeProviderChain().apply(); + }; + }); + + it('authenticates with a user provided credentials provider', async function () { + client = this.configuration.newClient(process.env.MONGODB_URI, { + authMechanismProperties: { + AWS_CREDENTIAL_PROVIDER: provider + } + }); + + 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'); + expect(providerCount).to.equal(0); + }); + }); + + // Run this test in an environment with AWS credentials configured as environment variables + // (e.g. AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN) + // Create a MongoClient configured to use AWS auth. Example: mongodb://localhost:27017/?authMechanism=MONGODB-AWS. + // Configure a custom credential provider to pass valid AWS credentials. The provider must track if it was called. + // Expect authentication to succeed and the custom credential provider was called. + context('Case 2: Custom Provider Takes Precedence Over Environment Variables', function () { + let providerCount = 0; + let provider; + + beforeEach(function () { + if (client?.options.credentials.username || !process.env.AWS_ACCESS_KEY_ID) { + this.skipReason = 'Test only runs when credentials are present in the environment'; + return this.skip(); + } + const credentialProvider = AWSSDKCredentialProvider.awsSDK; + provider = async () => { + providerCount++; + return await credentialProvider.fromNodeProviderChain().apply(); + }; + }); + + it('authenticates with a user provided credentials provider', async function () { + client = this.configuration.newClient(process.env.MONGODB_URI, { + authMechanismProperties: { + AWS_CREDENTIAL_PROVIDER: provider + } + }); + + 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'); + expect(providerCount).to.be.greaterThan(0); + }); + }); }); }); @@ -218,7 +311,7 @@ describe('MONGODB-AWS', function () { .catch(error => error); expect(client).to.have.nested.property('s.authProviders'); - const provider = client.s.authProviders.getOrCreateProvider('MONGODB-AWS'); + const provider = client.s.authProviders.getOrCreateProvider('MONGODB-AWS', {}); expect(provider).to.be.instanceOf(MongoDBAWS); }); 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 078705d7b4b..4790ddf808b 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 @@ -102,4 +102,61 @@ describe('26. Custom AWS Credential Providers', metadata, () => { }); } ); + + // Ensure a valid AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are present in the environment. + // Create a MongoClient named setupClient. + // Create a ClientEncryption object with the following options: + // class ClientEncryptionOpts { + // keyVaultClient: , + // keyVaultNamespace: "keyvault.datakeys", + // kmsProviders: { "aws": {} }, + // credentialProviders: { "aws": } + // } + // Use the client encryption to create a datakey using the "aws" KMS provider. This should successfully load + // and use the AWS credentials that were provided by the secrets manager for the remote provider. Assert the + // datakey was created and that the custom credential provider was called at least once. + context( + 'Case 4: ClientEncryption with credentialProviders and valid environment variables', + metadata, + function () { + let clientEncryption; + let providerCount = 0; + let previousAccessKey; + let previousSecretKey; + + beforeEach(function () { + previousAccessKey = process.env.AWS_ACCESS_KEY_ID; + previousSecretKey = process.env.AWS_SECRET_ACCESS_KEY; + process.env.AWS_ACCESS_KEY_ID = process.env.FLE_AWS_KEY; + process.env.AWS_SECRET_ACCESS_KEY = process.env.FLE_AWS_SECRET; + + const options = { + keyVaultNamespace: 'keyvault.datakeys', + kmsProviders: { aws: {} }, + credentialProviders: { + aws: async () => { + providerCount++; + return { + accessKeyId: process.env.FLE_AWS_KEY, + secretAccessKey: process.env.FLE_AWS_SECRET + }; + } + }, + extraOptions: getEncryptExtraOptions() + }; + clientEncryption = new ClientEncryption(keyVaultClient, options); + }); + + afterEach(function () { + process.env.AWS_ACCESS_KEY_ID = previousAccessKey; + process.env.AWS_SECRET_ACCESS_KEY = previousSecretKey; + }); + + it('is successful', metadata, async function () { + const dk = await clientEncryption.createDataKey('aws', { masterKey }); + expect(dk).to.be.instanceOf(Binary); + expect(providerCount).to.be.greaterThan(0); + }); + } + ); });