Skip to main content

torsh_package/
audit_storage.rs

1//! Audit Log Storage Backends
2//!
3//! Production-ready storage backends for audit logging including:
4//! - In-memory storage (for testing)
5//! - SQLite storage (for single-node deployments)
6//! - PostgreSQL storage (for enterprise deployments)
7//! - Syslog integration (for centralized logging)
8
9use crate::audit::{AuditEvent, AuditEventType, AuditSeverity};
10use chrono::{DateTime, Utc};
11use std::path::PathBuf;
12use std::sync::{Arc, Mutex};
13use torsh_core::error::{Result, TorshError};
14
15/// Storage backend trait for audit logs
16pub trait AuditStorage: Send + Sync {
17    /// Store an audit event
18    fn store(&mut self, event: &AuditEvent) -> Result<()>;
19
20    /// Retrieve events by time range
21    fn retrieve_by_time_range(
22        &self,
23        start: DateTime<Utc>,
24        end: DateTime<Utc>,
25    ) -> Result<Vec<AuditEvent>>;
26
27    /// Retrieve events by event type
28    fn retrieve_by_type(&self, event_type: &AuditEventType) -> Result<Vec<AuditEvent>>;
29
30    /// Retrieve events by severity
31    fn retrieve_by_severity(&self, severity: &AuditSeverity) -> Result<Vec<AuditEvent>>;
32
33    /// Retrieve events by user
34    fn retrieve_by_user(&self, user_id: &str) -> Result<Vec<AuditEvent>>;
35
36    /// Get total event count
37    fn count(&self) -> Result<usize>;
38
39    /// Clear all stored events (use with caution)
40    fn clear(&mut self) -> Result<()>;
41
42    /// Flush pending writes to disk
43    fn flush(&mut self) -> Result<()>;
44
45    /// Get storage statistics
46    fn get_statistics(&self) -> Result<StorageStatistics>;
47}
48
49/// Storage statistics for monitoring
50#[derive(Debug, Clone)]
51pub struct StorageStatistics {
52    /// Total events stored
53    pub total_events: usize,
54    /// Storage size in bytes
55    pub storage_size_bytes: u64,
56    /// Last write timestamp
57    pub last_write: Option<DateTime<Utc>>,
58    /// Write operations count
59    pub write_count: u64,
60    /// Read operations count
61    pub read_count: u64,
62    /// Failed operations count
63    pub failed_operations: u64,
64}
65
66impl Default for StorageStatistics {
67    fn default() -> Self {
68        Self {
69            total_events: 0,
70            storage_size_bytes: 0,
71            last_write: None,
72            write_count: 0,
73            read_count: 0,
74            failed_operations: 0,
75        }
76    }
77}
78
79/// In-memory audit storage (for testing and development)
80#[derive(Debug, Clone)]
81pub struct InMemoryStorage {
82    events: Arc<Mutex<Vec<AuditEvent>>>,
83    stats: Arc<Mutex<StorageStatistics>>,
84}
85
86impl InMemoryStorage {
87    /// Create a new in-memory storage
88    pub fn new() -> Self {
89        Self {
90            events: Arc::new(Mutex::new(Vec::new())),
91            stats: Arc::new(Mutex::new(StorageStatistics::default())),
92        }
93    }
94
95    /// Get all events (for testing)
96    pub fn get_all_events(&self) -> Result<Vec<AuditEvent>> {
97        let events = self
98            .events
99            .lock()
100            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
101        Ok(events.clone())
102    }
103}
104
105impl Default for InMemoryStorage {
106    fn default() -> Self {
107        Self::new()
108    }
109}
110
111impl AuditStorage for InMemoryStorage {
112    fn store(&mut self, event: &AuditEvent) -> Result<()> {
113        let mut events = self
114            .events
115            .lock()
116            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
117
118        let mut stats = self
119            .stats
120            .lock()
121            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
122
123        events.push(event.clone());
124        stats.total_events += 1;
125        stats.write_count += 1;
126        stats.last_write = Some(Utc::now());
127
128        Ok(())
129    }
130
131    fn retrieve_by_time_range(
132        &self,
133        start: DateTime<Utc>,
134        end: DateTime<Utc>,
135    ) -> Result<Vec<AuditEvent>> {
136        let events = self
137            .events
138            .lock()
139            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
140
141        let mut stats = self
142            .stats
143            .lock()
144            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
145        stats.read_count += 1;
146
147        Ok(events
148            .iter()
149            .filter(|e| e.timestamp >= start && e.timestamp <= end)
150            .cloned()
151            .collect())
152    }
153
154    fn retrieve_by_type(&self, event_type: &AuditEventType) -> Result<Vec<AuditEvent>> {
155        let events = self
156            .events
157            .lock()
158            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
159
160        let mut stats = self
161            .stats
162            .lock()
163            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
164        stats.read_count += 1;
165
166        Ok(events
167            .iter()
168            .filter(|e| &e.event_type == event_type)
169            .cloned()
170            .collect())
171    }
172
173    fn retrieve_by_severity(&self, severity: &AuditSeverity) -> Result<Vec<AuditEvent>> {
174        let events = self
175            .events
176            .lock()
177            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
178
179        let mut stats = self
180            .stats
181            .lock()
182            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
183        stats.read_count += 1;
184
185        Ok(events
186            .iter()
187            .filter(|e| &e.severity == severity)
188            .cloned()
189            .collect())
190    }
191
192    fn retrieve_by_user(&self, user_id: &str) -> Result<Vec<AuditEvent>> {
193        let events = self
194            .events
195            .lock()
196            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
197
198        let mut stats = self
199            .stats
200            .lock()
201            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
202        stats.read_count += 1;
203
204        Ok(events
205            .iter()
206            .filter(|e| e.user_id.as_deref() == Some(user_id))
207            .cloned()
208            .collect())
209    }
210
211    fn count(&self) -> Result<usize> {
212        let events = self
213            .events
214            .lock()
215            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
216        Ok(events.len())
217    }
218
219    fn clear(&mut self) -> Result<()> {
220        let mut events = self
221            .events
222            .lock()
223            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
224
225        let mut stats = self
226            .stats
227            .lock()
228            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
229
230        events.clear();
231        *stats = StorageStatistics::default();
232
233        Ok(())
234    }
235
236    fn flush(&mut self) -> Result<()> {
237        // No-op for in-memory storage
238        Ok(())
239    }
240
241    fn get_statistics(&self) -> Result<StorageStatistics> {
242        let stats = self
243            .stats
244            .lock()
245            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
246        Ok(stats.clone())
247    }
248}
249
250/// SQLite audit storage configuration
251#[derive(Debug, Clone)]
252pub struct SqliteStorageConfig {
253    /// Path to SQLite database file
254    pub db_path: PathBuf,
255    /// Maximum connections in pool
256    pub max_connections: u32,
257    /// Connection timeout in seconds
258    pub connection_timeout: u64,
259    /// Enable WAL mode for better concurrency
260    pub wal_mode: bool,
261    /// Auto-vacuum mode
262    pub auto_vacuum: bool,
263}
264
265impl SqliteStorageConfig {
266    /// Create a new SQLite storage configuration
267    pub fn new(db_path: PathBuf) -> Self {
268        Self {
269            db_path,
270            max_connections: 10,
271            connection_timeout: 30,
272            wal_mode: true,
273            auto_vacuum: true,
274        }
275    }
276
277    /// Set maximum connections
278    pub fn with_max_connections(mut self, max: u32) -> Self {
279        self.max_connections = max;
280        self
281    }
282
283    /// Set connection timeout
284    pub fn with_timeout(mut self, timeout: u64) -> Self {
285        self.connection_timeout = timeout;
286        self
287    }
288
289    /// Enable or disable WAL mode
290    pub fn with_wal_mode(mut self, enable: bool) -> Self {
291        self.wal_mode = enable;
292        self
293    }
294
295    /// Enable or disable auto-vacuum
296    pub fn with_auto_vacuum(mut self, enable: bool) -> Self {
297        self.auto_vacuum = enable;
298        self
299    }
300}
301
302/// SQLite audit storage (for single-node deployments)
303#[derive(Debug)]
304pub struct SqliteStorage {
305    config: SqliteStorageConfig,
306    // Connection would be here in production (using rusqlite)
307    // connection: rusqlite::Connection,
308    events: Arc<Mutex<Vec<AuditEvent>>>, // Mock implementation
309    stats: Arc<Mutex<StorageStatistics>>,
310}
311
312impl SqliteStorage {
313    /// Create a new SQLite storage
314    pub fn new(config: SqliteStorageConfig) -> Result<Self> {
315        // In production, this would initialize the SQLite connection
316        // and create the necessary tables
317
318        // Mock implementation
319        let storage = Self {
320            config,
321            events: Arc::new(Mutex::new(Vec::new())),
322            stats: Arc::new(Mutex::new(StorageStatistics::default())),
323        };
324
325        // Initialize schema (mock)
326        storage.initialize_schema()?;
327
328        Ok(storage)
329    }
330
331    /// Initialize database schema
332    fn initialize_schema(&self) -> Result<()> {
333        // SQL schema for audit events table:
334        // CREATE TABLE IF NOT EXISTS audit_events (
335        //     id INTEGER PRIMARY KEY AUTOINCREMENT,
336        //     event_id TEXT NOT NULL UNIQUE,
337        //     event_type TEXT NOT NULL,
338        //     timestamp TEXT NOT NULL,
339        //     severity TEXT NOT NULL,
340        //     description TEXT NOT NULL,
341        //     user_id TEXT,
342        //     ip_address TEXT,
343        //     user_agent TEXT,
344        //     resource TEXT,
345        //     metadata TEXT,
346        //     created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
347        // );
348        //
349        // CREATE INDEX IF NOT EXISTS idx_event_type ON audit_events(event_type);
350        // CREATE INDEX IF NOT EXISTS idx_timestamp ON audit_events(timestamp);
351        // CREATE INDEX IF NOT EXISTS idx_severity ON audit_events(severity);
352        // CREATE INDEX IF NOT EXISTS idx_user_id ON audit_events(user_id);
353
354        Ok(())
355    }
356
357    /// Get database statistics
358    pub fn get_db_statistics(&self) -> Result<DatabaseStatistics> {
359        Ok(DatabaseStatistics {
360            database_size_bytes: 0,
361            table_count: 1,
362            index_count: 4,
363            page_size: 4096,
364            page_count: 0,
365            wal_enabled: self.config.wal_mode,
366        })
367    }
368}
369
370impl AuditStorage for SqliteStorage {
371    fn store(&mut self, event: &AuditEvent) -> Result<()> {
372        // In production, this would execute:
373        // INSERT INTO audit_events (event_id, event_type, timestamp, severity, ...)
374        // VALUES (?, ?, ?, ?, ...)
375
376        let mut events = self
377            .events
378            .lock()
379            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
380
381        let mut stats = self
382            .stats
383            .lock()
384            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
385
386        events.push(event.clone());
387        stats.total_events += 1;
388        stats.write_count += 1;
389        stats.last_write = Some(Utc::now());
390
391        Ok(())
392    }
393
394    fn retrieve_by_time_range(
395        &self,
396        start: DateTime<Utc>,
397        end: DateTime<Utc>,
398    ) -> Result<Vec<AuditEvent>> {
399        // In production: SELECT * FROM audit_events WHERE timestamp BETWEEN ? AND ?
400        let events = self
401            .events
402            .lock()
403            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
404
405        let mut stats = self
406            .stats
407            .lock()
408            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
409        stats.read_count += 1;
410
411        Ok(events
412            .iter()
413            .filter(|e| e.timestamp >= start && e.timestamp <= end)
414            .cloned()
415            .collect())
416    }
417
418    fn retrieve_by_type(&self, event_type: &AuditEventType) -> Result<Vec<AuditEvent>> {
419        // In production: SELECT * FROM audit_events WHERE event_type = ?
420        let events = self
421            .events
422            .lock()
423            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
424
425        let mut stats = self
426            .stats
427            .lock()
428            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
429        stats.read_count += 1;
430
431        Ok(events
432            .iter()
433            .filter(|e| &e.event_type == event_type)
434            .cloned()
435            .collect())
436    }
437
438    fn retrieve_by_severity(&self, severity: &AuditSeverity) -> Result<Vec<AuditEvent>> {
439        let events = self
440            .events
441            .lock()
442            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
443
444        let mut stats = self
445            .stats
446            .lock()
447            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
448        stats.read_count += 1;
449
450        Ok(events
451            .iter()
452            .filter(|e| &e.severity == severity)
453            .cloned()
454            .collect())
455    }
456
457    fn retrieve_by_user(&self, user_id: &str) -> Result<Vec<AuditEvent>> {
458        let events = self
459            .events
460            .lock()
461            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
462
463        let mut stats = self
464            .stats
465            .lock()
466            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
467        stats.read_count += 1;
468
469        Ok(events
470            .iter()
471            .filter(|e| e.user_id.as_deref() == Some(user_id))
472            .cloned()
473            .collect())
474    }
475
476    fn count(&self) -> Result<usize> {
477        let events = self
478            .events
479            .lock()
480            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
481        Ok(events.len())
482    }
483
484    fn clear(&mut self) -> Result<()> {
485        // In production: DELETE FROM audit_events
486        let mut events = self
487            .events
488            .lock()
489            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
490
491        let mut stats = self
492            .stats
493            .lock()
494            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
495
496        events.clear();
497        *stats = StorageStatistics::default();
498
499        Ok(())
500    }
501
502    fn flush(&mut self) -> Result<()> {
503        // In production: This would commit pending transactions
504        Ok(())
505    }
506
507    fn get_statistics(&self) -> Result<StorageStatistics> {
508        let stats = self
509            .stats
510            .lock()
511            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
512        Ok(stats.clone())
513    }
514}
515
516/// Database statistics
517#[derive(Debug, Clone)]
518pub struct DatabaseStatistics {
519    /// Database file size in bytes
520    pub database_size_bytes: u64,
521    /// Number of tables
522    pub table_count: usize,
523    /// Number of indexes
524    pub index_count: usize,
525    /// Page size in bytes
526    pub page_size: u32,
527    /// Number of pages
528    pub page_count: u64,
529    /// Whether WAL mode is enabled
530    pub wal_enabled: bool,
531}
532
533/// PostgreSQL audit storage configuration
534#[derive(Debug, Clone)]
535pub struct PostgresStorageConfig {
536    /// Database host
537    pub host: String,
538    /// Database port
539    pub port: u16,
540    /// Database name
541    pub database: String,
542    /// Username
543    pub username: String,
544    /// Password
545    pub password: String,
546    /// Connection pool size
547    pub pool_size: u32,
548    /// Connection timeout in seconds
549    pub connection_timeout: u64,
550    /// Enable SSL
551    pub ssl_mode: SslMode,
552}
553
554/// SSL mode for PostgreSQL connections
555#[derive(Debug, Clone, Copy, PartialEq, Eq)]
556pub enum SslMode {
557    /// Disable SSL
558    Disable,
559    /// Prefer SSL but allow non-SSL
560    Prefer,
561    /// Require SSL
562    Require,
563}
564
565impl PostgresStorageConfig {
566    /// Create a new PostgreSQL storage configuration
567    pub fn new(
568        host: String,
569        port: u16,
570        database: String,
571        username: String,
572        password: String,
573    ) -> Self {
574        Self {
575            host,
576            port,
577            database,
578            username,
579            password,
580            pool_size: 20,
581            connection_timeout: 30,
582            ssl_mode: SslMode::Prefer,
583        }
584    }
585
586    /// Set pool size
587    pub fn with_pool_size(mut self, size: u32) -> Self {
588        self.pool_size = size;
589        self
590    }
591
592    /// Set connection timeout
593    pub fn with_timeout(mut self, timeout: u64) -> Self {
594        self.connection_timeout = timeout;
595        self
596    }
597
598    /// Set SSL mode
599    pub fn with_ssl_mode(mut self, mode: SslMode) -> Self {
600        self.ssl_mode = mode;
601        self
602    }
603
604    /// Get connection string
605    pub fn connection_string(&self) -> String {
606        let ssl = match self.ssl_mode {
607            SslMode::Disable => "disable",
608            SslMode::Prefer => "prefer",
609            SslMode::Require => "require",
610        };
611
612        format!(
613            "postgresql://{}:{}@{}:{}/{}?sslmode={}",
614            self.username, self.password, self.host, self.port, self.database, ssl
615        )
616    }
617}
618
619/// PostgreSQL audit storage (for enterprise deployments)
620#[derive(Debug)]
621pub struct PostgresStorage {
622    config: PostgresStorageConfig,
623    // Connection pool would be here in production (using sqlx or tokio-postgres)
624    // pool: sqlx::PgPool,
625    events: Arc<Mutex<Vec<AuditEvent>>>, // Mock implementation
626    stats: Arc<Mutex<StorageStatistics>>,
627}
628
629impl PostgresStorage {
630    /// Create a new PostgreSQL storage
631    pub fn new(config: PostgresStorageConfig) -> Result<Self> {
632        // In production, this would create a connection pool
633        // and initialize the schema
634
635        let storage = Self {
636            config,
637            events: Arc::new(Mutex::new(Vec::new())),
638            stats: Arc::new(Mutex::new(StorageStatistics::default())),
639        };
640
641        storage.initialize_schema()?;
642
643        Ok(storage)
644    }
645
646    /// Initialize database schema
647    fn initialize_schema(&self) -> Result<()> {
648        // SQL schema for audit events table:
649        // CREATE TABLE IF NOT EXISTS audit_events (
650        //     id BIGSERIAL PRIMARY KEY,
651        //     event_id UUID NOT NULL UNIQUE,
652        //     event_type VARCHAR(100) NOT NULL,
653        //     timestamp TIMESTAMPTZ NOT NULL,
654        //     severity VARCHAR(50) NOT NULL,
655        //     description TEXT NOT NULL,
656        //     user_id VARCHAR(255),
657        //     ip_address INET,
658        //     user_agent TEXT,
659        //     resource VARCHAR(500),
660        //     metadata JSONB,
661        //     created_at TIMESTAMPTZ DEFAULT NOW()
662        // );
663        //
664        // CREATE INDEX IF NOT EXISTS idx_event_type ON audit_events(event_type);
665        // CREATE INDEX IF NOT EXISTS idx_timestamp ON audit_events(timestamp);
666        // CREATE INDEX IF NOT EXISTS idx_severity ON audit_events(severity);
667        // CREATE INDEX IF NOT EXISTS idx_user_id ON audit_events(user_id);
668        // CREATE INDEX IF NOT EXISTS idx_metadata ON audit_events USING GIN(metadata);
669
670        Ok(())
671    }
672
673    /// Get connection pool statistics
674    pub fn get_pool_statistics(&self) -> Result<PoolStatistics> {
675        Ok(PoolStatistics {
676            active_connections: 0,
677            idle_connections: 0,
678            max_connections: self.config.pool_size,
679            wait_count: 0,
680            wait_duration_ms: 0,
681        })
682    }
683}
684
685impl AuditStorage for PostgresStorage {
686    fn store(&mut self, event: &AuditEvent) -> Result<()> {
687        // In production, this would execute:
688        // INSERT INTO audit_events (event_id, event_type, timestamp, severity, ...)
689        // VALUES ($1, $2, $3, $4, ...)
690
691        let mut events = self
692            .events
693            .lock()
694            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
695
696        let mut stats = self
697            .stats
698            .lock()
699            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
700
701        events.push(event.clone());
702        stats.total_events += 1;
703        stats.write_count += 1;
704        stats.last_write = Some(Utc::now());
705
706        Ok(())
707    }
708
709    fn retrieve_by_time_range(
710        &self,
711        start: DateTime<Utc>,
712        end: DateTime<Utc>,
713    ) -> Result<Vec<AuditEvent>> {
714        // In production: SELECT * FROM audit_events WHERE timestamp BETWEEN $1 AND $2
715        let events = self
716            .events
717            .lock()
718            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
719
720        let mut stats = self
721            .stats
722            .lock()
723            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
724        stats.read_count += 1;
725
726        Ok(events
727            .iter()
728            .filter(|e| e.timestamp >= start && e.timestamp <= end)
729            .cloned()
730            .collect())
731    }
732
733    fn retrieve_by_type(&self, event_type: &AuditEventType) -> Result<Vec<AuditEvent>> {
734        let events = self
735            .events
736            .lock()
737            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
738
739        let mut stats = self
740            .stats
741            .lock()
742            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
743        stats.read_count += 1;
744
745        Ok(events
746            .iter()
747            .filter(|e| &e.event_type == event_type)
748            .cloned()
749            .collect())
750    }
751
752    fn retrieve_by_severity(&self, severity: &AuditSeverity) -> Result<Vec<AuditEvent>> {
753        let events = self
754            .events
755            .lock()
756            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
757
758        let mut stats = self
759            .stats
760            .lock()
761            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
762        stats.read_count += 1;
763
764        Ok(events
765            .iter()
766            .filter(|e| &e.severity == severity)
767            .cloned()
768            .collect())
769    }
770
771    fn retrieve_by_user(&self, user_id: &str) -> Result<Vec<AuditEvent>> {
772        let events = self
773            .events
774            .lock()
775            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
776
777        let mut stats = self
778            .stats
779            .lock()
780            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
781        stats.read_count += 1;
782
783        Ok(events
784            .iter()
785            .filter(|e| e.user_id.as_deref() == Some(user_id))
786            .cloned()
787            .collect())
788    }
789
790    fn count(&self) -> Result<usize> {
791        let events = self
792            .events
793            .lock()
794            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
795        Ok(events.len())
796    }
797
798    fn clear(&mut self) -> Result<()> {
799        // In production: DELETE FROM audit_events
800        let mut events = self
801            .events
802            .lock()
803            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
804
805        let mut stats = self
806            .stats
807            .lock()
808            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
809
810        events.clear();
811        *stats = StorageStatistics::default();
812
813        Ok(())
814    }
815
816    fn flush(&mut self) -> Result<()> {
817        // In production: This would commit pending transactions
818        Ok(())
819    }
820
821    fn get_statistics(&self) -> Result<StorageStatistics> {
822        let stats = self
823            .stats
824            .lock()
825            .map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
826        Ok(stats.clone())
827    }
828}
829
830/// Connection pool statistics
831#[derive(Debug, Clone)]
832pub struct PoolStatistics {
833    /// Active connections in use
834    pub active_connections: u32,
835    /// Idle connections in pool
836    pub idle_connections: u32,
837    /// Maximum allowed connections
838    pub max_connections: u32,
839    /// Number of times connections were waited for
840    pub wait_count: u64,
841    /// Total wait duration in milliseconds
842    pub wait_duration_ms: u64,
843}
844
845#[cfg(test)]
846mod tests {
847    use super::*;
848
849    #[test]
850    fn test_in_memory_storage() {
851        let mut storage = InMemoryStorage::new();
852
853        let event = AuditEvent::new(AuditEventType::PackageDownload, "Test event".to_string());
854
855        assert!(storage.store(&event).is_ok());
856        assert_eq!(storage.count().unwrap(), 1);
857
858        let events = storage.get_all_events().unwrap();
859        assert_eq!(events.len(), 1);
860
861        assert!(storage.clear().is_ok());
862        assert_eq!(storage.count().unwrap(), 0);
863    }
864
865    #[test]
866    fn test_sqlite_storage_config() {
867        let config = SqliteStorageConfig::new(std::env::temp_dir().join("test.db"))
868            .with_max_connections(20)
869            .with_timeout(60)
870            .with_wal_mode(true)
871            .with_auto_vacuum(false);
872
873        assert_eq!(config.max_connections, 20);
874        assert_eq!(config.connection_timeout, 60);
875        assert!(config.wal_mode);
876        assert!(!config.auto_vacuum);
877    }
878
879    #[test]
880    fn test_postgres_storage_config() {
881        let config = PostgresStorageConfig::new(
882            "localhost".to_string(),
883            5432,
884            "audit_db".to_string(),
885            "user".to_string(),
886            "pass".to_string(),
887        )
888        .with_pool_size(30)
889        .with_ssl_mode(SslMode::Require);
890
891        assert_eq!(config.pool_size, 30);
892        assert_eq!(config.ssl_mode, SslMode::Require);
893
894        let conn_str = config.connection_string();
895        assert!(conn_str.contains("localhost:5432"));
896        assert!(conn_str.contains("sslmode=require"));
897    }
898
899    #[test]
900    fn test_storage_statistics() {
901        let stats = StorageStatistics::default();
902        assert_eq!(stats.total_events, 0);
903        assert_eq!(stats.write_count, 0);
904        assert_eq!(stats.read_count, 0);
905    }
906}