Skip to content
Draft
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
26 changes: 19 additions & 7 deletions driver/src/client/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,25 @@ impl AuthMechanism {
}
#[cfg(feature = "aws-auth")]
AuthMechanism::MongoDbAws => {
if credential.username.is_some() && credential.password.is_none() {
return Err(ErrorKind::InvalidArgument {
message: "Username cannot be provided without password for MONGODB-AWS \
authentication"
.to_string(),
}
.into());
if credential.username.is_some() {
return Err(Error::invalid_argument(
"Username cannot be provided for MONGODB-AWS authentication",
));
}
if credential.password.is_some() {
return Err(Error::invalid_argument(
"Password cannot be provided for MONGODB-AWS authentication",
));
}
if credential
.mechanism_properties
.as_ref()
.map_or(false, |mp| mp.contains_key("AWS_SESSION_TOKEN"))
{
return Err(Error::invalid_argument(
"AWS_SESSION_TOKEN mechanism property cannot be provided for MONGODB-AWS \
authentication",
));
}

Ok(())
Expand Down
41 changes: 12 additions & 29 deletions driver/src/client/auth/aws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,35 +105,18 @@ pub(super) async fn authenticate_stream(
}

// Find credentials using MongoDB URI or AWS SDK
pub(crate) async fn get_aws_credentials(credential: &Credential) -> Result<Credentials> {
if let (Some(access_key), Some(secret_key)) = (&credential.username, &credential.password) {
// Look for credentials in the MongoDB URI
Ok(Credentials::new(
access_key.clone(),
secret_key.clone(),
credential
.mechanism_properties
.as_ref()
.and_then(|mp| mp.get_str("AWS_SESSION_TOKEN").ok())
.map(str::to_owned),
None,
"MongoDB URI",
))
} else {
// If credentials are not provided in the URI, use the AWS SDK to load
let config = aws_config::load_defaults(BehaviorVersion::latest()).await;
let creds = config
.credentials_provider()
.ok_or_else(|| {
Error::authentication_error(MECH_NAME, "no credential provider configured")
})?
.provide_credentials()
.await
.map_err(|e| {
Error::authentication_error(MECH_NAME, &format!("failed to get creds: {e}"))
})?;
Ok(creds)
}
pub(crate) async fn get_aws_credentials(_credential: &Credential) -> Result<Credentials> {
// Use the AWS SDK to load credentials
let config = aws_config::load_defaults(BehaviorVersion::latest()).await;
let creds = config
.credentials_provider()
.ok_or_else(|| Error::authentication_error(MECH_NAME, "no credential provider configured"))?
.provide_credentials()
.await
.map_err(|e| {
Error::authentication_error(MECH_NAME, &format!("failed to get creds: {e}"))
})?;
Ok(creds)
}

pub fn compute_aws_sigv4_payload(
Expand Down
15 changes: 15 additions & 0 deletions 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://user@localhost.com/?authMechanism=MONGODB-AWS",
"valid": false
},
{
"description": "should throw an exception if username and password provided (MONGODB-AWS)",
"uri": "mongodb://user:pass@localhost.com/?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
9 changes: 9 additions & 0 deletions 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://user@localhost.com/?authMechanism=MONGODB-AWS
valid: false
- description: should throw an exception if username and password provided (MONGODB-AWS)
uri: mongodb://user:pass@localhost.com/?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
46 changes: 37 additions & 9 deletions spec/auth/mongodb-aws.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Drivers MUST test the following scenarios:

1. `Regular Credentials`: Auth via an `ACCESS_KEY_ID` and `SECRET_ACCESS_KEY` pair
1. `Regular Credentials`: Auth via an `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` pair
2. `EC2 Credentials`: Auth from an EC2 instance via temporary credentials assigned to the machine
3. `ECS Credentials`: Auth from an ECS instance via temporary credentials assigned to the task
4. `Assume Role`: Auth via temporary credentials obtained from an STS AssumeRole request
Expand All @@ -12,22 +12,50 @@ Drivers MUST test the following scenarios:
7. Caching of AWS credentials fetched by the driver.

For brevity, this section gives the values `<AccessKeyId>`, `<SecretAccessKey>` and `<Token>` in place of a valid access
key ID, secret access key and session token (also known as a security token). Note that if these values are passed into
the URI they MUST be URL encoded. Sample values are below.
key ID, secret access key and session token (also known as a security token). Sample values are below.

```text
AccessKeyId=AKIAI44QH8DHBEXAMPLE
SecretAccessKey=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Token=AQoDYXdzEJr...<remainder of security token>
```

## Testing custom credential providers

If the driver supports custom AWS credential providers, the driver MUST test the following:

### 1. Custom Credential Provider Authenticates

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.

### 2. Custom Credential Provider Authentication Precedence

#### Case 1: Credentials in URI Take Precedence *Removed*

#### Case 2: Custom Provider Takes Precedence Over Environment Variables

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.

## Regular credentials

Drivers MUST be able to authenticate by providing a valid access key id and secret access key pair as the username and
password, respectively, in the MongoDB URI. An example of a valid URI would be:
Drivers MUST be able to authenticate when a valid access key id and secret access key pair are present in the
environment. Drivers MUST provide the --nouri option to aws_tester.py in drivers-evergreen-tools for this test.

```text
mongodb://<AccessKeyId>:<SecretAccessKey>@localhost/?authMechanism=MONGODB-AWS
mongodb://localhost/?authMechanism=MONGODB-AWS
```

## EC2 Credentials
Expand Down Expand Up @@ -59,11 +87,11 @@ mongodb://localhost/?authMechanism=MONGODB-AWS
## AssumeRole

Drivers MUST be able to authenticate using temporary credentials returned from an assume role request. These temporary
credentials consist of an access key ID, a secret access key, and a security token passed into the URI. A sample URI
would be:
credentials consist of an access key ID, a secret access key, and a security token present in the environment. Drivers
MUST provide the --nouri option to aws_tester.py in drivers-evergreen-tools for this test. A sample URI would be:

```text
mongodb://<AccessKeyId>:<SecretAccessKey>@localhost/?authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:<Token>
mongodb://localhost/?authMechanism=MONGODB-AWS
```

## Assume Role with Web Identity
Expand Down
53 changes: 32 additions & 21 deletions spec/auth/mongodb-oidc.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,25 @@ ______________________________________________________________________

## Machine Authentication Flow Prose Tests

Drivers MUST run the machine prose tests when `OIDC_TOKEN_DIR` is set. Drivers can either set the `ENVIRONMENT:test`
auth mechanism property, or use a custom callback that also reads the file.
Drivers MUST run these tests in all supported OIDC environments:

Drivers can also choose to run the machine prose tests on GCP or Azure VMs, or on the Kubernetes clusters.
- A callback that reads the token file for `ENVIRONMENT:test`. A callback enables testing additional behaviors. Tests
and assertions limited to a callback are noted with `[callback-only]`.
- `ENVIRONMENT:test`
- `ENVIRONMENT:gcp`
- `ENVIRONMENT:azure`
- `ENVIRONMENT:k8s`

The token file `ENVIRONMENT:test` is located in `OIDC_TOKEN_DIR` set by
[drivers-evergreen-tools](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/README.md)
scripts.

Drivers MUST implement all prose tests in this section. Unless otherwise noted, all `MongoClient` instances MUST be
configured with `retryReads=false`.

> [!NOTE]
> For test cases that create fail points, drivers MUST either use a unique `appName` or explicitly remove the fail point
> callback to prevent interaction between test cases.
> For test cases that create fail points, drivers MUST use a unique `appName` to prevent interaction with other
> environment processes, and explicitly remove the fail point to prevent interaction between test runs.

After setting up your OIDC
[environment](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/README.md),
Expand All @@ -40,17 +48,17 @@ source the `secrets-export.sh` file and use the associated env variables in your

- Create an OIDC configured client.
- Perform a `find` operation that succeeds.
- Assert that the callback was called 1 time.
- `[callback-only]` Assert that the callback was called 1 time.
- Close the client.

**1.2 Callback is called once for multiple connections**

- Create an OIDC configured client.
- Start 10 threads and run 100 `find` operations in each thread that all succeed.
- Assert that the callback was called 1 time.
- `[callback-only]` Assert that the callback was called 1 time.
- Close the client.

### (2) OIDC Callback Validation
### (2) `[callback-only]` OIDC Callback Validation

**2.1 Valid Callback Inputs**

Expand Down Expand Up @@ -91,10 +99,10 @@ source the `secrets-export.sh` file and use the associated env variables in your
- Create an OIDC configured client.
- Poison the *Client Cache* with an invalid access token.
- Perform a `find` operation that succeeds.
- Assert that the callback was called 1 time.
- `[callback-only]` Assert that the callback was called 1 time.
- Close the client.

**3.2 Authentication failures without cached tokens return an error**
**3.2 `[callback-only]` Authentication failures without cached tokens return an error**

- Create an OIDC configured client with an OIDC callback that always returns invalid access tokens.
- Perform a `find` operation that fails.
Expand All @@ -103,7 +111,7 @@ source the `secrets-export.sh` file and use the associated env variables in your

**3.3 Unexpected error code does not clear the cache**

- Create a `MongoClient` with an OIDC callback that returns a valid token.
- Create an OIDC configured client.
- Set a fail point for `saslStart` commands of the form:

```javascript
Expand All @@ -122,9 +130,9 @@ source the `secrets-export.sh` file and use the associated env variables in your
```

- Perform a `find` operation that fails.
- Assert that the callback has been called once.
- `[callback-only]` Assert that the callback has been called once.
- Perform a `find` operation that succeeds.
- Assert that the callback has been called once.
- `[callback-only]` Assert that the callback has been called once.
- Close the client.

### (4) Reauthentication
Expand All @@ -150,10 +158,11 @@ source the `secrets-export.sh` file and use the associated env variables in your
```

- Perform a `find` operation that succeeds.
- Assert that the callback was called 2 times (once during the connection handshake, and again during reauthentication).
- `[callback-only]` Assert that the callback was called 2 times (once during the connection handshake, and again during
reauthentication).
- Close the client.

#### 4.2 Read Commands Fail If Reauthentication Fails
#### `[callback-only]` 4.2 Read Commands Fail If Reauthentication Fails

- Create a `MongoClient` whose OIDC callback returns one good token and then bad tokens after the first call.
- Perform a `find` operation that succeeds.
Expand All @@ -178,7 +187,7 @@ source the `secrets-export.sh` file and use the associated env variables in your
- Assert that the callback was called 2 times.
- Close the client.

#### 4.3 Write Commands Fail If Reauthentication Fails
#### `[callback-only]` 4.3 Write Commands Fail If Reauthentication Fails

- Create a `MongoClient` whose OIDC callback returns one good token and then bad tokens after the first call.
- Perform an `insert` operation that succeeds.
Expand All @@ -199,17 +208,18 @@ source the `secrets-export.sh` file and use the associated env variables in your
}
```

- Perform a `find` operation that fails.
- Perform a `insert` operation that fails.
- Assert that the callback was called 2 times.
- Close the client.

#### 4.4 Speculative Authentication should be ignored on Reauthentication

- Create an OIDC configured client.
- Populate the *Client Cache* with a valid access token to enforce Speculative Authentication.
- This may be done by authenticating a temporary OIDC configured client and copying the cached token.
- Perform an `insert` operation that succeeds.
- Assert that the callback was not called.
- Assert there were no `SaslStart` commands executed.
- Assert there were no `saslStart` commands executed.
- Set a fail point for `insert` commands of the form:

```javascript
Expand All @@ -228,8 +238,8 @@ source the `secrets-export.sh` file and use the associated env variables in your
```

- Perform an `insert` operation that succeeds.
- Assert that the callback was called once.
- Assert there were `SaslStart` commands executed.
- `[callback-only]` Assert that the callback was called once.
- Assert there were `saslStart` commands executed.
- Close the client.

#### 4.5 Reauthentication Succeeds when a Session is involved
Expand All @@ -254,7 +264,8 @@ source the `secrets-export.sh` file and use the associated env variables in your

- Start a new session.
- In the started session perform a `find` operation that succeeds.
- Assert that the callback was called 2 times (once during the connection handshake, and again during reauthentication).
- `[callback-only]` Assert that the callback was called 2 times (once during the connection handshake, and again during
reauthentication).
- Close the session and the client.

## (5) Azure Tests
Expand Down