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
2 changes: 1 addition & 1 deletion .evergreen/setup-mongodb-aws-auth-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ cd $DRIVERS_TOOLS/.evergreen/auth_aws
# Create a python virtual environment.
. ./activate-authawsvenv.sh
# Source the environment variables. Configure the environment and the server.
. aws_setup.sh $AWS_CREDENTIAL_TYPE
. aws_setup.sh --nouri $AWS_CREDENTIAL_TYPE

cd $BEFORE

Expand Down
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "drivers-evergreen-tools"]
path = drivers-evergreen-tools
url = https://github.com/mongodb-labs/drivers-evergreen-tools.git
url = https://github.com/mongodb-labs/drivers-evergreen-tools.git
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
"check:ts": "node ./node_modules/typescript/bin/tsc -v && node ./node_modules/typescript/bin/tsc --noEmit",
"check:atlas": "nyc mocha --config test/manual/mocharc.js test/manual/atlas_connectivity.test.ts",
"check:drivers-atlas-testing": "nyc mocha --config test/mocha_mongodb.js test/atlas/drivers_atlas_testing.test.ts",
"check:aws": "nyc mocha --config test/mocha_mongodb.js test/integration/auth/mongodb_aws.test.ts",
"check:aws": "nyc mocha --config test/mocha_mongodb.js test/integration/auth/mongodb_aws.test.ts test/integration/auth/mongodb_aws.prose.test.ts",
"check:oidc-auth": "nyc mocha --config test/mocha_mongodb.js test/integration/auth/auth.spec.test.ts",
"check:oidc-test": "nyc mocha --config test/mocha_mongodb.js test/integration/auth/mongodb_oidc.prose.test.ts",
"check:oidc-azure": "nyc mocha --config test/mocha_mongodb.js test/integration/auth/mongodb_oidc_azure.prose.05.test.ts",
Expand Down
1 change: 1 addition & 0 deletions src/cmap/auth/mongo_credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export interface AuthMechanismProperties extends Document {
SERVICE_NAME?: string;
SERVICE_REALM?: string;
CANONICALIZE_HOST_NAME?: GSSAPICanonicalizationValue;
/** @internal */
AWS_SESSION_TOKEN?: string;
/** A user provided OIDC machine callback function. */
OIDC_CALLBACK?: OIDCCallbackFunction;
Expand Down
10 changes: 4 additions & 6 deletions src/cmap/auth/mongodb_aws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,10 @@ export class MongoDBAWS extends AuthProvider {
);
}

if (!authContext.credentials.username) {
authContext.credentials = await makeTempCredentials(
authContext.credentials,
this.credentialFetcher
);
}
authContext.credentials = await makeTempCredentials(
authContext.credentials,
this.credentialFetcher
);

const { credentials } = authContext;

Expand Down
23 changes: 13 additions & 10 deletions src/connection_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ import { MongoCredentials } from './cmap/auth/mongo_credentials';
import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from './cmap/auth/providers';
import { Compressor, type CompressorName } from './cmap/wire_protocol/compression';
import { Encrypter } from './encrypter';
import {
MongoAPIError,
MongoInvalidArgumentError,
MongoMissingCredentialsError,
MongoParseError
} from './error';
import { MongoAPIError, MongoInvalidArgumentError, MongoParseError } from './error';
import {
MongoClient,
type MongoClientOptions,
Expand Down Expand Up @@ -417,10 +412,18 @@ export function parseOptions(
});
}

if (isAws && mongoOptions.credentials.username && !mongoOptions.credentials.password) {
throw new MongoMissingCredentialsError(
`When using ${mongoOptions.credentials.mechanism} password must be set when a username is specified`
);
if (isAws) {
const { username, password } = mongoOptions.credentials;
if (username || password) {
throw new MongoAPIError(
'username and password cannot be provided when using MONGODB-AWS. Credentials must be provided in a manner that can be read by the AWS SDK.'
);
}
if (mongoOptions.credentials.mechanismProperties.AWS_SESSION_TOKEN) {
throw new MongoAPIError(
'AWS_SESSION_TOKEN cannot be provided when using MONGODB-AWS. Credentials must be provided in a manner that can be read by the AWS SDK.'
);
}
}

mongoOptions.credentials.validate();
Expand Down
100 changes: 100 additions & 0 deletions test/integration/auth/mongodb_aws.prose.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as process from 'node:process';

import { expect } from 'chai';

import { AWSSDKCredentialProvider, type MongoClient, MongoServerError } from '../../mongodb';

const isMongoDBAWSAuthEnvironment = (process.env.MONGODB_URI ?? '').includes('MONGODB-AWS');

describe('MONGODB-AWS Prose Tests', function () {
let client: MongoClient;

beforeEach(function () {
if (!isMongoDBAWSAuthEnvironment) {
this.currentTest.skipReason = 'requires MONGODB_URI to contain MONGODB-AWS auth mechanism';
return this.skip();
}
});

afterEach(async () => {
await client?.close();
});

// 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
}
});

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 () {
// 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);
});
});
});
});
129 changes: 0 additions & 129 deletions test/integration/auth/mongodb_aws.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,135 +162,6 @@ describe('MONGODB-AWS', function () {
});
});

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;

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();
}
});

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
}
});

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);
});
});
});
});

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: '' }
Expand Down
11 changes: 3 additions & 8 deletions test/integration/node-specific/examples/aws_handler.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
// begin lambda connection
const { MongoClient } = require('mongodb');

// Get the URI for the cluster then set AWS_ACCESS_KEY_ID as the username in the
// URI and AWS_SECRET_ACCESS_KEY as the password, then set the appropriate auth
// options. Note that MongoClient now auto-connects so no need to store the connect()
// promise anywhere and reference it.
// Get the URI for the cluster and use AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
// from the environment, then set the appropriate auth options. Note that MongoClient
// now auto-connects so no need to store the connect() promise anywhere and reference it.
const client = new MongoClient(process.env.MONGODB_URI, {
auth: {
username: process.env.AWS_ACCESS_KEY_ID,
password: process.env.AWS_SECRET_ACCESS_KEY
},
authSource: '$external',
authMechanism: 'MONGODB-AWS'
});
Expand Down
15 changes: 15 additions & 0 deletions test/spec/auth/legacy/connection-string.json
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,21 @@
}
}
},
{
"description": "should throw an exception if username provided (MONGODB-AWS)",
"uri": "mongodb://[email protected]/?authMechanism=MONGODB-AWS",
"valid": false
},
{
"description": "should throw an exception if username and password provided (MONGODB-AWS)",
"uri": "mongodb://user:[email protected]/?authMechanism=MONGODB-AWS",
"valid": false
},
{
"description": "should throw an exception if AWS_SESSION_TOKEN provided (MONGODB-AWS)",
"uri": "mongodb://localhost/?authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:token",
"valid": false
},
{
"description": "should recognise the mechanism with test environment (MONGODB-OIDC)",
"uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test",
Expand Down
11 changes: 10 additions & 1 deletion test/spec/auth/legacy/connection-string.yml
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,15 @@ tests:
mechanism: MONGODB-AWS
mechanism_properties:
AWS_SESSION_TOKEN: token!@#$%^&*()_+
- description: should throw an exception if username provided (MONGODB-AWS)
uri: mongodb://[email protected]/?authMechanism=MONGODB-AWS
valid: false
- description: should throw an exception if username and password provided (MONGODB-AWS)
uri: mongodb://user:[email protected]/?authMechanism=MONGODB-AWS
valid: false
- description: should throw an exception if AWS_SESSION_TOKEN provided (MONGODB-AWS)
uri: mongodb://localhost/?authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:token
valid: false
- description: should recognise the mechanism with test environment (MONGODB-OIDC)
uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test
valid: true
Expand Down Expand Up @@ -468,4 +477,4 @@ tests:
(MONGODB-OIDC)
uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s
valid: false
credential: null
credential: null
Loading