feat(daemon): wire RecoveryStore into AppState with AUTHFORGE_RECOVERY_DIR override
StorageConfig gains a recovery_dir field; from_env_or_defaults reads AUTHFORGE_RECOVERY_DIR (default /var/lib/authforge/recovery). AppState exposes issue/list/revoke methods that Task 6 wires through D-Bus. Test fixtures in state.rs and dbus.rs updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -220,6 +220,7 @@ central_path = "{}"
|
|||||||
crate::state::StorageConfig {
|
crate::state::StorageConfig {
|
||||||
policy_dir,
|
policy_dir,
|
||||||
pending_dir: tmp.path().join("pending"),
|
pending_dir: tmp.path().join("pending"),
|
||||||
|
recovery_dir: tmp.path().join("recovery"),
|
||||||
userdb_path: tmp.path().join("users.db"),
|
userdb_path: tmp.path().join("users.db"),
|
||||||
pam_profile_path: tmp.path().join("authforge-pamconf"),
|
pam_profile_path: tmp.path().join("authforge-pamconf"),
|
||||||
pam_auth_update: shim,
|
pam_auth_update: shim,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use crate::policy_apply::{ApplyError, PolicyApplier};
|
|||||||
use crate::storage::credentials::{CredEntry, CredentialsStore, CredsError, CredsPathResolver};
|
use crate::storage::credentials::{CredEntry, CredentialsStore, CredsError, CredsPathResolver};
|
||||||
use crate::storage::pending::{PendingError, PendingStore};
|
use crate::storage::pending::{PendingError, PendingStore};
|
||||||
use crate::storage::policy::PolicyStore;
|
use crate::storage::policy::PolicyStore;
|
||||||
|
use crate::storage::recovery::{RecoveryEntry, RecoveryStore, RecoveryStoreError};
|
||||||
use crate::storage::userdb::{UserDb, UserDbError};
|
use crate::storage::userdb::{UserDb, UserDbError};
|
||||||
use authforge_common::policy::{Policy, PolicyError};
|
use authforge_common::policy::{Policy, PolicyError};
|
||||||
use authforge_common::types::{Credential, Method, PendingFlag, PolicyApplyResult, Transport};
|
use authforge_common::types::{Credential, Method, PendingFlag, PolicyApplyResult, Transport};
|
||||||
@@ -20,6 +21,8 @@ pub enum StateError {
|
|||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Pending(#[from] PendingError),
|
Pending(#[from] PendingError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
Recovery(#[from] RecoveryStoreError),
|
||||||
|
#[error(transparent)]
|
||||||
Creds(#[from] CredsError),
|
Creds(#[from] CredsError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
UserDb(#[from] UserDbError),
|
UserDb(#[from] UserDbError),
|
||||||
@@ -32,6 +35,7 @@ pub enum StateError {
|
|||||||
pub struct StorageConfig {
|
pub struct StorageConfig {
|
||||||
pub policy_dir: PathBuf,
|
pub policy_dir: PathBuf,
|
||||||
pub pending_dir: PathBuf,
|
pub pending_dir: PathBuf,
|
||||||
|
pub recovery_dir: PathBuf,
|
||||||
pub userdb_path: PathBuf,
|
pub userdb_path: PathBuf,
|
||||||
/// Where pam-auth-update reads the AuthForge profile from. Defaults to
|
/// Where pam-auth-update reads the AuthForge profile from. Defaults to
|
||||||
/// `/usr/share/pam-configs/authforge`; tests / non-root runs override.
|
/// `/usr/share/pam-configs/authforge`; tests / non-root runs override.
|
||||||
@@ -51,6 +55,7 @@ impl StorageConfig {
|
|||||||
Self {
|
Self {
|
||||||
policy_dir: env("AUTHFORGE_POLICY_DIR", "/etc/authforge/policy.d"),
|
policy_dir: env("AUTHFORGE_POLICY_DIR", "/etc/authforge/policy.d"),
|
||||||
pending_dir: env("AUTHFORGE_PENDING_DIR", "/var/lib/authforge/pending"),
|
pending_dir: env("AUTHFORGE_PENDING_DIR", "/var/lib/authforge/pending"),
|
||||||
|
recovery_dir: env("AUTHFORGE_RECOVERY_DIR", "/var/lib/authforge/recovery"),
|
||||||
userdb_path: env("AUTHFORGE_USERDB", "/var/lib/authforge/users.db"),
|
userdb_path: env("AUTHFORGE_USERDB", "/var/lib/authforge/users.db"),
|
||||||
pam_profile_path: env("AUTHFORGE_PAMCONF_PATH", "/usr/share/pam-configs/authforge"),
|
pam_profile_path: env("AUTHFORGE_PAMCONF_PATH", "/usr/share/pam-configs/authforge"),
|
||||||
pam_auth_update: env("AUTHFORGE_PAM_AUTH_UPDATE", "/usr/sbin/pam-auth-update"),
|
pam_auth_update: env("AUTHFORGE_PAM_AUTH_UPDATE", "/usr/sbin/pam-auth-update"),
|
||||||
@@ -61,6 +66,8 @@ impl StorageConfig {
|
|||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
policy: PolicyStore,
|
policy: PolicyStore,
|
||||||
pending: PendingStore,
|
pending: PendingStore,
|
||||||
|
#[allow(dead_code)] // wired through D-Bus in Task 6.
|
||||||
|
recovery: RecoveryStore,
|
||||||
userdb: Mutex<UserDb>,
|
userdb: Mutex<UserDb>,
|
||||||
authn: Arc<dyn Authenticator>,
|
authn: Arc<dyn Authenticator>,
|
||||||
applier: PolicyApplier,
|
applier: PolicyApplier,
|
||||||
@@ -70,11 +77,13 @@ impl AppState {
|
|||||||
pub fn open(cfg: StorageConfig, authn: Arc<dyn Authenticator>) -> Result<Self, StateError> {
|
pub fn open(cfg: StorageConfig, authn: Arc<dyn Authenticator>) -> Result<Self, StateError> {
|
||||||
let policy = PolicyStore::new(cfg.policy_dir);
|
let policy = PolicyStore::new(cfg.policy_dir);
|
||||||
let pending = PendingStore::new(cfg.pending_dir);
|
let pending = PendingStore::new(cfg.pending_dir);
|
||||||
|
let recovery = RecoveryStore::new(cfg.recovery_dir);
|
||||||
let userdb = Mutex::new(UserDb::open(&cfg.userdb_path)?);
|
let userdb = Mutex::new(UserDb::open(&cfg.userdb_path)?);
|
||||||
let applier = PolicyApplier::new(cfg.pam_profile_path, cfg.pam_auth_update);
|
let applier = PolicyApplier::new(cfg.pam_profile_path, cfg.pam_auth_update);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
policy,
|
policy,
|
||||||
pending,
|
pending,
|
||||||
|
recovery,
|
||||||
userdb,
|
userdb,
|
||||||
authn,
|
authn,
|
||||||
applier,
|
applier,
|
||||||
@@ -222,6 +231,21 @@ impl AppState {
|
|||||||
Ok(self.pending.clear(user)?)
|
Ok(self.pending.clear(user)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)] // wired through D-Bus in Task 6.
|
||||||
|
pub async fn issue_recovery(&self, user: &str) -> Result<String, StateError> {
|
||||||
|
Ok(self.recovery.issue(user, crate::recovery::DEFAULT_TTL_SECS)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)] // wired through D-Bus in Task 6.
|
||||||
|
pub async fn list_recovery(&self) -> Result<Vec<RecoveryEntry>, StateError> {
|
||||||
|
Ok(self.recovery.list()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)] // wired through D-Bus in Task 6.
|
||||||
|
pub async fn revoke_recovery(&self, user: &str) -> Result<bool, StateError> {
|
||||||
|
Ok(self.recovery.revoke(user)?)
|
||||||
|
}
|
||||||
|
|
||||||
/// Tests assert pending flag round-trips through SetPendingFlag /
|
/// Tests assert pending flag round-trips through SetPendingFlag /
|
||||||
/// ClearPendingFlag via this read accessor. Production readers (PAM
|
/// ClearPendingFlag via this read accessor. Production readers (PAM
|
||||||
/// module, GUI status) read the on-disk file directly in later phases.
|
/// module, GUI status) read the on-disk file directly in later phases.
|
||||||
@@ -250,6 +274,7 @@ mod tests {
|
|||||||
StorageConfig {
|
StorageConfig {
|
||||||
policy_dir: d.join("policy.d"),
|
policy_dir: d.join("policy.d"),
|
||||||
pending_dir: d.join("pending"),
|
pending_dir: d.join("pending"),
|
||||||
|
recovery_dir: d.join("recovery"),
|
||||||
userdb_path: d.join("users.db"),
|
userdb_path: d.join("users.db"),
|
||||||
pam_profile_path: d.join("authforge-pamconf"),
|
pam_profile_path: d.join("authforge-pamconf"),
|
||||||
pam_auth_update: shim,
|
pam_auth_update: shim,
|
||||||
@@ -309,6 +334,7 @@ mod tests {
|
|||||||
StorageConfig {
|
StorageConfig {
|
||||||
policy_dir,
|
policy_dir,
|
||||||
pending_dir: d.path().join("pending"),
|
pending_dir: d.path().join("pending"),
|
||||||
|
recovery_dir: d.path().join("recovery"),
|
||||||
userdb_path: d.path().join("users.db"),
|
userdb_path: d.path().join("users.db"),
|
||||||
pam_profile_path: d.path().join("authforge-pamconf"),
|
pam_profile_path: d.path().join("authforge-pamconf"),
|
||||||
pam_auth_update: shim,
|
pam_auth_update: shim,
|
||||||
|
|||||||
Reference in New Issue
Block a user