use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditConfig {
pub enabled: bool,
pub log_ddl: bool,
pub log_dml: bool,
pub log_select: bool,
pub log_transactions: bool,
pub log_auth: bool,
pub retention_days: u32,
pub async_buffer_size: usize,
pub enable_checksums: bool,
pub max_query_length: usize,
pub capture_metadata: MetadataCapture,
}
impl Default for AuditConfig {
fn default() -> Self {
Self {
enabled: true,
log_ddl: true,
log_dml: true,
log_select: false, log_transactions: false,
log_auth: true,
retention_days: 90,
async_buffer_size: 100,
enable_checksums: true,
max_query_length: 10000, capture_metadata: MetadataCapture::default(),
}
}
}
impl AuditConfig {
pub fn minimal() -> Self {
Self {
enabled: true,
log_ddl: true,
log_dml: false,
log_select: false,
log_transactions: false,
log_auth: false,
retention_days: 30,
async_buffer_size: 50,
enable_checksums: false,
max_query_length: 5000,
capture_metadata: MetadataCapture::minimal(),
}
}
pub fn verbose() -> Self {
Self {
enabled: true,
log_ddl: true,
log_dml: true,
log_select: true,
log_transactions: true,
log_auth: true,
retention_days: 365,
async_buffer_size: 500,
enable_checksums: true,
max_query_length: 50000,
capture_metadata: MetadataCapture::verbose(),
}
}
pub fn compliance() -> Self {
Self {
enabled: true,
log_ddl: true,
log_dml: true,
log_select: false, log_transactions: true,
log_auth: true,
retention_days: 2555, async_buffer_size: 100,
enable_checksums: true, max_query_length: 10000,
capture_metadata: MetadataCapture::compliance(),
}
}
pub fn should_log(&self, operation: &super::OperationType) -> bool {
if !self.enabled {
return false;
}
use super::OperationType;
match operation {
op if op.is_ddl() => self.log_ddl,
OperationType::Select => self.log_select,
op if op.is_dml() => self.log_dml,
op if op.is_transaction() => self.log_transactions,
op if op.is_auth() => self.log_auth,
_ => true, }
}
pub fn truncate_query(&self, query: &str) -> String {
if self.max_query_length == 0 || query.len() <= self.max_query_length {
query.to_string()
} else {
format!(
"{}... [truncated {} chars]",
&query[..self.max_query_length],
query.len() - self.max_query_length
)
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetadataCapture {
pub capture_client_ip: bool,
pub capture_application_name: bool,
pub capture_database_name: bool,
pub capture_execution_time: bool,
pub capture_custom_fields: bool,
}
impl Default for MetadataCapture {
fn default() -> Self {
Self {
capture_client_ip: true,
capture_application_name: true,
capture_database_name: true,
capture_execution_time: true,
capture_custom_fields: false,
}
}
}
impl MetadataCapture {
pub fn minimal() -> Self {
Self {
capture_client_ip: false,
capture_application_name: false,
capture_database_name: false,
capture_execution_time: false,
capture_custom_fields: false,
}
}
pub fn verbose() -> Self {
Self {
capture_client_ip: true,
capture_application_name: true,
capture_database_name: true,
capture_execution_time: true,
capture_custom_fields: true,
}
}
pub fn compliance() -> Self {
Self {
capture_client_ip: true,
capture_application_name: true,
capture_database_name: true,
capture_execution_time: true,
capture_custom_fields: false,
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = AuditConfig::default();
assert!(config.enabled);
assert!(config.log_ddl);
assert!(config.log_dml);
assert!(!config.log_select);
}
#[test]
fn test_minimal_config() {
let config = AuditConfig::minimal();
assert!(config.log_ddl);
assert!(!config.log_dml);
assert!(!config.log_select);
}
#[test]
fn test_verbose_config() {
let config = AuditConfig::verbose();
assert!(config.log_ddl);
assert!(config.log_dml);
assert!(config.log_select);
}
#[test]
fn test_should_log() {
let config = AuditConfig::default();
use super::super::OperationType;
assert!(config.should_log(&OperationType::CreateTable));
assert!(config.should_log(&OperationType::Insert));
assert!(!config.should_log(&OperationType::Select));
}
#[test]
fn test_truncate_query() {
let config = AuditConfig {
max_query_length: 10,
..Default::default()
};
let short = "SELECT *";
assert_eq!(config.truncate_query(short), short);
let long = "SELECT * FROM very_long_table_name";
let truncated = config.truncate_query(long);
assert!(truncated.contains("truncated"));
assert!(truncated.len() < long.len() + 50);
}
}