use super::{AnalyticsError, ComplianceMetrics, TimeRange};
use crate::storage::AuthStorage;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ComplianceConfig {
pub sox_compliance: bool,
pub gdpr_compliance: bool,
pub hipaa_compliance: bool,
pub custom_rules: Vec<ComplianceRule>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceRule {
pub id: String,
pub name: String,
pub description: String,
pub rule_type: ComplianceRuleType,
pub parameters: std::collections::HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ComplianceRuleType {
PermissionSeparation,
AccessReview,
PrivilegeEscalation,
DataAccess,
Custom(String),
}
pub struct ComplianceMonitor {
_config: ComplianceConfig,
storage: Arc<dyn AuthStorage>,
}
impl ComplianceMonitor {
pub fn new(config: ComplianceConfig, storage: Arc<dyn AuthStorage>) -> Self {
Self {
_config: config,
storage,
}
}
pub async fn check_compliance(
&self,
_time_range: TimeRange,
) -> Result<ComplianceMetrics, AnalyticsError> {
let keys = self
.storage
.list_kv_keys("analytics_event_")
.await
.unwrap_or_default();
let mut total_events = 0;
let mut policy_violations = 0;
let mut orphaned_permissions = 0;
let mut security_incidents = 0;
let mut revocation_durations = Vec::new();
let mut escalation_users = std::collections::HashSet::new();
for key in keys {
if let Ok(Some(data)) = self.storage.get_kv(&key).await {
if let Ok(event) = serde_json::from_slice::<crate::analytics::AnalyticsEvent>(&data)
{
total_events += 1;
if let Some(action) = &event.action {
if action.contains("Violation") || action.contains("Denied") {
policy_violations += 1;
}
if action.contains("Incident") {
security_incidents += 1;
}
if action.contains("Revoked") || action.contains("Revocation") {
if let Some(hours_str) = event.metadata.get("revocation_hours") {
if let Ok(hours) = hours_str.parse::<f64>() {
revocation_durations.push(hours);
}
}
}
}
if event.event_type == crate::analytics::RbacEventType::PermissionCheck
&& event.action.as_deref() == Some("Orphaned")
{
orphaned_permissions += 1;
}
if event.event_type == crate::analytics::RbacEventType::PrivilegeEscalation {
if let Some(ref user) = event.user_id {
escalation_users.insert(user.clone());
}
}
}
}
}
let compliance_score = if total_events > 0 {
100.0 - ((policy_violations as f64 / total_events as f64) * 100.0)
} else {
100.0
};
let avg_access_revocation_time_hours = if !revocation_durations.is_empty() {
revocation_durations.iter().sum::<f64>() / revocation_durations.len() as f64
} else {
0.0 };
let unused_roles = {
let defined_roles = self
.storage
.list_kv_keys("rbac:role:")
.await
.unwrap_or_default();
let assigned: std::collections::HashSet<String> = {
let user_role_keys = self
.storage
.list_kv_keys("rbac:user_roles:")
.await
.unwrap_or_default();
let mut set = std::collections::HashSet::new();
for key in &user_role_keys {
if let Ok(Some(data)) = self.storage.get_kv(key).await {
if let Ok(roles) = serde_json::from_slice::<Vec<String>>(&data) {
set.extend(roles);
}
}
}
set
};
defined_roles
.iter()
.filter(|k| {
let role_name = k.strip_prefix("rbac:role:").unwrap_or(k);
!assigned.contains(role_name)
})
.count() as u32
};
Ok(ComplianceMetrics {
role_assignment_compliance: compliance_score,
permission_scoping_compliance: compliance_score,
orphaned_permissions,
over_privileged_users: escalation_users.len() as u32,
unused_roles,
avg_access_revocation_time_hours,
policy_violations,
security_incidents,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compliance_config_default() {
let config = ComplianceConfig::default();
assert!(!config.sox_compliance);
assert!(!config.gdpr_compliance);
assert!(!config.hipaa_compliance);
assert!(config.custom_rules.is_empty());
}
#[test]
fn test_compliance_monitor_creation() {
let config = ComplianceConfig::default();
let _monitor = ComplianceMonitor::new(
config,
std::sync::Arc::new(crate::storage::MemoryStorage::new()),
);
}
#[tokio::test]
async fn test_check_compliance_returns_metrics() {
let monitor = ComplianceMonitor::new(
ComplianceConfig::default(),
std::sync::Arc::new(crate::storage::MemoryStorage::new()),
);
let range = TimeRange::last_days(7);
let metrics = monitor.check_compliance(range).await.unwrap();
assert!(metrics.role_assignment_compliance > 0.0);
assert!(metrics.permission_scoping_compliance > 0.0);
assert_eq!(metrics.security_incidents, 0);
}
}