use serde::{Serialize, Deserialize};
use chrono::{DateTime, Utc};
use crate::error::AuditError;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditLogEntry {
pub id: String,
pub timestamp: DateTime<Utc>,
pub event_type: AuditEventType,
pub user: String,
pub resource: String,
pub action: String,
pub result: ActionResult,
pub metadata: AuditMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AuditEventType {
Authentication,
Authorization,
ResourceAccess,
Configuration,
SystemEvent,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ActionResult {
Success,
Failure { reason: String },
Warning { message: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditMetadata {
pub ip_address: String,
pub user_agent: String,
pub session_id: Option<String>,
pub request_id: String,
pub additional_info: serde_json::Value,
}
pub struct AuditLogger {
entries: Arc<RwLock<Vec<AuditLogEntry>>>,
config: AuditConfig,
}
#[derive(Clone)]
pub struct AuditConfig {
pub retention_days: u32,
pub log_level: AuditLogLevel,
pub storage_path: String,
}
#[derive(Clone, PartialEq)]
pub enum AuditLogLevel {
Low,
Medium,
High,
}
impl AuditLogger {
pub fn new(config: AuditConfig) -> Self {
Self {
entries: Arc::new(RwLock::new(Vec::new())),
config,
}
}
pub async fn log_event(&self, entry: AuditLogEntry) -> Result<(), AuditError> {
if !self.should_log(&entry.event_type) {
return Ok(());
}
let mut entries = self.entries.write().await;
entries.push(entry.clone());
self.apply_retention_policy(&mut entries).await?;
self.persist_to_storage(&entry).await?;
Ok(())
}
async fn apply_retention_policy(&self, entries: &mut Vec<AuditLogEntry>) -> Result<(), AuditError> {
let retention_date = Utc::now() - chrono::Duration::days(self.config.retention_days as i64);
entries.retain(|entry| entry.timestamp > retention_date);
Ok(())
}
async fn persist_to_storage(&self, entry: &AuditLogEntry) -> Result<(), AuditError> {
let json = serde_json::to_string_pretty(entry).map_err(|e| AuditError {
error_type: crate::error::AuditErrorType::StorageFailed,
message: e.to_string(),
timestamp: Utc::now().timestamp(),
})?;
let filename = format!("{}/audit_{}.json",
self.config.storage_path,
entry.timestamp.format("%Y%m%d_%H%M%S"));
tokio::fs::write(&filename, json).await.map_err(|e| AuditError {
error_type: crate::error::AuditErrorType::StorageFailed,
message: e.to_string(),
timestamp: Utc::now().timestamp(),
})?;
Ok(())
}
fn should_log(&self, event_type: &AuditEventType) -> bool {
match self.config.log_level {
AuditLogLevel::Low => matches!(event_type, AuditEventType::Authentication | AuditEventType::Authorization),
AuditLogLevel::Medium => !matches!(event_type, AuditEventType::SystemEvent),
AuditLogLevel::High => true,
}
}
}