Skip to content

Commit fbec185

Browse files
RUST-1954 Disallow commas in authMechanismProperties values (#1315)
1 parent bb64b1e commit fbec185

17 files changed

+1460
-210
lines changed

src/client/auth.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ pub struct Credential {
401401
pub mechanism: Option<AuthMechanism>,
402402

403403
/// Additional properties for the given mechanism.
404+
///
405+
/// If any value in the properties contains a comma, this field must be set directly on
406+
/// [`ClientOptions`](crate::options::ClientOptions) and cannot be parsed from a connection
407+
/// string.
404408
pub mechanism_properties: Option<Document>,
405409

406410
/// The token callback for OIDC authentication.

src/client/options.rs

Lines changed: 17 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,17 +1345,6 @@ fn split_once_right<'a>(s: &'a str, delimiter: &str) -> (Option<&'a str>, &'a st
13451345
}
13461346
}
13471347

1348-
/// Splits a string into a section before a given index and a section exclusively after the index.
1349-
/// Empty portions are returned as `None`.
1350-
fn exclusive_split_at(s: &str, i: usize) -> (Option<&str>, Option<&str>) {
1351-
let (l, r) = s.split_at(i);
1352-
1353-
let lout = if !l.is_empty() { Some(l) } else { None };
1354-
let rout = if r.len() > 1 { Some(&r[1..]) } else { None };
1355-
1356-
(lout, rout)
1357-
}
1358-
13591348
fn percent_decode(s: &str, err_message: &str) -> Result<String> {
13601349
match percent_encoding::percent_decode_str(s).decode_utf8() {
13611350
Ok(result) => Ok(result.to_string()),
@@ -1817,47 +1806,26 @@ impl ConnectionString {
18171806
}
18181807
"authsource" => parts.auth_source = Some(value.to_string()),
18191808
"authmechanismproperties" => {
1820-
let mut doc = Document::new();
1821-
let err_func = || {
1822-
ErrorKind::InvalidArgument {
1823-
message: "improperly formatted authMechanismProperties".to_string(),
1824-
}
1825-
.into()
1826-
};
1809+
let mut properties = Document::new();
18271810

1828-
for kvp in value.split(',') {
1829-
match kvp.find(':') {
1830-
Some(index) => {
1831-
let (k, v) = exclusive_split_at(kvp, index);
1832-
let key = k.ok_or_else(err_func)?;
1833-
match key {
1834-
"ALLOWED_HOSTS" => {
1835-
return Err(Error::invalid_argument(
1836-
"ALLOWED_HOSTS must only be specified through client \
1837-
options",
1838-
));
1839-
}
1840-
"OIDC_CALLBACK" => {
1841-
return Err(Error::invalid_argument(
1842-
"OIDC_CALLBACK must only be specified through client \
1843-
options",
1844-
));
1845-
}
1846-
"OIDC_HUMAN_CALLBACK" => {
1847-
return Err(Error::invalid_argument(
1848-
"OIDC_HUMAN_CALLBACK must only be specified through \
1849-
client options",
1850-
));
1851-
}
1852-
_ => {}
1853-
}
1854-
let value = v.ok_or_else(err_func)?;
1855-
doc.insert(key, value);
1856-
}
1857-
None => return Err(err_func()),
1811+
for property in value.split(",") {
1812+
let Some((k, v)) = property.split_once(":") else {
1813+
return Err(Error::invalid_argument(format!(
1814+
"each entry in authMechanismProperties must be a colon-separated \
1815+
key-value pair, got {}",
1816+
property
1817+
)));
18581818
};
1819+
if k == "ALLOWED_HOSTS" || k == "OIDC_CALLBACK" || k == "OIDC_HUMAN_CALLBACK" {
1820+
return Err(Error::invalid_argument(format!(
1821+
"{} must only be specified through client options",
1822+
k
1823+
)));
1824+
}
1825+
properties.insert(k, v);
18591826
}
1860-
parts.auth_mechanism_properties = Some(doc);
1827+
1828+
parts.auth_mechanism_properties = Some(properties);
18611829
}
18621830
#[cfg(any(
18631831
feature = "zstd-compression",

src/client/options/test.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ static SKIPPED_TESTS: Lazy<Vec<&'static str>> = Lazy::new(|| {
2222
"maxPoolSize=0 does not error",
2323
#[cfg(not(feature = "cert-key-password"))]
2424
"Valid tlsCertificateKeyFilePassword is parsed correctly",
25-
// TODO RUST-1954: unskip these tests
26-
"Colon in a key value pair",
27-
"Comma in a key value pair causes a warning",
2825
];
2926

3027
// TODO RUST-1896: unskip this test when openssl-tls is enabled

src/test/spec/auth.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ async fn run_auth_test(test_file: TestFile) {
106106
}
107107

108108
#[tokio::test]
109-
async fn run() {
110-
run_spec_test(&["auth"], run_auth_test).await;
109+
async fn run_legacy() {
110+
run_spec_test(&["auth", "legacy"], run_auth_test).await;
111111
}
112+
113+
// TODO RUST-1665: run unified tests

src/test/spec/json/auth/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Auth Tests
2+
3+
## Introduction
4+
5+
This document describes the format of the driver spec tests included in the JSON and YAML files included in the `legacy`
6+
sub-directory. Tests in the `unified` directory are written using the
7+
[Unified Test Format](../../unified-test-format/unified-test-format.md).
8+
9+
The YAML and JSON files in the `legacy` directory tree are platform-independent tests that drivers can use to prove
10+
their conformance to the Auth Spec at least with respect to connection string URI input.
11+
12+
Drivers should do additional unit testing if there are alternate ways of configuring credentials on a client.
13+
14+
Driver must also conduct the prose tests in the Auth Spec test plan section.
15+
16+
## Format
17+
18+
Each YAML file contains an object with a single `tests` key. This key is an array of test case objects, each of which
19+
have the following keys:
20+
21+
- `description`: A string describing the test.
22+
- `uri`: A string containing the URI to be parsed.
23+
- `valid:` A boolean indicating if the URI should be considered valid.
24+
- `credential`: If null, the credential must not be considered configured for the the purpose of deciding if the driver
25+
should authenticate to the topology. If non-null, it is an object containing one or more of the following properties
26+
of a credential:
27+
- `username`: A string containing the username. For auth mechanisms that do not utilize a password, this may be the
28+
entire `userinfo` token from the connection string.
29+
- `password`: A string containing the password.
30+
- `source`: A string containing the authentication database.
31+
- `mechanism`: A string containing the authentication mechanism. A null value for this key is used to indicate that a
32+
mechanism wasn't specified and that mechanism negotiation is required. Test harnesses should modify the mechanism
33+
test as needed to assert this condition.
34+
- `mechanism_properties`: A document containing mechanism-specific properties. It specifies a subset of properties
35+
that must match. If a key exists in the test data, it must exist with the corresponding value in the credential.
36+
Other values may exist in the credential without failing the test.
37+
38+
If any key is missing, no assertion about that key is necessary. Except as specified explicitly above, if a key is
39+
present, but the test value is null, the observed value for that key must be uninitialized (whatever that means for a
40+
given driver and data type).
41+
42+
## Implementation notes
43+
44+
Testing whether a URI is valid or not should simply be a matter of checking whether URI parsing (or MongoClient
45+
construction) raises an error or exception.
46+
47+
If a credential is configured, its properties must be compared to the `credential` field.

src/test/spec/json/auth/README.rst

Lines changed: 0 additions & 53 deletions
This file was deleted.

src/test/spec/json/auth/connection-string.json renamed to src/test/spec/json/auth/legacy/connection-string.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,4 @@
648648
"credential": null
649649
}
650650
]
651-
}
651+
}

src/test/spec/json/auth/connection-string.yml renamed to src/test/spec/json/auth/legacy/connection-string.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,4 +468,4 @@ tests:
468468
(MONGODB-OIDC)
469469
uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s
470470
valid: false
471-
credential: null
471+
credential: null

0 commit comments

Comments
 (0)