use crate::settings::secrets::audit::{SecretAccessEvent, SecretAccessFilter, SecretAuditBackend};
use parking_lot::RwLock;
use std::sync::Arc;
pub struct MemorySecretAuditBackend {
events: Arc<RwLock<Vec<SecretAccessEvent>>>,
}
impl MemorySecretAuditBackend {
pub fn new() -> Self {
Self {
events: Arc::new(RwLock::new(Vec::new())),
}
}
pub fn clear(&self) {
self.events.write().clear();
}
pub fn len(&self) -> usize {
self.events.read().len()
}
pub fn is_empty(&self) -> bool {
self.events.read().is_empty()
}
}
impl Default for MemorySecretAuditBackend {
fn default() -> Self {
Self::new()
}
}
#[async_trait::async_trait]
impl SecretAuditBackend for MemorySecretAuditBackend {
async fn log_access(&self, event: SecretAccessEvent) -> Result<(), String> {
self.events.write().push(event);
Ok(())
}
async fn get_accesses(
&self,
filter: Option<SecretAccessFilter>,
) -> Result<Vec<SecretAccessEvent>, String> {
let events = self.events.read();
if let Some(filter) = filter {
let filtered: Vec<SecretAccessEvent> = events
.iter()
.filter(|event| {
if let Some(ref secret_name) = filter.secret_name
&& &event.secret_name != secret_name
{
return false;
}
if let Some(ref accessor) = filter.accessor
&& &event.accessor != accessor
{
return false;
}
if let Some(success_only) = filter.success_only
&& event.success != success_only
{
return false;
}
if let Some(start_time) = filter.start_time
&& event.timestamp < start_time
{
return false;
}
if let Some(end_time) = filter.end_time
&& event.timestamp > end_time
{
return false;
}
true
})
.cloned()
.collect();
Ok(filtered)
} else {
Ok(events.clone())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_memory_backend_new() {
let backend = MemorySecretAuditBackend::new();
assert!(backend.is_empty());
assert_eq!(backend.len(), 0);
}
#[tokio::test]
async fn test_memory_backend_log_access() {
let backend = MemorySecretAuditBackend::new();
let event =
SecretAccessEvent::new("secret".to_string(), "accessor".to_string(), true, None);
backend.log_access(event).await.unwrap();
assert_eq!(backend.len(), 1);
assert!(!backend.is_empty());
}
#[tokio::test]
async fn test_memory_backend_get_accesses() {
let backend = MemorySecretAuditBackend::new();
for i in 0..5 {
let event =
SecretAccessEvent::new(format!("secret_{}", i), "accessor".to_string(), true, None);
backend.log_access(event).await.unwrap();
}
let events = backend.get_accesses(None).await.unwrap();
assert_eq!(events.len(), 5);
}
#[tokio::test]
async fn test_memory_backend_filter_by_secret_name() {
let backend = MemorySecretAuditBackend::new();
let event1 =
SecretAccessEvent::new("secret1".to_string(), "accessor".to_string(), true, None);
let event2 =
SecretAccessEvent::new("secret2".to_string(), "accessor".to_string(), true, None);
let event3 =
SecretAccessEvent::new("secret1".to_string(), "accessor".to_string(), true, None);
backend.log_access(event1).await.unwrap();
backend.log_access(event2).await.unwrap();
backend.log_access(event3).await.unwrap();
let filter = SecretAccessFilter {
secret_name: Some("secret1".to_string()),
..Default::default()
};
let events = backend.get_accesses(Some(filter)).await.unwrap();
assert_eq!(events.len(), 2);
}
#[tokio::test]
async fn test_memory_backend_filter_by_success() {
let backend = MemorySecretAuditBackend::new();
let event1 =
SecretAccessEvent::new("secret".to_string(), "accessor".to_string(), true, None);
let event2 =
SecretAccessEvent::new("secret".to_string(), "accessor".to_string(), false, None);
let event3 =
SecretAccessEvent::new("secret".to_string(), "accessor".to_string(), true, None);
backend.log_access(event1).await.unwrap();
backend.log_access(event2).await.unwrap();
backend.log_access(event3).await.unwrap();
let filter = SecretAccessFilter {
success_only: Some(true),
..Default::default()
};
let events = backend.get_accesses(Some(filter)).await.unwrap();
assert_eq!(events.len(), 2);
}
#[tokio::test]
async fn test_memory_backend_clear() {
let backend = MemorySecretAuditBackend::new();
for _ in 0..3 {
let event =
SecretAccessEvent::new("secret".to_string(), "accessor".to_string(), true, None);
backend.log_access(event).await.unwrap();
}
assert_eq!(backend.len(), 3);
backend.clear();
assert_eq!(backend.len(), 0);
assert!(backend.is_empty());
}
}