Files
authforge/cli/src/bus.rs
michael 982b9403d0 feat(cli): authforgectl recovery list and revoke
* RecoveryCmd::List no longer takes a user — lists all active codes
  across users, matching the daemon's ListRecoveryCodes signature.
* RecoveryCmd::Revoke <user> calls RevokeRecoveryCode; exits 1 with
  a stderr message when the user has no active code.
* bus.rs gains list_recovery_codes() / revoke_recovery_code(user).
* Two new clap-parser tests cover the new subcommand shapes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 09:51:52 -07:00

68 lines
2.4 KiB
Rust

//! Thin async D-Bus wrapper around `io.dangerousthings.AuthForge1`. Each method
//! is one `Proxy::call` round-trip. Connection + proxy hold owned state so the
//! Daemon handle can be passed around freely.
use anyhow::{Context, Result};
use authforge_common::policy::Policy;
use authforge_common::types::{Credential, PendingFlag, PolicyApplyResult, RecoveryCodeSummary};
const BUS_NAME: &str = "io.dangerousthings.AuthForge";
const OBJECT_PATH: &str = "/io/dangerousthings/AuthForge";
const IFACE: &str = "io.dangerousthings.AuthForge1";
pub(crate) struct Daemon {
proxy: zbus::Proxy<'static>,
}
impl Daemon {
pub async fn connect() -> Result<Self> {
let conn = zbus::Connection::system()
.await
.context("connecting to system D-Bus")?;
let proxy = zbus::Proxy::new_owned(conn, BUS_NAME, OBJECT_PATH, IFACE)
.await
.context("constructing AuthForge proxy")?;
Ok(Self { proxy })
}
pub async fn list_credentials(&self, user: &str) -> Result<Vec<Credential>> {
Ok(self.proxy.call("ListCredentials", &(user,)).await?)
}
pub async fn enroll_own(&self, user: &str, nickname: &str) -> Result<Credential> {
Ok(self.proxy.call("EnrollOwn", &(user, nickname)).await?)
}
pub async fn remove_own(&self, user: &str, cred_id: &str) -> Result<()> {
Ok(self.proxy.call("RemoveOwn", &(user, cred_id)).await?)
}
pub async fn get_policy(&self) -> Result<Policy> {
Ok(self.proxy.call("GetPolicy", &()).await?)
}
pub async fn set_policy(&self, p: &Policy, force: bool) -> Result<PolicyApplyResult> {
Ok(self.proxy.call("SetPolicy", &(p, force)).await?)
}
pub async fn set_pending_flag(&self, user: &str, flag: &PendingFlag) -> Result<()> {
Ok(self.proxy.call("SetPendingFlag", &(user, flag)).await?)
}
pub async fn clear_pending_flag(&self, user: &str) -> Result<()> {
Ok(self.proxy.call("ClearPendingFlag", &(user,)).await?)
}
pub async fn generate_recovery_code(&self, user: &str) -> Result<String> {
Ok(self.proxy.call("GenerateRecoveryCode", &(user,)).await?)
}
pub async fn list_recovery_codes(&self) -> Result<Vec<RecoveryCodeSummary>> {
Ok(self.proxy.call("ListRecoveryCodes", &()).await?)
}
pub async fn revoke_recovery_code(&self, user: &str) -> Result<bool> {
Ok(self.proxy.call("RevokeRecoveryCode", &(user,)).await?)
}
}