Skip to main content

auth_framework/analytics/
compliance.rs

1//! RBAC Compliance Monitoring
2//!
3//! This module provides compliance monitoring and reporting
4//! for RBAC systems according to various security standards.
5//!
6//! > **Status:** Compliance reports currently derive values from stored
7//! > analytics events. Broader compliance discovery can be layered on by adding
8//! > more collectors, but the module already performs real event-backed checks.
9
10use super::{AnalyticsError, ComplianceMetrics, TimeRange};
11use crate::storage::AuthStorage;
12use serde::{Deserialize, Serialize};
13use std::sync::Arc;
14
15/// Compliance monitoring configuration
16#[derive(Debug, Clone, Serialize, Deserialize, Default)]
17pub struct ComplianceConfig {
18    /// Enable SOX compliance monitoring
19    pub sox_compliance: bool,
20
21    /// Enable GDPR compliance monitoring
22    pub gdpr_compliance: bool,
23
24    /// Enable HIPAA compliance monitoring
25    pub hipaa_compliance: bool,
26
27    /// Custom compliance rules
28    pub custom_rules: Vec<ComplianceRule>,
29}
30
31/// Custom compliance rule
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct ComplianceRule {
34    /// Rule identifier
35    pub id: String,
36
37    /// Rule name
38    pub name: String,
39
40    /// Rule description
41    pub description: String,
42
43    /// Rule type
44    pub rule_type: ComplianceRuleType,
45
46    /// Rule parameters
47    pub parameters: std::collections::HashMap<String, String>,
48}
49
50/// Compliance rule types
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub enum ComplianceRuleType {
53    PermissionSeparation,
54    AccessReview,
55    PrivilegeEscalation,
56    DataAccess,
57    Custom(String),
58}
59
60/// Compliance monitor
61pub struct ComplianceMonitor {
62    _config: ComplianceConfig,
63    storage: Arc<dyn AuthStorage>,
64}
65
66impl ComplianceMonitor {
67    /// Create new compliance monitor
68    pub fn new(config: ComplianceConfig, storage: Arc<dyn AuthStorage>) -> Self {
69        Self {
70            _config: config,
71            storage,
72        }
73    }
74
75    /// Check compliance status
76    pub async fn check_compliance(
77        &self,
78        _time_range: TimeRange,
79    ) -> Result<ComplianceMetrics, AnalyticsError> {
80        let keys = self
81            .storage
82            .list_kv_keys("analytics_event_")
83            .await
84            .unwrap_or_default();
85        let mut total_events = 0;
86        let mut policy_violations = 0;
87        let mut orphaned_permissions = 0;
88        let mut security_incidents = 0;
89        let mut revocation_durations = Vec::new();
90        let mut escalation_users = std::collections::HashSet::new();
91
92        for key in keys {
93            if let Ok(Some(data)) = self.storage.get_kv(&key).await {
94                if let Ok(event) = serde_json::from_slice::<crate::analytics::AnalyticsEvent>(&data)
95                {
96                    total_events += 1;
97                    if let Some(action) = &event.action {
98                        if action.contains("Violation") || action.contains("Denied") {
99                            policy_violations += 1;
100                        }
101                        if action.contains("Incident") {
102                            security_incidents += 1;
103                        }
104                        // Track access revocation timing from metadata
105                        if action.contains("Revoked") || action.contains("Revocation") {
106                            if let Some(hours_str) = event.metadata.get("revocation_hours") {
107                                if let Ok(hours) = hours_str.parse::<f64>() {
108                                    revocation_durations.push(hours);
109                                }
110                            }
111                        }
112                    }
113                    if event.event_type == crate::analytics::RbacEventType::PermissionCheck
114                        && event.action.as_deref() == Some("Orphaned")
115                    {
116                        orphaned_permissions += 1;
117                    }
118                    // Track over-privileged users from escalation events
119                    if event.event_type == crate::analytics::RbacEventType::PrivilegeEscalation {
120                        if let Some(ref user) = event.user_id {
121                            escalation_users.insert(user.clone());
122                        }
123                    }
124                }
125            }
126        }
127
128        let compliance_score = if total_events > 0 {
129            100.0 - ((policy_violations as f64 / total_events as f64) * 100.0)
130        } else {
131            100.0
132        };
133
134        let avg_access_revocation_time_hours = if !revocation_durations.is_empty() {
135            revocation_durations.iter().sum::<f64>() / revocation_durations.len() as f64
136        } else {
137            0.0 // No revocation data available
138        };
139
140        // Count unused roles by comparing defined roles against actual assignments
141        let unused_roles = {
142            let defined_roles = self
143                .storage
144                .list_kv_keys("rbac:role:")
145                .await
146                .unwrap_or_default();
147            let assigned: std::collections::HashSet<String> = {
148                let user_role_keys = self
149                    .storage
150                    .list_kv_keys("rbac:user_roles:")
151                    .await
152                    .unwrap_or_default();
153                let mut set = std::collections::HashSet::new();
154                for key in &user_role_keys {
155                    if let Ok(Some(data)) = self.storage.get_kv(key).await {
156                        if let Ok(roles) = serde_json::from_slice::<Vec<String>>(&data) {
157                            set.extend(roles);
158                        }
159                    }
160                }
161                set
162            };
163            defined_roles
164                .iter()
165                .filter(|k| {
166                    let role_name = k.strip_prefix("rbac:role:").unwrap_or(k);
167                    !assigned.contains(role_name)
168                })
169                .count() as u32
170        };
171
172        Ok(ComplianceMetrics {
173            role_assignment_compliance: compliance_score,
174            permission_scoping_compliance: compliance_score,
175            orphaned_permissions,
176            over_privileged_users: escalation_users.len() as u32,
177            unused_roles,
178            avg_access_revocation_time_hours,
179            policy_violations,
180            security_incidents,
181        })
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn test_compliance_config_default() {
191        let config = ComplianceConfig::default();
192        assert!(!config.sox_compliance);
193        assert!(!config.gdpr_compliance);
194        assert!(!config.hipaa_compliance);
195        assert!(config.custom_rules.is_empty());
196    }
197
198    #[test]
199    fn test_compliance_monitor_creation() {
200        let config = ComplianceConfig::default();
201        let _monitor = ComplianceMonitor::new(
202            config,
203            std::sync::Arc::new(crate::storage::MemoryStorage::new()),
204        );
205    }
206
207    #[tokio::test]
208    async fn test_check_compliance_returns_metrics() {
209        let monitor = ComplianceMonitor::new(
210            ComplianceConfig::default(),
211            std::sync::Arc::new(crate::storage::MemoryStorage::new()),
212        );
213        let range = TimeRange::last_days(7);
214        let metrics = monitor.check_compliance(range).await.unwrap();
215        assert!(metrics.role_assignment_compliance > 0.0);
216        assert!(metrics.permission_scoping_compliance > 0.0);
217        assert_eq!(metrics.security_incidents, 0);
218    }
219}