Skip to main content

kindly_guard_server/storage/
memory.rs

1// Copyright 2025 Kindly Software Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//! In-memory storage implementation
15//!
16//! This provides non-persistent storage for development and testing.
17//! All data is lost when the process restarts.
18
19use super::{
20    async_trait, Arc, CorrelationState, DateTime, Duration, EventFilter, EventId, RateLimitKey,
21    RateLimitState, Result, SecurityEvent, SnapshotId, StorageProvider, StorageStats, Utc,
22};
23use std::collections::{HashMap, VecDeque};
24use tokio::sync::RwLock;
25use tracing::{debug, info};
26use uuid::Uuid;
27
28/// In-memory storage provider
29pub struct InMemoryStorage {
30    /// Event storage
31    events: Arc<RwLock<HashMap<EventId, SecurityEvent>>>,
32    /// Event index by client
33    events_by_client: Arc<RwLock<HashMap<String, Vec<EventId>>>>,
34    /// Rate limit states
35    rate_limits: Arc<RwLock<HashMap<String, RateLimitState>>>,
36    /// Correlation states
37    correlations: Arc<RwLock<HashMap<String, CorrelationState>>>,
38    /// Snapshots
39    snapshots: Arc<RwLock<HashMap<SnapshotId, Snapshot>>>,
40    /// Event order tracking
41    event_order: Arc<RwLock<VecDeque<EventId>>>,
42    /// Maximum events to store
43    max_events: usize,
44}
45
46#[derive(Clone)]
47struct Snapshot {
48    id: SnapshotId,
49    created_at: DateTime<Utc>,
50    events: HashMap<EventId, SecurityEvent>,
51    rate_limits: HashMap<String, RateLimitState>,
52    correlations: HashMap<String, CorrelationState>,
53}
54
55impl Default for InMemoryStorage {
56    fn default() -> Self {
57        Self::new()
58    }
59}
60
61impl InMemoryStorage {
62    /// Create new in-memory storage
63    pub fn new() -> Self {
64        Self::with_capacity(100_000) // 100k events default
65    }
66
67    /// Create with specified capacity
68    pub fn with_capacity(max_events: usize) -> Self {
69        info!(
70            "Initializing in-memory storage with capacity: {}",
71            max_events
72        );
73        Self {
74            events: Arc::new(RwLock::new(HashMap::new())),
75            events_by_client: Arc::new(RwLock::new(HashMap::new())),
76            rate_limits: Arc::new(RwLock::new(HashMap::new())),
77            correlations: Arc::new(RwLock::new(HashMap::new())),
78            snapshots: Arc::new(RwLock::new(HashMap::new())),
79            event_order: Arc::new(RwLock::new(VecDeque::new())),
80            max_events,
81        }
82    }
83
84    /// Evict oldest events when at capacity
85    async fn evict_if_needed(&self) {
86        let mut order = self.event_order.write().await;
87        let mut events = self.events.write().await;
88
89        while order.len() >= self.max_events {
90            if let Some(old_id) = order.pop_front() {
91                if let Some(old_event) = events.remove(&old_id) {
92                    // Also remove from client index
93                    let mut by_client = self.events_by_client.write().await;
94                    if let Some(client_events) = by_client.get_mut(&old_event.client_id) {
95                        client_events.retain(|id| id != &old_id);
96                    }
97                }
98            }
99        }
100    }
101}
102
103#[async_trait]
104impl StorageProvider for InMemoryStorage {
105    async fn store_event(&self, event: &SecurityEvent) -> Result<EventId> {
106        // Evict old events if needed
107        self.evict_if_needed().await;
108
109        let id = EventId(Uuid::new_v4().to_string());
110
111        // Store event
112        {
113            let mut events = self.events.write().await;
114            events.insert(id.clone(), event.clone());
115        }
116
117        // Update client index
118        {
119            let mut by_client = self.events_by_client.write().await;
120            by_client
121                .entry(event.client_id.clone())
122                .or_insert_with(Vec::new)
123                .push(id.clone());
124        }
125
126        // Track order
127        {
128            let mut order = self.event_order.write().await;
129            order.push_back(id.clone());
130        }
131
132        debug!("Stored event {} for client {}", id.0, event.client_id);
133        Ok(id)
134    }
135
136    async fn get_event(&self, id: &EventId) -> Result<Option<SecurityEvent>> {
137        let events = self.events.read().await;
138        Ok(events.get(id).cloned())
139    }
140
141    async fn query_events(&self, filter: EventFilter) -> Result<Vec<SecurityEvent>> {
142        let events = self.events.read().await;
143        let by_client = self.events_by_client.read().await;
144
145        let mut results = Vec::new();
146
147        // If filtering by client, use index
148        if let Some(client_id) = &filter.client_id {
149            if let Some(event_ids) = by_client.get(client_id) {
150                for event_id in event_ids {
151                    if let Some(event) = events.get(event_id) {
152                        if Self::matches_filter(event, &filter) {
153                            results.push(event.clone());
154                        }
155                    }
156                }
157            }
158        } else {
159            // Full scan
160            for event in events.values() {
161                if Self::matches_filter(event, &filter) {
162                    results.push(event.clone());
163                }
164            }
165        }
166
167        // Sort by timestamp descending
168        results.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
169
170        // Apply limit
171        if let Some(limit) = filter.limit {
172            results.truncate(limit);
173        }
174
175        Ok(results)
176    }
177
178    async fn store_rate_limit_state(
179        &self,
180        key: &RateLimitKey,
181        state: &RateLimitState,
182    ) -> Result<()> {
183        let mut rate_limits = self.rate_limits.write().await;
184        let key_str = format!("{}:{}", key.client_id, key.method.as_deref().unwrap_or("*"));
185        rate_limits.insert(key_str, state.clone());
186        Ok(())
187    }
188
189    async fn get_rate_limit_state(&self, key: &RateLimitKey) -> Result<Option<RateLimitState>> {
190        let rate_limits = self.rate_limits.read().await;
191        let key_str = format!("{}:{}", key.client_id, key.method.as_deref().unwrap_or("*"));
192        Ok(rate_limits.get(&key_str).cloned())
193    }
194
195    async fn cleanup_rate_limit_states(&self, older_than: Duration) -> Result<u64> {
196        let mut rate_limits = self.rate_limits.write().await;
197        let cutoff = Utc::now() - chrono::Duration::from_std(older_than)?;
198        let initial_count = rate_limits.len();
199
200        rate_limits.retain(|_, state| state.last_refill > cutoff);
201
202        let removed = initial_count - rate_limits.len();
203        debug!("Cleaned up {} old rate limit states", removed);
204        Ok(removed as u64)
205    }
206
207    async fn store_correlation_state(
208        &self,
209        client_id: &str,
210        state: &CorrelationState,
211    ) -> Result<()> {
212        let mut correlations = self.correlations.write().await;
213        correlations.insert(client_id.to_string(), state.clone());
214        Ok(())
215    }
216
217    async fn get_correlation_state(&self, client_id: &str) -> Result<Option<CorrelationState>> {
218        let correlations = self.correlations.read().await;
219        Ok(correlations.get(client_id).cloned())
220    }
221
222    async fn create_snapshot(&self) -> Result<SnapshotId> {
223        let id = SnapshotId(Uuid::new_v4().to_string());
224
225        let snapshot = Snapshot {
226            id: id.clone(),
227            created_at: Utc::now(),
228            events: self.events.read().await.clone(),
229            rate_limits: self.rate_limits.read().await.clone(),
230            correlations: self.correlations.read().await.clone(),
231        };
232
233        let mut snapshots = self.snapshots.write().await;
234        snapshots.insert(id.clone(), snapshot);
235
236        info!("Created snapshot {}", id.0);
237        Ok(id)
238    }
239
240    async fn list_snapshots(&self) -> Result<Vec<(SnapshotId, DateTime<Utc>)>> {
241        let snapshots = self.snapshots.read().await;
242        let mut list: Vec<_> = snapshots
243            .values()
244            .map(|s| (s.id.clone(), s.created_at))
245            .collect();
246        list.sort_by(|a, b| b.1.cmp(&a.1)); // Newest first
247        Ok(list)
248    }
249
250    async fn restore_snapshot(&self, id: &SnapshotId) -> Result<()> {
251        let snapshots = self.snapshots.read().await;
252        let snapshot = snapshots
253            .get(id)
254            .ok_or_else(|| anyhow::anyhow!("Snapshot not found"))?
255            .clone();
256        drop(snapshots);
257
258        // Restore all data
259        *self.events.write().await = snapshot.events;
260        *self.rate_limits.write().await = snapshot.rate_limits;
261        *self.correlations.write().await = snapshot.correlations;
262
263        // Rebuild event order and client index
264        let mut order = self.event_order.write().await;
265        let mut by_client = self.events_by_client.write().await;
266
267        order.clear();
268        by_client.clear();
269
270        let events = self.events.read().await;
271        let mut event_list: Vec<_> = events.iter().collect();
272        event_list.sort_by_key(|(_, event)| event.timestamp);
273
274        for (id, event) in event_list {
275            order.push_back(id.clone());
276            by_client
277                .entry(event.client_id.clone())
278                .or_insert_with(Vec::new)
279                .push(id.clone());
280        }
281
282        info!("Restored from snapshot {}", id.0);
283        Ok(())
284    }
285
286    async fn delete_snapshot(&self, id: &SnapshotId) -> Result<()> {
287        let mut snapshots = self.snapshots.write().await;
288        snapshots
289            .remove(id)
290            .ok_or_else(|| anyhow::anyhow!("Snapshot not found"))?;
291        Ok(())
292    }
293
294    async fn get_stats(&self) -> Result<StorageStats> {
295        let events = self.events.read().await;
296        let rate_limits = self.rate_limits.read().await;
297        let correlations = self.correlations.read().await;
298
299        // Estimate memory usage
300        let event_size = events.len() * std::mem::size_of::<(EventId, SecurityEvent)>();
301        let rate_limit_size = rate_limits.len() * std::mem::size_of::<(String, RateLimitState)>();
302        let correlation_size =
303            correlations.len() * std::mem::size_of::<(String, CorrelationState)>();
304
305        Ok(StorageStats {
306            event_count: events.len() as u64,
307            total_size: (event_size + rate_limit_size + correlation_size) as u64,
308            rate_limit_entries: rate_limits.len() as u64,
309            correlation_states: correlations.len() as u64,
310            storage_type: "memory".to_string(),
311            metadata: serde_json::json!({
312                "max_events": self.max_events,
313                "snapshots": self.snapshots.read().await.len(),
314            }),
315        })
316    }
317
318    async fn compact(&self) -> Result<()> {
319        // For in-memory storage, compaction just shrinks hashmaps
320        self.events.write().await.shrink_to_fit();
321        self.events_by_client.write().await.shrink_to_fit();
322        self.rate_limits.write().await.shrink_to_fit();
323        self.correlations.write().await.shrink_to_fit();
324        debug!("Compacted in-memory storage");
325        Ok(())
326    }
327}
328
329impl InMemoryStorage {
330    /// Check if event matches filter
331    fn matches_filter(event: &SecurityEvent, filter: &EventFilter) -> bool {
332        // Event type filter
333        if let Some(event_type) = &filter.event_type {
334            if &event.event_type != event_type {
335                return false;
336            }
337        }
338
339        // Time range filter
340        let event_time =
341            DateTime::<Utc>::from_timestamp(event.timestamp as i64, 0).unwrap_or_else(Utc::now);
342
343        if let Some(from) = filter.from_time {
344            if event_time < from {
345                return false;
346            }
347        }
348
349        if let Some(to) = filter.to_time {
350            if event_time > to {
351                return false;
352            }
353        }
354
355        // Severity filter (if present in metadata)
356        if let Some(min_severity) = &filter.min_severity {
357            if let Some(severity) = event.metadata.get("severity").and_then(|v| v.as_str()) {
358                // Simple severity comparison (would be more sophisticated in production)
359                let severity_rank = match severity {
360                    "low" => 1,
361                    "medium" => 2,
362                    "high" => 3,
363                    "critical" => 4,
364                    _ => 0,
365                };
366                let min_rank = match min_severity.as_str() {
367                    "low" => 1,
368                    "medium" => 2,
369                    "high" => 3,
370                    "critical" => 4,
371                    _ => 0,
372                };
373                if severity_rank < min_rank {
374                    return false;
375                }
376            }
377        }
378
379        true
380    }
381}