use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tokio::time::{Duration, Instant};
pub mod compliance;
pub mod dashboard;
pub mod metrics;
pub mod reports;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalyticsConfig {
pub real_time_enabled: bool,
pub data_retention_days: u32,
pub collection_interval: Duration,
pub compliance_monitoring: bool,
pub performance_monitoring: bool,
pub max_event_buffer: usize,
}
impl Default for AnalyticsConfig {
fn default() -> Self {
Self {
real_time_enabled: true,
data_retention_days: 90,
collection_interval: Duration::from_secs(60),
compliance_monitoring: true,
performance_monitoring: true,
max_event_buffer: 10000,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum RbacEventType {
RoleAssignment,
PermissionCheck,
RoleManagement,
Authentication,
Authorization,
PolicyViolation,
PrivilegeEscalation,
AccessAnomaly,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalyticsEvent {
pub id: String,
pub event_type: RbacEventType,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub user_id: Option<String>,
pub role_id: Option<String>,
pub resource: Option<String>,
pub action: Option<String>,
pub result: EventResult,
pub metadata: HashMap<String, String>,
pub duration_ms: Option<u64>,
pub source_ip: Option<String>,
pub user_agent: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum EventResult {
Success,
Failure,
Denied,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoleUsageStats {
pub role_id: String,
pub role_name: String,
pub user_count: u32,
pub permission_checks: u64,
pub successful_access: u64,
pub denied_access: u64,
pub last_used: Option<chrono::DateTime<chrono::Utc>>,
pub avg_response_time_ms: f64,
pub top_resources: Vec<ResourceAccess>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceAccess {
pub resource: String,
pub access_count: u64,
pub success_rate: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PermissionUsageStats {
pub permission_id: String,
pub check_count: u64,
pub success_rate: f64,
pub used_by_roles: Vec<String>,
pub top_users: Vec<UserActivity>,
pub peak_hours: Vec<u8>, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserActivity {
pub user_id: String,
pub activity_count: u64,
pub last_activity: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceMetrics {
pub role_assignment_compliance: f64,
pub permission_scoping_compliance: f64,
pub orphaned_permissions: u32,
pub over_privileged_users: u32,
pub unused_roles: u32,
pub avg_access_revocation_time_hours: f64,
pub policy_violations: u32,
pub security_incidents: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
pub avg_permission_check_latency_ms: f64,
pub p95_permission_check_latency_ms: f64,
pub p99_permission_check_latency_ms: f64,
pub permission_checks_per_second: f64,
pub permission_cache_hit_rate: f64,
pub error_rate: f64,
pub cpu_usage_percent: f64,
pub memory_usage_mb: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeSeriesData {
pub timestamp: chrono::DateTime<chrono::Utc>,
pub value: f64,
pub tags: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendAnalysis {
pub metric_name: String,
pub current_value: f64,
pub previous_value: f64,
pub change_percent: f64,
pub trend: TrendDirection,
pub data_points: Vec<TimeSeriesData>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum TrendDirection {
Increasing,
Decreasing,
Stable,
Volatile,
}
pub struct AnalyticsManager {
config: AnalyticsConfig,
event_buffer: Vec<AnalyticsEvent>,
last_collection: Instant,
}
impl AnalyticsManager {
pub fn new(config: AnalyticsConfig) -> Self {
Self {
config,
event_buffer: Vec::new(),
last_collection: Instant::now(),
}
}
pub async fn record_event(&mut self, event: AnalyticsEvent) -> Result<(), AnalyticsError> {
if self.event_buffer.len() >= self.config.max_event_buffer {
self.flush_events().await?;
}
self.event_buffer.push(event);
if self.config.real_time_enabled {
self.process_real_time_event(self.event_buffer.last().unwrap())
.await?;
}
Ok(())
}
pub async fn get_role_usage_stats(
&self,
_role_id: Option<&str>,
_time_range: Option<TimeRange>,
) -> Result<Vec<RoleUsageStats>, AnalyticsError> {
Ok(vec![])
}
pub async fn get_permission_usage_stats(
&self,
_permission_id: Option<&str>,
_time_range: Option<TimeRange>,
) -> Result<Vec<PermissionUsageStats>, AnalyticsError> {
Ok(vec![])
}
pub async fn get_compliance_metrics(
&self,
_time_range: Option<TimeRange>,
) -> Result<ComplianceMetrics, AnalyticsError> {
Ok(ComplianceMetrics {
role_assignment_compliance: 95.5,
permission_scoping_compliance: 88.2,
orphaned_permissions: 5,
over_privileged_users: 12,
unused_roles: 3,
avg_access_revocation_time_hours: 2.5,
policy_violations: 8,
security_incidents: 1,
})
}
pub async fn get_performance_metrics(
&self,
_time_range: Option<TimeRange>,
) -> Result<PerformanceMetrics, AnalyticsError> {
Ok(PerformanceMetrics {
avg_permission_check_latency_ms: 15.5,
p95_permission_check_latency_ms: 45.2,
p99_permission_check_latency_ms: 125.8,
permission_checks_per_second: 1250.0,
permission_cache_hit_rate: 0.92,
error_rate: 0.001,
cpu_usage_percent: 15.5,
memory_usage_mb: 512,
})
}
pub async fn get_trend_analysis(
&self,
metric_name: &str,
_time_range: TimeRange,
) -> Result<TrendAnalysis, AnalyticsError> {
Ok(TrendAnalysis {
metric_name: metric_name.to_string(),
current_value: 100.0,
previous_value: 95.0,
change_percent: 5.26,
trend: TrendDirection::Increasing,
data_points: vec![],
})
}
pub async fn generate_report(
&self,
report_type: ReportType,
time_range: TimeRange,
) -> Result<AnalyticsReport, AnalyticsError> {
let role_stats = self
.get_role_usage_stats(None, Some(time_range.clone()))
.await?;
let permission_stats = self
.get_permission_usage_stats(None, Some(time_range.clone()))
.await?;
let compliance_metrics = self
.get_compliance_metrics(Some(time_range.clone()))
.await?;
let performance_metrics = self
.get_performance_metrics(Some(time_range.clone()))
.await?;
Ok(AnalyticsReport {
report_type,
time_range,
generated_at: chrono::Utc::now(),
role_stats,
permission_stats,
compliance_metrics: compliance_metrics.clone(),
performance_metrics: performance_metrics.clone(),
summary: self.generate_report_summary(&compliance_metrics, &performance_metrics),
})
}
async fn flush_events(&mut self) -> Result<(), AnalyticsError> {
if self.event_buffer.is_empty() {
return Ok(());
}
self.event_buffer.clear();
self.last_collection = Instant::now();
Ok(())
}
async fn process_real_time_event(&self, _event: &AnalyticsEvent) -> Result<(), AnalyticsError> {
Ok(())
}
fn generate_report_summary(
&self,
compliance: &ComplianceMetrics,
performance: &PerformanceMetrics,
) -> String {
format!(
"RBAC Analytics Summary: {}% compliance, {:.1}ms avg latency, {:.1}% error rate",
compliance.role_assignment_compliance,
performance.avg_permission_check_latency_ms,
performance.error_rate * 100.0
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeRange {
pub start: chrono::DateTime<chrono::Utc>,
pub end: chrono::DateTime<chrono::Utc>,
}
impl TimeRange {
pub fn last_hours(hours: u32) -> Self {
let end = chrono::Utc::now();
let start = end - chrono::Duration::hours(hours as i64);
Self { start, end }
}
pub fn last_days(days: u32) -> Self {
let end = chrono::Utc::now();
let start = end - chrono::Duration::days(days as i64);
Self { start, end }
}
pub fn today() -> Self {
let now = chrono::Utc::now();
let start = now.date_naive().and_hms_opt(0, 0, 0).unwrap().and_utc();
let end = now;
Self { start, end }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ReportType {
Daily,
Weekly,
Monthly,
Compliance,
Performance,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalyticsReport {
pub report_type: ReportType,
pub time_range: TimeRange,
pub generated_at: chrono::DateTime<chrono::Utc>,
pub role_stats: Vec<RoleUsageStats>,
pub permission_stats: Vec<PermissionUsageStats>,
pub compliance_metrics: ComplianceMetrics,
pub performance_metrics: PerformanceMetrics,
pub summary: String,
}
#[derive(Debug, thiserror::Error)]
pub enum AnalyticsError {
#[error("Data processing error: {0}")]
ProcessingError(String),
#[error("Storage error: {0}")]
StorageError(String),
#[error("Query error: {0}")]
QueryError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("Serialization error: {0}")]
SerializationError(#[from] serde_json::Error),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_analytics_config_default() {
let config = AnalyticsConfig::default();
assert!(config.real_time_enabled);
assert_eq!(config.data_retention_days, 90);
assert!(config.compliance_monitoring);
}
#[test]
fn test_time_range_creation() {
let range = TimeRange::last_hours(24);
assert!(range.end > range.start);
let today = TimeRange::today();
assert!(today.end > today.start);
}
#[tokio::test]
async fn test_analytics_manager_creation() {
let config = AnalyticsConfig::default();
let manager = AnalyticsManager::new(config);
assert_eq!(manager.event_buffer.len(), 0);
}
#[tokio::test]
async fn test_record_event() {
let config = AnalyticsConfig::default();
let mut manager = AnalyticsManager::new(config);
let event = AnalyticsEvent {
id: "test_event_1".to_string(),
event_type: RbacEventType::PermissionCheck,
timestamp: chrono::Utc::now(),
user_id: Some("user123".to_string()),
role_id: Some("admin".to_string()),
resource: Some("user_data".to_string()),
action: Some("read".to_string()),
result: EventResult::Success,
metadata: HashMap::new(),
duration_ms: Some(15),
source_ip: Some("192.168.1.1".to_string()),
user_agent: Some("TestAgent/1.0".to_string()),
};
let result = manager.record_event(event).await;
assert!(result.is_ok());
assert_eq!(manager.event_buffer.len(), 1);
}
}