Skip to content

Commit

Permalink
RUST-1659 Implement basic OIDC SASL support (#886)
Browse files Browse the repository at this point in the history
  • Loading branch information
abr-egn committed Jun 26, 2023
1 parent cbf69bc commit f1352b8
Show file tree
Hide file tree
Showing 11 changed files with 360 additions and 5 deletions.
63 changes: 63 additions & 0 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,48 @@ functions:
./test-contents/test-exe on_demand_gcp_credentials --nocapture"
$DRIVERS_TOOLS/.evergreen/csfle/gcpkms/run-command.sh
"bootstrap oidc":
- command: ec2.assume_role
params:
role_arn: ${aws_test_secrets_role}
- command: shell.exec
params:
working_dir: "src"
shell: bash
script: |
${PREPARE_SHELL}
cd ${DRIVERS_TOOLS}/.evergreen/auth_oidc
set +o xtrace
export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
export AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
export AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN}
export OIDC_TOKEN_DIR=/tmp/tokens
. ./activate-authoidcvenv.sh
python oidc_write_orchestration.py
python oidc_get_tokens.py
"setup oidc":
- command: shell.exec
params:
working_dir: "src"
shell: bash
script: |
${PREPARE_SHELL}
cd ${DRIVERS_TOOLS}/.evergreen/auth_oidc
mongosh setup_oidc.js
"run oidc tests":
- command: shell.exec
type: test
params:
working_dir: "src"
shell: bash
script: |
${PREPARE_SHELL}
export OIDC_TOKEN_DIR=/tmp/tokens
.evergreen/run-oidc-tests.sh
"compile only":
- command: shell.exec
Expand Down Expand Up @@ -1355,6 +1397,19 @@ tasks:
- func: "build and upload gcp kms test"
- func: "run gcp kms test"

- name: test-oidc-latest
commands:
- func: "bootstrap oidc"
- func: "install junit dependencies"
- func: "bootstrap mongo-orchestration"
vars:
AUTH: "auth"
ORCHESTRATION_FILE: "auth-oidc.json"
TOPOLOGY: "replica_set"
VERSION: "latest"
- func: "setup oidc"
- func: "run oidc tests"

- name: "compile-only"
commands:
- func: "compile only"
Expand Down Expand Up @@ -2022,6 +2077,14 @@ buildvariants:
- name: testgcpkms_task_group
batchtime: 20160

- matrix_name: "oidc"
display_name: "OIDC"
matrix_spec:
os:
- ubuntu-20.04
tasks:
- name: test-oidc-latest

- name: "lint"
display_name: "! Lint"
run_on:
Expand Down
23 changes: 23 additions & 0 deletions .evergreen/run-oidc-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
set -o errexit
set -o pipefail

source ./.evergreen/env.sh

set -o xtrace

OPTIONS="-- -Z unstable-options --format json --report-time"
CARGO_RESULT=0

cargo_test() {
RUST_BACKTRACE=1 \
cargo test $1 ${OPTIONS} | tee /dev/stderr | cargo2junit
CARGO_RESULT=$(( ${CARGO_RESULT} || $? ))
}

set +o errexit

cargo_test test::spec::oidc > prose.xml

junit-report-merger results.xml prose.xml

exit ${CARGO_RESULT}
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ serde = { version = ">= 0.0.0", features = ["rc"] }
serde_json = "1.0.64"
semver = "1.0.0"
time = "0.3.9"
tokio = { version = ">= 0.0.0", features = ["fs"] }
tracing-subscriber = "0.3.16"
regex = "1.6.0"

Expand Down
53 changes: 50 additions & 3 deletions src/client/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#[cfg(feature = "aws-auth")]
pub(crate) mod aws;
pub(crate) mod oidc;
mod plain;
mod sasl;
mod scram;
Expand All @@ -12,6 +13,7 @@ mod x509;

use std::{borrow::Cow, fmt::Debug, str::FromStr};

use derivative::Derivative;
use hmac::{digest::KeyInit, Mac};
use rand::Rng;
use serde::Deserialize;
Expand All @@ -32,6 +34,7 @@ const GSSAPI_STR: &str = "GSSAPI";
const MONGODB_AWS_STR: &str = "MONGODB-AWS";
const MONGODB_X509_STR: &str = "MONGODB-X509";
const PLAIN_STR: &str = "PLAIN";
const MONGODB_OIDC_STR: &str = "MONGODB-OIDC";

/// The authentication mechanisms supported by MongoDB.
///
Expand Down Expand Up @@ -83,6 +86,10 @@ pub enum AuthMechanism {
/// supports AWS authentication with the tokio runtime.
#[cfg(feature = "aws-auth")]
MongoDbAws,

/// MONGODB-OIDC authenticates using [OpenID Connect](https://openid.net/developers/specs/) access tokens. NOTE: this is not supported by the Rust driver.
// TODO RUST-1497: remove the NOTE.
MongoDbOidc,
}

impl AuthMechanism {
Expand Down Expand Up @@ -176,6 +183,33 @@ impl AuthMechanism {

Ok(())
}
AuthMechanism::MongoDbOidc => {
let is_automatic = credential
.mechanism_properties
.as_ref()
.map_or(false, |p| p.contains_key("PROVIDER_NAME"));
if credential.username.is_some() && is_automatic {
return Err(Error::invalid_argument(
"username and PROVIDER_NAME cannot both be specified for MONGODB-OIDC \
authentication",
));
}
if credential
.source
.as_ref()
.map_or(false, |s| s != "$external")
{
return Err(Error::invalid_argument(
"source must be $external for MONGODB-OIDC authentication",
));
}
if credential.password.is_some() {
return Err(Error::invalid_argument(
"password must not be set for MONGODB-OIDC authentication",
));
}
Ok(())
}
_ => Ok(()),
}
}
Expand All @@ -191,22 +225,23 @@ impl AuthMechanism {
AuthMechanism::Plain => PLAIN_STR,
#[cfg(feature = "aws-auth")]
AuthMechanism::MongoDbAws => MONGODB_AWS_STR,
AuthMechanism::MongoDbOidc => MONGODB_OIDC_STR,
}
}

/// Get the default authSource for a given mechanism depending on the database provided in the
/// connection string.
pub(crate) fn default_source<'a>(&'a self, uri_db: Option<&'a str>) -> &'a str {
// TODO: fill in others as they're implemented
match self {
AuthMechanism::ScramSha1 | AuthMechanism::ScramSha256 | AuthMechanism::MongoDbCr => {
uri_db.unwrap_or("admin")
}
AuthMechanism::MongoDbX509 => "$external",
AuthMechanism::Plain => uri_db.unwrap_or("$external"),
AuthMechanism::MongoDbOidc => "$external",
#[cfg(feature = "aws-auth")]
AuthMechanism::MongoDbAws => "$external",
_ => "",
AuthMechanism::Gssapi => "",
}
}

Expand All @@ -232,6 +267,7 @@ impl AuthMechanism {
x509::build_speculative_client_first(credential),
)))),
Self::Plain => Ok(None),
Self::MongoDbOidc => Ok(None),
#[cfg(feature = "aws-auth")]
AuthMechanism::MongoDbAws => Ok(None),
AuthMechanism::MongoDbCr => Err(ErrorKind::Authentication {
Expand Down Expand Up @@ -283,6 +319,9 @@ impl AuthMechanism {
.into(),
}
.into()),
AuthMechanism::MongoDbOidc => {
oidc::authenticate_stream(stream, credential, server_api).await
}
_ => Err(ErrorKind::Authentication {
message: format!("Authentication mechanism {:?} not yet implemented.", self),
}
Expand All @@ -302,6 +341,7 @@ impl FromStr for AuthMechanism {
MONGODB_X509_STR => Ok(AuthMechanism::MongoDbX509),
GSSAPI_STR => Ok(AuthMechanism::Gssapi),
PLAIN_STR => Ok(AuthMechanism::Plain),
MONGODB_OIDC_STR => Ok(AuthMechanism::MongoDbOidc),

#[cfg(feature = "aws-auth")]
MONGODB_AWS_STR => Ok(AuthMechanism::MongoDbAws),
Expand All @@ -325,7 +365,8 @@ impl FromStr for AuthMechanism {
///
/// Some fields (mechanism and source) may be omitted and will either be negotiated or assigned a
/// default value, depending on the values of other fields in the credential.
#[derive(Clone, Default, Deserialize, TypedBuilder, PartialEq)]
#[derive(Clone, Default, Deserialize, TypedBuilder, Derivative)]
#[derivative(PartialEq)]
#[builder(field_defaults(default, setter(into)))]
#[non_exhaustive]
pub struct Credential {
Expand All @@ -347,6 +388,12 @@ pub struct Credential {

/// Additional properties for the given mechanism.
pub mechanism_properties: Option<Document>,

/// The token callbacks for OIDC authentication.
/// TODO RUST-1497: make this `pub`
#[serde(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
pub(crate) oidc_callbacks: Option<oidc::Callbacks>,
}

impl Credential {
Expand Down
Loading

0 comments on commit f1352b8

Please sign in to comment.