1use crate::security::{AuditConfig, Result, SecurityError};
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use sqlx::{PgPool, Row};
6use std::collections::HashMap;
7use std::sync::Arc;
8use tracing::{debug, error, info, warn};
9use uuid::Uuid;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub enum AuditEventType {
14 Authentication,
15 Authorization,
16 DataAccess,
17 DataModification,
18 DataDeletion,
19 SystemAccess,
20 ConfigurationChange,
21 SecurityEvent,
22 RateLimitViolation,
23 Error,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub enum AuditSeverity {
29 Low,
30 Medium,
31 High,
32 Critical,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct AuditEvent {
38 pub id: String,
39 pub timestamp: DateTime<Utc>,
40 pub event_type: AuditEventType,
41 pub severity: AuditSeverity,
42 pub user_id: Option<String>,
43 pub session_id: Option<String>,
44 pub ip_address: Option<String>,
45 pub user_agent: Option<String>,
46 pub resource: Option<String>,
47 pub action: String,
48 pub outcome: AuditOutcome,
49 pub details: HashMap<String, Value>,
50 pub error_message: Option<String>,
51 pub request_id: Option<String>,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub enum AuditOutcome {
57 Success,
58 Failure,
59 Partial,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct AuditStatistics {
65 pub total_events: u64,
66 pub events_by_type: HashMap<String, u64>,
67 pub events_by_user: HashMap<String, u64>,
68 pub failed_events: u64,
69 pub critical_events: u64,
70 pub retention_days: u32,
71 pub oldest_event: Option<DateTime<Utc>>,
72 pub newest_event: Option<DateTime<Utc>>,
73}
74
75#[derive(Debug)]
77pub struct AuditManager {
78 config: AuditConfig,
79 db_pool: Arc<PgPool>,
80}
81
82impl AuditManager {
83 pub fn new(config: AuditConfig, db_pool: Arc<PgPool>) -> Self {
84 Self { config, db_pool }
85 }
86
87 pub async fn initialize(&self) -> Result<()> {
89 if !self.config.enabled {
90 debug!("Audit logging is disabled");
91 return Ok(());
92 }
93
94 info!("Initializing audit logging system");
95
96 let create_table_sql = r#"
98 CREATE TABLE IF NOT EXISTS audit_events (
99 id UUID PRIMARY KEY,
100 timestamp TIMESTAMPTZ NOT NULL,
101 event_type TEXT NOT NULL,
102 severity TEXT NOT NULL,
103 user_id TEXT,
104 session_id TEXT,
105 ip_address INET,
106 user_agent TEXT,
107 resource TEXT,
108 action TEXT NOT NULL,
109 outcome TEXT NOT NULL,
110 details JSONB,
111 error_message TEXT,
112 request_id TEXT,
113 created_at TIMESTAMPTZ DEFAULT NOW()
114 );
115 "#;
116
117 sqlx::query(create_table_sql)
118 .execute(self.db_pool.as_ref())
119 .await
120 .map_err(|e| SecurityError::AuditError {
121 message: format!("Failed to create audit events table: {e}"),
122 })?;
123
124 let create_indexes_sql = vec![
126 "CREATE INDEX IF NOT EXISTS idx_audit_events_timestamp ON audit_events (timestamp);",
127 "CREATE INDEX IF NOT EXISTS idx_audit_events_user_id ON audit_events (user_id);",
128 "CREATE INDEX IF NOT EXISTS idx_audit_events_event_type ON audit_events (event_type);",
129 "CREATE INDEX IF NOT EXISTS idx_audit_events_severity ON audit_events (severity);",
130 "CREATE INDEX IF NOT EXISTS idx_audit_events_outcome ON audit_events (outcome);",
131 ];
132
133 for sql in create_indexes_sql {
134 sqlx::query(sql)
135 .execute(self.db_pool.as_ref())
136 .await
137 .map_err(|e| SecurityError::AuditError {
138 message: format!("Failed to create audit index: {e}"),
139 })?;
140 }
141
142 info!("Audit logging system initialized successfully");
143 Ok(())
144 }
145
146 pub async fn log_event(&self, event: AuditEvent) -> Result<()> {
148 if !self.config.enabled {
149 return Ok(());
150 }
151
152 if !self.should_log_event(&event.event_type) {
154 return Ok(());
155 }
156
157 debug!(
158 "Logging audit event: {:?} - {}",
159 event.event_type, event.action
160 );
161
162 let insert_sql = r#"
163 INSERT INTO audit_events (
164 id, timestamp, event_type, severity, user_id, session_id,
165 ip_address, user_agent, resource, action, outcome, details,
166 error_message, request_id
167 ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
168 "#;
169
170 sqlx::query(insert_sql)
171 .bind(&event.id)
172 .bind(event.timestamp)
173 .bind(format!("{:?}", event.event_type))
174 .bind(format!("{:?}", event.severity))
175 .bind(&event.user_id)
176 .bind(&event.session_id)
177 .bind(event.ip_address.as_ref())
178 .bind(&event.user_agent)
179 .bind(&event.resource)
180 .bind(&event.action)
181 .bind(format!("{:?}", event.outcome))
182 .bind(serde_json::to_value(&event.details).unwrap_or(Value::Null))
183 .bind(&event.error_message)
184 .bind(&event.request_id)
185 .execute(self.db_pool.as_ref())
186 .await
187 .map_err(|e| SecurityError::AuditError {
188 message: format!("Failed to log audit event: {e}"),
189 })?;
190
191 if matches!(event.severity, AuditSeverity::Critical) {
193 error!(
194 "CRITICAL AUDIT EVENT - Type: {:?}, Action: {}, User: {:?}, Details: {:?}",
195 event.event_type, event.action, event.user_id, event.details
196 );
197 }
198
199 Ok(())
200 }
201
202 pub async fn log_authentication(
204 &self,
205 user_id: &str,
206 action: &str,
207 outcome: AuditOutcome,
208 ip_address: Option<String>,
209 details: HashMap<String, Value>,
210 ) -> Result<()> {
211 let event = AuditEvent {
212 id: Uuid::new_v4().to_string(),
213 timestamp: Utc::now(),
214 event_type: AuditEventType::Authentication,
215 severity: if matches!(outcome, AuditOutcome::Failure) {
216 AuditSeverity::High
217 } else {
218 AuditSeverity::Medium
219 },
220 user_id: Some(user_id.to_string()),
221 session_id: None,
222 ip_address,
223 user_agent: None,
224 resource: Some("authentication".to_string()),
225 action: action.to_string(),
226 outcome,
227 details,
228 error_message: None,
229 request_id: None,
230 };
231
232 self.log_event(event).await
233 }
234
235 pub async fn log_data_access(
237 &self,
238 user_id: Option<&str>,
239 resource: &str,
240 action: &str,
241 outcome: AuditOutcome,
242 details: HashMap<String, Value>,
243 ) -> Result<()> {
244 let event = AuditEvent {
245 id: Uuid::new_v4().to_string(),
246 timestamp: Utc::now(),
247 event_type: AuditEventType::DataAccess,
248 severity: AuditSeverity::Low,
249 user_id: user_id.map(|s| s.to_string()),
250 session_id: None,
251 ip_address: None,
252 user_agent: None,
253 resource: Some(resource.to_string()),
254 action: action.to_string(),
255 outcome,
256 details,
257 error_message: None,
258 request_id: None,
259 };
260
261 self.log_event(event).await
262 }
263
264 pub async fn log_data_modification(
266 &self,
267 user_id: Option<&str>,
268 resource: &str,
269 action: &str,
270 outcome: AuditOutcome,
271 details: HashMap<String, Value>,
272 ) -> Result<()> {
273 let event = AuditEvent {
274 id: Uuid::new_v4().to_string(),
275 timestamp: Utc::now(),
276 event_type: AuditEventType::DataModification,
277 severity: AuditSeverity::Medium,
278 user_id: user_id.map(|s| s.to_string()),
279 session_id: None,
280 ip_address: None,
281 user_agent: None,
282 resource: Some(resource.to_string()),
283 action: action.to_string(),
284 outcome,
285 details,
286 error_message: None,
287 request_id: None,
288 };
289
290 self.log_event(event).await
291 }
292
293 pub async fn log_security_event(
295 &self,
296 action: &str,
297 severity: AuditSeverity,
298 user_id: Option<&str>,
299 ip_address: Option<String>,
300 details: HashMap<String, Value>,
301 ) -> Result<()> {
302 let event = AuditEvent {
303 id: Uuid::new_v4().to_string(),
304 timestamp: Utc::now(),
305 event_type: AuditEventType::SecurityEvent,
306 severity,
307 user_id: user_id.map(|s| s.to_string()),
308 session_id: None,
309 ip_address,
310 user_agent: None,
311 resource: Some("security".to_string()),
312 action: action.to_string(),
313 outcome: AuditOutcome::Success,
314 details,
315 error_message: None,
316 request_id: None,
317 };
318
319 self.log_event(event).await
320 }
321
322 pub async fn log_auth_event(
324 &self,
325 client_id: &str,
326 user_id: &str,
327 method_name: &str,
328 success: bool,
329 error_message: Option<&str>,
330 ) -> Result<()> {
331 let mut details = HashMap::new();
332 details.insert(
333 "client_id".to_string(),
334 serde_json::Value::String(client_id.to_string()),
335 );
336 details.insert(
337 "method".to_string(),
338 serde_json::Value::String(method_name.to_string()),
339 );
340
341 let event = AuditEvent {
342 id: Uuid::new_v4().to_string(),
343 timestamp: Utc::now(),
344 event_type: AuditEventType::Authentication,
345 severity: if success {
346 AuditSeverity::Low
347 } else {
348 AuditSeverity::High
349 },
350 user_id: Some(user_id.to_string()),
351 session_id: None,
352 ip_address: None,
353 user_agent: None,
354 resource: Some("mcp_auth".to_string()),
355 action: if success {
356 "auth_success"
357 } else {
358 "auth_failure"
359 }
360 .to_string(),
361 outcome: if success {
362 AuditOutcome::Success
363 } else {
364 AuditOutcome::Failure
365 },
366 details,
367 error_message: error_message.map(|s| s.to_string()),
368 request_id: None,
369 };
370
371 self.log_event(event).await
372 }
373
374 pub async fn log_rate_limit_violation(
376 &self,
377 client_id: &str,
378 tool_name: &str,
379 limit_type: &str,
380 ) -> Result<()> {
381 let mut details = HashMap::new();
382 details.insert(
383 "client_id".to_string(),
384 serde_json::Value::String(client_id.to_string()),
385 );
386 details.insert(
387 "tool_name".to_string(),
388 serde_json::Value::String(tool_name.to_string()),
389 );
390 details.insert(
391 "limit_type".to_string(),
392 serde_json::Value::String(limit_type.to_string()),
393 );
394
395 let event = AuditEvent {
396 id: Uuid::new_v4().to_string(),
397 timestamp: Utc::now(),
398 event_type: AuditEventType::RateLimitViolation,
399 severity: AuditSeverity::Medium,
400 user_id: Some(client_id.to_string()),
401 session_id: None,
402 ip_address: None,
403 user_agent: None,
404 resource: Some("mcp_rate_limiter".to_string()),
405 action: "rate_limit_exceeded".to_string(),
406 outcome: AuditOutcome::Failure,
407 details,
408 error_message: Some(format!(
409 "Rate limit exceeded for {limit_type} on tool {tool_name}"
410 )),
411 request_id: None,
412 };
413
414 self.log_event(event).await
415 }
416
417 pub async fn get_events(&self, filter: AuditFilter) -> Result<Vec<AuditEvent>> {
419 if !self.config.enabled {
420 return Ok(Vec::new());
421 }
422
423 let mut where_clauses = Vec::new();
424 let mut bind_count = 0;
425
426 if let Some(_user_id) = &filter.user_id {
427 bind_count += 1;
428 where_clauses.push(format!("user_id = ${bind_count}"));
429 }
430
431 if let Some(_event_type) = &filter.event_type {
432 bind_count += 1;
433 where_clauses.push(format!("event_type = ${bind_count}"));
434 }
435
436 if let Some(_start_time) = &filter.start_time {
437 bind_count += 1;
438 where_clauses.push(format!("timestamp >= ${bind_count}"));
439 }
440
441 if let Some(_end_time) = &filter.end_time {
442 bind_count += 1;
443 where_clauses.push(format!("timestamp <= ${bind_count}"));
444 }
445
446 let where_clause = if where_clauses.is_empty() {
447 String::new()
448 } else {
449 format!("WHERE {}", where_clauses.join(" AND "))
450 };
451
452 let limit = filter.limit.unwrap_or(100).min(1000); let offset = filter.offset.unwrap_or(0);
454
455 let query = format!(
456 "SELECT * FROM audit_events {where_clause} ORDER BY timestamp DESC LIMIT {limit} OFFSET {offset}"
457 );
458
459 let mut sql_query = sqlx::query(&query);
460
461 if let Some(user_id) = &filter.user_id {
463 sql_query = sql_query.bind(user_id);
464 }
465 if let Some(event_type) = &filter.event_type {
466 sql_query = sql_query.bind(format!("{event_type:?}"));
467 }
468 if let Some(start_time) = &filter.start_time {
469 sql_query = sql_query.bind(start_time);
470 }
471 if let Some(end_time) = &filter.end_time {
472 sql_query = sql_query.bind(end_time);
473 }
474
475 let rows = sql_query
476 .fetch_all(self.db_pool.as_ref())
477 .await
478 .map_err(|e| SecurityError::AuditError {
479 message: format!("Failed to fetch audit events: {e}"),
480 })?;
481
482 let mut events = Vec::new();
483 for row in rows {
484 events.push(self.row_to_audit_event(row)?);
485 }
486
487 Ok(events)
488 }
489
490 pub async fn get_statistics(&self) -> Result<AuditStatistics> {
492 if !self.config.enabled {
493 return Ok(AuditStatistics {
494 total_events: 0,
495 events_by_type: HashMap::new(),
496 events_by_user: HashMap::new(),
497 failed_events: 0,
498 critical_events: 0,
499 retention_days: self.config.retention_days,
500 oldest_event: None,
501 newest_event: None,
502 });
503 }
504
505 let total_events: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM audit_events")
507 .fetch_one(self.db_pool.as_ref())
508 .await
509 .map_err(|e| SecurityError::AuditError {
510 message: format!("Failed to get total events count: {e}"),
511 })?;
512
513 let failed_events: i64 =
514 sqlx::query_scalar("SELECT COUNT(*) FROM audit_events WHERE outcome = 'Failure'")
515 .fetch_one(self.db_pool.as_ref())
516 .await
517 .map_err(|e| SecurityError::AuditError {
518 message: format!("Failed to get failed events count: {e}"),
519 })?;
520
521 let critical_events: i64 =
522 sqlx::query_scalar("SELECT COUNT(*) FROM audit_events WHERE severity = 'Critical'")
523 .fetch_one(self.db_pool.as_ref())
524 .await
525 .map_err(|e| SecurityError::AuditError {
526 message: format!("Failed to get critical events count: {e}"),
527 })?;
528
529 let oldest_event: Option<DateTime<Utc>> =
531 sqlx::query_scalar("SELECT MIN(timestamp) FROM audit_events")
532 .fetch_one(self.db_pool.as_ref())
533 .await
534 .map_err(|e| SecurityError::AuditError {
535 message: format!("Failed to get oldest event: {e}"),
536 })?;
537
538 let newest_event: Option<DateTime<Utc>> =
539 sqlx::query_scalar("SELECT MAX(timestamp) FROM audit_events")
540 .fetch_one(self.db_pool.as_ref())
541 .await
542 .map_err(|e| SecurityError::AuditError {
543 message: format!("Failed to get newest event: {e}"),
544 })?;
545
546 let type_rows = sqlx::query(
548 "SELECT event_type, COUNT(*) as count FROM audit_events GROUP BY event_type",
549 )
550 .fetch_all(self.db_pool.as_ref())
551 .await
552 .map_err(|e| SecurityError::AuditError {
553 message: format!("Failed to get events by type: {e}"),
554 })?;
555
556 let mut events_by_type = HashMap::new();
557 for row in type_rows {
558 let event_type: String = row.get("event_type");
559 let count: i64 = row.get("count");
560 events_by_type.insert(event_type, count as u64);
561 }
562
563 let user_rows = sqlx::query("SELECT user_id, COUNT(*) as count FROM audit_events WHERE user_id IS NOT NULL GROUP BY user_id ORDER BY count DESC LIMIT 20")
565 .fetch_all(self.db_pool.as_ref())
566 .await
567 .map_err(|e| SecurityError::AuditError {
568 message: format!("Failed to get events by user: {e}"),
569 })?;
570
571 let mut events_by_user = HashMap::new();
572 for row in user_rows {
573 let user_id: String = row.get("user_id");
574 let count: i64 = row.get("count");
575 events_by_user.insert(user_id, count as u64);
576 }
577
578 Ok(AuditStatistics {
579 total_events: total_events as u64,
580 events_by_type,
581 events_by_user,
582 failed_events: failed_events as u64,
583 critical_events: critical_events as u64,
584 retention_days: self.config.retention_days,
585 oldest_event,
586 newest_event,
587 })
588 }
589
590 pub async fn cleanup_old_events(&self) -> Result<u64> {
592 if !self.config.enabled {
593 return Ok(0);
594 }
595
596 let cutoff_date = Utc::now() - chrono::Duration::days(self.config.retention_days as i64);
597
598 let deleted_count: i64 =
599 sqlx::query_scalar("DELETE FROM audit_events WHERE timestamp < $1 RETURNING COUNT(*)")
600 .bind(cutoff_date)
601 .fetch_optional(self.db_pool.as_ref())
602 .await
603 .map_err(|e| SecurityError::AuditError {
604 message: format!("Failed to cleanup old audit events: {e}"),
605 })?
606 .unwrap_or(0);
607
608 if deleted_count > 0 {
609 info!("Cleaned up {} old audit events", deleted_count);
610 }
611
612 Ok(deleted_count as u64)
613 }
614
615 fn should_log_event(&self, event_type: &AuditEventType) -> bool {
616 match event_type {
617 AuditEventType::Authentication => self.config.log_auth_events,
618 AuditEventType::DataAccess => self.config.log_data_access,
619 AuditEventType::DataModification => self.config.log_modifications,
620 AuditEventType::DataDeletion => self.config.log_modifications,
621 _ => true, }
623 }
624
625 fn row_to_audit_event(&self, row: sqlx::postgres::PgRow) -> Result<AuditEvent> {
626 let event_type_str: String = row.get("event_type");
627 let event_type = match event_type_str.as_str() {
628 "Authentication" => AuditEventType::Authentication,
629 "Authorization" => AuditEventType::Authorization,
630 "DataAccess" => AuditEventType::DataAccess,
631 "DataModification" => AuditEventType::DataModification,
632 "DataDeletion" => AuditEventType::DataDeletion,
633 "SystemAccess" => AuditEventType::SystemAccess,
634 "ConfigurationChange" => AuditEventType::ConfigurationChange,
635 "SecurityEvent" => AuditEventType::SecurityEvent,
636 "RateLimitViolation" => AuditEventType::RateLimitViolation,
637 "Error" => AuditEventType::Error,
638 _ => AuditEventType::SystemAccess,
639 };
640
641 let severity_str: String = row.get("severity");
642 let severity = match severity_str.as_str() {
643 "Low" => AuditSeverity::Low,
644 "Medium" => AuditSeverity::Medium,
645 "High" => AuditSeverity::High,
646 "Critical" => AuditSeverity::Critical,
647 _ => AuditSeverity::Low,
648 };
649
650 let outcome_str: String = row.get("outcome");
651 let outcome = match outcome_str.as_str() {
652 "Success" => AuditOutcome::Success,
653 "Failure" => AuditOutcome::Failure,
654 "Partial" => AuditOutcome::Partial,
655 _ => AuditOutcome::Success,
656 };
657
658 let details_value: Value = row.get("details");
659 let details: HashMap<String, Value> =
660 serde_json::from_value(details_value).unwrap_or_else(|_| HashMap::new());
661
662 Ok(AuditEvent {
663 id: row.get("id"),
664 timestamp: row.get("timestamp"),
665 event_type,
666 severity,
667 user_id: row.get("user_id"),
668 session_id: row.get("session_id"),
669 ip_address: row.get::<Option<String>, _>("ip_address"),
670 user_agent: row.get("user_agent"),
671 resource: row.get("resource"),
672 action: row.get("action"),
673 outcome,
674 details,
675 error_message: row.get("error_message"),
676 request_id: row.get("request_id"),
677 })
678 }
679
680 pub fn is_enabled(&self) -> bool {
681 self.config.enabled
682 }
683}
684
685#[derive(Debug, Clone, Default)]
687pub struct AuditFilter {
688 pub user_id: Option<String>,
689 pub event_type: Option<AuditEventType>,
690 pub start_time: Option<DateTime<Utc>>,
691 pub end_time: Option<DateTime<Utc>>,
692 pub limit: Option<i64>,
693 pub offset: Option<i64>,
694}
695
696#[derive(Debug)]
699pub struct AuditLogger {
700 config: AuditConfig,
701 manager: Option<Arc<AuditManager>>,
702}
703
704impl AuditLogger {
705 pub fn new(config: AuditConfig) -> Result<Self> {
707 Ok(Self {
708 config,
709 manager: None,
710 })
711 }
712
713 pub fn with_manager(config: AuditConfig, manager: Arc<AuditManager>) -> Self {
715 Self {
716 config,
717 manager: Some(manager),
718 }
719 }
720
721 pub async fn log_auth_event(
723 &self,
724 client_id: &str,
725 user_id: &str,
726 method_name: &str,
727 success: bool,
728 error_message: Option<&str>,
729 ) {
730 if let Some(ref manager) = self.manager {
731 let _ = manager
732 .log_auth_event(client_id, user_id, method_name, success, error_message)
733 .await;
734 } else {
735 if success {
737 debug!(
738 "AUTH_SUCCESS: client_id={}, user_id={}, method={}",
739 client_id, user_id, method_name
740 );
741 } else {
742 error!(
743 "AUTH_FAILURE: client_id={}, user_id={}, method={}, error={:?}",
744 client_id, user_id, method_name, error_message
745 );
746 }
747 }
748 }
749
750 pub async fn log_rate_limit_violation(
752 &self,
753 client_id: &str,
754 tool_name: &str,
755 limit_type: &str,
756 ) {
757 if let Some(ref manager) = self.manager {
758 let _ = manager
759 .log_rate_limit_violation(client_id, tool_name, limit_type)
760 .await;
761 } else {
762 warn!(
764 "RATE_LIMIT_VIOLATION: client_id={}, tool={}, limit_type={}",
765 client_id, tool_name, limit_type
766 );
767 }
768 }
769
770 pub async fn log_security_event(
772 &self,
773 action: &str,
774 severity: AuditSeverity,
775 user_id: Option<&str>,
776 details: HashMap<String, Value>,
777 ) {
778 if let Some(ref manager) = self.manager {
779 let _ = manager
780 .log_security_event(action, severity, user_id, None, details)
781 .await;
782 } else {
783 match severity {
785 AuditSeverity::Critical => error!(
786 "SECURITY_CRITICAL: action={}, user_id={:?}, details={:?}",
787 action, user_id, details
788 ),
789 AuditSeverity::High => error!(
790 "SECURITY_HIGH: action={}, user_id={:?}, details={:?}",
791 action, user_id, details
792 ),
793 AuditSeverity::Medium => warn!(
794 "SECURITY_MEDIUM: action={}, user_id={:?}, details={:?}",
795 action, user_id, details
796 ),
797 AuditSeverity::Low => debug!(
798 "SECURITY_LOW: action={}, user_id={:?}, details={:?}",
799 action, user_id, details
800 ),
801 }
802 }
803 }
804}
805
806#[cfg(test)]
807mod tests {
808 use super::*;
809 use serde_json::json;
810
811 #[test]
812 fn test_audit_event_creation() {
813 let mut details = HashMap::new();
814 details.insert("test_key".to_string(), json!("test_value"));
815
816 let event = AuditEvent {
817 id: Uuid::new_v4().to_string(),
818 timestamp: Utc::now(),
819 event_type: AuditEventType::Authentication,
820 severity: AuditSeverity::Medium,
821 user_id: Some("test-user".to_string()),
822 session_id: None,
823 ip_address: Some("192.168.1.1".to_string()),
824 user_agent: None,
825 resource: Some("login".to_string()),
826 action: "user_login".to_string(),
827 outcome: AuditOutcome::Success,
828 details,
829 error_message: None,
830 request_id: None,
831 };
832
833 assert!(!event.id.is_empty());
834 assert_eq!(event.action, "user_login");
835 assert!(matches!(event.event_type, AuditEventType::Authentication));
836 assert!(matches!(event.severity, AuditSeverity::Medium));
837 assert!(matches!(event.outcome, AuditOutcome::Success));
838 assert_eq!(event.user_id.unwrap(), "test-user");
839 }
840
841 #[test]
842 fn test_audit_filter_default() {
843 let filter = AuditFilter::default();
844 assert!(filter.user_id.is_none());
845 assert!(filter.event_type.is_none());
846 assert!(filter.start_time.is_none());
847 assert!(filter.end_time.is_none());
848 assert!(filter.limit.is_none());
849 assert!(filter.offset.is_none());
850 }
851
852 #[test]
853 fn test_audit_statistics_default() {
854 let stats = AuditStatistics {
855 total_events: 0,
856 events_by_type: HashMap::new(),
857 events_by_user: HashMap::new(),
858 failed_events: 0,
859 critical_events: 0,
860 retention_days: 90,
861 oldest_event: None,
862 newest_event: None,
863 };
864
865 assert_eq!(stats.total_events, 0);
866 assert_eq!(stats.failed_events, 0);
867 assert_eq!(stats.critical_events, 0);
868 assert_eq!(stats.retention_days, 90);
869 assert!(stats.events_by_type.is_empty());
870 assert!(stats.events_by_user.is_empty());
871 }
872
873 #[test]
874 fn test_event_type_serialization() {
875 let event_type = AuditEventType::Authentication;
876 let serialized = serde_json::to_string(&event_type).unwrap();
877 assert_eq!(serialized, "\"Authentication\"");
878
879 let deserialized: AuditEventType = serde_json::from_str(&serialized).unwrap();
880 assert!(matches!(deserialized, AuditEventType::Authentication));
881 }
882
883 #[test]
884 fn test_severity_ordering() {
885 let low = AuditSeverity::Low;
887 let critical = AuditSeverity::Critical;
888
889 match low {
891 AuditSeverity::Low => assert!(true),
892 _ => assert!(false),
893 }
894
895 match critical {
896 AuditSeverity::Critical => assert!(true),
897 _ => assert!(false),
898 }
899 }
900
901 #[test]
902 fn test_outcome_variants() {
903 let outcomes = vec![
904 AuditOutcome::Success,
905 AuditOutcome::Failure,
906 AuditOutcome::Partial,
907 ];
908
909 assert_eq!(outcomes.len(), 3);
910
911 for outcome in outcomes {
912 match outcome {
913 AuditOutcome::Success | AuditOutcome::Failure | AuditOutcome::Partial => {
914 assert!(true);
916 }
917 }
918 }
919 }
920}