use crate::audit::{AuditEvent, AuditEventType, AuditSeverity};
use chrono::{DateTime, Utc};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use torsh_core::error::{Result, TorshError};
pub trait AuditStorage: Send + Sync {
fn store(&mut self, event: &AuditEvent) -> Result<()>;
fn retrieve_by_time_range(
&self,
start: DateTime<Utc>,
end: DateTime<Utc>,
) -> Result<Vec<AuditEvent>>;
fn retrieve_by_type(&self, event_type: &AuditEventType) -> Result<Vec<AuditEvent>>;
fn retrieve_by_severity(&self, severity: &AuditSeverity) -> Result<Vec<AuditEvent>>;
fn retrieve_by_user(&self, user_id: &str) -> Result<Vec<AuditEvent>>;
fn count(&self) -> Result<usize>;
fn clear(&mut self) -> Result<()>;
fn flush(&mut self) -> Result<()>;
fn get_statistics(&self) -> Result<StorageStatistics>;
}
#[derive(Debug, Clone)]
pub struct StorageStatistics {
pub total_events: usize,
pub storage_size_bytes: u64,
pub last_write: Option<DateTime<Utc>>,
pub write_count: u64,
pub read_count: u64,
pub failed_operations: u64,
}
impl Default for StorageStatistics {
fn default() -> Self {
Self {
total_events: 0,
storage_size_bytes: 0,
last_write: None,
write_count: 0,
read_count: 0,
failed_operations: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct InMemoryStorage {
events: Arc<Mutex<Vec<AuditEvent>>>,
stats: Arc<Mutex<StorageStatistics>>,
}
impl InMemoryStorage {
pub fn new() -> Self {
Self {
events: Arc::new(Mutex::new(Vec::new())),
stats: Arc::new(Mutex::new(StorageStatistics::default())),
}
}
pub fn get_all_events(&self) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
Ok(events.clone())
}
}
impl Default for InMemoryStorage {
fn default() -> Self {
Self::new()
}
}
impl AuditStorage for InMemoryStorage {
fn store(&mut self, event: &AuditEvent) -> Result<()> {
let mut events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
events.push(event.clone());
stats.total_events += 1;
stats.write_count += 1;
stats.last_write = Some(Utc::now());
Ok(())
}
fn retrieve_by_time_range(
&self,
start: DateTime<Utc>,
end: DateTime<Utc>,
) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| e.timestamp >= start && e.timestamp <= end)
.cloned()
.collect())
}
fn retrieve_by_type(&self, event_type: &AuditEventType) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| &e.event_type == event_type)
.cloned()
.collect())
}
fn retrieve_by_severity(&self, severity: &AuditSeverity) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| &e.severity == severity)
.cloned()
.collect())
}
fn retrieve_by_user(&self, user_id: &str) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| e.user_id.as_deref() == Some(user_id))
.cloned()
.collect())
}
fn count(&self) -> Result<usize> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
Ok(events.len())
}
fn clear(&mut self) -> Result<()> {
let mut events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
events.clear();
*stats = StorageStatistics::default();
Ok(())
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
fn get_statistics(&self) -> Result<StorageStatistics> {
let stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
Ok(stats.clone())
}
}
#[derive(Debug, Clone)]
pub struct SqliteStorageConfig {
pub db_path: PathBuf,
pub max_connections: u32,
pub connection_timeout: u64,
pub wal_mode: bool,
pub auto_vacuum: bool,
}
impl SqliteStorageConfig {
pub fn new(db_path: PathBuf) -> Self {
Self {
db_path,
max_connections: 10,
connection_timeout: 30,
wal_mode: true,
auto_vacuum: true,
}
}
pub fn with_max_connections(mut self, max: u32) -> Self {
self.max_connections = max;
self
}
pub fn with_timeout(mut self, timeout: u64) -> Self {
self.connection_timeout = timeout;
self
}
pub fn with_wal_mode(mut self, enable: bool) -> Self {
self.wal_mode = enable;
self
}
pub fn with_auto_vacuum(mut self, enable: bool) -> Self {
self.auto_vacuum = enable;
self
}
}
#[derive(Debug)]
pub struct SqliteStorage {
config: SqliteStorageConfig,
events: Arc<Mutex<Vec<AuditEvent>>>, stats: Arc<Mutex<StorageStatistics>>,
}
impl SqliteStorage {
pub fn new(config: SqliteStorageConfig) -> Result<Self> {
let storage = Self {
config,
events: Arc::new(Mutex::new(Vec::new())),
stats: Arc::new(Mutex::new(StorageStatistics::default())),
};
storage.initialize_schema()?;
Ok(storage)
}
fn initialize_schema(&self) -> Result<()> {
Ok(())
}
pub fn get_db_statistics(&self) -> Result<DatabaseStatistics> {
Ok(DatabaseStatistics {
database_size_bytes: 0,
table_count: 1,
index_count: 4,
page_size: 4096,
page_count: 0,
wal_enabled: self.config.wal_mode,
})
}
}
impl AuditStorage for SqliteStorage {
fn store(&mut self, event: &AuditEvent) -> Result<()> {
let mut events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
events.push(event.clone());
stats.total_events += 1;
stats.write_count += 1;
stats.last_write = Some(Utc::now());
Ok(())
}
fn retrieve_by_time_range(
&self,
start: DateTime<Utc>,
end: DateTime<Utc>,
) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| e.timestamp >= start && e.timestamp <= end)
.cloned()
.collect())
}
fn retrieve_by_type(&self, event_type: &AuditEventType) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| &e.event_type == event_type)
.cloned()
.collect())
}
fn retrieve_by_severity(&self, severity: &AuditSeverity) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| &e.severity == severity)
.cloned()
.collect())
}
fn retrieve_by_user(&self, user_id: &str) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| e.user_id.as_deref() == Some(user_id))
.cloned()
.collect())
}
fn count(&self) -> Result<usize> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
Ok(events.len())
}
fn clear(&mut self) -> Result<()> {
let mut events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
events.clear();
*stats = StorageStatistics::default();
Ok(())
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
fn get_statistics(&self) -> Result<StorageStatistics> {
let stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
Ok(stats.clone())
}
}
#[derive(Debug, Clone)]
pub struct DatabaseStatistics {
pub database_size_bytes: u64,
pub table_count: usize,
pub index_count: usize,
pub page_size: u32,
pub page_count: u64,
pub wal_enabled: bool,
}
#[derive(Debug, Clone)]
pub struct PostgresStorageConfig {
pub host: String,
pub port: u16,
pub database: String,
pub username: String,
pub password: String,
pub pool_size: u32,
pub connection_timeout: u64,
pub ssl_mode: SslMode,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SslMode {
Disable,
Prefer,
Require,
}
impl PostgresStorageConfig {
pub fn new(
host: String,
port: u16,
database: String,
username: String,
password: String,
) -> Self {
Self {
host,
port,
database,
username,
password,
pool_size: 20,
connection_timeout: 30,
ssl_mode: SslMode::Prefer,
}
}
pub fn with_pool_size(mut self, size: u32) -> Self {
self.pool_size = size;
self
}
pub fn with_timeout(mut self, timeout: u64) -> Self {
self.connection_timeout = timeout;
self
}
pub fn with_ssl_mode(mut self, mode: SslMode) -> Self {
self.ssl_mode = mode;
self
}
pub fn connection_string(&self) -> String {
let ssl = match self.ssl_mode {
SslMode::Disable => "disable",
SslMode::Prefer => "prefer",
SslMode::Require => "require",
};
format!(
"postgresql://{}:{}@{}:{}/{}?sslmode={}",
self.username, self.password, self.host, self.port, self.database, ssl
)
}
}
#[derive(Debug)]
pub struct PostgresStorage {
config: PostgresStorageConfig,
events: Arc<Mutex<Vec<AuditEvent>>>, stats: Arc<Mutex<StorageStatistics>>,
}
impl PostgresStorage {
pub fn new(config: PostgresStorageConfig) -> Result<Self> {
let storage = Self {
config,
events: Arc::new(Mutex::new(Vec::new())),
stats: Arc::new(Mutex::new(StorageStatistics::default())),
};
storage.initialize_schema()?;
Ok(storage)
}
fn initialize_schema(&self) -> Result<()> {
Ok(())
}
pub fn get_pool_statistics(&self) -> Result<PoolStatistics> {
Ok(PoolStatistics {
active_connections: 0,
idle_connections: 0,
max_connections: self.config.pool_size,
wait_count: 0,
wait_duration_ms: 0,
})
}
}
impl AuditStorage for PostgresStorage {
fn store(&mut self, event: &AuditEvent) -> Result<()> {
let mut events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
events.push(event.clone());
stats.total_events += 1;
stats.write_count += 1;
stats.last_write = Some(Utc::now());
Ok(())
}
fn retrieve_by_time_range(
&self,
start: DateTime<Utc>,
end: DateTime<Utc>,
) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| e.timestamp >= start && e.timestamp <= end)
.cloned()
.collect())
}
fn retrieve_by_type(&self, event_type: &AuditEventType) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| &e.event_type == event_type)
.cloned()
.collect())
}
fn retrieve_by_severity(&self, severity: &AuditSeverity) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| &e.severity == severity)
.cloned()
.collect())
}
fn retrieve_by_user(&self, user_id: &str) -> Result<Vec<AuditEvent>> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
stats.read_count += 1;
Ok(events
.iter()
.filter(|e| e.user_id.as_deref() == Some(user_id))
.cloned()
.collect())
}
fn count(&self) -> Result<usize> {
let events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
Ok(events.len())
}
fn clear(&mut self) -> Result<()> {
let mut events = self
.events
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
let mut stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
events.clear();
*stats = StorageStatistics::default();
Ok(())
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
fn get_statistics(&self) -> Result<StorageStatistics> {
let stats = self
.stats
.lock()
.map_err(|e| TorshError::InvalidArgument(format!("Lock error: {}", e)))?;
Ok(stats.clone())
}
}
#[derive(Debug, Clone)]
pub struct PoolStatistics {
pub active_connections: u32,
pub idle_connections: u32,
pub max_connections: u32,
pub wait_count: u64,
pub wait_duration_ms: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_in_memory_storage() {
let mut storage = InMemoryStorage::new();
let event = AuditEvent::new(AuditEventType::PackageDownload, "Test event".to_string());
assert!(storage.store(&event).is_ok());
assert_eq!(storage.count().unwrap(), 1);
let events = storage.get_all_events().unwrap();
assert_eq!(events.len(), 1);
assert!(storage.clear().is_ok());
assert_eq!(storage.count().unwrap(), 0);
}
#[test]
fn test_sqlite_storage_config() {
let config = SqliteStorageConfig::new(std::env::temp_dir().join("test.db"))
.with_max_connections(20)
.with_timeout(60)
.with_wal_mode(true)
.with_auto_vacuum(false);
assert_eq!(config.max_connections, 20);
assert_eq!(config.connection_timeout, 60);
assert!(config.wal_mode);
assert!(!config.auto_vacuum);
}
#[test]
fn test_postgres_storage_config() {
let config = PostgresStorageConfig::new(
"localhost".to_string(),
5432,
"audit_db".to_string(),
"user".to_string(),
"pass".to_string(),
)
.with_pool_size(30)
.with_ssl_mode(SslMode::Require);
assert_eq!(config.pool_size, 30);
assert_eq!(config.ssl_mode, SslMode::Require);
let conn_str = config.connection_string();
assert!(conn_str.contains("localhost:5432"));
assert!(conn_str.contains("sslmode=require"));
}
#[test]
fn test_storage_statistics() {
let stats = StorageStatistics::default();
assert_eq!(stats.total_events, 0);
assert_eq!(stats.write_count, 0);
assert_eq!(stats.read_count, 0);
}
}