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 serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use std::time::SystemTime;
15
16/// Audit event types
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub enum AuditEventType {
19    // Authentication events
20    LoginSuccess,
21    LoginFailure,
22    Logout,
23    TokenRefresh,
24    TokenExpired,
25    TokenRevoked,
26
27    // MFA events
28    MfaSetup,
29    MfaChallengeCreated,
30    MfaVerificationSuccess,
31    MfaVerificationFailure,
32    MfaMethodEnabled,
33    MfaMethodDisabled,
34
35    // Authorization events
36    PermissionGranted,
37    PermissionDenied,
38    RoleAssigned,
39    RoleRevoked,
40    RoleCreated,
41    RoleUpdated,
42    RoleDeleted,
43
44    // User management events
45    UserCreated,
46    UserUpdated,
47    UserDeleted,
48    UserActivated,
49    UserDeactivated,
50    UserPasswordChanged,
51    UserPasswordReset,
52
53    // Security events
54    AccountLocked,
55    AccountUnlocked,
56    SuspiciousActivity,
57    BruteForceDetected,
58    RateLimitExceeded,
59    SecurityPolicyViolation,
60    SecurityViolation,
61
62    // Administrative events
63    AdminAction,
64    ConfigurationChanged,
65    SystemStartup,
66    SystemShutdown,
67    BackupCreated,
68    DataExported,
69    DataImported,
70}
71
72/// Security risk level
73#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)]
74pub enum RiskLevel {
75    Low,
76    Medium,
77    High,
78    Critical,
79}
80
81/// Audit event outcome
82#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
83pub enum EventOutcome {
84    Success,
85    Failure,
86    Partial,
87    Unknown,
88}
89
90/// Comprehensive audit event
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct AuditEvent {
93    /// Unique event ID
94    pub id: String,
95    /// Type of event
96    pub event_type: AuditEventType,
97    /// When the event occurred
98    pub timestamp: SystemTime,
99    /// User who initiated the event (if applicable)
100    pub user_id: Option<String>,
101    /// Session ID (if applicable)
102    pub session_id: Option<String>,
103    /// Event outcome
104    pub outcome: EventOutcome,
105    /// Risk level assessment
106    pub risk_level: RiskLevel,
107    /// Human-readable event description
108    pub description: String,
109    /// Additional event details
110    pub details: HashMap<String, String>,
111    /// Request metadata
112    pub request_metadata: RequestMetadata,
113    /// Resource affected (if applicable)
114    pub resource: Option<ResourceInfo>,
115    /// Actor information
116    pub actor: ActorInfo,
117    /// Correlation ID for tracking related events
118    pub correlation_id: Option<String>,
119}
120
121/// Request metadata for audit context
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct RequestMetadata {
124    /// Source IP address
125    pub ip_address: Option<String>,
126    /// User agent string
127    pub user_agent: Option<String>,
128    /// Request ID
129    pub request_id: Option<String>,
130    /// API endpoint or action
131    pub endpoint: Option<String>,
132    /// HTTP method (if applicable)
133    pub http_method: Option<String>,
134    /// Geographic location (if available)
135    pub geolocation: Option<GeolocationInfo>,
136    /// Device information
137    pub device_info: Option<DeviceInfo>,
138}
139
140/// Geographic location information
141#[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/// Device information
151#[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/// Resource information
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct ResourceInfo {
163    /// Resource type (user, document, api, etc.)
164    pub resource_type: String,
165    /// Resource ID
166    pub resource_id: String,
167    /// Resource name or title
168    pub resource_name: Option<String>,
169    /// Additional resource attributes
170    pub attributes: HashMap<String, String>,
171}
172
173/// Actor information (who performed the action)
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct ActorInfo {
176    /// Actor type (user, system, api_client, etc.)
177    pub actor_type: String,
178    /// Actor ID
179    pub actor_id: String,
180    /// Actor name or identifier
181    pub actor_name: Option<String>,
182    /// Roles or permissions of the actor
183    pub roles: Vec<String>,
184}
185
186/// Audit log storage trait
187#[async_trait]
188pub trait AuditStorage: Send + Sync {
189    /// Store an audit event
190    async fn store_event(&self, event: &AuditEvent) -> Result<()>;
191
192    /// Query audit events with filters
193    async fn query_events(&self, query: &AuditQuery) -> Result<Vec<AuditEvent>>;
194
195    /// Get event by ID
196    async fn get_event(&self, event_id: &str) -> Result<Option<AuditEvent>>;
197
198    /// Count events matching criteria
199    async fn count_events(&self, query: &AuditQuery) -> Result<u64>;
200
201    /// Delete old events (for retention management)
202    async fn delete_old_events(&self, before: SystemTime) -> Result<u64>;
203
204    /// Get audit statistics
205    async fn get_statistics(&self, query: &StatsQuery) -> Result<AuditStatistics>;
206}
207
208/// Query parameters for audit events
209#[derive(Debug, Clone)]
210pub struct AuditQuery {
211    /// Filter by event types
212    pub event_types: Option<Vec<AuditEventType>>,
213    /// Filter by user ID
214    pub user_id: Option<String>,
215    /// Filter by risk level
216    pub risk_level: Option<RiskLevel>,
217    /// Filter by outcome
218    pub outcome: Option<EventOutcome>,
219    /// Time range filter
220    pub time_range: Option<TimeRange>,
221    /// IP address filter
222    pub ip_address: Option<String>,
223    /// Resource filter
224    pub resource_type: Option<String>,
225    /// Actor filter
226    pub actor_id: Option<String>,
227    /// Correlation ID filter
228    pub correlation_id: Option<String>,
229    /// Limit number of results
230    pub limit: Option<u64>,
231    /// Offset for pagination
232    pub offset: Option<u64>,
233    /// Sort order
234    pub sort_order: SortOrder,
235}
236
237/// Time range for queries
238#[derive(Debug, Clone)]
239pub struct TimeRange {
240    pub start: SystemTime,
241    pub end: SystemTime,
242}
243
244/// Sort order for queries
245#[derive(Debug, Clone)]
246pub enum SortOrder {
247    TimestampAsc,
248    TimestampDesc,
249    RiskLevelDesc,
250}
251
252/// Statistics query parameters
253#[derive(Debug, Clone)]
254pub struct StatsQuery {
255    pub time_range: TimeRange,
256    pub group_by: Vec<StatsGroupBy>,
257}
258
259/// Grouping options for statistics
260#[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/// Audit statistics result
273#[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/// Time series data point
285#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct TimeSeriesPoint {
287    pub timestamp: SystemTime,
288    pub count: u64,
289}
290
291/// User event count
292#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct UserEventCount {
294    pub user_id: String,
295    pub event_count: u64,
296}
297
298/// IP address event count
299#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct IpEventCount {
301    pub ip_address: String,
302    pub event_count: u64,
303}
304
305/// Main audit logger
306pub struct AuditLogger<S: AuditStorage> {
307    storage: S,
308    correlation_generator: CorrelationIdGenerator,
309}
310
311impl<S: AuditStorage> AuditLogger<S> {
312    /// Create a new audit logger
313    pub fn new(storage: S) -> Self {
314        Self {
315            storage,
316            correlation_generator: CorrelationIdGenerator::new(),
317        }
318    }
319
320    /// Log an audit event
321    pub async fn log_event(&self, mut event: AuditEvent) -> Result<()> {
322        // Generate ID if not provided
323        if event.id.is_empty() {
324            event.id = uuid::Uuid::new_v4().to_string();
325        }
326
327        // Set timestamp if not provided
328        if event.timestamp == SystemTime::UNIX_EPOCH {
329            event.timestamp = SystemTime::now();
330        }
331
332        // Store the event
333        self.storage.store_event(&event).await?;
334
335        // Check for security alerts
336        self.check_security_alerts(&event).await?;
337
338        Ok(())
339    }
340
341    /// Log authentication success
342    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    /// Log authentication failure
373    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    /// Log permission denied event
408    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    /// Log suspicious activity
446    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    /// Check for security alerts based on event patterns
481    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                // Could trigger immediate alerts
488                self.trigger_security_alert(event).await?;
489            }
490            _ => {}
491        }
492        Ok(())
493    }
494
495    /// Check for brute force attack patterns
496    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), // Last 5 minutes
502                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            // Log brute force detection
519            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    /// Trigger security alert
554    async fn trigger_security_alert(&self, _event: &AuditEvent) -> Result<()> {
555        // In a real implementation, this would:
556        // - Send notifications to security team
557        // - Update security dashboards
558        // - Trigger automated responses
559        Ok(())
560    }
561
562    /// Query audit events
563    pub async fn query_events(&self, query: &AuditQuery) -> Result<Vec<AuditEvent>> {
564        self.storage.query_events(query).await
565    }
566
567    /// Get audit statistics
568    pub async fn get_statistics(&self, query: &StatsQuery) -> Result<AuditStatistics> {
569        self.storage.get_statistics(query).await
570    }
571
572    /// Get failed login count in the last 24 hours
573    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    /// Get successful login count in the last 24 hours
595    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    /// Get token issued count in the last 24 hours
617    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    /// Get unique users count in the last 24 hours
642    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    /// Get password reset count in the last 24 hours
668    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    /// Get admin action count in the last 24 hours
690    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    /// Get security alert count in the last 24 hours
720    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    /// Clean up old audit events
746    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
753/// Correlation ID generator for tracking related events
754pub 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
780/// Helper for creating request metadata
781impl 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