Skip to main content

uvb_storage_memory/
audit.rs

1use async_trait::async_trait;
2use std::collections::VecDeque;
3use std::sync::Arc;
4use tokio::sync::RwLock;
5use uvb_storage_api::{AuditError, AuditEvent, AuditLogStore, AuditQueryFilters};
6
7const MAX_EVENTS: usize = 10_000; // Prevent unbounded growth
8
9pub struct InMemoryAuditLogStore {
10    events: Arc<RwLock<VecDeque<AuditEvent>>>,
11}
12
13impl InMemoryAuditLogStore {
14    pub fn new() -> Self {
15        Self {
16            events: Arc::new(RwLock::new(VecDeque::new())),
17        }
18    }
19}
20
21impl Default for InMemoryAuditLogStore {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27#[async_trait]
28impl AuditLogStore for InMemoryAuditLogStore {
29    async fn log(&self, event: AuditEvent) -> Result<(), AuditError> {
30        let mut events = self.events.write().await;
31
32        // Trim old events if we exceed max
33        if events.len() >= MAX_EVENTS {
34            events.pop_front();
35        }
36
37        events.push_back(event);
38        Ok(())
39    }
40
41    async fn query(&self, filters: AuditQueryFilters) -> Result<Vec<AuditEvent>, AuditError> {
42        let events = self.events.read().await;
43        let mut results: Vec<AuditEvent> = events
44            .iter()
45            .filter(|e| {
46                if let Some(ref user_id) = filters.user_id {
47                    if e.user_id.as_ref() != Some(user_id) {
48                        return false;
49                    }
50                }
51                if let Some(ref tenant_id) = filters.tenant_id {
52                    if &e.tenant_id != tenant_id {
53                        return false;
54                    }
55                }
56                if let Some(ref types) = filters.event_types {
57                    if !types.contains(&e.event_type) {
58                        return false;
59                    }
60                }
61                if let Some(start) = filters.start_time {
62                    if e.timestamp < start {
63                        return false;
64                    }
65                }
66                if let Some(end) = filters.end_time {
67                    if e.timestamp > end {
68                        return false;
69                    }
70                }
71                if let Some(success) = filters.success {
72                    if e.success != success {
73                        return false;
74                    }
75                }
76                true
77            })
78            .cloned()
79            .collect();
80
81        // Sort by timestamp, newest first
82        results.sort_by_key(|a| std::cmp::Reverse(a.timestamp));
83
84        // Apply pagination
85        if let Some(offset) = filters.offset {
86            if offset < results.len() {
87                results = results[offset..].to_vec();
88            } else {
89                results.clear();
90            }
91        }
92
93        if let Some(limit) = filters.limit {
94            results.truncate(limit);
95        }
96
97        Ok(results)
98    }
99
100    async fn count(&self, filters: AuditQueryFilters) -> Result<u64, AuditError> {
101        let events = self.query(filters).await?;
102        Ok(events.len() as u64)
103    }
104}