use super::{Authority, MachinePass, PassRefusal};
pub struct PassLength<'a>(pub(crate) &'a MachinePass);
impl std::fmt::Debug for PassLength<'_> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0.0.lock() {
Ok(held) => held.token.len().fmt(formatter),
Err(_) => PassRefusal("pass_holder_refused").fmt(formatter),
}
}
}
impl std::fmt::Debug for Authority {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Authority")
.field("current", &self.current)
.field("pass", &PassLength(&self.pass))
.field("dial", &self.dial)
.field("adapter", &self.adapter)
.field("boot_pending", &self.boot_pending)
.finish()
}
}
#[cfg(test)]
pub fn test_machine()
-> Result<(tempfile::TempDir, super::MachineConfig), Box<dyn std::error::Error>> {
let root = tempfile::tempdir()?;
let pass_path = root.path().join("machine.pass");
let token = lys_pass::conformance::cases()
.into_iter()
.find(|case| case.name == "valid")
.and_then(|case| case.token)
.ok_or("valid conformance pass absent")?;
std::fs::write(&pass_path, token)?;
let mut machine: super::MachineConfig = toml::from_str("identity_path = 'unused'")?;
machine.pass_path = pass_path;
Ok((root, machine))
}
#[cfg(test)]
mod tests {
use super::super::{
Arc, Authority, Current, Ed25519Identity, LinkHost, LocalMachine, LysPassAdapter,
LysPassConsumer, MachineConfig, MachineLinkRights, MachinePass, Mutex, Rights, Zeroizing,
};
use crate::link_dial::LinkDialer;
fn authority()
-> Result<(tempfile::TempDir, MachineConfig, Arc<Authority>), Box<dyn std::error::Error>> {
use lys_pass::conformance::{AUDIENCE, ISSUER, KEYS, NOW};
let (root, machine) = super::test_machine()?;
let pass = MachinePass::configured(&machine)?;
let adapter = Arc::new(LysPassAdapter::from_keys_json(KEYS, ISSUER.to_owned())?);
let token = pass.current()?;
let verified = adapter.verify_offline(token.as_bytes(), AUDIENCE, NOW)?;
let authority = Arc::new(Authority {
current: Mutex::new(Some(Current {
rights: MachineLinkRights::new(&adapter, verified),
expiry: u64::MAX,
renew_at: u64::MAX,
renewed: false,
})),
pass,
dial: None,
adapter,
boot_pending: std::sync::atomic::AtomicBool::new(false),
});
Ok((root, machine, authority))
}
#[test]
fn authority_debug_reports_pass_length() -> Result<(), Box<dyn std::error::Error>> {
let (root, machine, authority) = authority()?;
let token = authority.pass.current()?;
let debug = format!("{authority:?}");
assert!(
debug.contains(&format!("pass: {}", token.len())),
"pass length absent"
);
assert!(!debug.contains(token.as_str()), "credential bytes escaped");
assert!(
debug.contains("current:") && debug.contains("dial:") && debug.contains("adapter:")
);
assert_eq!(
token.len(),
std::fs::read_to_string(machine.pass_path)?.trim().len()
);
root.close()?;
Ok(())
}
#[test]
fn dialer_debug_reports_pass_length() -> Result<(), Box<dyn std::error::Error>> {
let (root, machine, authority) = authority()?;
let host = LinkHost::new(
LocalMachine::new(Ed25519Identity::from_seed(&Zeroizing::new([1; 32]))),
Box::new(Rights(Arc::clone(&authority))),
std::collections::BTreeMap::new(),
);
let dialer = LinkDialer::new(&host, &machine);
let token = authority.pass.current()?;
let debug = format!("{dialer:?}");
assert!(
debug.contains(&format!("pass: Ok({})", token.len())),
"pass length absent"
);
assert!(!debug.contains(token.as_str()), "credential bytes escaped");
assert!(debug.contains("host:") && debug.contains("topology:"));
root.close()?;
Ok(())
}
}