feat(daemon): EnrollTotp + IsTotpEnrolled + RevokeTotp D-Bus methods
This commit is contained in:
@@ -4,6 +4,8 @@ use authforge_common::policy::Policy;
|
||||
use authforge_common::types::{
|
||||
Credential, PendingFlag, PendingStatus, PolicyApplyResult, RecoveryCodeSummary,
|
||||
};
|
||||
#[cfg(feature = "totp")]
|
||||
use authforge_common::types::TotpEnrollment;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct AuthForge {
|
||||
@@ -174,6 +176,35 @@ impl AuthForge {
|
||||
.map_err(|e| zbus::fdo::Error::Failed(e.to_string()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "totp")]
|
||||
async fn enroll_totp(&self, user: String) -> zbus::fdo::Result<TotpEnrollment> {
|
||||
self.authz("io.dangerousthings.AuthForge.enroll-totp")
|
||||
.await?;
|
||||
self.state
|
||||
.enroll_totp(&user)
|
||||
.await
|
||||
.map_err(|e| zbus::fdo::Error::Failed(e.to_string()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "totp")]
|
||||
async fn is_totp_enrolled(&self, user: String) -> zbus::fdo::Result<bool> {
|
||||
// No polkit gate — pure read; daemon's pattern is gates on writes only.
|
||||
self.state
|
||||
.is_totp_enrolled(&user)
|
||||
.await
|
||||
.map_err(|e| zbus::fdo::Error::Failed(e.to_string()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "totp")]
|
||||
async fn revoke_totp(&self, user: String) -> zbus::fdo::Result<bool> {
|
||||
self.authz("io.dangerousthings.AuthForge.revoke-totp")
|
||||
.await?;
|
||||
self.state
|
||||
.revoke_totp(&user)
|
||||
.await
|
||||
.map_err(|e| zbus::fdo::Error::Failed(e.to_string()))
|
||||
}
|
||||
|
||||
/// Emitted by main.rs whenever the inotify watcher on the policy.d/ dir
|
||||
/// reports any change. Subscribers (GUI) reload via `GetPolicy`.
|
||||
#[zbus(signal)]
|
||||
@@ -520,4 +551,42 @@ central_path = "{}"
|
||||
.expect("signal not received within 2s")
|
||||
.expect("stream closed before signal");
|
||||
}
|
||||
|
||||
#[cfg(feature = "totp")]
|
||||
#[tokio::test]
|
||||
async fn enroll_totp_returns_otpauth_uri() {
|
||||
let (_srv, client, _state, _tmp) = p2p_pair().await;
|
||||
let p = proxy(&client).await;
|
||||
let e: TotpEnrollment = p.call("EnrollTotp", &("alice",)).await.unwrap();
|
||||
assert_eq!(e.user, "alice");
|
||||
assert!(e.otpauth_uri.starts_with("otpauth://totp/AuthForge:alice?"));
|
||||
assert!(e
|
||||
.secret_b32
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_uppercase() || c.is_ascii_digit()));
|
||||
}
|
||||
|
||||
#[cfg(feature = "totp")]
|
||||
#[tokio::test]
|
||||
async fn is_totp_enrolled_round_trips() {
|
||||
let (_srv, client, _state, _tmp) = p2p_pair().await;
|
||||
let p = proxy(&client).await;
|
||||
let before: bool = p.call("IsTotpEnrolled", &("alice",)).await.unwrap();
|
||||
assert!(!before);
|
||||
let _: TotpEnrollment = p.call("EnrollTotp", &("alice",)).await.unwrap();
|
||||
let after: bool = p.call("IsTotpEnrolled", &("alice",)).await.unwrap();
|
||||
assert!(after);
|
||||
}
|
||||
|
||||
#[cfg(feature = "totp")]
|
||||
#[tokio::test]
|
||||
async fn revoke_totp_removes_enrollment() {
|
||||
let (_srv, client, _state, _tmp) = p2p_pair().await;
|
||||
let p = proxy(&client).await;
|
||||
let _: TotpEnrollment = p.call("EnrollTotp", &("alice",)).await.unwrap();
|
||||
let removed: bool = p.call("RevokeTotp", &("alice",)).await.unwrap();
|
||||
assert!(removed);
|
||||
let after: bool = p.call("IsTotpEnrolled", &("alice",)).await.unwrap();
|
||||
assert!(!after);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user