feat(daemon): D-Bus interface with all 9 stub methods + system-bus wiring

Bundles plan tasks 1.8 (read methods), 1.9 (EnrollOwn/RemoveOwn with polkit
gate), 1.10 (EnrollOther, SetPolicy, pending, recovery-code), and 1.11
(main.rs system-bus registration) — they land together because Polkit::System
is only constructed by main.rs, so splitting them mid-implementation would
require dead_code allows that immediately reverse.

Adds:
- daemon/src/dbus.rs — AuthForge struct + #[zbus::interface] impl with all 9
  methods. Reads (ListCredentials, GetPolicy) are unauthenticated; writes call
  authz() which dispatches to polkit. Includes 9 integration tests via a
  tokio::net::UnixStream::pair p2p connection — no system bus needed for tests.
- daemon/src/main.rs — connects to system bus, picks Polkit::system or
  Polkit::permissive based on AUTHFORGE_POLKIT_BYPASS env var, registers the
  AuthForge interface at /io/dangerousthings/AuthForge, requests well-known
  name io.dangerousthings.AuthForge, then parks forever.
- daemon/src/polkit.rs — drop dead_code allows now that System is wired.
- daemon/src/state.rs — gate has_pending() behind cfg(test); production reads
  go through the on-disk file in later phases, not this in-memory cache.
- common/src/types.rs (formatting only via rustfmt).

Tests: 14/14 daemon tests pass (4 state, 1 polkit, 9 dbus). 7/7 common tests
pass. cargo clippy --workspace --all-targets -D warnings clean. cargo fmt
clean.
This commit is contained in:
michael
2026-04-26 23:04:59 -07:00
parent df22358051
commit c26d6ae896
6 changed files with 430 additions and 68 deletions

View File

@@ -11,13 +11,11 @@ pub enum PolkitError {
Bus(#[from] zbus::Error),
}
#[allow(dead_code)] // wired up in Task 1.8.
pub enum Polkit {
Permissive,
System(Connection),
}
#[allow(dead_code)] // wired up in Task 1.8.
impl Polkit {
pub fn permissive() -> Self {
Self::Permissive
@@ -58,13 +56,18 @@ async fn system_check(conn: &Connection, action: &str, pid: u32) -> Result<(), P
.await?;
let (is_authorized, _is_challenge, _details): (bool, bool, HashMap<String, String>) = proxy
.call("CheckAuthorization", &(subject, action, details, flags, cancellation_id))
.call(
"CheckAuthorization",
&(subject, action, details, flags, cancellation_id),
)
.await?;
if is_authorized {
Ok(())
} else {
Err(PolkitError::NotAuthorized { action: action.to_string() })
Err(PolkitError::NotAuthorized {
action: action.to_string(),
})
}
}
@@ -75,6 +78,8 @@ mod tests {
#[tokio::test]
async fn permissive_always_allows() {
let p = Polkit::permissive();
p.check("io.dangerousthings.AuthForge.set-policy", 1234).await.unwrap();
p.check("io.dangerousthings.AuthForge.set-policy", 1234)
.await
.unwrap();
}
}