use brazen::{Cred, CredStore, Secret};
use super::XdgCredStore;
mod ambient;
fn store_at(dir: std::path::PathBuf) -> XdgCredStore {
XdgCredStore { dir: Some(dir) }
}
fn oauth_cred(access: &str, expires_at: u64) -> Cred {
Cred::OAuth2 {
access_token: Secret::new(access),
refresh_token: Secret::new("refresh-tok"),
expires_at,
scope: Some("openid profile".to_owned()),
account_id: Some("acct-1".to_owned()),
}
}
#[test]
fn get_after_put_roundtrips_oauth2() {
let tmp = tempfile::tempdir().unwrap();
let store = store_at(tmp.path().join("credentials"));
assert_eq!(
store.get("anthropic"),
None,
"a miss before any write is None"
);
let cred = oauth_cred("access-tok", 1_750_000_000);
store.put("anthropic", &cred).unwrap();
assert_eq!(
store.get("anthropic"),
Some(cred),
"get must round-trip the persisted Cred::OAuth2 byte-for-byte"
);
assert_eq!(
store.get("openai"),
None,
"an unwritten provider is still a miss, not a cross-read"
);
}
#[cfg(unix)]
#[test]
fn written_file_is_0600_and_dir_is_0700() {
use std::os::unix::fs::PermissionsExt;
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().join("credentials");
let store = store_at(dir.clone());
store.put("anthropic", &oauth_cred("a", 1)).unwrap();
let file_mode = std::fs::metadata(dir.join("anthropic.json"))
.unwrap()
.permissions()
.mode();
assert_eq!(file_mode & 0o777, 0o600, "the cred file must be owner-only");
let dir_mode = std::fs::metadata(&dir).unwrap().permissions().mode();
assert_eq!(dir_mode & 0o777, 0o700, "the cred dir must be owner-only");
}
#[cfg(unix)]
#[test]
fn concurrent_reads_never_observe_a_partial_write() {
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
let tmp = tempfile::tempdir().unwrap();
let store = Arc::new(store_at(tmp.path().join("credentials")));
let a = oauth_cred("token-aaaaaaaaaaaaaaaaaaaa", 100);
let b = oauth_cred("token-bbbbbbbbbbbbbbbbbbbb", 200);
store.put("anthropic", &a).unwrap();
let done = Arc::new(AtomicBool::new(false));
let readers: Vec<_> = (0..3)
.map(|_| {
let (store, done, a, b) = (store.clone(), done.clone(), a.clone(), b.clone());
thread::spawn(move || {
let mut reads = 0u64;
while !done.load(Ordering::Relaxed) {
let got = store.get("anthropic");
assert!(
got.as_ref() == Some(&a) || got.as_ref() == Some(&b),
"reader observed a torn/partial cred: {got:?}"
);
reads += 1;
}
reads
})
})
.collect();
for i in 0..3000u64 {
store
.put("anthropic", if i % 2 == 0 { &a } else { &b })
.unwrap();
}
done.store(true, Ordering::Relaxed);
let total: u64 = readers.into_iter().map(|h| h.join().unwrap()).sum();
assert!(total > 0, "the readers must have raced at least one write");
}