1impl Default for CorrelationIdGenerator {
6 fn default() -> Self {
7 Self::new()
8 }
9}
10use crate::errors::Result;
11use async_trait::async_trait;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use std::time::SystemTime;
15
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub enum AuditEventType {
19 LoginSuccess,
21 LoginFailure,
22 Logout,
23 TokenRefresh,
24 TokenExpired,
25 TokenRevoked,
26
27 MfaSetup,
29 MfaChallengeCreated,
30 MfaVerificationSuccess,
31 MfaVerificationFailure,
32 MfaMethodEnabled,
33 MfaMethodDisabled,
34
35 PermissionGranted,
37 PermissionDenied,
38 RoleAssigned,
39 RoleRevoked,
40 RoleCreated,
41 RoleUpdated,
42 RoleDeleted,
43
44 UserCreated,
46 UserUpdated,
47 UserDeleted,
48 UserActivated,
49 UserDeactivated,
50 UserPasswordChanged,
51 UserPasswordReset,
52
53 AccountLocked,
55 AccountUnlocked,
56 SuspiciousActivity,
57 BruteForceDetected,
58 RateLimitExceeded,
59 SecurityPolicyViolation,
60 SecurityViolation,
61
62 AdminAction,
64 ConfigurationChanged,
65 SystemStartup,
66 SystemShutdown,
67 BackupCreated,
68 DataExported,
69 DataImported,
70}
71
72#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)]
74pub enum RiskLevel {
75 Low,
76 Medium,
77 High,
78 Critical,
79}
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
83pub enum EventOutcome {
84 Success,
85 Failure,
86 Partial,
87 Unknown,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct AuditEvent {
93 pub id: String,
95 pub event_type: AuditEventType,
97 pub timestamp: SystemTime,
99 pub user_id: Option<String>,
101 pub session_id: Option<String>,
103 pub outcome: EventOutcome,
105 pub risk_level: RiskLevel,
107 pub description: String,
109 pub details: HashMap<String, String>,
111 pub request_metadata: RequestMetadata,
113 pub resource: Option<ResourceInfo>,
115 pub actor: ActorInfo,
117 pub correlation_id: Option<String>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct RequestMetadata {
124 pub ip_address: Option<String>,
126 pub user_agent: Option<String>,
128 pub request_id: Option<String>,
130 pub endpoint: Option<String>,
132 pub http_method: Option<String>,
134 pub geolocation: Option<GeolocationInfo>,
136 pub device_info: Option<DeviceInfo>,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct GeolocationInfo {
143 pub country: Option<String>,
144 pub region: Option<String>,
145 pub city: Option<String>,
146 pub latitude: Option<f64>,
147 pub longitude: Option<f64>,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct DeviceInfo {
153 pub device_type: Option<String>,
154 pub operating_system: Option<String>,
155 pub browser: Option<String>,
156 pub is_mobile: bool,
157 pub screen_resolution: Option<String>,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct ResourceInfo {
163 pub resource_type: String,
165 pub resource_id: String,
167 pub resource_name: Option<String>,
169 pub attributes: HashMap<String, String>,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct ActorInfo {
176 pub actor_type: String,
178 pub actor_id: String,
180 pub actor_name: Option<String>,
182 pub roles: Vec<String>,
184}
185
186#[async_trait]
188pub trait AuditStorage: Send + Sync {
189 async fn store_event(&self, event: &AuditEvent) -> Result<()>;
191
192 async fn query_events(&self, query: &AuditQuery) -> Result<Vec<AuditEvent>>;
194
195 async fn get_event(&self, event_id: &str) -> Result<Option<AuditEvent>>;
197
198 async fn count_events(&self, query: &AuditQuery) -> Result<u64>;
200
201 async fn delete_old_events(&self, before: SystemTime) -> Result<u64>;
203
204 async fn get_statistics(&self, query: &StatsQuery) -> Result<AuditStatistics>;
206}
207
208#[derive(Debug, Clone)]
210pub struct AuditQuery {
211 pub event_types: Option<Vec<AuditEventType>>,
213 pub user_id: Option<String>,
215 pub risk_level: Option<RiskLevel>,
217 pub outcome: Option<EventOutcome>,
219 pub time_range: Option<TimeRange>,
221 pub ip_address: Option<String>,
223 pub resource_type: Option<String>,
225 pub actor_id: Option<String>,
227 pub correlation_id: Option<String>,
229 pub limit: Option<u64>,
231 pub offset: Option<u64>,
233 pub sort_order: SortOrder,
235}
236
237#[derive(Debug, Clone)]
239pub struct TimeRange {
240 pub start: SystemTime,
241 pub end: SystemTime,
242}
243
244#[derive(Debug, Clone)]
246pub enum SortOrder {
247 TimestampAsc,
248 TimestampDesc,
249 RiskLevelDesc,
250}
251
252#[derive(Debug, Clone)]
254pub struct StatsQuery {
255 pub time_range: TimeRange,
256 pub group_by: Vec<StatsGroupBy>,
257}
258
259#[derive(Debug, Clone)]
261pub enum StatsGroupBy {
262 EventType,
263 RiskLevel,
264 Outcome,
265 Hour,
266 Day,
267 Week,
268 UserId,
269 IpAddress,
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct AuditStatistics {
275 pub total_events: u64,
276 pub event_type_counts: HashMap<String, u64>,
277 pub risk_level_counts: HashMap<String, u64>,
278 pub outcome_counts: HashMap<String, u64>,
279 pub time_series: Vec<TimeSeriesPoint>,
280 pub top_users: Vec<UserEventCount>,
281 pub top_ips: Vec<IpEventCount>,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct TimeSeriesPoint {
287 pub timestamp: SystemTime,
288 pub count: u64,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct UserEventCount {
294 pub user_id: String,
295 pub event_count: u64,
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct IpEventCount {
301 pub ip_address: String,
302 pub event_count: u64,
303}
304
305pub struct AuditLogger<S: AuditStorage> {
307 storage: S,
308 correlation_generator: CorrelationIdGenerator,
309}
310
311impl<S: AuditStorage> AuditLogger<S> {
312 pub fn new(storage: S) -> Self {
314 Self {
315 storage,
316 correlation_generator: CorrelationIdGenerator::new(),
317 }
318 }
319
320 pub async fn log_event(&self, mut event: AuditEvent) -> Result<()> {
322 if event.id.is_empty() {
324 event.id = uuid::Uuid::new_v4().to_string();
325 }
326
327 if event.timestamp == SystemTime::UNIX_EPOCH {
329 event.timestamp = SystemTime::now();
330 }
331
332 self.storage.store_event(&event).await?;
334
335 self.check_security_alerts(&event).await?;
337
338 Ok(())
339 }
340
341 pub async fn log_login_success(
343 &self,
344 user_id: &str,
345 session_id: &str,
346 metadata: RequestMetadata,
347 ) -> Result<()> {
348 let event = AuditEvent {
349 id: String::new(),
350 event_type: AuditEventType::LoginSuccess,
351 timestamp: SystemTime::UNIX_EPOCH,
352 user_id: Some(user_id.to_string()),
353 session_id: Some(session_id.to_string()),
354 outcome: EventOutcome::Success,
355 risk_level: RiskLevel::Low,
356 description: "User successfully authenticated".to_string(),
357 details: HashMap::new(),
358 request_metadata: metadata,
359 resource: None,
360 actor: ActorInfo {
361 actor_type: "user".to_string(),
362 actor_id: user_id.to_string(),
363 actor_name: None,
364 roles: vec![],
365 },
366 correlation_id: Some(self.correlation_generator.generate()),
367 };
368
369 self.log_event(event).await
370 }
371
372 pub async fn log_login_failure(
374 &self,
375 attempted_user: &str,
376 reason: &str,
377 metadata: RequestMetadata,
378 ) -> Result<()> {
379 let mut details = HashMap::new();
380 details.insert("failure_reason".to_string(), reason.to_string());
381 details.insert("attempted_user".to_string(), attempted_user.to_string());
382
383 let event = AuditEvent {
384 id: String::new(),
385 event_type: AuditEventType::LoginFailure,
386 timestamp: SystemTime::UNIX_EPOCH,
387 user_id: None,
388 session_id: None,
389 outcome: EventOutcome::Failure,
390 risk_level: RiskLevel::Medium,
391 description: format!("Authentication failed for user: {}", attempted_user),
392 details,
393 request_metadata: metadata,
394 resource: None,
395 actor: ActorInfo {
396 actor_type: "user".to_string(),
397 actor_id: attempted_user.to_string(),
398 actor_name: None,
399 roles: vec![],
400 },
401 correlation_id: Some(self.correlation_generator.generate()),
402 };
403
404 self.log_event(event).await
405 }
406
407 pub async fn log_permission_denied(
409 &self,
410 user_id: &str,
411 resource: ResourceInfo,
412 permission: &str,
413 metadata: RequestMetadata,
414 ) -> Result<()> {
415 let mut details = HashMap::new();
416 details.insert("requested_permission".to_string(), permission.to_string());
417
418 let event = AuditEvent {
419 id: String::new(),
420 event_type: AuditEventType::PermissionDenied,
421 timestamp: SystemTime::UNIX_EPOCH,
422 user_id: Some(user_id.to_string()),
423 session_id: None,
424 outcome: EventOutcome::Failure,
425 risk_level: RiskLevel::Medium,
426 description: format!(
427 "Permission denied: {} on {}",
428 permission, resource.resource_type
429 ),
430 details,
431 request_metadata: metadata,
432 resource: Some(resource),
433 actor: ActorInfo {
434 actor_type: "user".to_string(),
435 actor_id: user_id.to_string(),
436 actor_name: None,
437 roles: vec![],
438 },
439 correlation_id: Some(self.correlation_generator.generate()),
440 };
441
442 self.log_event(event).await
443 }
444
445 pub async fn log_suspicious_activity(
447 &self,
448 user_id: Option<&str>,
449 activity_type: &str,
450 description: &str,
451 metadata: RequestMetadata,
452 ) -> Result<()> {
453 let mut details = HashMap::new();
454 details.insert("activity_type".to_string(), activity_type.to_string());
455
456 let event = AuditEvent {
457 id: String::new(),
458 event_type: AuditEventType::SuspiciousActivity,
459 timestamp: SystemTime::UNIX_EPOCH,
460 user_id: user_id.map(|s| s.to_string()),
461 session_id: None,
462 outcome: EventOutcome::Unknown,
463 risk_level: RiskLevel::High,
464 description: description.to_string(),
465 details,
466 request_metadata: metadata,
467 resource: None,
468 actor: ActorInfo {
469 actor_type: user_id.map(|_| "user").unwrap_or("system").to_string(),
470 actor_id: user_id.unwrap_or("system").to_string(),
471 actor_name: None,
472 roles: vec![],
473 },
474 correlation_id: Some(self.correlation_generator.generate()),
475 };
476
477 self.log_event(event).await
478 }
479
480 async fn check_security_alerts(&self, event: &AuditEvent) -> Result<()> {
482 match event.event_type {
483 AuditEventType::LoginFailure => {
484 self.check_brute_force_pattern(event).await?;
485 }
486 AuditEventType::SuspiciousActivity => {
487 self.trigger_security_alert(event).await?;
489 }
490 _ => {}
491 }
492 Ok(())
493 }
494
495 async fn check_brute_force_pattern(&self, event: &AuditEvent) -> Result<()> {
497 let query = AuditQuery {
498 event_types: Some(vec![AuditEventType::LoginFailure]),
499 ip_address: event.request_metadata.ip_address.clone(),
500 time_range: Some(TimeRange {
501 start: SystemTime::now() - std::time::Duration::from_secs(300), end: SystemTime::now(),
503 }),
504 limit: Some(10),
505 offset: None,
506 user_id: None,
507 risk_level: None,
508 outcome: None,
509 resource_type: None,
510 actor_id: None,
511 correlation_id: None,
512 sort_order: SortOrder::TimestampDesc,
513 };
514
515 let recent_failures = self.storage.query_events(&query).await?;
516
517 if recent_failures.len() >= 5 {
518 let mut details = HashMap::new();
520 details.insert(
521 "failure_count".to_string(),
522 recent_failures.len().to_string(),
523 );
524 details.insert("time_window".to_string(), "300".to_string());
525
526 let brute_force_event = AuditEvent {
527 id: String::new(),
528 event_type: AuditEventType::BruteForceDetected,
529 timestamp: SystemTime::now(),
530 user_id: None,
531 session_id: None,
532 outcome: EventOutcome::Success,
533 risk_level: RiskLevel::Critical,
534 description: "Brute force attack detected".to_string(),
535 details,
536 request_metadata: event.request_metadata.clone(),
537 resource: None,
538 actor: ActorInfo {
539 actor_type: "system".to_string(),
540 actor_id: "security_monitor".to_string(),
541 actor_name: Some("Security Monitor".to_string()),
542 roles: vec!["system".to_string()],
543 },
544 correlation_id: Some(self.correlation_generator.generate()),
545 };
546
547 self.storage.store_event(&brute_force_event).await?;
548 }
549
550 Ok(())
551 }
552
553 async fn trigger_security_alert(&self, _event: &AuditEvent) -> Result<()> {
555 Ok(())
560 }
561
562 pub async fn query_events(&self, query: &AuditQuery) -> Result<Vec<AuditEvent>> {
564 self.storage.query_events(query).await
565 }
566
567 pub async fn get_statistics(&self, query: &StatsQuery) -> Result<AuditStatistics> {
569 self.storage.get_statistics(query).await
570 }
571
572 pub async fn get_failed_login_count_24h(&self) -> Result<u64> {
574 let query = AuditQuery {
575 event_types: Some(vec![AuditEventType::LoginFailure]),
576 time_range: Some(TimeRange {
577 start: SystemTime::now() - std::time::Duration::from_secs(24 * 60 * 60),
578 end: SystemTime::now(),
579 }),
580 limit: None,
581 offset: None,
582 user_id: None,
583 risk_level: None,
584 outcome: None,
585 resource_type: None,
586 actor_id: None,
587 correlation_id: None,
588 ip_address: None,
589 sort_order: SortOrder::TimestampDesc,
590 };
591 self.storage.count_events(&query).await
592 }
593
594 pub async fn get_successful_login_count_24h(&self) -> Result<u64> {
596 let query = AuditQuery {
597 event_types: Some(vec![AuditEventType::LoginSuccess]),
598 time_range: Some(TimeRange {
599 start: SystemTime::now() - std::time::Duration::from_secs(24 * 60 * 60),
600 end: SystemTime::now(),
601 }),
602 limit: None,
603 offset: None,
604 user_id: None,
605 risk_level: None,
606 outcome: None,
607 resource_type: None,
608 actor_id: None,
609 correlation_id: None,
610 ip_address: None,
611 sort_order: SortOrder::TimestampDesc,
612 };
613 self.storage.count_events(&query).await
614 }
615
616 pub async fn get_token_issued_count_24h(&self) -> Result<u64> {
618 let query = AuditQuery {
619 event_types: Some(vec![
620 AuditEventType::TokenRefresh,
621 AuditEventType::LoginSuccess,
622 ]),
623 time_range: Some(TimeRange {
624 start: SystemTime::now() - std::time::Duration::from_secs(24 * 60 * 60),
625 end: SystemTime::now(),
626 }),
627 limit: None,
628 offset: None,
629 user_id: None,
630 risk_level: None,
631 outcome: None,
632 resource_type: None,
633 actor_id: None,
634 correlation_id: None,
635 ip_address: None,
636 sort_order: SortOrder::TimestampDesc,
637 };
638 self.storage.count_events(&query).await
639 }
640
641 pub async fn get_unique_users_24h(&self) -> Result<u64> {
643 let query = AuditQuery {
644 event_types: Some(vec![AuditEventType::LoginSuccess]),
645 time_range: Some(TimeRange {
646 start: SystemTime::now() - std::time::Duration::from_secs(24 * 60 * 60),
647 end: SystemTime::now(),
648 }),
649 limit: None,
650 offset: None,
651 user_id: None,
652 risk_level: None,
653 outcome: None,
654 resource_type: None,
655 actor_id: None,
656 correlation_id: None,
657 ip_address: None,
658 sort_order: SortOrder::TimestampDesc,
659 };
660
661 let events = self.storage.query_events(&query).await?;
662 let unique_users: std::collections::HashSet<_> =
663 events.iter().filter_map(|e| e.user_id.as_ref()).collect();
664 Ok(unique_users.len() as u64)
665 }
666
667 pub async fn get_password_reset_count_24h(&self) -> Result<u64> {
669 let query = AuditQuery {
670 event_types: Some(vec![AuditEventType::UserPasswordReset]),
671 time_range: Some(TimeRange {
672 start: SystemTime::now() - std::time::Duration::from_secs(24 * 60 * 60),
673 end: SystemTime::now(),
674 }),
675 limit: None,
676 offset: None,
677 user_id: None,
678 risk_level: None,
679 outcome: None,
680 resource_type: None,
681 actor_id: None,
682 correlation_id: None,
683 ip_address: None,
684 sort_order: SortOrder::TimestampDesc,
685 };
686 self.storage.count_events(&query).await
687 }
688
689 pub async fn get_admin_action_count_24h(&self) -> Result<u64> {
691 let query = AuditQuery {
692 event_types: Some(vec![
693 AuditEventType::AdminAction,
694 AuditEventType::UserCreated,
695 AuditEventType::UserUpdated,
696 AuditEventType::UserDeleted,
697 AuditEventType::RoleCreated,
698 AuditEventType::RoleUpdated,
699 AuditEventType::RoleDeleted,
700 ]),
701 time_range: Some(TimeRange {
702 start: SystemTime::now() - std::time::Duration::from_secs(24 * 60 * 60),
703 end: SystemTime::now(),
704 }),
705 limit: None,
706 offset: None,
707 user_id: None,
708 risk_level: None,
709 outcome: None,
710 resource_type: None,
711 actor_id: None,
712 correlation_id: None,
713 ip_address: None,
714 sort_order: SortOrder::TimestampDesc,
715 };
716 self.storage.count_events(&query).await
717 }
718
719 pub async fn get_security_alert_count_24h(&self) -> Result<u64> {
721 let query = AuditQuery {
722 event_types: Some(vec![
723 AuditEventType::SuspiciousActivity,
724 AuditEventType::BruteForceDetected,
725 AuditEventType::SecurityViolation,
726 ]),
727 time_range: Some(TimeRange {
728 start: SystemTime::now() - std::time::Duration::from_secs(24 * 60 * 60),
729 end: SystemTime::now(),
730 }),
731 limit: None,
732 offset: None,
733 user_id: None,
734 risk_level: Some(RiskLevel::High),
735 outcome: None,
736 resource_type: None,
737 actor_id: None,
738 correlation_id: None,
739 ip_address: None,
740 sort_order: SortOrder::TimestampDesc,
741 };
742 self.storage.count_events(&query).await
743 }
744
745 pub async fn cleanup_old_events(&self, retention_days: u32) -> Result<u64> {
747 let cutoff_time =
748 SystemTime::now() - std::time::Duration::from_secs(retention_days as u64 * 86400);
749 self.storage.delete_old_events(cutoff_time).await
750 }
751}
752
753pub struct CorrelationIdGenerator {
755 counter: std::sync::atomic::AtomicU64,
756}
757
758impl CorrelationIdGenerator {
759 pub fn new() -> Self {
760 Self {
761 counter: std::sync::atomic::AtomicU64::new(0),
762 }
763 }
764
765 pub fn generate(&self) -> String {
766 let count = self
767 .counter
768 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
769 format!(
770 "corr_{:016x}_{}",
771 SystemTime::now()
772 .duration_since(SystemTime::UNIX_EPOCH)
773 .unwrap_or_default()
774 .as_secs(),
775 count
776 )
777 }
778}
779
780impl RequestMetadata {
782 pub fn new() -> Self {
783 Self {
784 ip_address: None,
785 user_agent: None,
786 request_id: None,
787 endpoint: None,
788 http_method: None,
789 geolocation: None,
790 device_info: None,
791 }
792 }
793
794 pub fn with_ip(mut self, ip: impl Into<String>) -> Self {
795 self.ip_address = Some(ip.into());
796 self
797 }
798
799 pub fn with_user_agent(mut self, user_agent: impl Into<String>) -> Self {
800 self.user_agent = Some(user_agent.into());
801 self
802 }
803
804 pub fn with_endpoint(mut self, endpoint: impl Into<String>) -> Self {
805 self.endpoint = Some(endpoint.into());
806 self
807 }
808}
809
810impl Default for RequestMetadata {
811 fn default() -> Self {
812 Self::new()
813 }
814}
815
816#[cfg(test)]
817mod tests {
818 use super::*;
819
820 #[test]
821 fn test_correlation_id_generation() {
822 let generator = CorrelationIdGenerator::new();
823 let id1 = generator.generate();
824 let id2 = generator.generate();
825
826 assert_ne!(id1, id2);
827 assert!(id1.starts_with("corr_"));
828 assert!(id2.starts_with("corr_"));
829 }
830
831 #[test]
832 fn test_request_metadata_builder() {
833 let metadata = RequestMetadata::new()
834 .with_ip("192.168.1.1")
835 .with_user_agent("Mozilla/5.0")
836 .with_endpoint("/api/auth/login");
837
838 assert_eq!(metadata.ip_address, Some("192.168.1.1".to_string()));
839 assert_eq!(metadata.user_agent, Some("Mozilla/5.0".to_string()));
840 assert_eq!(metadata.endpoint, Some("/api/auth/login".to_string()));
841 }
842}
843
844