use async_trait::async_trait;
use std::collections::VecDeque;
use std::sync::Arc;
use tokio::sync::RwLock;
use uvb_storage_api::{AuditError, AuditEvent, AuditLogStore, AuditQueryFilters};
const MAX_EVENTS: usize = 10_000;
pub struct InMemoryAuditLogStore {
events: Arc<RwLock<VecDeque<AuditEvent>>>,
}
impl InMemoryAuditLogStore {
pub fn new() -> Self {
Self {
events: Arc::new(RwLock::new(VecDeque::new())),
}
}
}
impl Default for InMemoryAuditLogStore {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl AuditLogStore for InMemoryAuditLogStore {
async fn log(&self, event: AuditEvent) -> Result<(), AuditError> {
let mut events = self.events.write().await;
if events.len() >= MAX_EVENTS {
events.pop_front();
}
events.push_back(event);
Ok(())
}
async fn query(&self, filters: AuditQueryFilters) -> Result<Vec<AuditEvent>, AuditError> {
let events = self.events.read().await;
let mut results: Vec<AuditEvent> = events
.iter()
.filter(|e| {
if let Some(ref user_id) = filters.user_id {
if e.user_id.as_ref() != Some(user_id) {
return false;
}
}
if let Some(ref tenant_id) = filters.tenant_id {
if &e.tenant_id != tenant_id {
return false;
}
}
if let Some(ref types) = filters.event_types {
if !types.contains(&e.event_type) {
return false;
}
}
if let Some(start) = filters.start_time {
if e.timestamp < start {
return false;
}
}
if let Some(end) = filters.end_time {
if e.timestamp > end {
return false;
}
}
if let Some(success) = filters.success {
if e.success != success {
return false;
}
}
true
})
.cloned()
.collect();
results.sort_by_key(|a| std::cmp::Reverse(a.timestamp));
if let Some(offset) = filters.offset {
if offset < results.len() {
results = results[offset..].to_vec();
} else {
results.clear();
}
}
if let Some(limit) = filters.limit {
results.truncate(limit);
}
Ok(results)
}
async fn count(&self, filters: AuditQueryFilters) -> Result<u64, AuditError> {
let events = self.query(filters).await?;
Ok(events.len() as u64)
}
}