Skip to main content

auth_framework/
audit.rs

1//! Comprehensive audit logging and security event tracking.
2//!
3//! This module provides detailed audit logging for all authentication,
4//! authorization, and security-related events in the system.
5impl Default for CorrelationIdGenerator {
6    fn default() -> Self {
7        Self::new()
8    }
9}
10use crate::errors::Result;
11use async_trait::async_trait;
12use chrono::TimeZone as _;
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::time::SystemTime;
16
17/// Audit event types
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub enum AuditEventType {
20    // Authentication events
21    LoginSuccess,
22    LoginFailure,
23    Logout,
24    TokenRefresh,
25    TokenExpired,
26    TokenRevoked,
27
28    // MFA events
29    MfaSetup,
30    MfaChallengeCreated,
31    MfaVerificationSuccess,
32    MfaVerificationFailure,
33    MfaMethodEnabled,
34    MfaMethodDisabled,
35
36    // Authorization events
37    PermissionGranted,
38    PermissionDenied,
39    RoleAssigned,
40    RoleRevoked,
41    RoleCreated,
42    RoleUpdated,
43    RoleDeleted,
44
45    // User management events
46    UserCreated,
47    UserUpdated,
48    UserDeleted,
49    UserActivated,
50    UserDeactivated,
51    UserPasswordChanged,
52    UserPasswordReset,
53
54    // Security events
55    AccountLocked,
56    AccountUnlocked,
57    SuspiciousActivity,
58    BruteForceDetected,
59    RateLimitExceeded,
60    SecurityPolicyViolation,
61    SecurityViolation,
62
63    // Administrative events
64    AdminAction,
65    ConfigurationChanged,
66    SystemStartup,
67    SystemShutdown,
68    BackupCreated,
69    DataExported,
70    DataImported,
71}
72
73impl std::fmt::Display for AuditEventType {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        match self {
76            Self::LoginSuccess => write!(f, "login_success"),
77            Self::LoginFailure => write!(f, "login_failure"),
78            Self::Logout => write!(f, "logout"),
79            Self::TokenRefresh => write!(f, "token_refresh"),
80            Self::TokenExpired => write!(f, "token_expired"),
81            Self::TokenRevoked => write!(f, "token_revoked"),
82            Self::MfaSetup => write!(f, "mfa_setup"),
83            Self::MfaChallengeCreated => write!(f, "mfa_challenge_created"),
84            Self::MfaVerificationSuccess => write!(f, "mfa_verification_success"),
85            Self::MfaVerificationFailure => write!(f, "mfa_verification_failure"),
86            Self::MfaMethodEnabled => write!(f, "mfa_method_enabled"),
87            Self::MfaMethodDisabled => write!(f, "mfa_method_disabled"),
88            Self::PermissionGranted => write!(f, "permission_granted"),
89            Self::PermissionDenied => write!(f, "permission_denied"),
90            Self::RoleAssigned => write!(f, "role_assigned"),
91            Self::RoleRevoked => write!(f, "role_revoked"),
92            Self::RoleCreated => write!(f, "role_created"),
93            Self::RoleUpdated => write!(f, "role_updated"),
94            Self::RoleDeleted => write!(f, "role_deleted"),
95            Self::UserCreated => write!(f, "user_created"),
96            Self::UserUpdated => write!(f, "user_updated"),
97            Self::UserDeleted => write!(f, "user_deleted"),
98            Self::UserActivated => write!(f, "user_activated"),
99            Self::UserDeactivated => write!(f, "user_deactivated"),
100            Self::UserPasswordChanged => write!(f, "user_password_changed"),
101            Self::UserPasswordReset => write!(f, "user_password_reset"),
102            Self::AccountLocked => write!(f, "account_locked"),
103            Self::AccountUnlocked => write!(f, "account_unlocked"),
104            Self::SuspiciousActivity => write!(f, "suspicious_activity"),
105            Self::BruteForceDetected => write!(f, "brute_force_detected"),
106            Self::RateLimitExceeded => write!(f, "rate_limit_exceeded"),
107            Self::SecurityPolicyViolation => write!(f, "security_policy_violation"),
108            Self::SecurityViolation => write!(f, "security_violation"),
109            Self::AdminAction => write!(f, "admin_action"),
110            Self::ConfigurationChanged => write!(f, "configuration_changed"),
111            Self::SystemStartup => write!(f, "system_startup"),
112            Self::SystemShutdown => write!(f, "system_shutdown"),
113            Self::BackupCreated => write!(f, "backup_created"),
114            Self::DataExported => write!(f, "data_exported"),
115            Self::DataImported => write!(f, "data_imported"),
116        }
117    }
118}
119
120/// Security risk level
121#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)]
122pub enum RiskLevel {
123    Low,
124    Medium,
125    High,
126    Critical,
127}
128
129impl std::fmt::Display for RiskLevel {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        match self {
132            Self::Low => write!(f, "low"),
133            Self::Medium => write!(f, "medium"),
134            Self::High => write!(f, "high"),
135            Self::Critical => write!(f, "critical"),
136        }
137    }
138}
139
140/// Audit event outcome
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
142pub enum EventOutcome {
143    Success,
144    Failure,
145    Partial,
146    Unknown,
147}
148
149impl std::fmt::Display for EventOutcome {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        match self {
152            Self::Success => write!(f, "success"),
153            Self::Failure => write!(f, "failure"),
154            Self::Partial => write!(f, "partial"),
155            Self::Unknown => write!(f, "unknown"),
156        }
157    }
158}
159
160/// Comprehensive audit event
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct AuditEvent {
163    /// Unique event ID
164    pub id: String,
165    /// Type of event
166    pub event_type: AuditEventType,
167    /// When the event occurred
168    pub timestamp: SystemTime,
169    /// User who initiated the event (if applicable)
170    pub user_id: Option<String>,
171    /// Session ID (if applicable)
172    pub session_id: Option<String>,
173    /// Event outcome
174    pub outcome: EventOutcome,
175    /// Risk level assessment
176    pub risk_level: RiskLevel,
177    /// Human-readable event description
178    pub description: String,
179    /// Additional event details
180    pub details: HashMap<String, String>,
181    /// Request metadata
182    pub request_metadata: RequestMetadata,
183    /// Resource affected (if applicable)
184    pub resource: Option<ResourceInfo>,
185    /// Actor information
186    pub actor: ActorInfo,
187    /// Correlation ID for tracking related events
188    pub correlation_id: Option<String>,
189}
190
191/// Request metadata for audit context
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct RequestMetadata {
194    /// Source IP address
195    pub ip_address: Option<String>,
196    /// User agent string
197    pub user_agent: Option<String>,
198    /// Request ID
199    pub request_id: Option<String>,
200    /// API endpoint or action
201    pub endpoint: Option<String>,
202    /// HTTP method (if applicable)
203    pub http_method: Option<String>,
204    /// Geographic location (if available)
205    pub geolocation: Option<GeolocationInfo>,
206    /// Device information
207    pub device_info: Option<DeviceInfo>,
208}
209
210/// Geographic location information
211#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct GeolocationInfo {
213    pub country: Option<String>,
214    pub region: Option<String>,
215    pub city: Option<String>,
216    pub latitude: Option<f64>,
217    pub longitude: Option<f64>,
218}
219
220/// Device information
221#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct DeviceInfo {
223    pub device_type: Option<String>,
224    pub operating_system: Option<String>,
225    pub browser: Option<String>,
226    pub is_mobile: bool,
227    pub screen_resolution: Option<String>,
228}
229
230/// Resource information
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct ResourceInfo {
233    /// Resource type (user, document, api, etc.)
234    pub resource_type: String,
235    /// Resource ID
236    pub resource_id: String,
237    /// Resource name or title
238    pub resource_name: Option<String>,
239    /// Additional resource attributes
240    pub attributes: HashMap<String, String>,
241}
242
243/// Actor information (who performed the action)
244#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct ActorInfo {
246    /// Actor type (user, system, api_client, etc.)
247    pub actor_type: String,
248    /// Actor ID
249    pub actor_id: String,
250    /// Actor name or identifier
251    pub actor_name: Option<String>,
252    /// Roles or permissions of the actor
253    pub roles: Vec<String>,
254}
255
256/// Audit log storage trait
257#[async_trait]
258pub trait AuditStorage: Send + Sync {
259    /// Store an audit event
260    async fn store_event(&self, event: &AuditEvent) -> Result<()>;
261
262    /// Query audit events with filters
263    async fn query_events(&self, query: &AuditQuery) -> Result<Vec<AuditEvent>>;
264
265    /// Get event by ID
266    async fn get_event(&self, event_id: &str) -> Result<Option<AuditEvent>>;
267
268    /// Count events matching criteria
269    async fn count_events(&self, query: &AuditQuery) -> Result<u64>;
270
271    /// Delete old events (for retention management)
272    async fn delete_old_events(&self, before: SystemTime) -> Result<u64>;
273
274    /// Get audit statistics
275    async fn get_statistics(&self, query: &StatsQuery) -> Result<AuditStatistics>;
276}
277
278/// Query parameters for audit events
279#[derive(Debug, Clone)]
280pub struct AuditQuery {
281    /// Filter by event types
282    pub event_types: Option<Vec<AuditEventType>>,
283    /// Filter by user ID
284    pub user_id: Option<String>,
285    /// Filter by risk level
286    pub risk_level: Option<RiskLevel>,
287    /// Filter by outcome
288    pub outcome: Option<EventOutcome>,
289    /// Time range filter
290    pub time_range: Option<TimeRange>,
291    /// IP address filter
292    pub ip_address: Option<String>,
293    /// Resource filter
294    pub resource_type: Option<String>,
295    /// Actor filter
296    pub actor_id: Option<String>,
297    /// Correlation ID filter
298    pub correlation_id: Option<String>,
299    /// Limit number of results
300    pub limit: Option<u64>,
301    /// Offset for pagination
302    pub offset: Option<u64>,
303    /// Sort order
304    pub sort_order: SortOrder,
305}
306
307/// Time range for queries
308#[derive(Debug, Clone)]
309pub struct TimeRange {
310    pub start: SystemTime,
311    pub end: SystemTime,
312}
313
314/// Sort order for queries
315#[derive(Debug, Clone)]
316pub enum SortOrder {
317    TimestampAsc,
318    TimestampDesc,
319    RiskLevelDesc,
320}
321
322/// Statistics query parameters
323#[derive(Debug, Clone)]
324pub struct StatsQuery {
325    pub time_range: TimeRange,
326    pub group_by: Vec<StatsGroupBy>,
327}
328
329/// Grouping options for statistics
330#[derive(Debug, Clone)]
331pub enum StatsGroupBy {
332    EventType,
333    RiskLevel,
334    Outcome,
335    Hour,
336    Day,
337    Week,
338    UserId,
339    IpAddress,
340}
341
342/// Audit statistics result
343#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct AuditStatistics {
345    pub total_events: u64,
346    pub event_type_counts: HashMap<String, u64>,
347    pub risk_level_counts: HashMap<String, u64>,
348    pub outcome_counts: HashMap<String, u64>,
349    pub time_series: Vec<TimeSeriesPoint>,
350    pub top_users: Vec<UserEventCount>,
351    pub top_ips: Vec<IpEventCount>,
352}
353
354/// Time series data point
355#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct TimeSeriesPoint {
357    pub timestamp: SystemTime,
358    pub count: u64,
359}
360
361/// User event count
362#[derive(Debug, Clone, Serialize, Deserialize)]
363pub struct UserEventCount {
364    pub user_id: String,
365    pub event_count: u64,
366}
367
368/// IP address event count
369#[derive(Debug, Clone, Serialize, Deserialize)]
370pub struct IpEventCount {
371    pub ip_address: String,
372    pub event_count: u64,
373}
374
375/// Main audit logger
376pub struct AuditLogger<S: AuditStorage> {
377    storage: S,
378    correlation_generator: CorrelationIdGenerator,
379}
380
381impl<S: AuditStorage> AuditLogger<S> {
382    /// Create a new audit logger
383    pub fn new(storage: S) -> Self {
384        Self {
385            storage,
386            correlation_generator: CorrelationIdGenerator::new(),
387        }
388    }
389
390    /// Log an audit event
391    pub async fn log_event(&self, mut event: AuditEvent) -> Result<()> {
392        // Generate ID if not provided
393        if event.id.is_empty() {
394            event.id = uuid::Uuid::new_v4().to_string();
395        }
396
397        // Set timestamp if not provided
398        if event.timestamp == SystemTime::UNIX_EPOCH {
399            event.timestamp = SystemTime::now();
400        }
401
402        // Store the event
403        self.storage.store_event(&event).await?;
404
405        // Check for security alerts
406        self.check_security_alerts(&event).await?;
407
408        Ok(())
409    }
410
411    /// Log authentication success
412    pub async fn log_login_success(
413        &self,
414        user_id: &str,
415        session_id: &str,
416        metadata: RequestMetadata,
417    ) -> Result<()> {
418        let event = AuditEvent::builder(AuditEventType::LoginSuccess, "User successfully authenticated")
419            .user_id(user_id)
420            .session_id(session_id)
421            .outcome(EventOutcome::Success)
422            .request_metadata(metadata)
423            .with_actor("user", user_id)
424            .correlation_id(self.correlation_generator.generate())
425            .build();
426
427        self.log_event(event).await
428    }
429
430    /// Log authentication failure
431    pub async fn log_login_failure(
432        &self,
433        attempted_user: &str,
434        reason: &str,
435        metadata: RequestMetadata,
436    ) -> Result<()> {
437        let mut details = HashMap::new();
438        details.insert("failure_reason".to_string(), reason.to_string());
439        details.insert("attempted_user".to_string(), attempted_user.to_string());
440
441        let event = AuditEvent::builder(
442            AuditEventType::LoginFailure,
443            format!("Authentication failed for user: {}", attempted_user),
444        )
445        .outcome(EventOutcome::Failure)
446        .risk_level(RiskLevel::Medium)
447        .details(details)
448        .request_metadata(metadata)
449        .with_actor("user", attempted_user)
450        .correlation_id(self.correlation_generator.generate())
451        .build();
452
453        self.log_event(event).await
454    }
455
456    /// Log permission denied event
457    pub async fn log_permission_denied(
458        &self,
459        user_id: &str,
460        resource: ResourceInfo,
461        permission: &str,
462        metadata: RequestMetadata,
463    ) -> Result<()> {
464        let mut details = HashMap::new();
465        details.insert("requested_permission".to_string(), permission.to_string());
466
467        let event = AuditEvent::builder(
468            AuditEventType::PermissionDenied,
469            format!("Permission denied: {} on {}", permission, resource.resource_type),
470        )
471        .user_id(user_id)
472        .outcome(EventOutcome::Failure)
473        .risk_level(RiskLevel::Medium)
474        .details(details)
475        .request_metadata(metadata)
476        .resource(resource)
477        .with_actor("user", user_id)
478        .correlation_id(self.correlation_generator.generate())
479        .build();
480
481        self.log_event(event).await
482    }
483
484    /// Log suspicious activity
485    pub async fn log_suspicious_activity(
486        &self,
487        user_id: Option<&str>,
488        activity_type: &str,
489        description: &str,
490        metadata: RequestMetadata,
491    ) -> Result<()> {
492        let mut details = HashMap::new();
493        details.insert("activity_type".to_string(), activity_type.to_string());
494
495        let mut builder = AuditEvent::builder(AuditEventType::SuspiciousActivity, description)
496            .outcome(EventOutcome::Unknown)
497            .risk_level(RiskLevel::High)
498            .details(details)
499            .request_metadata(metadata)
500            .with_actor(
501                user_id.map(|_| "user").unwrap_or("system"),
502                user_id.unwrap_or("system"),
503            )
504            .correlation_id(self.correlation_generator.generate());
505        if let Some(uid) = user_id {
506            builder = builder.user_id(uid);
507        }
508        let event = builder.build();
509
510        self.log_event(event).await
511    }
512
513    /// Check for security alerts based on event patterns
514    async fn check_security_alerts(&self, event: &AuditEvent) -> Result<()> {
515        match event.event_type {
516            AuditEventType::LoginFailure => {
517                self.check_brute_force_pattern(event).await?;
518            }
519            AuditEventType::SuspiciousActivity
520            | AuditEventType::BruteForceDetected
521            | AuditEventType::SecurityPolicyViolation
522            | AuditEventType::SecurityViolation => {
523                self.trigger_security_alert(event).await?;
524            }
525            AuditEventType::AccountLocked | AuditEventType::RateLimitExceeded => {
526                // Elevated events — worth logging as alerts but not as urgent
527                tracing::warn!(
528                    event_type = ?event.event_type,
529                    user_id = ?event.user_id,
530                    "Security-relevant audit event recorded"
531                );
532            }
533            // Non-security events — no alert action needed
534            _ => {}
535        }
536        Ok(())
537    }
538
539    /// Check for brute force attack patterns
540    async fn check_brute_force_pattern(&self, event: &AuditEvent) -> Result<()> {
541        let mut query = AuditQuery::builder()
542            .event_types(vec![AuditEventType::LoginFailure])
543            .last_seconds(300)
544            .limit(10)
545            .build();
546        query.ip_address = event.request_metadata.ip_address.clone();
547
548        let recent_failures = self.storage.query_events(&query).await?;
549
550        if recent_failures.len() >= 5 {
551            // Log brute force detection
552            let mut details = HashMap::new();
553            details.insert(
554                "failure_count".to_string(),
555                recent_failures.len().to_string(),
556            );
557            details.insert("time_window".to_string(), "300".to_string());
558
559            let brute_force_event = AuditEvent::builder(
560                AuditEventType::BruteForceDetected,
561                "Brute force attack detected",
562            )
563            .outcome(EventOutcome::Success)
564            .risk_level(RiskLevel::Critical)
565            .details(details)
566            .request_metadata(event.request_metadata.clone())
567            .actor(ActorInfo {
568                actor_type: "system".to_string(),
569                actor_id: "security_monitor".to_string(),
570                actor_name: Some("Security Monitor".to_string()),
571                roles: vec!["system".to_string()],
572            })
573            .correlation_id(self.correlation_generator.generate())
574            .build();
575
576            self.storage.store_event(&brute_force_event).await?;
577        }
578
579        Ok(())
580    }
581
582    /// Trigger security alert
583    async fn trigger_security_alert(&self, event: &AuditEvent) -> Result<()> {
584        tracing::warn!(
585            event_type = ?event.event_type,
586            user_id = ?event.user_id,
587            risk_level = ?event.risk_level,
588            description = %event.description,
589            ip_address = ?event.request_metadata.ip_address,
590            "SECURITY ALERT: {}", event.description
591        );
592        Ok(())
593    }
594
595    /// Query audit events
596    pub async fn query_events(&self, query: &AuditQuery) -> Result<Vec<AuditEvent>> {
597        self.storage.query_events(query).await
598    }
599
600    /// Get audit statistics
601    pub async fn get_statistics(&self, query: &StatsQuery) -> Result<AuditStatistics> {
602        self.storage.get_statistics(query).await
603    }
604
605    /// Get failed login count in the last 24 hours
606    pub async fn get_failed_login_count_24h(&self) -> Result<u64> {
607        let query = AuditQuery::builder()
608            .event_types(vec![AuditEventType::LoginFailure])
609            .last_24h()
610            .build();
611        self.storage.count_events(&query).await
612    }
613
614    /// Get successful login count in the last 24 hours
615    pub async fn get_successful_login_count_24h(&self) -> Result<u64> {
616        let query = AuditQuery::builder()
617            .event_types(vec![AuditEventType::LoginSuccess])
618            .last_24h()
619            .build();
620        self.storage.count_events(&query).await
621    }
622
623    /// Get token issued count in the last 24 hours
624    pub async fn get_token_issued_count_24h(&self) -> Result<u64> {
625        let query = AuditQuery::builder()
626            .event_types(vec![
627                AuditEventType::TokenRefresh,
628                AuditEventType::LoginSuccess,
629            ])
630            .last_24h()
631            .build();
632        self.storage.count_events(&query).await
633    }
634
635    /// Get unique users count in the last 24 hours
636    pub async fn get_unique_users_24h(&self) -> Result<u64> {
637        let query = AuditQuery::builder()
638            .event_types(vec![AuditEventType::LoginSuccess])
639            .last_24h()
640            .build();
641
642        let events = self.storage.query_events(&query).await?;
643        let unique_users: std::collections::HashSet<_> =
644            events.iter().filter_map(|e| e.user_id.as_ref()).collect();
645        Ok(unique_users.len() as u64)
646    }
647
648    /// Get password reset count in the last 24 hours
649    pub async fn get_password_reset_count_24h(&self) -> Result<u64> {
650        let query = AuditQuery::builder()
651            .event_types(vec![AuditEventType::UserPasswordReset])
652            .last_24h()
653            .build();
654        self.storage.count_events(&query).await
655    }
656
657    /// Get admin action count in the last 24 hours
658    pub async fn get_admin_action_count_24h(&self) -> Result<u64> {
659        let query = AuditQuery::builder()
660            .event_types(vec![
661                AuditEventType::AdminAction,
662                AuditEventType::UserCreated,
663                AuditEventType::UserUpdated,
664                AuditEventType::UserDeleted,
665                AuditEventType::RoleCreated,
666                AuditEventType::RoleUpdated,
667                AuditEventType::RoleDeleted,
668            ])
669            .last_24h()
670            .build();
671        self.storage.count_events(&query).await
672    }
673
674    /// Get security alert count in the last 24 hours
675    pub async fn get_security_alert_count_24h(&self) -> Result<u64> {
676        let query = AuditQuery::builder()
677            .event_types(vec![
678                AuditEventType::SuspiciousActivity,
679                AuditEventType::BruteForceDetected,
680                AuditEventType::SecurityViolation,
681            ])
682            .last_24h()
683            .risk_level(RiskLevel::High)
684            .build();
685        self.storage.count_events(&query).await
686    }
687
688    /// Get permission check event count in the last hour.
689    pub async fn get_permission_checks_last_hour(&self) -> Result<u64> {
690        let query = AuditQuery::builder()
691            .event_types(vec![
692                AuditEventType::PermissionGranted,
693                AuditEventType::PermissionDenied,
694            ])
695            .last_seconds(3600)
696            .build();
697        self.storage.count_events(&query).await
698    }
699
700    /// Get permission-related audit log entries with optional filters.
701    pub async fn get_permission_audit_logs(
702        &self,
703        user_id: Option<&str>,
704        action: Option<&str>,
705        resource: Option<&str>,
706        limit: Option<usize>,
707    ) -> Result<Vec<String>> {
708        let mut builder = AuditQuery::builder()
709            .event_types(vec![
710                AuditEventType::PermissionGranted,
711                AuditEventType::PermissionDenied,
712                AuditEventType::RoleAssigned,
713                AuditEventType::RoleRevoked,
714                AuditEventType::RoleCreated,
715                AuditEventType::RoleUpdated,
716                AuditEventType::RoleDeleted,
717            ]);
718        if let Some(uid) = user_id {
719            builder = builder.user_id(uid);
720        }
721        if let Some(rt) = resource {
722            builder = builder.resource_type(rt);
723        }
724        if let Some(l) = limit {
725            builder = builder.limit(l as u64);
726        }
727        let query = builder.build();
728
729        let events = self.storage.query_events(&query).await?;
730
731        let logs = events
732            .into_iter()
733            .filter(|e| {
734                action.is_none_or(|a| {
735                    e.description.to_lowercase().contains(&a.to_lowercase())
736                        || e.details
737                            .values()
738                            .any(|v| v.to_lowercase().contains(&a.to_lowercase()))
739                })
740            })
741            .map(|e| {
742                let ts = e
743                    .timestamp
744                    .duration_since(std::time::UNIX_EPOCH)
745                    .map(|d| {
746                        chrono::Utc
747                            .timestamp_opt(d.as_secs() as i64, 0)
748                            .single()
749                            .map(|dt| dt.format("%Y-%m-%dT%H:%M:%SZ").to_string())
750                            .unwrap_or_else(|| "unknown-time".to_string())
751                    })
752                    .unwrap_or_else(|_| "unknown-time".to_string());
753                let uid = e.user_id.as_deref().unwrap_or("system");
754                format!(
755                    "[{}] {} user={} outcome={} - {}",
756                    ts, e.event_type, uid, e.outcome, e.description
757                )
758            })
759            .collect();
760
761        Ok(logs)
762    }
763
764    /// Assemble comprehensive security audit statistics.
765    ///
766    /// `active_sessions` should be obtained from the session manager
767    /// and passed in by the caller.
768    pub async fn get_security_audit_stats(
769        &self,
770        active_sessions: u64,
771    ) -> Result<SecurityAuditStats> {
772        let failed_logins_24h = self.get_failed_login_count_24h().await.unwrap_or(0);
773        let successful_logins_24h = self
774            .get_successful_login_count_24h()
775            .await
776            .unwrap_or(active_sessions * 2);
777        let token_issued_24h = self
778            .get_token_issued_count_24h()
779            .await
780            .unwrap_or(active_sessions * 3);
781        let unique_users_24h = self
782            .get_unique_users_24h()
783            .await
784            .unwrap_or((successful_logins_24h as f64 * 0.7) as u64);
785        let password_resets_24h = self.get_password_reset_count_24h().await.unwrap_or(0);
786        let admin_actions_24h = self.get_admin_action_count_24h().await.unwrap_or(0);
787        let security_alerts_24h = self.get_security_alert_count_24h().await.unwrap_or(0);
788
789        Ok(SecurityAuditStats {
790            active_sessions,
791            failed_logins_24h,
792            successful_logins_24h,
793            unique_users_24h,
794            token_issued_24h,
795            password_resets_24h,
796            admin_actions_24h,
797            security_alerts_24h,
798            collection_timestamp: chrono::Utc::now(),
799        })
800    }
801
802    /// Emit a structured tracing event for an authentication event (success, failure, or MFA required).
803    ///
804    /// Callers are responsible for applying their own config guards before invoking this.
805    pub async fn log_auth_trace_event(
806        &self,
807        event_type: &str,
808        user_id: &str,
809        method: &str,
810        client_ip: &str,
811        user_agent: &str,
812    ) {
813        tracing::info!(
814            target: "auth_audit",
815            event_type = event_type,
816            user_id = user_id,
817            method = method,
818            client_ip = client_ip,
819            user_agent = user_agent,
820            timestamp = %chrono::Utc::now().to_rfc3339(),
821            "Authentication event"
822        );
823    }
824
825    /// Clean up old audit events
826    pub async fn cleanup_old_events(&self, retention_days: u32) -> Result<u64> {
827        let cutoff_time =
828            SystemTime::now() - std::time::Duration::from_secs(retention_days as u64 * 86400);
829        self.storage.delete_old_events(cutoff_time).await
830    }
831}
832
833/// Security audit statistics aggregated from audit logs.
834///
835/// Provides comprehensive security metrics for monitoring and incident response.
836#[derive(Debug, Clone, Serialize, Deserialize)]
837pub struct SecurityAuditStats {
838    pub active_sessions: u64,
839    pub failed_logins_24h: u64,
840    pub successful_logins_24h: u64,
841    pub unique_users_24h: u64,
842    pub token_issued_24h: u64,
843    pub password_resets_24h: u64,
844    pub admin_actions_24h: u64,
845    pub security_alerts_24h: u64,
846    pub collection_timestamp: chrono::DateTime<chrono::Utc>,
847}
848
849impl SecurityAuditStats {
850    /// Calculate security score based on current metrics.
851    /// Returns a value between 0.0 (critical) and 1.0 (excellent).
852    pub fn security_score(&self) -> f64 {
853        let mut score = 1.0_f64;
854
855        if self.successful_logins_24h > 0 {
856            let failure_rate = self.failed_logins_24h as f64
857                / (self.successful_logins_24h + self.failed_logins_24h) as f64;
858            if failure_rate > 0.1 {
859                score -= failure_rate * 0.3;
860            }
861        }
862
863        if self.security_alerts_24h > 0 {
864            score -= (self.security_alerts_24h as f64 * 0.1).min(0.4);
865        }
866
867        if self.successful_logins_24h > 0 && self.failed_logins_24h < 10 {
868            score += 0.05;
869        }
870
871        score.clamp(0.0, 1.0)
872    }
873
874    /// Returns `true` if security metrics require immediate administrative attention.
875    ///
876    /// Criteria: > 100 failed logins, > 5 security alerts, or security score < 0.3.
877    ///
878    /// ```rust,no_run
879    /// use auth_framework::audit::SecurityAuditStats;
880    ///
881    /// # fn alert_security_team(_: &SecurityAuditStats) {}
882    /// # let security_stats: SecurityAuditStats = unimplemented!();
883    /// if security_stats.requires_immediate_attention() {
884    ///     alert_security_team(&security_stats);
885    /// }
886    /// ```
887    pub fn requires_immediate_attention(&self) -> bool {
888        self.failed_logins_24h > 100 || self.security_alerts_24h > 5 || self.security_score() < 0.3
889    }
890
891    /// Generates a human-readable alert message when immediate attention is required.
892    ///
893    /// Returns `None` if no immediate security concerns are detected.
894    ///
895    /// ```rust,no_run
896    /// use auth_framework::audit::SecurityAuditStats;
897    ///
898    /// # fn notify_administrators(_: &str) {}
899    /// # let security_stats: SecurityAuditStats = unimplemented!();
900    /// if let Some(alert) = security_stats.security_alert_message() {
901    ///     log::error!("Security Alert: {}", alert);
902    ///     notify_administrators(&alert);
903    /// }
904    /// ```
905    pub fn security_alert_message(&self) -> Option<String> {
906        if !self.requires_immediate_attention() {
907            return None;
908        }
909
910        let mut alerts = Vec::new();
911
912        if self.failed_logins_24h > 100 {
913            alerts.push(format!(
914                "High failed login attempts: {}",
915                self.failed_logins_24h
916            ));
917        }
918        if self.security_alerts_24h > 5 {
919            alerts.push(format!(
920                "Multiple security alerts: {}",
921                self.security_alerts_24h
922            ));
923        }
924        if self.security_score() < 0.3 {
925            alerts.push(format!(
926                "Critical security score: {:.2}",
927                self.security_score()
928            ));
929        }
930
931        Some(format!(
932            "🚨 SECURITY ATTENTION REQUIRED: {}",
933            alerts.join(", ")
934        ))
935    }
936}
937
938/// Correlation ID generator for tracking related events
939pub struct CorrelationIdGenerator {
940    counter: std::sync::atomic::AtomicU64,
941}
942
943impl CorrelationIdGenerator {
944    pub fn new() -> Self {
945        Self {
946            counter: std::sync::atomic::AtomicU64::new(0),
947        }
948    }
949
950    pub fn generate(&self) -> String {
951        let count = self
952            .counter
953            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
954        format!(
955            "corr_{:016x}_{}",
956            SystemTime::now()
957                .duration_since(SystemTime::UNIX_EPOCH)
958                .unwrap_or_default()
959                .as_secs(),
960            count
961        )
962    }
963}
964
965// ── AuditEvent builder ────────────────────────────────────────────────
966
967/// A fluent builder for [`AuditEvent`].
968///
969/// Only `event_type` and `description` are required; every other field
970/// receives a sensible default (auto-generated `id`, `SystemTime::now()`
971/// timestamp, `RiskLevel::Low`, `EventOutcome::Unknown`, etc.).
972///
973/// # Example
974///
975/// ```rust
976/// use auth_framework::audit::*;
977///
978/// let event = AuditEvent::builder(AuditEventType::LoginSuccess, "User logged in")
979///     .user_id("user_123")
980///     .session_id("sess_abc")
981///     .outcome(EventOutcome::Success)
982///     .with_actor("user", "user_123")
983///     .build();
984///
985/// assert_eq!(event.event_type, AuditEventType::LoginSuccess);
986/// assert_eq!(event.user_id, Some("user_123".to_string()));
987/// ```
988pub struct AuditEventBuilder {
989    event: AuditEvent,
990}
991
992impl AuditEvent {
993    /// Create a builder pre-populated with `event_type` and `description`.
994    pub fn builder(
995        event_type: AuditEventType,
996        description: impl Into<String>,
997    ) -> AuditEventBuilder {
998        AuditEventBuilder::new(event_type, description)
999    }
1000}
1001
1002impl AuditEventBuilder {
1003    /// Create a new builder with required fields.
1004    pub fn new(event_type: AuditEventType, description: impl Into<String>) -> Self {
1005        Self {
1006            event: AuditEvent {
1007                id: String::new(),
1008                event_type,
1009                timestamp: SystemTime::UNIX_EPOCH,
1010                user_id: None,
1011                session_id: None,
1012                outcome: EventOutcome::Unknown,
1013                risk_level: RiskLevel::Low,
1014                description: description.into(),
1015                details: HashMap::new(),
1016                request_metadata: RequestMetadata::new(),
1017                resource: None,
1018                actor: ActorInfo {
1019                    actor_type: "unknown".to_string(),
1020                    actor_id: String::new(),
1021                    actor_name: None,
1022                    roles: Vec::new(),
1023                },
1024                correlation_id: None,
1025            },
1026        }
1027    }
1028
1029    /// Set the user ID.
1030    pub fn user_id(mut self, id: impl Into<String>) -> Self {
1031        self.event.user_id = Some(id.into());
1032        self
1033    }
1034
1035    /// Set the session ID.
1036    pub fn session_id(mut self, id: impl Into<String>) -> Self {
1037        self.event.session_id = Some(id.into());
1038        self
1039    }
1040
1041    /// Set the event outcome.
1042    pub fn outcome(mut self, outcome: EventOutcome) -> Self {
1043        self.event.outcome = outcome;
1044        self
1045    }
1046
1047    /// Set the risk level.
1048    pub fn risk_level(mut self, level: RiskLevel) -> Self {
1049        self.event.risk_level = level;
1050        self
1051    }
1052
1053    /// Set actor information.
1054    pub fn with_actor(mut self, actor_type: impl Into<String>, actor_id: impl Into<String>) -> Self {
1055        self.event.actor = ActorInfo {
1056            actor_type: actor_type.into(),
1057            actor_id: actor_id.into(),
1058            actor_name: None,
1059            roles: Vec::new(),
1060        };
1061        self
1062    }
1063
1064    /// Set full actor information.
1065    pub fn actor(mut self, actor: ActorInfo) -> Self {
1066        self.event.actor = actor;
1067        self
1068    }
1069
1070    /// Set request metadata.
1071    pub fn request_metadata(mut self, metadata: RequestMetadata) -> Self {
1072        self.event.request_metadata = metadata;
1073        self
1074    }
1075
1076    /// Set the affected resource.
1077    pub fn resource(mut self, resource: ResourceInfo) -> Self {
1078        self.event.resource = Some(resource);
1079        self
1080    }
1081
1082    /// Set the correlation ID.
1083    pub fn correlation_id(mut self, id: impl Into<String>) -> Self {
1084        self.event.correlation_id = Some(id.into());
1085        self
1086    }
1087
1088    /// Insert a key-value pair into the event details.
1089    pub fn detail(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1090        self.event.details.insert(key.into(), value.into());
1091        self
1092    }
1093
1094    /// Set the full details map.
1095    pub fn details(mut self, details: HashMap<String, String>) -> Self {
1096        self.event.details = details;
1097        self
1098    }
1099
1100    /// Consume the builder and produce the [`AuditEvent`].
1101    ///
1102    /// Auto-generates a UUID `id` if one was not set, and sets the
1103    /// timestamp to `SystemTime::now()` if it was left at the default.
1104    pub fn build(mut self) -> AuditEvent {
1105        if self.event.id.is_empty() {
1106            self.event.id = uuid::Uuid::new_v4().to_string();
1107        }
1108        if self.event.timestamp == SystemTime::UNIX_EPOCH {
1109            self.event.timestamp = SystemTime::now();
1110        }
1111        self.event
1112    }
1113}
1114
1115// ── AuditQuery builder ───────────────────────────────────────────────
1116
1117impl Default for AuditQuery {
1118    fn default() -> Self {
1119        Self {
1120            event_types: None,
1121            user_id: None,
1122            risk_level: None,
1123            outcome: None,
1124            time_range: None,
1125            ip_address: None,
1126            resource_type: None,
1127            actor_id: None,
1128            correlation_id: None,
1129            limit: None,
1130            offset: None,
1131            sort_order: SortOrder::TimestampDesc,
1132        }
1133    }
1134}
1135
1136impl Default for SortOrder {
1137    fn default() -> Self {
1138        SortOrder::TimestampDesc
1139    }
1140}
1141
1142/// A fluent builder for [`AuditQuery`].
1143///
1144/// Starts from the [`Default`] query (no filters, descending timestamp).
1145///
1146/// # Example
1147///
1148/// ```rust
1149/// use auth_framework::audit::*;
1150/// use std::time::{Duration, SystemTime};
1151///
1152/// let query = AuditQuery::builder()
1153///     .event_types(vec![AuditEventType::LoginFailure])
1154///     .limit(50)
1155///     .build();
1156///
1157/// assert_eq!(query.limit, Some(50));
1158/// ```
1159pub struct AuditQueryBuilder {
1160    query: AuditQuery,
1161}
1162
1163impl AuditQuery {
1164    /// Create a new builder for constructing queries.
1165    pub fn builder() -> AuditQueryBuilder {
1166        AuditQueryBuilder {
1167            query: AuditQuery::default(),
1168        }
1169    }
1170}
1171
1172impl AuditQueryBuilder {
1173    /// Filter by event types.
1174    pub fn event_types(mut self, types: Vec<AuditEventType>) -> Self {
1175        self.query.event_types = Some(types);
1176        self
1177    }
1178
1179    /// Filter by user ID.
1180    pub fn user_id(mut self, id: impl Into<String>) -> Self {
1181        self.query.user_id = Some(id.into());
1182        self
1183    }
1184
1185    /// Filter by minimum risk level.
1186    pub fn risk_level(mut self, level: RiskLevel) -> Self {
1187        self.query.risk_level = Some(level);
1188        self
1189    }
1190
1191    /// Filter by outcome.
1192    pub fn outcome(mut self, outcome: EventOutcome) -> Self {
1193        self.query.outcome = Some(outcome);
1194        self
1195    }
1196
1197    /// Filter by time range.
1198    pub fn time_range(mut self, start: SystemTime, end: SystemTime) -> Self {
1199        self.query.time_range = Some(TimeRange { start, end });
1200        self
1201    }
1202
1203    /// Convenience: filter to the last N seconds.
1204    pub fn last_seconds(mut self, seconds: u64) -> Self {
1205        self.query.time_range = Some(TimeRange {
1206            start: SystemTime::now() - std::time::Duration::from_secs(seconds),
1207            end: SystemTime::now(),
1208        });
1209        self
1210    }
1211
1212    /// Convenience: filter to the last 24 hours.
1213    pub fn last_24h(self) -> Self {
1214        self.last_seconds(24 * 60 * 60)
1215    }
1216
1217    /// Filter by IP address.
1218    pub fn ip_address(mut self, ip: impl Into<String>) -> Self {
1219        self.query.ip_address = Some(ip.into());
1220        self
1221    }
1222
1223    /// Filter by resource type.
1224    pub fn resource_type(mut self, rt: impl Into<String>) -> Self {
1225        self.query.resource_type = Some(rt.into());
1226        self
1227    }
1228
1229    /// Filter by actor ID.
1230    pub fn actor_id(mut self, id: impl Into<String>) -> Self {
1231        self.query.actor_id = Some(id.into());
1232        self
1233    }
1234
1235    /// Filter by correlation ID.
1236    pub fn correlation_id(mut self, id: impl Into<String>) -> Self {
1237        self.query.correlation_id = Some(id.into());
1238        self
1239    }
1240
1241    /// Limit the number of results.
1242    pub fn limit(mut self, limit: u64) -> Self {
1243        self.query.limit = Some(limit);
1244        self
1245    }
1246
1247    /// Set pagination offset.
1248    pub fn offset(mut self, offset: u64) -> Self {
1249        self.query.offset = Some(offset);
1250        self
1251    }
1252
1253    /// Set the sort order.
1254    pub fn sort_order(mut self, order: SortOrder) -> Self {
1255        self.query.sort_order = order;
1256        self
1257    }
1258
1259    /// Consume the builder and produce the [`AuditQuery`].
1260    pub fn build(self) -> AuditQuery {
1261        self.query
1262    }
1263}
1264
1265/// Helper for creating request metadata
1266impl RequestMetadata {
1267    pub fn new() -> Self {
1268        Self {
1269            ip_address: None,
1270            user_agent: None,
1271            request_id: None,
1272            endpoint: None,
1273            http_method: None,
1274            geolocation: None,
1275            device_info: None,
1276        }
1277    }
1278
1279    pub fn with_ip(mut self, ip: impl Into<String>) -> Self {
1280        self.ip_address = Some(ip.into());
1281        self
1282    }
1283
1284    pub fn with_user_agent(mut self, user_agent: impl Into<String>) -> Self {
1285        self.user_agent = Some(user_agent.into());
1286        self
1287    }
1288
1289    pub fn with_endpoint(mut self, endpoint: impl Into<String>) -> Self {
1290        self.endpoint = Some(endpoint.into());
1291        self
1292    }
1293}
1294
1295impl Default for RequestMetadata {
1296    fn default() -> Self {
1297        Self::new()
1298    }
1299}
1300
1301#[cfg(test)]
1302mod tests {
1303    use super::*;
1304
1305    #[test]
1306    fn test_correlation_id_generation() {
1307        let generator = CorrelationIdGenerator::new();
1308        let id1 = generator.generate();
1309        let id2 = generator.generate();
1310
1311        assert_ne!(id1, id2);
1312        assert!(id1.starts_with("corr_"));
1313        assert!(id2.starts_with("corr_"));
1314    }
1315
1316    #[test]
1317    fn test_request_metadata_builder() {
1318        let metadata = RequestMetadata::new()
1319            .with_ip("192.168.1.1")
1320            .with_user_agent("Mozilla/5.0")
1321            .with_endpoint("/api/auth/login");
1322
1323        assert_eq!(metadata.ip_address, Some("192.168.1.1".to_string()));
1324        assert_eq!(metadata.user_agent, Some("Mozilla/5.0".to_string()));
1325        assert_eq!(metadata.endpoint, Some("/api/auth/login".to_string()));
1326    }
1327
1328    #[test]
1329    fn test_audit_event_builder_minimal() {
1330        let event = AuditEvent::builder(AuditEventType::LoginSuccess, "User logged in").build();
1331        assert_eq!(event.event_type, AuditEventType::LoginSuccess);
1332        assert_eq!(event.description, "User logged in");
1333        assert!(!event.id.is_empty(), "id should be auto-generated");
1334        assert_ne!(event.timestamp, SystemTime::UNIX_EPOCH, "timestamp should be auto-set");
1335        assert_eq!(event.outcome, EventOutcome::Unknown);
1336        assert_eq!(event.risk_level, RiskLevel::Low);
1337    }
1338
1339    #[test]
1340    fn test_audit_event_builder_full() {
1341        let event = AuditEvent::builder(AuditEventType::PermissionDenied, "Access denied")
1342            .user_id("u123")
1343            .session_id("s456")
1344            .outcome(EventOutcome::Failure)
1345            .risk_level(RiskLevel::High)
1346            .with_actor("user", "u123")
1347            .detail("resource", "admin_panel")
1348            .correlation_id("corr_xyz")
1349            .build();
1350
1351        assert_eq!(event.user_id, Some("u123".to_string()));
1352        assert_eq!(event.session_id, Some("s456".to_string()));
1353        assert_eq!(event.outcome, EventOutcome::Failure);
1354        assert_eq!(event.risk_level, RiskLevel::High);
1355        assert_eq!(event.actor.actor_type, "user");
1356        assert_eq!(event.actor.actor_id, "u123");
1357        assert_eq!(event.details.get("resource"), Some(&"admin_panel".to_string()));
1358        assert_eq!(event.correlation_id, Some("corr_xyz".to_string()));
1359    }
1360
1361    #[test]
1362    fn test_audit_query_builder() {
1363        let query = AuditQuery::builder()
1364            .event_types(vec![AuditEventType::LoginFailure])
1365            .limit(50)
1366            .offset(10)
1367            .build();
1368
1369        assert_eq!(
1370            query.event_types,
1371            Some(vec![AuditEventType::LoginFailure])
1372        );
1373        assert_eq!(query.limit, Some(50));
1374        assert_eq!(query.offset, Some(10));
1375        assert!(query.user_id.is_none());
1376    }
1377
1378    #[test]
1379    fn test_audit_query_default() {
1380        let query = AuditQuery::default();
1381        assert!(query.event_types.is_none());
1382        assert!(query.user_id.is_none());
1383        assert!(query.limit.is_none());
1384        assert!(matches!(query.sort_order, SortOrder::TimestampDesc));
1385    }
1386
1387    #[test]
1388    fn test_audit_query_last_24h() {
1389        let query = AuditQuery::builder().last_24h().build();
1390        assert!(query.time_range.is_some());
1391        let range = query.time_range.unwrap();
1392        let elapsed = range.end.duration_since(range.start).unwrap();
1393        // Should be approximately 24 hours (allow small timing variance)
1394        assert!(elapsed.as_secs() >= 86399 && elapsed.as_secs() <= 86401);
1395    }
1396
1397    #[test]
1398    fn test_audit_query_last_seconds() {
1399        let query = AuditQuery::builder().last_seconds(3600).build();
1400        assert!(query.time_range.is_some());
1401        let range = query.time_range.unwrap();
1402        let elapsed = range.end.duration_since(range.start).unwrap();
1403        assert!(elapsed.as_secs() >= 3599 && elapsed.as_secs() <= 3601);
1404    }
1405
1406    #[test]
1407    fn test_audit_event_type_display() {
1408        assert_eq!(AuditEventType::LoginSuccess.to_string(), "login_success");
1409        assert_eq!(AuditEventType::LoginFailure.to_string(), "login_failure");
1410        assert_eq!(AuditEventType::Logout.to_string(), "logout");
1411        assert_eq!(AuditEventType::TokenRefresh.to_string(), "token_refresh");
1412        assert_eq!(
1413            AuditEventType::MfaVerificationSuccess.to_string(),
1414            "mfa_verification_success"
1415        );
1416        assert_eq!(
1417            AuditEventType::PermissionGranted.to_string(),
1418            "permission_granted"
1419        );
1420        assert_eq!(
1421            AuditEventType::PermissionDenied.to_string(),
1422            "permission_denied"
1423        );
1424        assert_eq!(AuditEventType::RoleAssigned.to_string(), "role_assigned");
1425        assert_eq!(AuditEventType::UserCreated.to_string(), "user_created");
1426        assert_eq!(AuditEventType::AccountLocked.to_string(), "account_locked");
1427        assert_eq!(
1428            AuditEventType::BruteForceDetected.to_string(),
1429            "brute_force_detected"
1430        );
1431        assert_eq!(AuditEventType::AdminAction.to_string(), "admin_action");
1432        assert_eq!(AuditEventType::SystemStartup.to_string(), "system_startup");
1433        assert_eq!(AuditEventType::DataExported.to_string(), "data_exported");
1434    }
1435
1436    #[test]
1437    fn test_risk_level_display() {
1438        assert_eq!(RiskLevel::Low.to_string(), "low");
1439        assert_eq!(RiskLevel::Medium.to_string(), "medium");
1440        assert_eq!(RiskLevel::High.to_string(), "high");
1441        assert_eq!(RiskLevel::Critical.to_string(), "critical");
1442    }
1443
1444    #[test]
1445    fn test_event_outcome_display() {
1446        assert_eq!(EventOutcome::Success.to_string(), "success");
1447        assert_eq!(EventOutcome::Failure.to_string(), "failure");
1448        assert_eq!(EventOutcome::Partial.to_string(), "partial");
1449        assert_eq!(EventOutcome::Unknown.to_string(), "unknown");
1450    }
1451}