feat(daemon): add AppState with fixture-backed in-memory store
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
use anyhow::Result;
|
||||
use tracing::info;
|
||||
|
||||
mod state;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
|
||||
124
daemon/src/state.rs
Normal file
124
daemon/src/state.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
use authforge_common::types::{Credential, Method, PendingFlag, Policy, Transport};
|
||||
use std::collections::HashMap;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AppState {
|
||||
inner: RwLock<StateInner>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct StateInner {
|
||||
credentials: HashMap<String, Vec<Credential>>,
|
||||
policy: Policy,
|
||||
pending: HashMap<String, PendingFlag>,
|
||||
}
|
||||
|
||||
// `dead_code` allow lifts in Task 1.8 when dbus.rs starts calling these methods.
|
||||
#[allow(dead_code)]
|
||||
impl AppState {
|
||||
pub fn with_fixtures() -> Self {
|
||||
let mut inner = StateInner::default();
|
||||
inner.credentials.insert(
|
||||
"alice".to_string(),
|
||||
vec![Credential {
|
||||
id: "fixture-cred-1".to_string(),
|
||||
nickname: "Alice's Yubikey".to_string(),
|
||||
method: Method::Fido2,
|
||||
transport: Transport::Usb,
|
||||
created_unix: 1_700_000_000,
|
||||
}],
|
||||
);
|
||||
Self { inner: RwLock::new(inner) }
|
||||
}
|
||||
|
||||
pub async fn list_credentials(&self, user: &str) -> Vec<Credential> {
|
||||
self.inner.read().await.credentials.get(user).cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub async fn add_credential(&self, user: &str, c: Credential) {
|
||||
self.inner.write().await.credentials.entry(user.to_string()).or_default().push(c);
|
||||
}
|
||||
|
||||
pub async fn remove_credential(&self, user: &str, cred_id: &str) -> bool {
|
||||
let mut g = self.inner.write().await;
|
||||
let Some(list) = g.credentials.get_mut(user) else { return false };
|
||||
let before = list.len();
|
||||
list.retain(|c| c.id != cred_id);
|
||||
list.len() != before
|
||||
}
|
||||
|
||||
pub async fn get_policy(&self) -> Policy {
|
||||
self.inner.read().await.policy.clone()
|
||||
}
|
||||
|
||||
pub async fn set_policy(&self, p: Policy) {
|
||||
self.inner.write().await.policy = p;
|
||||
}
|
||||
|
||||
pub async fn set_pending(&self, user: &str, f: PendingFlag) {
|
||||
self.inner.write().await.pending.insert(user.to_string(), f);
|
||||
}
|
||||
|
||||
pub async fn clear_pending(&self, user: &str) -> bool {
|
||||
self.inner.write().await.pending.remove(user).is_some()
|
||||
}
|
||||
|
||||
pub async fn has_pending(&self, user: &str) -> bool {
|
||||
self.inner.read().await.pending.contains_key(user)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn fixtures_seeded_for_alice() {
|
||||
let s = AppState::with_fixtures();
|
||||
let creds = s.list_credentials("alice").await;
|
||||
assert_eq!(creds.len(), 1);
|
||||
assert_eq!(creds[0].nickname, "Alice's Yubikey");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn unknown_user_has_no_creds() {
|
||||
let s = AppState::with_fixtures();
|
||||
assert!(s.list_credentials("nobody").await.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn add_then_remove_credential() {
|
||||
let s = AppState::default();
|
||||
let c = Credential {
|
||||
id: "id1".to_string(),
|
||||
nickname: "x".to_string(),
|
||||
method: Method::Fido2,
|
||||
transport: Transport::Usb,
|
||||
created_unix: 0,
|
||||
};
|
||||
s.add_credential("bob", c).await;
|
||||
assert_eq!(s.list_credentials("bob").await.len(), 1);
|
||||
assert!(s.remove_credential("bob", "id1").await);
|
||||
assert!(s.list_credentials("bob").await.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn pending_set_clear_roundtrip() {
|
||||
let s = AppState::default();
|
||||
assert!(!s.has_pending("alice").await);
|
||||
s.set_pending(
|
||||
"alice",
|
||||
PendingFlag {
|
||||
required_methods: vec![Method::Fido2],
|
||||
created_unix: 1,
|
||||
deadline_unix: 0,
|
||||
re_enroll: false,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
assert!(s.has_pending("alice").await);
|
||||
assert!(s.clear_pending("alice").await);
|
||||
assert!(!s.has_pending("alice").await);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user