Skip to content

Commit c329969

Browse files
authored
RUST-847 Redact Credential Debug output (1.2.x) (#357)
1 parent 6c8e3a9 commit c329969

File tree

9 files changed

+76
-13
lines changed

9 files changed

+76
-13
lines changed

src/client/auth/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ mod scram;
1010
mod test;
1111
mod x509;
1212

13-
use std::{borrow::Cow, str::FromStr};
13+
use std::{borrow::Cow, fmt::Debug, str::FromStr};
1414

1515
use hmac::Mac;
1616
use rand::Rng;
@@ -324,7 +324,7 @@ impl FromStr for AuthMechanism {
324324
///
325325
/// Some fields (mechanism and source) may be omitted and will either be negotiated or assigned a
326326
/// default value, depending on the values of other fields in the credential.
327-
#[derive(Clone, Debug, Default, Deserialize, TypedBuilder, PartialEq)]
327+
#[derive(Clone, Default, Deserialize, TypedBuilder, PartialEq)]
328328
#[non_exhaustive]
329329
pub struct Credential {
330330
/// The username to authenticate with. This applies to all mechanisms but may be omitted when
@@ -437,6 +437,14 @@ impl Credential {
437437
}
438438
}
439439

440+
impl Debug for Credential {
441+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
442+
f.debug_tuple("Credential")
443+
.field(&"REDACTED".to_string())
444+
.finish()
445+
}
446+
}
447+
440448
/// Contains the first client message sent as part of the authentication handshake.
441449
pub(crate) enum ClientFirst {
442450
Scram(ScramVersion, scram::ClientFirst),

src/client/options/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,7 @@ impl ClientOptionsParser {
10801080
None
10811081
} else {
10821082
let port_string_without_colon = &port[1..];
1083-
let p = u16::from_str_radix(port_string_without_colon, 10).map_err(|_| {
1083+
let p: u16 = port_string_without_colon.parse().map_err(|_| {
10841084
ErrorKind::ArgumentError {
10851085
message: format!(
10861086
"invalid port specified in connection string: {}",
@@ -1325,7 +1325,7 @@ impl ClientOptionsParser {
13251325

13261326
macro_rules! get_duration {
13271327
($value:expr, $option:expr) => {
1328-
match u64::from_str_radix($value, 10) {
1328+
match $value.parse::<u64>() {
13291329
Ok(i) => i,
13301330
_ => {
13311331
return Err(ErrorKind::ArgumentError {
@@ -1342,7 +1342,7 @@ impl ClientOptionsParser {
13421342

13431343
macro_rules! get_u32 {
13441344
($value:expr, $option:expr) => {
1345-
match u32::from_str_radix(value, 10) {
1345+
match value.parse::<u32>() {
13461346
Ok(u) => u,
13471347
Err(_) => {
13481348
return Err(ErrorKind::ArgumentError {
@@ -1359,7 +1359,7 @@ impl ClientOptionsParser {
13591359

13601360
macro_rules! get_i32 {
13611361
($value:expr, $option:expr) => {
1362-
match i32::from_str_radix(value, 10) {
1362+
match value.parse::<i32>() {
13631363
Ok(u) => u,
13641364
Err(_) => {
13651365
return Err(ErrorKind::ArgumentError {
@@ -1631,7 +1631,7 @@ impl ClientOptionsParser {
16311631
"w" => {
16321632
let mut write_concern = self.write_concern.get_or_insert_with(Default::default);
16331633

1634-
match i32::from_str_radix(value, 10) {
1634+
match value.parse::<i32>() {
16351635
Ok(w) => {
16361636
if w < 0 {
16371637
return Err(ErrorKind::ArgumentError {

src/cmap/establish/handshake/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,8 @@ impl Handshaker {
224224
});
225225

226226
Ok(HandshakeResult {
227-
first_round,
228227
is_master_reply,
228+
first_round,
229229
})
230230
}
231231
}

src/cmap/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ impl ConnectionPool {
8686
address,
8787
manager,
8888
connection_requester,
89+
generation_subscriber,
8990
wait_queue_timeout,
9091
event_handler,
91-
generation_subscriber,
9292
}
9393
}
9494

src/cmap/test/mod.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ use crate::{
2020
test::{
2121
assert_matches,
2222
run_spec_test,
23+
CmapEvent,
2324
EventClient,
2425
Matchable,
26+
TestClient,
2527
CLIENT_OPTIONS,
2628
LOCK,
2729
SERVER_API,
2830
},
31+
Client,
2932
RUNTIME,
3033
};
3134
use bson::doc;
@@ -435,3 +438,46 @@ async fn cmap_spec_tests() {
435438

436439
run_spec_test(&["connection-monitoring-and-pooling"], run_cmap_spec_tests).await;
437440
}
441+
442+
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
443+
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
444+
async fn redact_credential() {
445+
let _lock = LOCK.run_concurrently().await;
446+
447+
let client = TestClient::new().await;
448+
if !client.auth_enabled() {
449+
println!("skipping redact_credential due to auth not being enabled");
450+
return;
451+
}
452+
453+
let mut options = CLIENT_OPTIONS.clone();
454+
options.direct_connection = Some(true);
455+
options.hosts.drain(1..);
456+
457+
let credential = options.credential.clone().unwrap();
458+
459+
let handler = Arc::new(crate::test::EventHandler::new());
460+
let mut subscriber = handler.subscribe();
461+
options.cmap_event_handler = Some(handler.clone());
462+
463+
let _client = Client::with_options(options).unwrap();
464+
465+
subscriber
466+
.wait_for_event(EVENT_TIMEOUT, |e| {
467+
if let crate::test::Event::CmapEvent(CmapEvent::ConnectionPoolCreated(event)) = e {
468+
let event_debug = format!("{:?}", event);
469+
if let Some(ref pass) = credential.password {
470+
assert!(!event_debug.contains(pass))
471+
}
472+
if let Some(ref user) = credential.username {
473+
assert!(!event_debug.contains(user))
474+
}
475+
assert!(event_debug.contains("REDACTED"));
476+
true
477+
} else {
478+
false
479+
}
480+
})
481+
.await
482+
.expect("expected connection pool created event");
483+
}

src/coll/options.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,8 @@ pub struct FindOneAndUpdateOptions {
449449
/// Specifies the options to a [`Collection::aggregate`](../struct.Collection.html#method.aggregate)
450450
/// operation.
451451
#[skip_serializing_none]
452-
#[serde(rename_all = "camelCase")]
453452
#[derive(Clone, Debug, Default, Deserialize, TypedBuilder, Serialize)]
453+
#[serde(rename_all = "camelCase")]
454454
#[non_exhaustive]
455455
pub struct AggregateOptions {
456456
/// Enables writing to temporary files. When set to true, aggregation stages can write data to

src/test/spec/unified_runner/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use tokio::sync::RwLockWriteGuard;
1313
use crate::{
1414
bson::{doc, Document},
1515
options::{CollectionOptions, FindOptions, ReadConcern, ReadPreference, SelectionCriteria},
16-
test::{run_spec_test, LOCK},
16+
test::{run_spec_test, TestClient, LOCK},
1717
};
1818

1919
pub use self::{
@@ -245,5 +245,10 @@ async fn test_examples() {
245245
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
246246
async fn test_versioned_api() {
247247
let _guard: RwLockWriteGuard<_> = LOCK.run_exclusively().await;
248+
249+
// TODO RUST-725 Unskip these tests on 5.0
250+
if TestClient::new().await.server_version_gte(5, 0) {
251+
return;
252+
}
248253
run_spec_test(&["versioned-api"], run_unified_format_test).await;
249254
}

src/test/util/event.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
bson::doc,
1515
client::options::ServerApi,
1616
event::{
17-
cmap::{CmapEventHandler, PoolClearedEvent, PoolReadyEvent},
17+
cmap::{CmapEventHandler, PoolClearedEvent, PoolCreatedEvent, PoolReadyEvent},
1818
command::{
1919
CommandEventHandler,
2020
CommandFailedEvent,
@@ -107,6 +107,10 @@ impl EventHandler {
107107
}
108108

109109
impl CmapEventHandler for EventHandler {
110+
fn handle_pool_created_event(&self, event: PoolCreatedEvent) {
111+
self.handle(CmapEvent::ConnectionPoolCreated(event));
112+
}
113+
110114
fn handle_pool_cleared_event(&self, event: PoolClearedEvent) {
111115
self.handle(CmapEvent::ConnectionPoolCleared(event.clone()));
112116
self.pool_cleared_events.write().unwrap().push_back(event);

src/test/util/matchable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub trait Matchable: Sized + 'static {
1717
if expected.is_placeholder() {
1818
return true;
1919
}
20-
if let Some(expected) = Any::downcast_ref::<Self>(expected) {
20+
if let Some(expected) = <dyn Any>::downcast_ref::<Self>(expected) {
2121
self.content_matches(expected)
2222
} else {
2323
false

0 commit comments

Comments
 (0)