1use std::collections::HashMap;
44use std::fs::{File, OpenOptions};
45use std::io::{BufWriter, Write};
46use std::path::Path;
47use std::sync::{Arc, Mutex};
48use std::time::{SystemTime, UNIX_EPOCH};
49
50use serde::{Deserialize, Serialize};
51
52use super::authorization::Permission;
53use super::error::SecurityError;
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
57#[serde(rename_all = "snake_case")]
58pub enum AuditEventType {
59 Authentication,
61 Authorization,
63 DataAccess,
65 DataModification,
67 KeyExchange,
69 CellFormation,
71 LeaderElection,
73 SecurityViolation,
75 SessionManagement,
77 Encryption,
79}
80
81impl std::fmt::Display for AuditEventType {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 AuditEventType::Authentication => write!(f, "AUTHENTICATION"),
85 AuditEventType::Authorization => write!(f, "AUTHORIZATION"),
86 AuditEventType::DataAccess => write!(f, "DATA_ACCESS"),
87 AuditEventType::DataModification => write!(f, "DATA_MODIFICATION"),
88 AuditEventType::KeyExchange => write!(f, "KEY_EXCHANGE"),
89 AuditEventType::CellFormation => write!(f, "CELL_FORMATION"),
90 AuditEventType::LeaderElection => write!(f, "LEADER_ELECTION"),
91 AuditEventType::SecurityViolation => write!(f, "SECURITY_VIOLATION"),
92 AuditEventType::SessionManagement => write!(f, "SESSION_MANAGEMENT"),
93 AuditEventType::Encryption => write!(f, "ENCRYPTION"),
94 }
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
100#[serde(rename_all = "snake_case")]
101pub enum SecurityViolation {
102 InvalidSignature,
104 ReplayAttack,
106 UnauthorizedAccess,
108 CertificateError,
110 TamperedMessage,
112 RateLimitExceeded,
114 UnknownDevice,
116 ExpiredCredentials,
118 ProtocolViolation,
120}
121
122impl std::fmt::Display for SecurityViolation {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 match self {
125 SecurityViolation::InvalidSignature => write!(f, "INVALID_SIGNATURE"),
126 SecurityViolation::ReplayAttack => write!(f, "REPLAY_ATTACK"),
127 SecurityViolation::UnauthorizedAccess => write!(f, "UNAUTHORIZED_ACCESS"),
128 SecurityViolation::CertificateError => write!(f, "CERTIFICATE_ERROR"),
129 SecurityViolation::TamperedMessage => write!(f, "TAMPERED_MESSAGE"),
130 SecurityViolation::RateLimitExceeded => write!(f, "RATE_LIMIT_EXCEEDED"),
131 SecurityViolation::UnknownDevice => write!(f, "UNKNOWN_DEVICE"),
132 SecurityViolation::ExpiredCredentials => write!(f, "EXPIRED_CREDENTIALS"),
133 SecurityViolation::ProtocolViolation => write!(f, "PROTOCOL_VIOLATION"),
134 }
135 }
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct AuditLogEntry {
141 pub timestamp: u64,
143 pub timestamp_iso: String,
145 pub event_type: AuditEventType,
147 pub entity_id: String,
149 pub success: bool,
151 pub description: String,
153 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
155 pub context: HashMap<String, String>,
156 pub sequence: u64,
158}
159
160impl AuditLogEntry {
161 pub fn new(
163 event_type: AuditEventType,
164 entity_id: impl Into<String>,
165 success: bool,
166 description: impl Into<String>,
167 sequence: u64,
168 ) -> Self {
169 let now = SystemTime::now()
170 .duration_since(UNIX_EPOCH)
171 .map(|d| d.as_secs())
172 .unwrap_or(0);
173
174 Self {
175 timestamp: now,
176 timestamp_iso: format_timestamp(now),
177 event_type,
178 entity_id: entity_id.into(),
179 success,
180 description: description.into(),
181 context: HashMap::new(),
182 sequence,
183 }
184 }
185
186 pub fn with_context(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
188 self.context.insert(key.into(), value.into());
189 self
190 }
191
192 pub fn to_json(&self) -> String {
194 serde_json::to_string(self).unwrap_or_else(|_| {
195 format!(
196 "{{\"error\":\"serialization failed for seq {}\"}}",
197 self.sequence
198 )
199 })
200 }
201}
202
203fn format_timestamp(secs: u64) -> String {
205 let dt = chrono::DateTime::from_timestamp(secs as i64, 0)
207 .or_else(|| chrono::DateTime::from_timestamp(0, 0))
208 .expect("Unix epoch is always a valid timestamp");
209 dt.format("%Y-%m-%dT%H:%M:%SZ").to_string()
210}
211
212pub trait AuditLogger: Send + Sync {
214 fn log_authentication(&self, entity_id: &str, success: bool, reason: Option<&str>);
216
217 fn log_grant(&self, entity_id: &str, permission: Permission, target: &str);
219
220 fn log_denial(&self, entity_id: &str, permission: &str, target: &str, reason: &str);
222
223 fn log_operation(&self, entity_id: &str, operation: &str, target: &str, success: bool);
225
226 fn log_violation(&self, entity_id: &str, violation: SecurityViolation, details: &str);
228
229 fn log_cell_event(&self, entity_id: &str, cell_id: &str, action: &str, success: bool);
231
232 fn log_key_exchange(&self, entity_id: &str, peer_id: &str, success: bool);
234
235 fn log_session(&self, entity_id: &str, session_id: &str, action: &str, success: bool);
237
238 fn log_encryption(&self, entity_id: &str, operation: &str, target: &str, success: bool);
240
241 fn entry_count(&self) -> u64;
243
244 fn flush(&self) -> Result<(), SecurityError>;
246}
247
248#[derive(Debug)]
250pub struct MemoryAuditLogger {
251 entries: Arc<Mutex<Vec<AuditLogEntry>>>,
252 sequence: Arc<Mutex<u64>>,
253}
254
255impl MemoryAuditLogger {
256 pub fn new() -> Self {
258 Self {
259 entries: Arc::new(Mutex::new(Vec::new())),
260 sequence: Arc::new(Mutex::new(0)),
261 }
262 }
263
264 pub fn entries(&self) -> Vec<AuditLogEntry> {
266 self.entries.lock().expect("entries lock poisoned").clone()
267 }
268
269 pub fn entries_by_type(&self, event_type: AuditEventType) -> Vec<AuditLogEntry> {
271 self.entries
272 .lock()
273 .expect("entries lock poisoned")
274 .iter()
275 .filter(|e| e.event_type == event_type)
276 .cloned()
277 .collect()
278 }
279
280 pub fn clear(&self) {
282 self.entries.lock().expect("entries lock poisoned").clear();
283 }
284
285 fn next_sequence(&self) -> u64 {
286 let mut seq = self.sequence.lock().expect("sequence lock poisoned");
287 *seq += 1;
288 *seq
289 }
290
291 fn add_entry(&self, entry: AuditLogEntry) {
292 self.entries
293 .lock()
294 .expect("entries lock poisoned")
295 .push(entry);
296 }
297}
298
299impl Default for MemoryAuditLogger {
300 fn default() -> Self {
301 Self::new()
302 }
303}
304
305impl AuditLogger for MemoryAuditLogger {
306 fn log_authentication(&self, entity_id: &str, success: bool, reason: Option<&str>) {
307 let description = match (success, reason) {
308 (true, Some(r)) => format!("Authentication succeeded: {}", r),
309 (true, None) => "Authentication succeeded".to_string(),
310 (false, Some(r)) => format!("Authentication failed: {}", r),
311 (false, None) => "Authentication failed".to_string(),
312 };
313
314 let entry = AuditLogEntry::new(
315 AuditEventType::Authentication,
316 entity_id,
317 success,
318 description,
319 self.next_sequence(),
320 );
321 self.add_entry(entry);
322 }
323
324 fn log_grant(&self, entity_id: &str, permission: Permission, target: &str) {
325 let entry = AuditLogEntry::new(
326 AuditEventType::Authorization,
327 entity_id,
328 true,
329 format!("Permission granted: {:?}", permission),
330 self.next_sequence(),
331 )
332 .with_context("permission", format!("{:?}", permission))
333 .with_context("target", target);
334 self.add_entry(entry);
335 }
336
337 fn log_denial(&self, entity_id: &str, permission: &str, target: &str, reason: &str) {
338 let entry = AuditLogEntry::new(
339 AuditEventType::Authorization,
340 entity_id,
341 false,
342 format!("Permission denied: {} - {}", permission, reason),
343 self.next_sequence(),
344 )
345 .with_context("permission", permission)
346 .with_context("target", target)
347 .with_context("reason", reason);
348 self.add_entry(entry);
349 }
350
351 fn log_operation(&self, entity_id: &str, operation: &str, target: &str, success: bool) {
352 let event_type = if operation.starts_with("read")
353 || operation.starts_with("get")
354 || operation.starts_with("query")
355 {
356 AuditEventType::DataAccess
357 } else {
358 AuditEventType::DataModification
359 };
360
361 let entry = AuditLogEntry::new(
362 event_type,
363 entity_id,
364 success,
365 format!("{} on {}", operation, target),
366 self.next_sequence(),
367 )
368 .with_context("operation", operation)
369 .with_context("target", target);
370 self.add_entry(entry);
371 }
372
373 fn log_violation(&self, entity_id: &str, violation: SecurityViolation, details: &str) {
374 let entry = AuditLogEntry::new(
375 AuditEventType::SecurityViolation,
376 entity_id,
377 false,
378 format!("Security violation: {} - {}", violation, details),
379 self.next_sequence(),
380 )
381 .with_context("violation_type", violation.to_string())
382 .with_context("details", details);
383 self.add_entry(entry);
384 }
385
386 fn log_cell_event(&self, entity_id: &str, cell_id: &str, action: &str, success: bool) {
387 let entry = AuditLogEntry::new(
388 AuditEventType::CellFormation,
389 entity_id,
390 success,
391 format!("Cell {}: {}", action, cell_id),
392 self.next_sequence(),
393 )
394 .with_context("cell_id", cell_id)
395 .with_context("action", action);
396 self.add_entry(entry);
397 }
398
399 fn log_key_exchange(&self, entity_id: &str, peer_id: &str, success: bool) {
400 let entry = AuditLogEntry::new(
401 AuditEventType::KeyExchange,
402 entity_id,
403 success,
404 format!("Key exchange with peer: {}", peer_id),
405 self.next_sequence(),
406 )
407 .with_context("peer_id", peer_id);
408 self.add_entry(entry);
409 }
410
411 fn log_session(&self, entity_id: &str, session_id: &str, action: &str, success: bool) {
412 let entry = AuditLogEntry::new(
413 AuditEventType::SessionManagement,
414 entity_id,
415 success,
416 format!("Session {}: {}", action, session_id),
417 self.next_sequence(),
418 )
419 .with_context("session_id", session_id)
420 .with_context("action", action);
421 self.add_entry(entry);
422 }
423
424 fn log_encryption(&self, entity_id: &str, operation: &str, target: &str, success: bool) {
425 let entry = AuditLogEntry::new(
426 AuditEventType::Encryption,
427 entity_id,
428 success,
429 format!("Encryption {}: {}", operation, target),
430 self.next_sequence(),
431 )
432 .with_context("operation", operation)
433 .with_context("target", target);
434 self.add_entry(entry);
435 }
436
437 fn entry_count(&self) -> u64 {
438 self.entries.lock().expect("entries lock poisoned").len() as u64
439 }
440
441 fn flush(&self) -> Result<(), SecurityError> {
442 Ok(()) }
444}
445
446pub struct FileAuditLogger {
448 writer: Arc<Mutex<BufWriter<File>>>,
449 sequence: Arc<Mutex<u64>>,
450 path: String,
451}
452
453impl FileAuditLogger {
454 pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, SecurityError> {
458 let path_str = path.as_ref().to_string_lossy().to_string();
459
460 let file = OpenOptions::new().create(true).append(true).open(&path)?;
461
462 Ok(Self {
463 writer: Arc::new(Mutex::new(BufWriter::new(file))),
464 sequence: Arc::new(Mutex::new(0)),
465 path: path_str,
466 })
467 }
468
469 pub fn path(&self) -> &str {
471 &self.path
472 }
473
474 fn next_sequence(&self) -> u64 {
475 let mut seq = self.sequence.lock().expect("sequence lock poisoned");
476 *seq += 1;
477 *seq
478 }
479
480 fn write_entry(&self, entry: &AuditLogEntry) {
481 let json = entry.to_json();
482 if let Ok(mut writer) = self.writer.lock() {
483 let _ = writeln!(writer, "{}", json);
484 let _ = writer.flush();
486 }
487 }
488}
489
490impl std::fmt::Debug for FileAuditLogger {
491 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
492 f.debug_struct("FileAuditLogger")
493 .field("path", &self.path)
494 .field("sequence", &self.sequence)
495 .finish()
496 }
497}
498
499impl AuditLogger for FileAuditLogger {
500 fn log_authentication(&self, entity_id: &str, success: bool, reason: Option<&str>) {
501 let description = match (success, reason) {
502 (true, Some(r)) => format!("Authentication succeeded: {}", r),
503 (true, None) => "Authentication succeeded".to_string(),
504 (false, Some(r)) => format!("Authentication failed: {}", r),
505 (false, None) => "Authentication failed".to_string(),
506 };
507
508 let entry = AuditLogEntry::new(
509 AuditEventType::Authentication,
510 entity_id,
511 success,
512 description,
513 self.next_sequence(),
514 );
515 self.write_entry(&entry);
516 }
517
518 fn log_grant(&self, entity_id: &str, permission: Permission, target: &str) {
519 let entry = AuditLogEntry::new(
520 AuditEventType::Authorization,
521 entity_id,
522 true,
523 format!("Permission granted: {:?}", permission),
524 self.next_sequence(),
525 )
526 .with_context("permission", format!("{:?}", permission))
527 .with_context("target", target);
528 self.write_entry(&entry);
529 }
530
531 fn log_denial(&self, entity_id: &str, permission: &str, target: &str, reason: &str) {
532 let entry = AuditLogEntry::new(
533 AuditEventType::Authorization,
534 entity_id,
535 false,
536 format!("Permission denied: {} - {}", permission, reason),
537 self.next_sequence(),
538 )
539 .with_context("permission", permission)
540 .with_context("target", target)
541 .with_context("reason", reason);
542 self.write_entry(&entry);
543 }
544
545 fn log_operation(&self, entity_id: &str, operation: &str, target: &str, success: bool) {
546 let event_type = if operation.starts_with("read")
547 || operation.starts_with("get")
548 || operation.starts_with("query")
549 {
550 AuditEventType::DataAccess
551 } else {
552 AuditEventType::DataModification
553 };
554
555 let entry = AuditLogEntry::new(
556 event_type,
557 entity_id,
558 success,
559 format!("{} on {}", operation, target),
560 self.next_sequence(),
561 )
562 .with_context("operation", operation)
563 .with_context("target", target);
564 self.write_entry(&entry);
565 }
566
567 fn log_violation(&self, entity_id: &str, violation: SecurityViolation, details: &str) {
568 let entry = AuditLogEntry::new(
569 AuditEventType::SecurityViolation,
570 entity_id,
571 false,
572 format!("Security violation: {} - {}", violation, details),
573 self.next_sequence(),
574 )
575 .with_context("violation_type", violation.to_string())
576 .with_context("details", details);
577 self.write_entry(&entry);
578 }
579
580 fn log_cell_event(&self, entity_id: &str, cell_id: &str, action: &str, success: bool) {
581 let entry = AuditLogEntry::new(
582 AuditEventType::CellFormation,
583 entity_id,
584 success,
585 format!("Cell {}: {}", action, cell_id),
586 self.next_sequence(),
587 )
588 .with_context("cell_id", cell_id)
589 .with_context("action", action);
590 self.write_entry(&entry);
591 }
592
593 fn log_key_exchange(&self, entity_id: &str, peer_id: &str, success: bool) {
594 let entry = AuditLogEntry::new(
595 AuditEventType::KeyExchange,
596 entity_id,
597 success,
598 format!("Key exchange with peer: {}", peer_id),
599 self.next_sequence(),
600 )
601 .with_context("peer_id", peer_id);
602 self.write_entry(&entry);
603 }
604
605 fn log_session(&self, entity_id: &str, session_id: &str, action: &str, success: bool) {
606 let entry = AuditLogEntry::new(
607 AuditEventType::SessionManagement,
608 entity_id,
609 success,
610 format!("Session {}: {}", action, session_id),
611 self.next_sequence(),
612 )
613 .with_context("session_id", session_id)
614 .with_context("action", action);
615 self.write_entry(&entry);
616 }
617
618 fn log_encryption(&self, entity_id: &str, operation: &str, target: &str, success: bool) {
619 let entry = AuditLogEntry::new(
620 AuditEventType::Encryption,
621 entity_id,
622 success,
623 format!("Encryption {}: {}", operation, target),
624 self.next_sequence(),
625 )
626 .with_context("operation", operation)
627 .with_context("target", target);
628 self.write_entry(&entry);
629 }
630
631 fn entry_count(&self) -> u64 {
632 *self.sequence.lock().expect("sequence lock poisoned")
633 }
634
635 fn flush(&self) -> Result<(), SecurityError> {
636 self.writer
637 .lock()
638 .map_err(|e| SecurityError::Internal(e.to_string()))?
639 .flush()?;
640 Ok(())
641 }
642}
643
644#[derive(Debug, Default)]
646pub struct NullAuditLogger;
647
648impl NullAuditLogger {
649 pub fn new() -> Self {
650 Self
651 }
652}
653
654impl AuditLogger for NullAuditLogger {
655 fn log_authentication(&self, _entity_id: &str, _success: bool, _reason: Option<&str>) {}
656 fn log_grant(&self, _entity_id: &str, _permission: Permission, _target: &str) {}
657 fn log_denial(&self, _entity_id: &str, _permission: &str, _target: &str, _reason: &str) {}
658 fn log_operation(&self, _entity_id: &str, _operation: &str, _target: &str, _success: bool) {}
659 fn log_violation(&self, _entity_id: &str, _violation: SecurityViolation, _details: &str) {}
660 fn log_cell_event(&self, _entity_id: &str, _cell_id: &str, _action: &str, _success: bool) {}
661 fn log_key_exchange(&self, _entity_id: &str, _peer_id: &str, _success: bool) {}
662 fn log_session(&self, _entity_id: &str, _session_id: &str, _action: &str, _success: bool) {}
663 fn log_encryption(&self, _entity_id: &str, _operation: &str, _target: &str, _success: bool) {}
664 fn entry_count(&self) -> u64 {
665 0
666 }
667 fn flush(&self) -> Result<(), SecurityError> {
668 Ok(())
669 }
670}
671
672#[cfg(test)]
673mod tests {
674 use super::*;
675
676 #[test]
677 fn test_audit_entry_creation() {
678 let entry = AuditLogEntry::new(
679 AuditEventType::Authentication,
680 "device:abc123",
681 true,
682 "Test entry",
683 1,
684 );
685
686 assert_eq!(entry.entity_id, "device:abc123");
687 assert!(entry.success);
688 assert_eq!(entry.event_type, AuditEventType::Authentication);
689 assert_eq!(entry.sequence, 1);
690 assert!(entry.timestamp > 0);
691 }
692
693 #[test]
694 fn test_audit_entry_with_context() {
695 let entry = AuditLogEntry::new(
696 AuditEventType::Authorization,
697 "device:abc123",
698 false,
699 "Access denied",
700 1,
701 )
702 .with_context("permission", "SetCellLeader")
703 .with_context("cell_id", "cell-1");
704
705 assert_eq!(entry.context.get("permission").unwrap(), "SetCellLeader");
706 assert_eq!(entry.context.get("cell_id").unwrap(), "cell-1");
707 }
708
709 #[test]
710 fn test_audit_entry_json_serialization() {
711 let entry = AuditLogEntry::new(
712 AuditEventType::Authentication,
713 "device:abc123",
714 true,
715 "Login successful",
716 42,
717 );
718
719 let json = entry.to_json();
720 assert!(json.contains("\"entity_id\":\"device:abc123\""));
721 assert!(json.contains("\"success\":true"));
722 assert!(json.contains("\"sequence\":42"));
723 }
724
725 #[test]
726 fn test_memory_audit_logger() {
727 let logger = MemoryAuditLogger::new();
728
729 logger.log_authentication("device:abc", true, Some("verified"));
730 logger.log_denial("device:abc", "SetLeader", "cell-1", "not authorized");
731 logger.log_operation("device:abc", "store_cell", "cell-1", true);
732
733 assert_eq!(logger.entry_count(), 3);
734
735 let auth_entries = logger.entries_by_type(AuditEventType::Authentication);
736 assert_eq!(auth_entries.len(), 1);
737 assert!(auth_entries[0].success);
738
739 let authz_entries = logger.entries_by_type(AuditEventType::Authorization);
740 assert_eq!(authz_entries.len(), 1);
741 assert!(!authz_entries[0].success);
742 }
743
744 #[test]
745 fn test_memory_logger_all_event_types() {
746 let logger = MemoryAuditLogger::new();
747
748 logger.log_authentication("dev1", true, None);
749 logger.log_grant("dev1", Permission::JoinCell, "cell-1");
750 logger.log_denial("dev1", "SetLeader", "cell-1", "not leader");
751 logger.log_operation("dev1", "read_cell", "cell-1", true);
752 logger.log_operation("dev1", "store_cell", "cell-1", true);
753 logger.log_violation("dev2", SecurityViolation::InvalidSignature, "bad sig");
754 logger.log_cell_event("dev1", "cell-1", "join", true);
755 logger.log_key_exchange("dev1", "dev2", true);
756 logger.log_session("user1", "sess-1", "create", true);
757 logger.log_encryption("dev1", "encrypt_for_cell", "cell-1", true);
758
759 assert_eq!(logger.entry_count(), 10);
760
761 assert_eq!(
763 logger.entries_by_type(AuditEventType::Authentication).len(),
764 1
765 );
766 assert_eq!(
767 logger.entries_by_type(AuditEventType::Authorization).len(),
768 2
769 );
770 assert_eq!(logger.entries_by_type(AuditEventType::DataAccess).len(), 1);
771 assert_eq!(
772 logger
773 .entries_by_type(AuditEventType::DataModification)
774 .len(),
775 1
776 );
777 assert_eq!(
778 logger
779 .entries_by_type(AuditEventType::SecurityViolation)
780 .len(),
781 1
782 );
783 assert_eq!(
784 logger.entries_by_type(AuditEventType::CellFormation).len(),
785 1
786 );
787 assert_eq!(logger.entries_by_type(AuditEventType::KeyExchange).len(), 1);
788 assert_eq!(
789 logger
790 .entries_by_type(AuditEventType::SessionManagement)
791 .len(),
792 1
793 );
794 assert_eq!(logger.entries_by_type(AuditEventType::Encryption).len(), 1);
795 }
796
797 #[test]
798 fn test_file_audit_logger() {
799 let temp_dir = tempfile::tempdir().unwrap();
800 let log_path = temp_dir.path().join("audit.log");
801
802 let logger = FileAuditLogger::new(&log_path).unwrap();
803 logger.log_authentication("device:test", true, Some("test auth"));
804 logger.log_denial("device:test", "TestPerm", "target", "testing");
805 logger.flush().unwrap();
806
807 let contents = std::fs::read_to_string(&log_path).unwrap();
809 let lines: Vec<&str> = contents.lines().collect();
810 assert_eq!(lines.len(), 2);
811
812 let entry: AuditLogEntry = serde_json::from_str(lines[0]).unwrap();
814 assert_eq!(entry.entity_id, "device:test");
815 assert!(entry.success);
816 }
817
818 #[test]
819 fn test_null_audit_logger() {
820 let logger = NullAuditLogger::new();
821
822 logger.log_authentication("test", true, None);
824 logger.log_denial("test", "perm", "target", "reason");
825
826 assert_eq!(logger.entry_count(), 0);
827 assert!(logger.flush().is_ok());
828 }
829
830 #[test]
831 fn test_security_violation_types() {
832 let violations = vec![
833 SecurityViolation::InvalidSignature,
834 SecurityViolation::ReplayAttack,
835 SecurityViolation::UnauthorizedAccess,
836 SecurityViolation::CertificateError,
837 SecurityViolation::TamperedMessage,
838 SecurityViolation::RateLimitExceeded,
839 SecurityViolation::UnknownDevice,
840 SecurityViolation::ExpiredCredentials,
841 SecurityViolation::ProtocolViolation,
842 ];
843
844 let logger = MemoryAuditLogger::new();
845 for violation in violations {
846 logger.log_violation("test", violation, "test details");
847 }
848
849 assert_eq!(logger.entry_count(), 9);
850 }
851
852 #[test]
853 fn test_audit_entry_sequence_ordering() {
854 let logger = MemoryAuditLogger::new();
855
856 for i in 0..10 {
857 logger.log_authentication(&format!("dev{}", i), true, None);
858 }
859
860 let entries = logger.entries();
861 for (i, entry) in entries.iter().enumerate() {
862 assert_eq!(entry.sequence, (i + 1) as u64);
863 }
864 }
865
866 #[test]
867 fn test_event_type_display() {
868 assert_eq!(
869 format!("{}", AuditEventType::Authentication),
870 "AUTHENTICATION"
871 );
872 assert_eq!(
873 format!("{}", AuditEventType::Authorization),
874 "AUTHORIZATION"
875 );
876 assert_eq!(
877 format!("{}", AuditEventType::SecurityViolation),
878 "SECURITY_VIOLATION"
879 );
880 }
881}