113 lines
4.3 KiB
Rust
113 lines
4.3 KiB
Rust
//! D-Bus client for the GUI. zbus runs on the workspace tokio runtime; the
|
|
//! runtime guard installed in `main` keeps the runtime context alive for the
|
|
//! lifetime of `app.run()`. Closures dispatch async work via
|
|
//! `glib::MainContext::spawn_local`, which polls the future on the GTK main
|
|
//! thread; tokio's reactor wakes the future when zbus I/O is ready.
|
|
|
|
use authforge_common::policy::Policy;
|
|
use authforge_common::types::{Credential, PendingStatus, PolicyApplyResult, RecoveryCodeSummary};
|
|
|
|
const BUS_NAME: &str = "io.dangerousthings.AuthForge";
|
|
const OBJECT_PATH: &str = "/io/dangerousthings/AuthForge";
|
|
const IFACE: &str = "io.dangerousthings.AuthForge1";
|
|
|
|
#[derive(Clone)]
|
|
#[allow(dead_code)] // wired through keys_page.rs / enroll_dialog.rs in Tasks 4-6.
|
|
pub(crate) struct Daemon {
|
|
proxy: zbus::Proxy<'static>,
|
|
}
|
|
|
|
#[allow(dead_code)] // wired through keys_page.rs / enroll_dialog.rs in Tasks 4-6.
|
|
impl Daemon {
|
|
pub async fn connect() -> zbus::Result<Self> {
|
|
let conn = zbus::Connection::system().await?;
|
|
// Mirror the CLI's known-working pattern; `Proxy::new_owned` returns a
|
|
// Proxy<'static> owning its connection so `Daemon` can be passed
|
|
// around freely.
|
|
let proxy = zbus::Proxy::new_owned(conn, BUS_NAME, OBJECT_PATH, IFACE).await?;
|
|
Ok(Self { proxy })
|
|
}
|
|
|
|
pub async fn list_credentials(&self, user: &str) -> zbus::Result<Vec<Credential>> {
|
|
self.proxy.call("ListCredentials", &(user,)).await
|
|
}
|
|
|
|
pub async fn enroll_own(&self, user: &str, nickname: &str) -> zbus::Result<Credential> {
|
|
self.proxy.call("EnrollOwn", &(user, nickname)).await
|
|
}
|
|
|
|
pub async fn remove_own(&self, user: &str, cred_id: &str) -> zbus::Result<()> {
|
|
self.proxy.call("RemoveOwn", &(user, cred_id)).await
|
|
}
|
|
|
|
/// Subscribes to `EnrollmentFailed`. The returned stream is alive as long
|
|
/// as the caller holds it; the touch dialog drops it when it closes. We
|
|
/// don't subscribe to `EnrollmentSucceeded` because the call's return
|
|
/// value carries the new `Credential` — the source of truth for success.
|
|
pub async fn subscribe_enrollment_failed(
|
|
&self,
|
|
) -> zbus::Result<zbus::proxy::SignalStream<'static>> {
|
|
self.proxy.receive_signal("EnrollmentFailed").await
|
|
}
|
|
|
|
pub async fn get_pending_status(&self, user: &str) -> zbus::Result<PendingStatus> {
|
|
self.proxy.call("GetPendingStatus", &(user,)).await
|
|
}
|
|
|
|
pub async fn clear_pending_flag(&self, user: &str) -> zbus::Result<()> {
|
|
self.proxy.call("ClearPendingFlag", &(user,)).await
|
|
}
|
|
|
|
pub async fn get_policy(&self) -> zbus::Result<Policy> {
|
|
self.proxy.call("GetPolicy", &()).await
|
|
}
|
|
|
|
/// `force=false` runs the daemon's lockout simulator first; on
|
|
/// violations, returns `PolicyApplyResult { applied: false, violations }`
|
|
/// without writing.
|
|
pub async fn set_policy(&self, p: &Policy, force: bool) -> zbus::Result<PolicyApplyResult> {
|
|
self.proxy.call("SetPolicy", &(p, force)).await
|
|
}
|
|
|
|
pub async fn generate_recovery_code(&self, user: &str) -> zbus::Result<String> {
|
|
self.proxy.call("GenerateRecoveryCode", &(user,)).await
|
|
}
|
|
|
|
pub async fn list_recovery_codes(&self) -> zbus::Result<Vec<RecoveryCodeSummary>> {
|
|
self.proxy.call("ListRecoveryCodes", &()).await
|
|
}
|
|
|
|
pub async fn revoke_recovery_code(&self, user: &str) -> zbus::Result<bool> {
|
|
self.proxy.call("RevokeRecoveryCode", &(user,)).await
|
|
}
|
|
|
|
pub async fn subscribe_policy_changed(
|
|
&self,
|
|
) -> zbus::Result<zbus::proxy::SignalStream<'static>> {
|
|
self.proxy.receive_signal("PolicyChanged").await
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "totp")]
|
|
impl Daemon {
|
|
pub async fn enroll_totp(
|
|
&self,
|
|
user: &str,
|
|
) -> zbus::Result<authforge_common::types::TotpEnrollment> {
|
|
self.proxy.call("EnrollTotp", &(user,)).await
|
|
}
|
|
|
|
pub async fn is_totp_enrolled(&self, user: &str) -> zbus::Result<bool> {
|
|
self.proxy.call("IsTotpEnrolled", &(user,)).await
|
|
}
|
|
|
|
pub async fn revoke_totp(&self, user: &str) -> zbus::Result<bool> {
|
|
self.proxy.call("RevokeTotp", &(user,)).await
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)] // wired through keys_page.rs in Task 4.
|
|
pub(crate) fn current_user() -> String {
|
|
std::env::var("USER").unwrap_or_else(|_| "unknown".into())
|
|
}
|