1impl 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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub enum AuditEventType {
20 LoginSuccess,
22 LoginFailure,
23 Logout,
24 TokenRefresh,
25 TokenExpired,
26 TokenRevoked,
27
28 MfaSetup,
30 MfaChallengeCreated,
31 MfaVerificationSuccess,
32 MfaVerificationFailure,
33 MfaMethodEnabled,
34 MfaMethodDisabled,
35
36 PermissionGranted,
38 PermissionDenied,
39 RoleAssigned,
40 RoleRevoked,
41 RoleCreated,
42 RoleUpdated,
43 RoleDeleted,
44
45 UserCreated,
47 UserUpdated,
48 UserDeleted,
49 UserActivated,
50 UserDeactivated,
51 UserPasswordChanged,
52 UserPasswordReset,
53
54 AccountLocked,
56 AccountUnlocked,
57 SuspiciousActivity,
58 BruteForceDetected,
59 RateLimitExceeded,
60 SecurityPolicyViolation,
61 SecurityViolation,
62
63 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#[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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct AuditEvent {
163 pub id: String,
165 pub event_type: AuditEventType,
167 pub timestamp: SystemTime,
169 pub user_id: Option<String>,
171 pub session_id: Option<String>,
173 pub outcome: EventOutcome,
175 pub risk_level: RiskLevel,
177 pub description: String,
179 pub details: HashMap<String, String>,
181 pub request_metadata: RequestMetadata,
183 pub resource: Option<ResourceInfo>,
185 pub actor: ActorInfo,
187 pub correlation_id: Option<String>,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct RequestMetadata {
194 pub ip_address: Option<String>,
196 pub user_agent: Option<String>,
198 pub request_id: Option<String>,
200 pub endpoint: Option<String>,
202 pub http_method: Option<String>,
204 pub geolocation: Option<GeolocationInfo>,
206 pub device_info: Option<DeviceInfo>,
208}
209
210#[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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct ResourceInfo {
233 pub resource_type: String,
235 pub resource_id: String,
237 pub resource_name: Option<String>,
239 pub attributes: HashMap<String, String>,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct ActorInfo {
246 pub actor_type: String,
248 pub actor_id: String,
250 pub actor_name: Option<String>,
252 pub roles: Vec<String>,
254}
255
256#[async_trait]
258pub trait AuditStorage: Send + Sync {
259 async fn store_event(&self, event: &AuditEvent) -> Result<()>;
261
262 async fn query_events(&self, query: &AuditQuery) -> Result<Vec<AuditEvent>>;
264
265 async fn get_event(&self, event_id: &str) -> Result<Option<AuditEvent>>;
267
268 async fn count_events(&self, query: &AuditQuery) -> Result<u64>;
270
271 async fn delete_old_events(&self, before: SystemTime) -> Result<u64>;
273
274 async fn get_statistics(&self, query: &StatsQuery) -> Result<AuditStatistics>;
276}
277
278#[derive(Debug, Clone)]
280pub struct AuditQuery {
281 pub event_types: Option<Vec<AuditEventType>>,
283 pub user_id: Option<String>,
285 pub risk_level: Option<RiskLevel>,
287 pub outcome: Option<EventOutcome>,
289 pub time_range: Option<TimeRange>,
291 pub ip_address: Option<String>,
293 pub resource_type: Option<String>,
295 pub actor_id: Option<String>,
297 pub correlation_id: Option<String>,
299 pub limit: Option<u64>,
301 pub offset: Option<u64>,
303 pub sort_order: SortOrder,
305}
306
307#[derive(Debug, Clone)]
309pub struct TimeRange {
310 pub start: SystemTime,
311 pub end: SystemTime,
312}
313
314#[derive(Debug, Clone)]
316pub enum SortOrder {
317 TimestampAsc,
318 TimestampDesc,
319 RiskLevelDesc,
320}
321
322#[derive(Debug, Clone)]
324pub struct StatsQuery {
325 pub time_range: TimeRange,
326 pub group_by: Vec<StatsGroupBy>,
327}
328
329#[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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct TimeSeriesPoint {
357 pub timestamp: SystemTime,
358 pub count: u64,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
363pub struct UserEventCount {
364 pub user_id: String,
365 pub event_count: u64,
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize)]
370pub struct IpEventCount {
371 pub ip_address: String,
372 pub event_count: u64,
373}
374
375pub struct AuditLogger<S: AuditStorage> {
377 storage: S,
378 correlation_generator: CorrelationIdGenerator,
379}
380
381impl<S: AuditStorage> AuditLogger<S> {
382 pub fn new(storage: S) -> Self {
384 Self {
385 storage,
386 correlation_generator: CorrelationIdGenerator::new(),
387 }
388 }
389
390 pub async fn log_event(&self, mut event: AuditEvent) -> Result<()> {
392 if event.id.is_empty() {
394 event.id = uuid::Uuid::new_v4().to_string();
395 }
396
397 if event.timestamp == SystemTime::UNIX_EPOCH {
399 event.timestamp = SystemTime::now();
400 }
401
402 self.storage.store_event(&event).await?;
404
405 self.check_security_alerts(&event).await?;
407
408 Ok(())
409 }
410
411 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 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 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 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 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 tracing::warn!(
528 event_type = ?event.event_type,
529 user_id = ?event.user_id,
530 "Security-relevant audit event recorded"
531 );
532 }
533 _ => {}
535 }
536 Ok(())
537 }
538
539 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 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 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 pub async fn query_events(&self, query: &AuditQuery) -> Result<Vec<AuditEvent>> {
597 self.storage.query_events(query).await
598 }
599
600 pub async fn get_statistics(&self, query: &StatsQuery) -> Result<AuditStatistics> {
602 self.storage.get_statistics(query).await
603 }
604
605 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 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 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 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 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 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 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 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 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 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 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 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#[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 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 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 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
938pub 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
965pub struct AuditEventBuilder {
989 event: AuditEvent,
990}
991
992impl AuditEvent {
993 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 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 pub fn user_id(mut self, id: impl Into<String>) -> Self {
1031 self.event.user_id = Some(id.into());
1032 self
1033 }
1034
1035 pub fn session_id(mut self, id: impl Into<String>) -> Self {
1037 self.event.session_id = Some(id.into());
1038 self
1039 }
1040
1041 pub fn outcome(mut self, outcome: EventOutcome) -> Self {
1043 self.event.outcome = outcome;
1044 self
1045 }
1046
1047 pub fn risk_level(mut self, level: RiskLevel) -> Self {
1049 self.event.risk_level = level;
1050 self
1051 }
1052
1053 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 pub fn actor(mut self, actor: ActorInfo) -> Self {
1066 self.event.actor = actor;
1067 self
1068 }
1069
1070 pub fn request_metadata(mut self, metadata: RequestMetadata) -> Self {
1072 self.event.request_metadata = metadata;
1073 self
1074 }
1075
1076 pub fn resource(mut self, resource: ResourceInfo) -> Self {
1078 self.event.resource = Some(resource);
1079 self
1080 }
1081
1082 pub fn correlation_id(mut self, id: impl Into<String>) -> Self {
1084 self.event.correlation_id = Some(id.into());
1085 self
1086 }
1087
1088 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 pub fn details(mut self, details: HashMap<String, String>) -> Self {
1096 self.event.details = details;
1097 self
1098 }
1099
1100 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
1115impl 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
1142pub struct AuditQueryBuilder {
1160 query: AuditQuery,
1161}
1162
1163impl AuditQuery {
1164 pub fn builder() -> AuditQueryBuilder {
1166 AuditQueryBuilder {
1167 query: AuditQuery::default(),
1168 }
1169 }
1170}
1171
1172impl AuditQueryBuilder {
1173 pub fn event_types(mut self, types: Vec<AuditEventType>) -> Self {
1175 self.query.event_types = Some(types);
1176 self
1177 }
1178
1179 pub fn user_id(mut self, id: impl Into<String>) -> Self {
1181 self.query.user_id = Some(id.into());
1182 self
1183 }
1184
1185 pub fn risk_level(mut self, level: RiskLevel) -> Self {
1187 self.query.risk_level = Some(level);
1188 self
1189 }
1190
1191 pub fn outcome(mut self, outcome: EventOutcome) -> Self {
1193 self.query.outcome = Some(outcome);
1194 self
1195 }
1196
1197 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 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 pub fn last_24h(self) -> Self {
1214 self.last_seconds(24 * 60 * 60)
1215 }
1216
1217 pub fn ip_address(mut self, ip: impl Into<String>) -> Self {
1219 self.query.ip_address = Some(ip.into());
1220 self
1221 }
1222
1223 pub fn resource_type(mut self, rt: impl Into<String>) -> Self {
1225 self.query.resource_type = Some(rt.into());
1226 self
1227 }
1228
1229 pub fn actor_id(mut self, id: impl Into<String>) -> Self {
1231 self.query.actor_id = Some(id.into());
1232 self
1233 }
1234
1235 pub fn correlation_id(mut self, id: impl Into<String>) -> Self {
1237 self.query.correlation_id = Some(id.into());
1238 self
1239 }
1240
1241 pub fn limit(mut self, limit: u64) -> Self {
1243 self.query.limit = Some(limit);
1244 self
1245 }
1246
1247 pub fn offset(mut self, offset: u64) -> Self {
1249 self.query.offset = Some(offset);
1250 self
1251 }
1252
1253 pub fn sort_order(mut self, order: SortOrder) -> Self {
1255 self.query.sort_order = order;
1256 self
1257 }
1258
1259 pub fn build(self) -> AuditQuery {
1261 self.query
1262 }
1263}
1264
1265impl 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 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}