Lands plan tasks 2.15 (AppState refactor with StorageConfig + open()) and 2.16 (dbus.rs tests switched to tempdir-backed AppState; storage errors threaded through D-Bus methods as Failed). Bundled because the AppState surface change forces dbus.rs adjustments in the same commit. - daemon/src/state.rs: AppState::open(StorageConfig) replaces with_fixtures(). StorageConfig.from_env_or_defaults() reads AUTHFORGE_POLICY_DIR / _PENDING_DIR / _USERDB env vars (defaults: /etc/authforge/policy.d, /var/lib/authforge/pending, /var/lib/authforge/users.db). State delegates list/add/remove credentials to CredsPathResolver + CredentialsStore picked per-call from current Policy; pending and userdb operate independently. - daemon/src/main.rs: opens state via env-driven config; reuses cfg.policy_dir for the watcher to keep one source of truth. - daemon/src/dbus.rs: every write method maps StateError to fdo::Error::Failed. p2p_pair seeds 00-test.conf with [storage] backend = central pointing into the tempdir so credential writes don't try to touch /home/<user>/... (alice/bob/carol aren't real accounts in tests). - Renamed: list_credentials_returns_fixture_for_alice -> list_credentials_after_enroll. Removed: with_fixtures(). - .gitignore: add .claude/ so leftover Phase 1 worktree state isn't committed. Test count: 26/26 daemon tests green (was 17). Common: 13/13. Clippy + fmt clean.
84 lines
2.8 KiB
Rust
84 lines
2.8 KiB
Rust
use anyhow::{Context, Result};
|
|
use std::sync::Arc;
|
|
use tracing::{info, warn};
|
|
|
|
mod dbus;
|
|
mod polkit;
|
|
mod state;
|
|
mod storage;
|
|
|
|
const BUS_NAME: &str = "io.dangerousthings.AuthForge";
|
|
const OBJECT_PATH: &str = "/io/dangerousthings/AuthForge";
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
|
|
.init();
|
|
info!("authforged {} starting", env!("CARGO_PKG_VERSION"));
|
|
|
|
let conn = zbus::Connection::system()
|
|
.await
|
|
.context("connecting to system D-Bus")?;
|
|
|
|
let polkit = if std::env::var("AUTHFORGE_POLKIT_BYPASS").is_ok() {
|
|
warn!(
|
|
"AUTHFORGE_POLKIT_BYPASS set — polkit checks are DISABLED. Do not use in production."
|
|
);
|
|
polkit::Polkit::permissive()
|
|
} else {
|
|
polkit::Polkit::system(conn.clone()).await
|
|
};
|
|
|
|
let cfg = state::StorageConfig::from_env_or_defaults();
|
|
let policy_dir = cfg.policy_dir.clone();
|
|
let state = Arc::new(state::AppState::open(cfg)?);
|
|
let auth = dbus::AuthForge {
|
|
state: state.clone(),
|
|
polkit: Arc::new(polkit),
|
|
};
|
|
|
|
conn.object_server().at(OBJECT_PATH, auth).await?;
|
|
conn.request_name(BUS_NAME)
|
|
.await
|
|
.context("acquiring well-known bus name (another instance running?)")?;
|
|
|
|
info!("listening on D-Bus as {BUS_NAME} at {OBJECT_PATH}");
|
|
|
|
// Inotify watcher → PolicyChanged signal.
|
|
let policy_store = storage::policy::PolicyStore::new(policy_dir.clone());
|
|
match policy_store.watch() {
|
|
Ok((watcher, mut rx)) => {
|
|
let conn_for_signal = conn.clone();
|
|
tokio::spawn(async move {
|
|
while rx.changed().await.is_ok() {
|
|
let iface_ref = match conn_for_signal
|
|
.object_server()
|
|
.interface::<_, dbus::AuthForge>(OBJECT_PATH)
|
|
.await
|
|
{
|
|
Ok(r) => r,
|
|
Err(e) => {
|
|
warn!(error=%e, "no AuthForge interface; skipping signal");
|
|
continue;
|
|
}
|
|
};
|
|
if let Err(e) =
|
|
dbus::AuthForge::policy_changed(iface_ref.signal_context()).await
|
|
{
|
|
warn!(error=%e, "PolicyChanged emit failed");
|
|
}
|
|
}
|
|
});
|
|
// Daemon lives until SIGTERM; deliberately leak to keep watcher alive.
|
|
std::mem::forget(watcher);
|
|
info!("watching {} for policy changes", policy_dir.display());
|
|
}
|
|
Err(e) => warn!(error=%e, "failed to start policy.d watcher"),
|
|
}
|
|
|
|
// Park forever. systemd will SIGTERM the process when stopping the unit.
|
|
std::future::pending::<()>().await;
|
|
Ok(())
|
|
}
|