use std::process::ExitCode;
use link_assistant_router::admin_recovery::Recovery;
use link_assistant_router::token::TokenManager;
fn success_envelope(recovery: &Recovery) -> serde_json::Value {
serde_json::json!({
"recovered": true,
"token": recovery.token,
"token_id": recovery.token_id,
"revoked": recovery.revoked,
"retained_admins": recovery.retained_admins,
})
}
fn failure_envelope(error: &str) -> serde_json::Value {
serde_json::json!({ "recovered": false, "error": error })
}
pub fn run(
manager: &TokenManager,
revoke_others: bool,
ttl_hours: i64,
label: &str,
json: bool,
) -> ExitCode {
let store = manager.store();
let recovery = match link_assistant_router::admin_recovery::recover(
manager,
&store,
ttl_hours,
label,
revoke_others,
) {
Ok(recovery) => recovery,
Err(error) => {
if json {
println!("{}", failure_envelope(&error));
} else {
eprintln!("error: {error}");
}
return ExitCode::from(1);
}
};
if json {
println!("{}", success_envelope(&recovery));
return ExitCode::SUCCESS;
}
println!("─────────────────────────────────────────────────────────────");
println!("Admin token (shown once, store it now): {}", recovery.token);
println!("Use it as: Authorization: Bearer <token>");
println!("Recorded in the token store as id {}", recovery.token_id);
println!("─────────────────────────────────────────────────────────────");
for id in &recovery.revoked {
println!("revoked previous admin token {id}");
}
if recovery.retained_admins > 0 {
println!(
"note: {} other admin token(s) remain valid, including the one you lost; \
rerun with --revoke-others to retire them",
recovery.retained_admins
);
}
ExitCode::SUCCESS
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_refusal_reports_itself_in_the_same_envelope_shape() {
let envelope = failure_envelope("a zero lifetime is refused");
assert_eq!(envelope["recovered"], serde_json::Value::Bool(false));
assert_eq!(envelope["error"], "a zero lifetime is refused");
assert!(
envelope.get("token").is_none(),
"a refusal carries no credential: {envelope}"
);
}
#[test]
fn a_successful_envelope_carries_the_fields_tooling_reads() {
let recovery = Recovery {
token: "la_sk_example".to_string(),
token_id: "an-id".to_string(),
revoked: vec!["old-admin".to_string()],
retained_admins: 2,
};
let envelope = success_envelope(&recovery);
assert_eq!(envelope["recovered"], serde_json::Value::Bool(true));
assert_eq!(envelope["token"], "la_sk_example");
assert_eq!(envelope["token_id"], "an-id");
assert_eq!(envelope["revoked"][0], "old-admin");
assert_eq!(envelope["retained_admins"], 2);
}
}