use std::collections::BTreeSet;
use super::{PassPrincipal, WirePassV1};
use crate::config::{AuthConfig, PassConfig};
const PARTICIPANT: [u8; 6] = [200, 201, 202, 203, 204, 205];
fn renderings(bytes: &[u8]) -> Vec<String> {
let pair = &bytes[..2.min(bytes.len())];
let decimal = pair
.iter()
.map(u8::to_string)
.collect::<Vec<_>>()
.join(", ");
vec![decimal, hex::encode(pair)]
}
fn assert_hides(rendered: &str, what: &str, bytes: &[u8]) {
for rendering in renderings(bytes) {
assert!(
!rendered.contains(&rendering),
"the Debug of {what} shows its credential bytes as {rendering:?}: {rendered}"
);
}
}
fn principal() -> PassPrincipal {
PassPrincipal {
participant: PARTICIPANT.to_vec(),
public_key: [210; 32],
conversations: BTreeSet::from([7, 9]),
live: "workspace/acme/".to_owned(),
may_enroll: true,
}
}
#[test]
fn a_pass_principal_debug_names_its_credential_fields_by_length_only() {
let principal = principal();
let rendered = format!("{principal:?}");
assert_hides(
&rendered,
"PassPrincipal.participant",
&principal.participant,
);
assert_hides(&rendered, "PassPrincipal.public_key", &principal.public_key);
assert!(
rendered.contains("participant: 6 bytes") && rendered.contains("public_key: 32 bytes"),
"the Debug of PassPrincipal must name each credential field by its length: {rendered}"
);
}
#[test]
fn a_wire_pass_debug_names_its_credential_fields_by_length_only() {
let pass = WirePassV1 {
participant: PARTICIPANT.to_vec(),
public_key: [210; 32],
conversations: vec![7, 9],
live: "workspace/acme/".to_owned(),
may_enroll: true,
issued_at: 1,
expires_at: 2,
signature: [220; 64],
};
let rendered = format!("{pass:?}");
assert_hides(&rendered, "WirePassV1.participant", &pass.participant);
assert_hides(&rendered, "WirePassV1.public_key", &pass.public_key);
assert_hides(&rendered, "WirePassV1.signature", &pass.signature);
assert!(
rendered.contains("signature: 64 bytes"),
"the Debug of WirePassV1 must name its signature by length: {rendered}"
);
}
#[test]
fn an_auth_config_debug_hides_its_token() {
let token = "token-marker-7f3e";
let auth = AuthConfig {
token: token.to_owned(),
pass: Some(PassConfig {
registry_verifying_key: "00".repeat(32),
maximum_clock_skew_seconds: 30,
}),
require_verified_worker: false,
lys: None,
};
let rendered = format!("{auth:?}");
assert!(
!rendered.contains(token),
"the Debug of AuthConfig shows its token: {rendered}"
);
assert!(
rendered.contains(&format!("token: {} bytes", token.len())),
"the Debug of AuthConfig must name its token by length: {rendered}"
);
}
#[test]
fn a_verified_holder_debug_names_its_participant_by_length_only() {
let holder = super::VerifiedHolder {
participant: PARTICIPANT.to_vec(),
live: "workspace/acme/".to_owned(),
source: super::HolderSource::RegistryPass,
};
let rendered = format!("{holder:?}");
assert_hides(&rendered, "VerifiedHolder.participant", &PARTICIPANT);
assert!(
rendered.contains("participant: 6 bytes"),
"the Debug of VerifiedHolder must name its participant by length: {rendered}"
);
}
#[test]
fn a_debug_never_names_the_holder_through_its_live_prefix() {
let live = "watcher.";
let principal = PassPrincipal {
live: live.to_owned(),
..principal()
};
let pass = WirePassV1 {
participant: PARTICIPANT.to_vec(),
public_key: [210; 32],
conversations: vec![7],
live: live.to_owned(),
may_enroll: false,
issued_at: 1,
expires_at: 2,
signature: [220; 64],
};
let holder = super::VerifiedHolder {
participant: PARTICIPANT.to_vec(),
live: live.to_owned(),
source: super::HolderSource::RegistryPass,
};
for (what, rendered) in [
("PassPrincipal", format!("{principal:?}")),
("WirePassV1", format!("{pass:?}")),
("VerifiedHolder", format!("{holder:?}")),
] {
assert!(
!rendered.contains("watcher"),
"the Debug of {what} names the holder through its live prefix: {rendered}"
);
assert!(
rendered.contains(&format!("live: {} bytes", live.len())),
"the Debug of {what} must name its live prefix by length: {rendered}"
);
}
}
#[test]
fn a_lys_holder_debug_names_its_participant_by_length_only() {
let holder = super::VerifiedHolder {
participant: PARTICIPANT.to_vec(),
live: String::new(),
source: super::HolderSource::Lys {
kind: Some("Agent".to_owned()),
},
};
let rendered = format!("{holder:?}");
assert_hides(&rendered, "VerifiedHolder.participant", &PARTICIPANT);
assert!(
rendered.contains("participant: 6 bytes") && rendered.contains("live: 0 bytes"),
"the Debug of a Lys VerifiedHolder must name its fields by length: {rendered}"
);
}
#[test]
fn a_holders_channel_grant_follows_the_verification_that_proved_it() {
let registry = super::VerifiedHolder {
participant: PARTICIPANT.to_vec(),
live: "watcher.".to_owned(),
source: super::HolderSource::RegistryPass,
};
assert!(registry.allows_channel("watcher.findings"));
assert!(!registry.allows_channel("archie.findings"));
assert_eq!(registry.kind(), None);
let empty = super::VerifiedHolder {
live: String::new(),
..registry
};
assert!(!empty.allows_channel("watcher.findings"));
let lys = super::VerifiedHolder {
participant: PARTICIPANT.to_vec(),
live: String::new(),
source: super::HolderSource::Lys {
kind: Some("Agent".to_owned()),
},
};
assert!(lys.allows_channel("archie.findings"));
assert_eq!(lys.kind(), Some("Agent"));
}