Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions src/cmap/auth/mongo_credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
151 changes: 122 additions & 29 deletions test/integration/auth/mongodb_aws.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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://<AccessKeyId>:<SecretAccessKey>@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);
});
});
});
});

Expand All @@ -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);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: <setupClient>,
// keyVaultNamespace: "keyvault.datakeys",
// kmsProviders: { "aws": {} },
// credentialProviders: { "aws": <object/function that returns valid credentials from the secrets manager> }
// }
// 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);
});
}
);
});