auth_framework/analytics/
compliance.rs1use super::{AnalyticsError, ComplianceMetrics, TimeRange};
11use crate::storage::AuthStorage;
12use serde::{Deserialize, Serialize};
13use std::sync::Arc;
14
15#[derive(Debug, Clone, Serialize, Deserialize, Default)]
17pub struct ComplianceConfig {
18 pub sox_compliance: bool,
20
21 pub gdpr_compliance: bool,
23
24 pub hipaa_compliance: bool,
26
27 pub custom_rules: Vec<ComplianceRule>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct ComplianceRule {
34 pub id: String,
36
37 pub name: String,
39
40 pub description: String,
42
43 pub rule_type: ComplianceRuleType,
45
46 pub parameters: std::collections::HashMap<String, String>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub enum ComplianceRuleType {
53 PermissionSeparation,
54 AccessReview,
55 PrivilegeEscalation,
56 DataAccess,
57 Custom(String),
58}
59
60pub struct ComplianceMonitor {
62 _config: ComplianceConfig,
63 storage: Arc<dyn AuthStorage>,
64}
65
66impl ComplianceMonitor {
67 pub fn new(config: ComplianceConfig, storage: Arc<dyn AuthStorage>) -> Self {
69 Self {
70 _config: config,
71 storage,
72 }
73 }
74
75 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 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 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 };
139
140 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}