diff --git a/src/api/handlers/settings.rs b/src/api/handlers/settings.rs index f19dcaa..d1ab3ae 100644 --- a/src/api/handlers/settings.rs +++ b/src/api/handlers/settings.rs @@ -15,6 +15,8 @@ use crate::state::AppState; pub struct KeylimeSettings { pub verifier_url: String, pub registrar_url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub seed_mock_data: Option, } /// GET /api/settings/keylime -- return current Registrar/Verifier URLs. @@ -25,6 +27,11 @@ pub async fn get_keylime( let settings = KeylimeSettings { verifier_url: kl.verifier_url().to_string(), registrar_url: kl.registrar_url().to_string(), + seed_mock_data: if state.seed_mock_data() { + Some(true) + } else { + None + }, }; Ok(Json(ApiResponse::ok(settings))) } @@ -50,11 +57,24 @@ pub async fn update_keylime( let new_client = KeylimeClient::new(config)?; state.swap_keylime(new_client); + + if let Some(seed) = body.seed_mock_data { + state.set_seed_mock_data(seed); + if seed { + state.alert_repo.seed_if_empty().await; + } + } + state.persist_settings(); let settings = KeylimeSettings { verifier_url: body.verifier_url, registrar_url: body.registrar_url, + seed_mock_data: if state.seed_mock_data() { + Some(true) + } else { + None + }, }; Ok(Json(ApiResponse::ok(settings))) } @@ -256,11 +276,43 @@ mod tests { let settings = KeylimeSettings { verifier_url: "http://v:3000".into(), registrar_url: "http://r:3001".into(), + seed_mock_data: None, }; let json = serde_json::to_string(&settings).unwrap(); let deserialized: KeylimeSettings = serde_json::from_str(&json).unwrap(); assert_eq!(deserialized.verifier_url, "http://v:3000"); assert_eq!(deserialized.registrar_url, "http://r:3001"); + assert_eq!(deserialized.seed_mock_data, None); + } + + #[test] + fn keylime_settings_seed_mock_data_roundtrip() { + let settings = KeylimeSettings { + verifier_url: "http://v:3000".into(), + registrar_url: "http://r:3001".into(), + seed_mock_data: Some(true), + }; + let json = serde_json::to_string(&settings).unwrap(); + let deserialized: KeylimeSettings = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized.seed_mock_data, Some(true)); + } + + #[test] + fn keylime_settings_omits_seed_mock_data_when_none() { + let settings = KeylimeSettings { + verifier_url: "http://v:3000".into(), + registrar_url: "http://r:3001".into(), + seed_mock_data: None, + }; + let json = serde_json::to_string(&settings).unwrap(); + assert!(!json.contains("seed_mock_data")); + } + + #[test] + fn keylime_settings_deserializes_without_seed_field() { + let json = r#"{"verifier_url":"http://v:3000","registrar_url":"http://r:3001"}"#; + let deserialized: KeylimeSettings = serde_json::from_str(json).unwrap(); + assert_eq!(deserialized.seed_mock_data, None); } #[test] diff --git a/src/main.rs b/src/main.rs index a44b8c9..4849182 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,6 +43,11 @@ async fn main() -> anyhow::Result<()> { .or_else(|| std::env::var("KEYLIME_REGISTRAR_URL").ok()) .unwrap_or_else(|| "http://localhost:3001".to_string()); + let seed_mock_data = persisted + .as_ref() + .and_then(|s| s.seed_mock_data) + .unwrap_or(false); + let mtls = persisted.and_then(|s| s.mtls); let observation_interval_secs: u64 = std::env::var("OBSERVATION_INTERVAL_SECS") @@ -66,7 +71,11 @@ async fn main() -> anyhow::Result<()> { let db = SqliteDb::connect(&url).await?; db.init_schema().await?; tracing::info!("SQLite database connected: {url}"); - db.repositories() + let repos = db.repositories(); + if seed_mock_data { + repos.alert.seed_if_empty().await; + } + repos } Ok(url) => { tracing::warn!("Unsupported DATABASE_URL scheme: {url} — using in-memory repos"); @@ -100,6 +109,7 @@ async fn main() -> anyhow::Result<()> { repos.audit, cache, config_path, + seed_mock_data, ); let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(()); diff --git a/src/models/alert.rs b/src/models/alert.rs index e195bbb..a0e8817 100644 --- a/src/models/alert.rs +++ b/src/models/alert.rs @@ -1,4 +1,4 @@ -use chrono::{DateTime, Utc}; +use chrono::{DateTime, Duration, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -76,6 +76,139 @@ pub struct AlertSummary { pub active_warnings: u64, } +pub fn seed_alerts() -> Vec { + let now = Utc::now(); + + vec![ + Alert { + id: Uuid::parse_str("a0000001-0000-4000-8000-000000000001").unwrap(), + alert_type: AlertType::AttestationFailure, + severity: AlertSeverity::Critical, + description: "Agent attestation failed: quote verification returned INVALID — \ + PCR values do not match expected policy" + .into(), + affected_agents: vec!["a1b2c3d4-0000-1111-2222-333344445555".into()], + state: AlertState::New, + created_timestamp: now - Duration::minutes(45), + acknowledged_timestamp: None, + assigned_to: None, + investigation_notes: None, + root_cause: None, + resolution: None, + auto_resolved: false, + escalation_count: 0, + sla_window: Some("15m".into()), + source: "verifier".into(), + external_ticket_id: None, + }, + Alert { + id: Uuid::parse_str("a0000001-0000-4000-8000-000000000002").unwrap(), + alert_type: AlertType::AttestationFailure, + severity: AlertSeverity::Warning, + description: "Push-mode agent has 3 consecutive attestation failures — \ + evidence submission timeout" + .into(), + affected_agents: vec!["b2c3d4e5-a1b0-8765-4321-fedcba987654".into()], + state: AlertState::Acknowledged, + created_timestamp: now - Duration::hours(2), + acknowledged_timestamp: Some(now - Duration::hours(1)), + assigned_to: Some("operator@example.com".into()), + investigation_notes: None, + root_cause: None, + resolution: None, + auto_resolved: false, + escalation_count: 0, + sla_window: Some("30m".into()), + source: "verifier".into(), + external_ticket_id: None, + }, + Alert { + id: Uuid::parse_str("a0000001-0000-4000-8000-000000000003").unwrap(), + alert_type: AlertType::CertExpiry, + severity: AlertSeverity::Warning, + description: "EK certificate expires in 28 days — renewal recommended".into(), + affected_agents: vec!["d432fbb3-d2f1-4a97-9ef7-75bd81c00000".into()], + state: AlertState::New, + created_timestamp: now - Duration::hours(6), + acknowledged_timestamp: None, + assigned_to: None, + investigation_notes: None, + root_cause: None, + resolution: None, + auto_resolved: false, + escalation_count: 0, + sla_window: None, + source: "certificate-monitor".into(), + external_ticket_id: None, + }, + Alert { + id: Uuid::parse_str("a0000001-0000-4000-8000-000000000004").unwrap(), + alert_type: AlertType::PcrChange, + severity: AlertSeverity::Info, + description: "PCR-14 value changed after kernel update — \ + verified as legitimate change" + .into(), + affected_agents: vec!["f7e6d5c4-b3a2-9180-7654-321098765432".into()], + state: AlertState::Resolved, + created_timestamp: now - Duration::hours(26), + acknowledged_timestamp: Some(now - Duration::hours(25)), + assigned_to: Some("admin@example.com".into()), + investigation_notes: Some( + "Kernel updated from 6.1.0 to 6.1.5 — PCR change expected".into(), + ), + root_cause: Some("Planned kernel update".into()), + resolution: Some("Policy updated to reflect new kernel measurements".into()), + auto_resolved: false, + escalation_count: 0, + sla_window: None, + source: "verifier".into(), + external_ticket_id: None, + }, + Alert { + id: Uuid::parse_str("a0000001-0000-4000-8000-000000000005").unwrap(), + alert_type: AlertType::PolicyViolation, + severity: AlertSeverity::Critical, + description: "IMA policy violation: unauthorized binary /usr/local/bin/suspect \ + executed on agent" + .into(), + affected_agents: vec!["a1b2c3d4-0000-1111-2222-333344445555".into()], + state: AlertState::UnderInvestigation, + created_timestamp: now - Duration::hours(1), + acknowledged_timestamp: Some(now - Duration::minutes(50)), + assigned_to: Some("security-team@example.com".into()), + investigation_notes: Some( + "Binary hash does not match any known package. Escalated to security team.".into(), + ), + root_cause: None, + resolution: None, + auto_resolved: false, + escalation_count: 1, + sla_window: Some("15m".into()), + source: "verifier".into(), + external_ticket_id: Some("SEC-2024-0042".into()), + }, + Alert { + id: Uuid::parse_str("a0000001-0000-4000-8000-000000000006").unwrap(), + alert_type: AlertType::ClockSkew, + severity: AlertSeverity::Info, + description: "Clock skew of 2.3s detected between agent and verifier".into(), + affected_agents: vec!["c5d6e7f8-a9b0-4321-8765-abcdef012345".into()], + state: AlertState::Dismissed, + created_timestamp: now - Duration::hours(48), + acknowledged_timestamp: Some(now - Duration::hours(47)), + assigned_to: None, + investigation_notes: None, + root_cause: None, + resolution: Some("NTP sync corrected the drift — false positive".into()), + auto_resolved: false, + escalation_count: 0, + sla_window: None, + source: "verifier".into(), + external_ticket_id: None, + }, + ] +} + /// Notification for external channels (FR-010). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Notification { diff --git a/src/repository/alert.rs b/src/repository/alert.rs index 60277cd..e0a8c94 100644 --- a/src/repository/alert.rs +++ b/src/repository/alert.rs @@ -1,10 +1,10 @@ use std::sync::RwLock; use async_trait::async_trait; -use chrono::{Duration, Utc}; +use chrono::Utc; use uuid::Uuid; -use crate::models::alert::{Alert, AlertSeverity, AlertState, AlertSummary, AlertType}; +use crate::models::alert::{seed_alerts, Alert, AlertSeverity, AlertState, AlertSummary}; #[async_trait] pub trait AlertRepository: Send + Sync + 'static { @@ -16,6 +16,7 @@ pub trait AlertRepository: Send + Sync + 'static { async fn resolve(&self, id: Uuid, resolution: Option) -> Result<(), String>; async fn dismiss(&self, id: Uuid) -> Result<(), String>; async fn escalate(&self, id: Uuid) -> Result<(), String>; + async fn seed_if_empty(&self) {} } pub struct InMemoryAlertRepository { @@ -24,140 +25,8 @@ pub struct InMemoryAlertRepository { impl InMemoryAlertRepository { pub fn new_with_seed_data() -> Self { - let now = Utc::now(); - - let alerts = vec![ - Alert { - id: Uuid::parse_str("a0000001-0000-4000-8000-000000000001").unwrap(), - alert_type: AlertType::AttestationFailure, - severity: AlertSeverity::Critical, - description: "Agent attestation failed: quote verification returned INVALID — \ - PCR values do not match expected policy" - .into(), - affected_agents: vec!["a1b2c3d4-0000-1111-2222-333344445555".into()], - state: AlertState::New, - created_timestamp: now - Duration::minutes(45), - acknowledged_timestamp: None, - assigned_to: None, - investigation_notes: None, - root_cause: None, - resolution: None, - auto_resolved: false, - escalation_count: 0, - sla_window: Some("15m".into()), - source: "verifier".into(), - external_ticket_id: None, - }, - Alert { - id: Uuid::parse_str("a0000001-0000-4000-8000-000000000002").unwrap(), - alert_type: AlertType::AttestationFailure, - severity: AlertSeverity::Warning, - description: "Push-mode agent has 3 consecutive attestation failures — \ - evidence submission timeout" - .into(), - affected_agents: vec!["b2c3d4e5-a1b0-8765-4321-fedcba987654".into()], - state: AlertState::Acknowledged, - created_timestamp: now - Duration::hours(2), - acknowledged_timestamp: Some(now - Duration::hours(1)), - assigned_to: Some("operator@example.com".into()), - investigation_notes: None, - root_cause: None, - resolution: None, - auto_resolved: false, - escalation_count: 0, - sla_window: Some("30m".into()), - source: "verifier".into(), - external_ticket_id: None, - }, - Alert { - id: Uuid::parse_str("a0000001-0000-4000-8000-000000000003").unwrap(), - alert_type: AlertType::CertExpiry, - severity: AlertSeverity::Warning, - description: "EK certificate expires in 28 days — renewal recommended".into(), - affected_agents: vec!["d432fbb3-d2f1-4a97-9ef7-75bd81c00000".into()], - state: AlertState::New, - created_timestamp: now - Duration::hours(6), - acknowledged_timestamp: None, - assigned_to: None, - investigation_notes: None, - root_cause: None, - resolution: None, - auto_resolved: false, - escalation_count: 0, - sla_window: None, - source: "certificate-monitor".into(), - external_ticket_id: None, - }, - Alert { - id: Uuid::parse_str("a0000001-0000-4000-8000-000000000004").unwrap(), - alert_type: AlertType::PcrChange, - severity: AlertSeverity::Info, - description: "PCR-14 value changed after kernel update — \ - verified as legitimate change" - .into(), - affected_agents: vec!["f7e6d5c4-b3a2-9180-7654-321098765432".into()], - state: AlertState::Resolved, - created_timestamp: now - Duration::hours(26), - acknowledged_timestamp: Some(now - Duration::hours(25)), - assigned_to: Some("admin@example.com".into()), - investigation_notes: Some( - "Kernel updated from 6.1.0 to 6.1.5 — PCR change expected".into(), - ), - root_cause: Some("Planned kernel update".into()), - resolution: Some("Policy updated to reflect new kernel measurements".into()), - auto_resolved: false, - escalation_count: 0, - sla_window: None, - source: "verifier".into(), - external_ticket_id: None, - }, - Alert { - id: Uuid::parse_str("a0000001-0000-4000-8000-000000000005").unwrap(), - alert_type: AlertType::PolicyViolation, - severity: AlertSeverity::Critical, - description: "IMA policy violation: unauthorized binary /usr/local/bin/suspect \ - executed on agent" - .into(), - affected_agents: vec!["a1b2c3d4-0000-1111-2222-333344445555".into()], - state: AlertState::UnderInvestigation, - created_timestamp: now - Duration::hours(1), - acknowledged_timestamp: Some(now - Duration::minutes(50)), - assigned_to: Some("security-team@example.com".into()), - investigation_notes: Some( - "Binary hash does not match any known package. Escalated to security team." - .into(), - ), - root_cause: None, - resolution: None, - auto_resolved: false, - escalation_count: 1, - sla_window: Some("15m".into()), - source: "verifier".into(), - external_ticket_id: Some("SEC-2024-0042".into()), - }, - Alert { - id: Uuid::parse_str("a0000001-0000-4000-8000-000000000006").unwrap(), - alert_type: AlertType::ClockSkew, - severity: AlertSeverity::Info, - description: "Clock skew of 2.3s detected between agent and verifier".into(), - affected_agents: vec!["c5d6e7f8-a9b0-4321-8765-abcdef012345".into()], - state: AlertState::Dismissed, - created_timestamp: now - Duration::hours(48), - acknowledged_timestamp: Some(now - Duration::hours(47)), - assigned_to: None, - investigation_notes: None, - root_cause: None, - resolution: Some("NTP sync corrected the drift — false positive".into()), - auto_resolved: false, - escalation_count: 0, - sla_window: None, - source: "verifier".into(), - external_ticket_id: None, - }, - ]; - Self { - alerts: RwLock::new(alerts), + alerts: RwLock::new(seed_alerts()), } } } diff --git a/src/repository/repository_tests.rs b/src/repository/repository_tests.rs index 69e7e35..9459abe 100644 --- a/src/repository/repository_tests.rs +++ b/src/repository/repository_tests.rs @@ -9,7 +9,7 @@ mod tests { use crate::models::alert::{Alert, AlertSeverity, AlertState, AlertType}; use crate::models::attestation::{AttestationResult, FailureType}; use crate::models::policy::{ApprovalStatus, Policy, PolicyChange, PolicyKind}; - use crate::repository::sqlite::{insert_test_alert, test_db}; + use crate::repository::sqlite::{insert_alert, test_db}; use crate::repository::{ AlertRepository, AttestationRepository, AuditRepository, Repositories, }; @@ -463,12 +463,12 @@ mod tests { #[tokio::test] async fn alert_query_equivalence_sqlite() { let db = test_db().await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[0], AlertSeverity::Critical, AlertState::New), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert( SEED_IDS[1], @@ -477,17 +477,17 @@ mod tests { ), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[2], AlertSeverity::Warning, AlertState::New), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[3], AlertSeverity::Info, AlertState::Resolved), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert( SEED_IDS[4], @@ -496,7 +496,7 @@ mod tests { ), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[5], AlertSeverity::Info, AlertState::Dismissed), ) @@ -542,12 +542,12 @@ mod tests { #[tokio::test] async fn acknowledge_equivalence_sqlite() { let db = test_db().await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[0], AlertSeverity::Critical, AlertState::New), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert( SEED_IDS[1], @@ -602,12 +602,12 @@ mod tests { #[tokio::test] async fn investigate_equivalence_sqlite() { let db = test_db().await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[0], AlertSeverity::Critical, AlertState::New), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[3], AlertSeverity::Info, AlertState::Resolved), ) @@ -655,12 +655,12 @@ mod tests { #[tokio::test] async fn resolve_equivalence_sqlite() { let db = test_db().await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[2], AlertSeverity::Warning, AlertState::New), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[3], AlertSeverity::Info, AlertState::Resolved), ) @@ -700,12 +700,12 @@ mod tests { #[tokio::test] async fn dismiss_equivalence_sqlite() { let db = test_db().await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[0], AlertSeverity::Critical, AlertState::New), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[5], AlertSeverity::Info, AlertState::Dismissed), ) @@ -746,12 +746,12 @@ mod tests { #[tokio::test] async fn escalate_equivalence_sqlite() { let db = test_db().await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[0], AlertSeverity::Critical, AlertState::New), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_test_alert(SEED_IDS[3], AlertSeverity::Info, AlertState::Resolved), ) diff --git a/src/repository/sqlite/alert.rs b/src/repository/sqlite/alert.rs index f49ec23..9f0219c 100644 --- a/src/repository/sqlite/alert.rs +++ b/src/repository/sqlite/alert.rs @@ -3,7 +3,7 @@ use chrono::{DateTime, Utc}; use sqlx::{Row, SqlitePool}; use uuid::Uuid; -use crate::models::alert::{Alert, AlertSummary}; +use crate::models::alert::{seed_alerts, Alert, AlertSummary}; use crate::repository::AlertRepository; pub struct SqliteAlertRepository { @@ -23,7 +23,6 @@ fn parse_enum(raw: &str) -> T { }) } -#[cfg(test)] fn serialize_enum(val: &T) -> String { let json = serde_json::to_string(val).expect("enum serialization"); json.trim_matches('"').to_string() @@ -262,6 +261,22 @@ impl AlertRepository for SqliteAlertRepository { Ok(()) } + async fn seed_if_empty(&self) { + let count: Result<(i64,), _> = sqlx::query_as("SELECT COUNT(*) FROM alerts") + .fetch_one(&self.pool) + .await; + match count { + Ok((0,)) => { + tracing::info!("seeding alerts table with mock data"); + for alert in seed_alerts() { + insert_alert(&self.pool, &alert).await; + } + } + Ok(_) => {} + Err(e) => tracing::warn!("failed to check alerts count for seeding: {e}"), + } + } + async fn escalate(&self, id: Uuid) -> Result<(), String> { let mut tx = self.pool.begin().await.map_err(|e| e.to_string())?; let id_str = id.to_string(); @@ -289,8 +304,7 @@ impl AlertRepository for SqliteAlertRepository { } } -#[cfg(test)] -pub(crate) async fn insert_test_alert(pool: &SqlitePool, alert: &Alert) { +pub(crate) async fn insert_alert(pool: &SqlitePool, alert: &Alert) { let agents_json = serde_json::to_string(&alert.affected_agents).unwrap(); sqlx::query( "INSERT INTO alerts (id, alert_type, severity, description, affected_agents, state, \ @@ -357,7 +371,7 @@ mod tests { let id_acked = Uuid::parse_str("a0000001-0000-4000-8000-000000000002").unwrap(); let id_resolved = Uuid::parse_str("a0000001-0000-4000-8000-000000000003").unwrap(); - insert_test_alert( + insert_alert( &db.pool, &make_alert( "a0000001-0000-4000-8000-000000000001", @@ -366,7 +380,7 @@ mod tests { ), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_alert( "a0000001-0000-4000-8000-000000000002", @@ -375,7 +389,7 @@ mod tests { ), ) .await; - insert_test_alert( + insert_alert( &db.pool, &make_alert( "a0000001-0000-4000-8000-000000000003", diff --git a/src/repository/sqlite/mod.rs b/src/repository/sqlite/mod.rs index 09b32cc..49353a2 100644 --- a/src/repository/sqlite/mod.rs +++ b/src/repository/sqlite/mod.rs @@ -9,7 +9,7 @@ pub use audit::SqliteAuditRepository; pub use policy::SqlitePolicyRepository; #[cfg(test)] -pub(crate) use alert::insert_test_alert; +pub(crate) use alert::insert_alert; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; use sqlx::SqlitePool; diff --git a/src/settings_store.rs b/src/settings_store.rs index ad8302e..a1b4c8a 100644 --- a/src/settings_store.rs +++ b/src/settings_store.rs @@ -23,6 +23,8 @@ use crate::config::MtlsConfig; pub struct PersistedSettings { pub keylime: Option, pub mtls: Option, + #[serde(default)] + pub seed_mock_data: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -140,7 +142,7 @@ mod tests { verifier_url: "https://v.example.com:8881".into(), registrar_url: "https://r.example.com:8891".into(), }), - mtls: None, + ..Default::default() }; let toml_str = toml::to_string_pretty(&settings).unwrap(); let parsed: PersistedSettings = toml::from_str(&toml_str).unwrap(); @@ -163,6 +165,7 @@ mod tests { key: "/etc/key.pem".into(), ca_cert: PathBuf::from("/etc/ca.pem"), }), + ..Default::default() }; let toml_str = toml::to_string_pretty(&settings).unwrap(); let parsed: PersistedSettings = toml::from_str(&toml_str).unwrap(); @@ -188,7 +191,7 @@ mod tests { verifier_url: "http://localhost:3000".into(), registrar_url: "http://localhost:3001".into(), }), - mtls: None, + ..Default::default() }; save_sync(&path, &settings); diff --git a/src/state.rs b/src/state.rs index 96cd102..1f65f47 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::path::PathBuf; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use std::time::Duration; @@ -28,11 +29,13 @@ pub struct AppState { pub audit_repo: Arc, pub cache: Arc, config_path: Option, + seed_mock_data: Arc, ssh_config: Arc, attestation_tracker: Arc>>, } impl AppState { + #[allow(clippy::too_many_arguments)] pub fn new( keylime: KeylimeClient, alert_repo: Arc, @@ -41,6 +44,7 @@ impl AppState { audit_repo: Arc, cache: Arc, config_path: Option, + seed_mock_data: bool, ) -> Self { Self { keylime_inner: Arc::new(RwLock::new(Arc::new(keylime))), @@ -50,6 +54,7 @@ impl AppState { audit_repo, cache, config_path, + seed_mock_data: Arc::new(AtomicBool::new(seed_mock_data)), ssh_config: Arc::new(SshConfig::default()), attestation_tracker: Arc::new(RwLock::new(HashMap::new())), } @@ -72,6 +77,14 @@ impl AppState { *self.keylime_inner.write().unwrap() = Arc::new(new_client); } + pub fn seed_mock_data(&self) -> bool { + self.seed_mock_data.load(Ordering::Relaxed) + } + + pub fn set_seed_mock_data(&self, val: bool) { + self.seed_mock_data.store(val, Ordering::Relaxed); + } + pub fn persist_settings(&self) { let Some(path) = self.config_path.clone() else { return; @@ -83,6 +96,11 @@ impl AppState { registrar_url: kl.registrar_url().to_string(), }), mtls: kl.mtls_config().cloned(), + seed_mock_data: if self.seed_mock_data() { + Some(true) + } else { + None + }, }; tokio::spawn(settings_store::save_persisted_settings(path, settings)); } @@ -148,6 +166,7 @@ mod tests { repos.audit, Arc::new(InMemoryCacheBackend::new()), None, + false, ) } diff --git a/src/tasks/observations.rs b/src/tasks/observations.rs index b47efe6..70eec64 100644 --- a/src/tasks/observations.rs +++ b/src/tasks/observations.rs @@ -145,6 +145,7 @@ mod tests { repos.audit, Arc::new(InMemoryCacheBackend::new()), None, + false, ) }