Skip to content

Commit 537f555

Browse files
RUST-1665 Run OIDC unified tests (#1329)
1 parent e76044c commit 537f555

File tree

9 files changed

+195
-39
lines changed

9 files changed

+195
-39
lines changed

.evergreen/build-static-test-tarball.sh

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ set -o pipefail
55

66
source ./.evergreen/env.sh
77

8+
rm -rf test_files && mkdir test_files
9+
cp ${TEST_FILES}/* test_files
10+
811
export RUSTFLAGS="-C target-feature=+crt-static"
912
cargo test ${BUILD_FEATURES} --target x86_64-unknown-linux-gnu get_exe_name
1013
TEST_BINARY=$(cat exe_name.txt)
1114
TEST_TARBALL="/tmp/mongo-rust-driver.tar.gz"
12-
tar czvf ${TEST_TARBALL} ${TEST_BINARY} ./.evergreen
15+
tar czvf ${TEST_TARBALL} ${TEST_BINARY} ./.evergreen test_files
1316

1417
cat <<EOT > static-test-tarball-expansion.yml
1518
STATIC_TEST_BINARY: ${TEST_BINARY}

.evergreen/config.yml

+5
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,7 @@ tasks:
11691169
- func: "build static test tarball"
11701170
vars:
11711171
BUILD_FEATURES: "--features azure-oidc"
1172+
TEST_FILES: "${PROJECT_DIRECTORY}/src/test/spec/json/auth/unified"
11721173
- command: subprocess.exec
11731174
type: test
11741175
params:
@@ -1185,6 +1186,7 @@ tasks:
11851186
- func: "build static test tarball"
11861187
vars:
11871188
BUILD_FEATURES: "--features gcp-oidc"
1189+
TEST_FILES: "${PROJECT_DIRECTORY}/src/test/spec/json/auth/unified"
11881190
- command: subprocess.exec
11891191
type: test
11901192
params:
@@ -1199,6 +1201,8 @@ tasks:
11991201
- name: "oidc-auth-test-k8s-latest"
12001202
commands:
12011203
- func: "build static test tarball"
1204+
vars:
1205+
TEST_FILES: "${PROJECT_DIRECTORY}/src/test/spec/json/auth/unified"
12021206
- command: ec2.assume_role
12031207
params:
12041208
role_arn: ${aws_test_secrets_role}
@@ -1927,6 +1931,7 @@ functions:
19271931
include_expansions_in_env:
19281932
- PROJECT_DIRECTORY
19291933
- BUILD_FEATURES
1934+
- TEST_FILES
19301935
- command: expansions.update
19311936
params:
19321937
file: src/static-test-tarball-expansion.yml

src/client/auth.rs

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ pub enum AuthMechanism {
9090
MongoDbAws,
9191

9292
/// MONGODB-OIDC authenticates using [OpenID Connect](https://openid.net/developers/specs/) access tokens.
93+
#[serde(alias = "MONGODB-OIDC")]
9394
MongoDbOidc,
9495
}
9596

src/test.rs

+46-2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub(crate) use self::{
4848
},
4949
};
5050

51+
use futures::FutureExt;
5152
use home::home_dir;
5253
use once_cell::sync::Lazy;
5354
use tokio::sync::OnceCell;
@@ -61,7 +62,13 @@ use crate::{
6162
options::{ServerApi, ServerApiVersion},
6263
},
6364
hello::HelloCommandResponse,
64-
options::{ClientOptions, ServerAddress},
65+
options::{
66+
oidc::{Callback, IdpServerResponse},
67+
AuthMechanism,
68+
ClientOptions,
69+
ServerAddress,
70+
},
71+
test::spec::oidc::get_access_token_test_user_1,
6572
Client,
6673
};
6774
use std::{fs::read_to_string, str::FromStr};
@@ -89,7 +96,20 @@ async fn get_test_client_metadata() -> &'static TestClientMetadata {
8996
static TEST_CLIENT_METADATA: OnceCell<TestClientMetadata> = OnceCell::const_new();
9097
TEST_CLIENT_METADATA
9198
.get_or_init(|| async {
92-
let client = Client::for_test().await;
99+
let mut client_options = get_client_options().await.clone();
100+
// OIDC admin credentials are required to call getParameter when running with OIDC
101+
// authentication.
102+
if let (Ok(username), Ok(password)) = (
103+
std::env::var("OIDC_ADMIN_USER"),
104+
std::env::var("OIDC_ADMIN_PWD"),
105+
) {
106+
let credential = Credential::builder()
107+
.username(username)
108+
.password(password)
109+
.build();
110+
client_options.credential = Some(credential);
111+
}
112+
let client = Client::for_test().options(client_options).await;
93113

94114
let build_info = client
95115
.database("test")
@@ -265,6 +285,8 @@ pub(crate) static LOAD_BALANCED_SINGLE_URI: Lazy<Option<String>> =
265285
Lazy::new(|| std::env::var("SINGLE_MONGOS_LB_URI").ok());
266286
pub(crate) static LOAD_BALANCED_MULTIPLE_URI: Lazy<Option<String>> =
267287
Lazy::new(|| std::env::var("MULTI_MONGOS_LB_URI").ok());
288+
pub(crate) static OIDC_URI: Lazy<Option<String>> =
289+
Lazy::new(|| std::env::var("MONGODB_URI_SINGLE").ok());
268290
pub(crate) static SERVERLESS_ATLAS_USER: Lazy<Option<String>> =
269291
Lazy::new(|| std::env::var("SERVERLESS_ATLAS_USER").ok());
270292
pub(crate) static SERVERLESS_ATLAS_PASSWORD: Lazy<Option<String>> =
@@ -308,6 +330,25 @@ pub(crate) fn update_options_for_testing(options: &mut ClientOptions) {
308330
.build(),
309331
);
310332
}
333+
334+
if let Some(ref mut credential) = options.credential {
335+
if credential.mechanism == Some(AuthMechanism::MongoDbOidc)
336+
&& credential
337+
.mechanism_properties
338+
.as_ref()
339+
.map(|properties| properties.get("ENVIRONMENT").is_none())
340+
.unwrap_or(true)
341+
{
342+
credential.oidc_callback = Callback::machine(move |_| {
343+
async move {
344+
Ok(IdpServerResponse::builder()
345+
.access_token(get_access_token_test_user_1().await)
346+
.build())
347+
}
348+
.boxed()
349+
});
350+
}
351+
}
311352
}
312353

313354
fn get_default_uri() -> String {
@@ -316,6 +357,9 @@ fn get_default_uri() -> String {
316357
return uri;
317358
}
318359
}
360+
if let Some(uri) = &*OIDC_URI {
361+
return uri.clone();
362+
}
319363
if let Ok(uri) = std::env::var("MONGODB_URI") {
320364
return uri;
321365
}

src/test/spec.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ mod handshake;
1111
mod initial_dns_seedlist_discovery;
1212
mod load_balancers;
1313
#[path = "spec/oidc.rs"]
14-
mod oidc_skip_ci;
14+
pub(crate) mod oidc_skip_ci;
1515
mod read_write_concern;
1616
mod retryable_reads;
1717
mod retryable_writes;
@@ -37,25 +37,39 @@ use std::{
3737
use serde::{de::DeserializeOwned, Deserialize};
3838

3939
pub(crate) use self::{
40+
oidc_skip_ci as oidc,
4041
unified_runner::{merge_uri_options, ExpectedEventType, Topology},
4142
v2_runner::{operation::Operation, test_file::RunOn},
4243
};
4344
use crate::{bson::Bson, test::SERVERLESS};
4445

4546
use super::log_uncaptured;
4647

48+
pub(crate) fn deserialize_spec_tests_from_exact_path<T: DeserializeOwned>(
49+
path: &[&str],
50+
skipped_files: Option<&[&str]>,
51+
) -> Vec<(T, PathBuf)> {
52+
deserialize_spec_tests_common(path.iter().collect(), skipped_files)
53+
}
54+
4755
pub(crate) fn deserialize_spec_tests<T: DeserializeOwned>(
4856
spec: &[&str],
4957
skipped_files: Option<&[&str]>,
5058
) -> Vec<(T, PathBuf)> {
51-
let dir_path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "src", "test", "spec", "json"]
59+
let mut path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "src", "test", "spec", "json"]
5260
.iter()
53-
.chain(spec.iter())
5461
.collect();
62+
path.extend(spec);
63+
deserialize_spec_tests_common(path, skipped_files)
64+
}
5565

66+
fn deserialize_spec_tests_common<T: DeserializeOwned>(
67+
path: PathBuf,
68+
skipped_files: Option<&[&str]>,
69+
) -> Vec<(T, PathBuf)> {
5670
let mut tests = vec![];
57-
for entry in read_dir(&dir_path)
58-
.unwrap_or_else(|e| panic!("Failed to read directory at {:?}: {}", &dir_path, e))
71+
for entry in
72+
read_dir(&path).unwrap_or_else(|e| panic!("Failed to read directory at {:?}: {}", &path, e))
5973
{
6074
let path = entry.unwrap().path();
6175
let Some(filename) = path

src/test/spec/auth.rs

-2
Original file line numberDiff line numberDiff line change
@@ -109,5 +109,3 @@ async fn run_auth_test(test_file: TestFile) {
109109
async fn run_legacy() {
110110
run_spec_test(&["auth", "legacy"], run_auth_test).await;
111111
}
112-
113-
// TODO RUST-1665: run unified tests

src/test/spec/oidc.rs

+81-26
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ use std::path::PathBuf;
33
use once_cell::sync::Lazy;
44
use tokio::sync::OnceCell;
55

6+
use crate::{
7+
bson::Bson,
8+
test::spec::unified_runner::{TestFile, TestFileEntity},
9+
};
10+
611
static MONGODB_URI: Lazy<String> = Lazy::new(|| get_env_var("MONGODB_URI"));
712
static MONGODB_URI_SINGLE: Lazy<String> = Lazy::new(|| get_env_var("MONGODB_URI_SINGLE"));
813
#[cfg(target_os = "linux")]
@@ -30,7 +35,7 @@ async fn get_access_token_test_user(once_cell: &'static OnceCell<String>, user_n
3035
.await
3136
.to_string()
3237
}
33-
async fn get_access_token_test_user_1() -> String {
38+
pub(crate) async fn get_access_token_test_user_1() -> String {
3439
static ACCESS_TOKEN_TEST_USER_1: OnceCell<String> = OnceCell::const_new();
3540
get_access_token_test_user(&ACCESS_TOKEN_TEST_USER_1, 1).await
3641
}
@@ -44,11 +49,37 @@ fn get_env_var(var: &str) -> String {
4449
std::env::var(var).expect(var)
4550
}
4651

52+
fn remove_mechanism_properties_placeholder(test_file: &mut TestFile) {
53+
if let Some(ref mut create_entities) = test_file.create_entities {
54+
for ref mut entity in create_entities {
55+
if let TestFileEntity::Client(ref mut client) = entity {
56+
if let Some(ref mut uri_options) = client.uri_options {
57+
if let Some(mut mechanism_properties) = uri_options
58+
.remove("authMechanismProperties")
59+
.and_then(|bson| match bson {
60+
Bson::Document(document) => Some(document),
61+
_ => None,
62+
})
63+
{
64+
mechanism_properties.remove("$$placeholder");
65+
if !mechanism_properties.is_empty() {
66+
uri_options.insert("authMechanismProperties", mechanism_properties);
67+
}
68+
}
69+
}
70+
}
71+
}
72+
}
73+
}
74+
4775
mod basic {
4876
use crate::{
4977
client::auth::{oidc, AuthMechanism, Credential},
5078
options::ClientOptions,
51-
test::util::fail_point::{FailPoint, FailPointMode},
79+
test::{
80+
spec::unified_runner::run_unified_tests,
81+
util::fail_point::{FailPoint, FailPointMode},
82+
},
5283
Client,
5384
};
5485
use bson::{doc, Document};
@@ -61,6 +92,7 @@ mod basic {
6192

6293
use super::{
6394
get_access_token_test_user_1,
95+
remove_mechanism_properties_placeholder,
6496
MONGODB_URI,
6597
MONGODB_URI_SINGLE,
6698
TEST_USER_1_USERNAME,
@@ -74,6 +106,13 @@ mod basic {
74106
TEST_USER_2_USERNAME,
75107
};
76108

109+
#[tokio::test(flavor = "multi_thread")]
110+
async fn run_unified() {
111+
run_unified_tests(&["auth", "unified"])
112+
.transform_files(remove_mechanism_properties_placeholder)
113+
.await;
114+
}
115+
77116
// Machine Callback tests
78117
#[tokio::test]
79118
async fn machine_1_1_callback_is_called() -> anyhow::Result<()> {
@@ -1281,10 +1320,25 @@ mod basic {
12811320
}
12821321

12831322
mod azure {
1284-
use crate::client::{options::ClientOptions, Client};
1285-
use bson::{doc, Document};
1323+
use crate::{
1324+
bson::{doc, Document},
1325+
client::{
1326+
auth::oidc::{AZURE_ENVIRONMENT_VALUE_STR, ENVIRONMENT_PROP_STR},
1327+
options::ClientOptions,
1328+
Client,
1329+
},
1330+
test::spec::unified_runner::run_unified_tests,
1331+
};
12861332

1287-
use super::MONGODB_URI_SINGLE;
1333+
use super::{remove_mechanism_properties_placeholder, MONGODB_URI_SINGLE};
1334+
1335+
#[tokio::test(flavor = "multi_thread")]
1336+
async fn run_unified() {
1337+
run_unified_tests(&["test_files"])
1338+
.transform_files(remove_mechanism_properties_placeholder)
1339+
.use_exact_path()
1340+
.await;
1341+
}
12881342

12891343
#[tokio::test]
12901344
async fn machine_5_1_azure_with_no_username() -> anyhow::Result<()> {
@@ -1318,8 +1372,6 @@ mod azure {
13181372

13191373
#[tokio::test]
13201374
async fn machine_5_3_token_resource_must_be_set_for_azure() -> anyhow::Result<()> {
1321-
use crate::client::auth::oidc::{AZURE_ENVIRONMENT_VALUE_STR, ENVIRONMENT_PROP_STR};
1322-
13231375
let mut opts = ClientOptions::parse(&*MONGODB_URI_SINGLE).await?;
13241376
opts.credential.as_mut().unwrap().mechanism_properties = Some(doc! {
13251377
ENVIRONMENT_PROP_STR: AZURE_ENVIRONMENT_VALUE_STR,
@@ -1341,10 +1393,21 @@ mod azure {
13411393
}
13421394

13431395
mod gcp {
1344-
use crate::client::{options::ClientOptions, Client};
1345-
use bson::{doc, Document};
1396+
use crate::{
1397+
bson::{doc, Document},
1398+
client::{options::ClientOptions, Client},
1399+
test::spec::unified_runner::run_unified_tests,
1400+
};
13461401

1347-
use super::MONGODB_URI_SINGLE;
1402+
use super::{remove_mechanism_properties_placeholder, MONGODB_URI_SINGLE};
1403+
1404+
#[tokio::test(flavor = "multi_thread")]
1405+
async fn run_unified() {
1406+
run_unified_tests(&["test_files"])
1407+
.transform_files(remove_mechanism_properties_placeholder)
1408+
.use_exact_path()
1409+
.await;
1410+
}
13481411

13491412
#[tokio::test]
13501413
async fn machine_5_4_gcp_with_no_username() -> anyhow::Result<()> {
@@ -1385,23 +1448,15 @@ mod gcp {
13851448
}
13861449

13871450
mod k8s {
1388-
use crate::{
1389-
bson::{doc, Document},
1390-
Client,
1391-
};
1451+
use crate::test::spec::unified_runner::run_unified_tests;
13921452

1393-
use super::MONGODB_URI_SINGLE;
1453+
use super::remove_mechanism_properties_placeholder;
13941454

1395-
// There's no spec test for K8s, so we run this simple sanity check.
1396-
#[tokio::test]
1397-
async fn successfully_authenticates() -> anyhow::Result<()> {
1398-
let client = Client::with_uri_str(&*MONGODB_URI_SINGLE).await?;
1399-
client
1400-
.database("test")
1401-
.collection::<Document>("test")
1402-
.find_one(doc! {})
1403-
.await?;
1404-
1405-
Ok(())
1455+
#[tokio::test(flavor = "multi_thread")]
1456+
async fn run_unified() {
1457+
run_unified_tests(&["test_files"])
1458+
.transform_files(remove_mechanism_properties_placeholder)
1459+
.use_exact_path()
1460+
.await;
14061461
}
14071462
}

0 commit comments

Comments
 (0)