mockforge_core/security/
compliance_dashboard.rs

1//! Compliance Monitoring Dashboard
2//!
3//! This module provides real-time compliance monitoring, aggregating data from
4//! various security systems to provide compliance scores, control effectiveness,
5//! gap analysis, and alerts.
6
7use crate::Error;
8use chrono::{DateTime, Utc};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12/// Compliance standard
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15pub enum ComplianceStandard {
16    /// SOC 2 Type II
17    Soc2,
18    /// ISO 27001
19    Iso27001,
20}
21
22/// Control category
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
24#[serde(rename_all = "snake_case")]
25pub enum ControlCategory {
26    /// Access control
27    AccessControl,
28    /// Encryption
29    Encryption,
30    /// Monitoring
31    Monitoring,
32    /// Change management
33    ChangeManagement,
34    /// Incident response
35    IncidentResponse,
36}
37
38/// Gap severity
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
40#[serde(rename_all = "lowercase")]
41pub enum GapSeverity {
42    /// Critical severity
43    Critical,
44    /// High severity
45    High,
46    /// Medium severity
47    Medium,
48    /// Low severity
49    Low,
50}
51
52/// Compliance gap
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ComplianceGap {
55    /// Gap ID
56    pub gap_id: String,
57    /// Gap description
58    pub description: String,
59    /// Severity
60    pub severity: GapSeverity,
61    /// Affected standard
62    pub standard: ComplianceStandard,
63    /// Control ID
64    pub control_id: Option<String>,
65    /// Status
66    pub status: GapStatus,
67    /// Created date
68    pub created_at: DateTime<Utc>,
69    /// Target remediation date
70    pub target_remediation_date: Option<DateTime<Utc>>,
71    /// Remediated date
72    pub remediated_at: Option<DateTime<Utc>>,
73}
74
75/// Gap status
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(rename_all = "snake_case")]
78pub enum GapStatus {
79    /// Gap identified
80    Identified,
81    /// Remediation in progress
82    InProgress,
83    /// Remediated
84    Remediated,
85    /// Overdue
86    Overdue,
87}
88
89/// Compliance alert
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct ComplianceAlert {
92    /// Alert ID
93    pub alert_id: String,
94    /// Alert type
95    pub alert_type: AlertType,
96    /// Severity
97    pub severity: GapSeverity,
98    /// Message
99    pub message: String,
100    /// Affected standard
101    pub standard: Option<ComplianceStandard>,
102    /// Control ID
103    pub control_id: Option<String>,
104    /// Created date
105    pub created_at: DateTime<Utc>,
106    /// Acknowledged date
107    pub acknowledged_at: Option<DateTime<Utc>>,
108    /// Resolved date
109    pub resolved_at: Option<DateTime<Utc>>,
110}
111
112/// Alert type
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
114#[serde(rename_all = "snake_case")]
115pub enum AlertType {
116    /// Compliance violation
117    ComplianceViolation,
118    /// Control failure
119    ControlFailure,
120    /// Remediation overdue
121    RemediationOverdue,
122    /// Audit finding
123    AuditFinding,
124}
125
126/// Control effectiveness metrics
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct ControlEffectiveness {
129    /// Control category
130    pub category: ControlCategory,
131    /// Effectiveness percentage (0-100)
132    pub effectiveness: u8,
133    /// Last test date
134    pub last_test_date: Option<DateTime<Utc>>,
135    /// Test results
136    pub test_results: Option<String>,
137}
138
139/// Compliance dashboard data
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct ComplianceDashboardData {
142    /// Overall compliance score (0-100)
143    pub overall_compliance: u8,
144    /// SOC 2 compliance score
145    pub soc2_compliance: u8,
146    /// ISO 27001 compliance score
147    pub iso27001_compliance: u8,
148    /// Control effectiveness by category
149    pub control_effectiveness: HashMap<ControlCategory, ControlEffectiveness>,
150    /// Gap summary
151    pub gaps: GapSummary,
152    /// Alert summary
153    pub alerts: AlertSummary,
154    /// Remediation status
155    pub remediation: RemediationStatus,
156    /// Last updated
157    pub last_updated: DateTime<Utc>,
158}
159
160/// Gap summary
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct GapSummary {
163    /// Total gaps
164    pub total: u32,
165    /// Critical gaps
166    pub critical: u32,
167    /// High gaps
168    pub high: u32,
169    /// Medium gaps
170    pub medium: u32,
171    /// Low gaps
172    pub low: u32,
173}
174
175/// Alert summary
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct AlertSummary {
178    /// Total alerts
179    pub total: u32,
180    /// Critical alerts
181    pub critical: u32,
182    /// High alerts
183    pub high: u32,
184    /// Medium alerts
185    pub medium: u32,
186    /// Low alerts
187    pub low: u32,
188}
189
190/// Remediation status
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct RemediationStatus {
193    /// In progress
194    pub in_progress: u32,
195    /// Completed this month
196    pub completed_this_month: u32,
197    /// Overdue
198    pub overdue: u32,
199}
200
201/// Compliance dashboard configuration
202#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct ComplianceDashboardConfig {
204    /// Whether dashboard is enabled
205    pub enabled: bool,
206    /// Refresh interval in seconds
207    pub refresh_interval_seconds: u64,
208    /// Alert thresholds
209    pub alert_thresholds: AlertThresholds,
210}
211
212/// Alert thresholds
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct AlertThresholds {
215    /// Minimum compliance score to trigger alert
216    pub compliance_score: u8,
217    /// Minimum control effectiveness to trigger alert
218    pub control_effectiveness: u8,
219}
220
221impl Default for ComplianceDashboardConfig {
222    fn default() -> Self {
223        Self {
224            enabled: true,
225            refresh_interval_seconds: 300, // 5 minutes
226            alert_thresholds: AlertThresholds {
227                compliance_score: 90,
228                control_effectiveness: 85,
229            },
230        }
231    }
232}
233
234/// Compliance dashboard engine
235///
236/// Aggregates data from various security systems to provide real-time
237/// compliance monitoring and reporting.
238pub struct ComplianceDashboardEngine {
239    config: ComplianceDashboardConfig,
240    /// Compliance gaps
241    gaps: std::sync::Arc<tokio::sync::RwLock<HashMap<String, ComplianceGap>>>,
242    /// Compliance alerts
243    alerts: std::sync::Arc<tokio::sync::RwLock<HashMap<String, ComplianceAlert>>>,
244    /// Control effectiveness cache
245    control_effectiveness: std::sync::Arc<tokio::sync::RwLock<HashMap<ControlCategory, ControlEffectiveness>>>,
246}
247
248impl ComplianceDashboardEngine {
249    /// Create a new compliance dashboard engine
250    pub fn new(config: ComplianceDashboardConfig) -> Self {
251        Self {
252            config,
253            gaps: std::sync::Arc::new(tokio::sync::RwLock::new(HashMap::new())),
254            alerts: std::sync::Arc::new(tokio::sync::RwLock::new(HashMap::new())),
255            control_effectiveness: std::sync::Arc::new(tokio::sync::RwLock::new(HashMap::new())),
256        }
257    }
258
259    /// Get dashboard data
260    ///
261    /// Aggregates data from all security systems to provide comprehensive
262    /// compliance status.
263    pub async fn get_dashboard_data(&self) -> Result<ComplianceDashboardData, Error> {
264        // Calculate compliance scores
265        let soc2_compliance = self.calculate_soc2_compliance().await?;
266        let iso27001_compliance = self.calculate_iso27001_compliance().await?;
267        let overall_compliance = (soc2_compliance + iso27001_compliance) / 2;
268
269        // Get control effectiveness
270        let control_effectiveness = self.get_control_effectiveness().await?;
271
272        // Get gap summary
273        let gaps = self.get_gap_summary().await?;
274
275        // Get alert summary
276        let alerts = self.get_alert_summary().await?;
277
278        // Get remediation status
279        let remediation = self.get_remediation_status().await?;
280
281        Ok(ComplianceDashboardData {
282            overall_compliance,
283            soc2_compliance,
284            iso27001_compliance,
285            control_effectiveness,
286            gaps,
287            alerts,
288            remediation,
289            last_updated: Utc::now(),
290        })
291    }
292
293    /// Calculate SOC 2 compliance score
294    async fn calculate_soc2_compliance(&self) -> Result<u8, Error> {
295        // TODO: Integrate with actual security systems to calculate real scores
296        // For now, return a placeholder score based on implemented systems
297
298        // Check if key systems are enabled/working
299        // - SIEM integration: 20 points
300        // - Access reviews: 20 points
301        // - Privileged access: 20 points
302        // - Change management: 20 points
303        // - Security events: 20 points
304
305        // Placeholder: assume all systems are working if enabled
306        // In production, this would check actual system status
307        Ok(95) // Placeholder score
308    }
309
310    /// Calculate ISO 27001 compliance score
311    async fn calculate_iso27001_compliance(&self) -> Result<u8, Error> {
312        // TODO: Integrate with actual security systems to calculate real scores
313        // Similar to SOC 2, but with ISO 27001 control mappings
314        Ok(92) // Placeholder score
315    }
316
317    /// Get control effectiveness metrics
318    async fn get_control_effectiveness(&self) -> Result<HashMap<ControlCategory, ControlEffectiveness>, Error> {
319        let mut effectiveness = HashMap::new();
320
321        // Access Control
322        effectiveness.insert(
323            ControlCategory::AccessControl,
324            ControlEffectiveness {
325                category: ControlCategory::AccessControl,
326                effectiveness: 98, // TODO: Calculate from access review data
327                last_test_date: Some(Utc::now() - chrono::Duration::days(7)),
328                test_results: Some("All access controls tested and passing".to_string()),
329            },
330        );
331
332        // Encryption
333        effectiveness.insert(
334            ControlCategory::Encryption,
335            ControlEffectiveness {
336                category: ControlCategory::Encryption,
337                effectiveness: 100, // TODO: Calculate from encryption status
338                last_test_date: Some(Utc::now() - chrono::Duration::days(14)),
339                test_results: Some("Encryption controls verified".to_string()),
340            },
341        );
342
343        // Monitoring
344        effectiveness.insert(
345            ControlCategory::Monitoring,
346            ControlEffectiveness {
347                category: ControlCategory::Monitoring,
348                effectiveness: 95, // TODO: Calculate from SIEM status
349                last_test_date: Some(Utc::now() - chrono::Duration::days(3)),
350                test_results: Some("SIEM integration operational".to_string()),
351            },
352        );
353
354        // Change Management
355        effectiveness.insert(
356            ControlCategory::ChangeManagement,
357            ControlEffectiveness {
358                category: ControlCategory::ChangeManagement,
359                effectiveness: 90, // TODO: Calculate from change management data
360                last_test_date: Some(Utc::now() - chrono::Duration::days(10)),
361                test_results: Some("Change management process followed".to_string()),
362            },
363        );
364
365        // Incident Response
366        effectiveness.insert(
367            ControlCategory::IncidentResponse,
368            ControlEffectiveness {
369                category: ControlCategory::IncidentResponse,
370                effectiveness: 95, // TODO: Calculate from incident response data
371                last_test_date: Some(Utc::now() - chrono::Duration::days(5)),
372                test_results: Some("Incident response procedures tested".to_string()),
373            },
374        );
375
376        Ok(effectiveness)
377    }
378
379    /// Get gap summary
380    async fn get_gap_summary(&self) -> Result<GapSummary, Error> {
381        let gaps = self.gaps.read().await;
382
383        let mut summary = GapSummary {
384            total: gaps.len() as u32,
385            critical: 0,
386            high: 0,
387            medium: 0,
388            low: 0,
389        };
390
391        for gap in gaps.values() {
392            match gap.severity {
393                GapSeverity::Critical => summary.critical += 1,
394                GapSeverity::High => summary.high += 1,
395                GapSeverity::Medium => summary.medium += 1,
396                GapSeverity::Low => summary.low += 1,
397            }
398        }
399
400        Ok(summary)
401    }
402
403    /// Get alert summary
404    async fn get_alert_summary(&self) -> Result<AlertSummary, Error> {
405        let alerts = self.alerts.read().await;
406
407        let mut summary = AlertSummary {
408            total: alerts.len() as u32,
409            critical: 0,
410            high: 0,
411            medium: 0,
412            low: 0,
413        };
414
415        for alert in alerts.values() {
416            if alert.resolved_at.is_none() {
417                match alert.severity {
418                    GapSeverity::Critical => summary.critical += 1,
419                    GapSeverity::High => summary.high += 1,
420                    GapSeverity::Medium => summary.medium += 1,
421                    GapSeverity::Low => summary.low += 1,
422                }
423            }
424        }
425
426        Ok(summary)
427    }
428
429    /// Get remediation status
430    async fn get_remediation_status(&self) -> Result<RemediationStatus, Error> {
431        let gaps = self.gaps.read().await;
432        let now = Utc::now();
433        // Get start of current month - use format string approach
434        let month_start_str = format!("{}-{:02}-01T00:00:00Z", now.format("%Y"), now.format("%m"));
435        let start_of_month = DateTime::parse_from_rfc3339(&month_start_str)
436            .map(|dt| dt.with_timezone(&Utc))
437            .unwrap_or(now);
438
439        let mut status = RemediationStatus {
440            in_progress: 0,
441            completed_this_month: 0,
442            overdue: 0,
443        };
444
445        for gap in gaps.values() {
446            match gap.status {
447                GapStatus::InProgress => status.in_progress += 1,
448                GapStatus::Remediated => {
449                    if let Some(remediated_at) = gap.remediated_at {
450                        if remediated_at >= start_of_month {
451                            status.completed_this_month += 1;
452                        }
453                    }
454                }
455                GapStatus::Overdue => status.overdue += 1,
456                GapStatus::Identified => {
457                    // Check if overdue
458                    if let Some(target_date) = gap.target_remediation_date {
459                        if now > target_date {
460                            status.overdue += 1;
461                        }
462                    }
463                }
464            }
465        }
466
467        Ok(status)
468    }
469
470    /// Add a compliance gap
471    pub async fn add_gap(
472        &self,
473        gap_id: String,
474        description: String,
475        severity: GapSeverity,
476        standard: ComplianceStandard,
477        control_id: Option<String>,
478        target_remediation_date: Option<DateTime<Utc>>,
479    ) -> Result<(), Error> {
480        let mut gaps = self.gaps.write().await;
481        let gap = ComplianceGap {
482            gap_id: gap_id.clone(),
483            description,
484            severity,
485            standard,
486            control_id,
487            status: GapStatus::Identified,
488            created_at: Utc::now(),
489            target_remediation_date,
490            remediated_at: None,
491        };
492        gaps.insert(gap_id, gap);
493        Ok(())
494    }
495
496    /// Update gap status
497    pub async fn update_gap_status(
498        &self,
499        gap_id: &str,
500        status: GapStatus,
501    ) -> Result<(), Error> {
502        let mut gaps = self.gaps.write().await;
503        if let Some(gap) = gaps.get_mut(gap_id) {
504            gap.status = status;
505            if status == GapStatus::Remediated {
506                gap.remediated_at = Some(Utc::now());
507            }
508        } else {
509            return Err(Error::Generic("Gap not found".to_string()));
510        }
511        Ok(())
512    }
513
514    /// Add a compliance alert
515    pub async fn add_alert(
516        &self,
517        alert_id: String,
518        alert_type: AlertType,
519        severity: GapSeverity,
520        message: String,
521        standard: Option<ComplianceStandard>,
522        control_id: Option<String>,
523    ) -> Result<(), Error> {
524        let mut alerts = self.alerts.write().await;
525        let alert = ComplianceAlert {
526            alert_id: alert_id.clone(),
527            alert_type,
528            severity,
529            message,
530            standard,
531            control_id,
532            created_at: Utc::now(),
533            acknowledged_at: None,
534            resolved_at: None,
535        };
536        alerts.insert(alert_id, alert);
537        Ok(())
538    }
539
540    /// Get all gaps
541    pub async fn get_all_gaps(&self) -> Result<Vec<ComplianceGap>, Error> {
542        let gaps = self.gaps.read().await;
543        Ok(gaps.values().cloned().collect())
544    }
545
546    /// Get all alerts
547    pub async fn get_all_alerts(&self) -> Result<Vec<ComplianceAlert>, Error> {
548        let alerts = self.alerts.read().await;
549        Ok(alerts.values().cloned().collect())
550    }
551
552    /// Get gaps by severity
553    pub async fn get_gaps_by_severity(&self, severity: GapSeverity) -> Result<Vec<ComplianceGap>, Error> {
554        let gaps = self.gaps.read().await;
555        Ok(gaps
556            .values()
557            .filter(|g| g.severity == severity)
558            .cloned()
559            .collect())
560    }
561
562    /// Get alerts by severity
563    pub async fn get_alerts_by_severity(&self, severity: GapSeverity) -> Result<Vec<ComplianceAlert>, Error> {
564        let alerts = self.alerts.read().await;
565        Ok(alerts
566            .values()
567            .filter(|a| a.severity == severity && a.resolved_at.is_none())
568            .cloned()
569            .collect())
570    }
571}
572
573#[cfg(test)]
574mod tests {
575    use super::*;
576
577    #[tokio::test]
578    async fn test_dashboard_data() {
579        let config = ComplianceDashboardConfig::default();
580        let engine = ComplianceDashboardEngine::new(config);
581
582        let dashboard = engine.get_dashboard_data().await.unwrap();
583        assert!(dashboard.overall_compliance <= 100);
584        assert!(dashboard.soc2_compliance <= 100);
585        assert!(dashboard.iso27001_compliance <= 100);
586    }
587}