feat(gui): D-Bus client wrapper around AuthForge interface

This commit is contained in:
michael
2026-04-27 09:13:14 -07:00
parent d7e6d562dd
commit d07d3469fe
2 changed files with 57 additions and 0 deletions

56
gui/src/bus.rs Normal file
View File

@@ -0,0 +1,56 @@
//! 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::types::Credential;
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
}
}
#[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())
}

View File

@@ -1,6 +1,7 @@
use adw::prelude::*;
use gtk::glib;
mod bus;
mod error;
const APP_ID: &str = "io.dangerousthings.AuthForge";