Skip to main content

oxigdal_security/audit/
query.rs

1//! Audit log queries.
2
3use crate::audit::{AuditEventType, AuditLogEntry, AuditResult, storage::StorageQuery};
4
5/// Audit query builder.
6pub struct AuditQuery {
7    query: StorageQuery,
8}
9
10impl AuditQuery {
11    /// Create new audit query.
12    pub fn new() -> Self {
13        Self {
14            query: StorageQuery::new(),
15        }
16    }
17
18    /// Filter by subject.
19    pub fn subject(mut self, subject: String) -> Self {
20        self.query = self.query.with_subject(subject);
21        self
22    }
23
24    /// Filter by resource.
25    pub fn resource(mut self, resource: String) -> Self {
26        self.query = self.query.with_resource(resource);
27        self
28    }
29
30    /// Filter by event type.
31    pub fn event_type(mut self, event_type: AuditEventType) -> Self {
32        self.query = self.query.with_event_type(event_type);
33        self
34    }
35
36    /// Filter by result.
37    pub fn result(mut self, result: AuditResult) -> Self {
38        self.query = self.query.with_result(result);
39        self
40    }
41
42    /// Set limit.
43    pub fn limit(mut self, limit: usize) -> Self {
44        self.query = self.query.with_limit(limit);
45        self
46    }
47
48    /// Build the storage query.
49    pub fn build(self) -> StorageQuery {
50        self.query
51    }
52}
53
54impl Default for AuditQuery {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60/// Audit report generator.
61pub struct AuditReport {
62    entries: Vec<AuditLogEntry>,
63}
64
65impl AuditReport {
66    /// Create new audit report.
67    pub fn new(entries: Vec<AuditLogEntry>) -> Self {
68        Self { entries }
69    }
70
71    /// Get total count.
72    pub fn total_count(&self) -> usize {
73        self.entries.len()
74    }
75
76    /// Get success count.
77    pub fn success_count(&self) -> usize {
78        self.entries
79            .iter()
80            .filter(|e| e.result == AuditResult::Success)
81            .count()
82    }
83
84    /// Get failure count.
85    pub fn failure_count(&self) -> usize {
86        self.entries
87            .iter()
88            .filter(|e| e.result == AuditResult::Failure)
89            .count()
90    }
91
92    /// Get denied count.
93    pub fn denied_count(&self) -> usize {
94        self.entries
95            .iter()
96            .filter(|e| e.result == AuditResult::Denied)
97            .count()
98    }
99
100    /// Group by event type.
101    pub fn by_event_type(&self) -> std::collections::HashMap<AuditEventType, usize> {
102        let mut map = std::collections::HashMap::new();
103        for entry in &self.entries {
104            *map.entry(entry.event_type).or_insert(0) += 1;
105        }
106        map
107    }
108
109    /// Group by subject.
110    pub fn by_subject(&self) -> std::collections::HashMap<String, usize> {
111        let mut map = std::collections::HashMap::new();
112        for entry in &self.entries {
113            if let Some(ref subject) = entry.subject {
114                *map.entry(subject.clone()).or_insert(0) += 1;
115            }
116        }
117        map
118    }
119
120    /// Generate summary text.
121    pub fn summary(&self) -> String {
122        format!(
123            "Audit Report:\n\
124             Total Events: {}\n\
125             Success: {}\n\
126             Failure: {}\n\
127             Denied: {}",
128            self.total_count(),
129            self.success_count(),
130            self.failure_count(),
131            self.denied_count()
132        )
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_audit_query_builder() {
142        let query = AuditQuery::new()
143            .subject("user-123".to_string())
144            .event_type(AuditEventType::Authentication)
145            .result(AuditResult::Success)
146            .limit(10)
147            .build();
148
149        assert_eq!(query.subject, Some("user-123".to_string()));
150        assert_eq!(query.event_type, Some(AuditEventType::Authentication));
151        assert_eq!(query.limit, Some(10));
152    }
153
154    #[test]
155    fn test_audit_report() {
156        let entry1 = AuditLogEntry::new(AuditEventType::Authentication, AuditResult::Success);
157        let entry2 = AuditLogEntry::new(AuditEventType::Authentication, AuditResult::Failure);
158        let entry3 = AuditLogEntry::new(AuditEventType::DataAccess, AuditResult::Denied);
159
160        let report = AuditReport::new(vec![entry1, entry2, entry3]);
161
162        assert_eq!(report.total_count(), 3);
163        assert_eq!(report.success_count(), 1);
164        assert_eq!(report.failure_count(), 1);
165        assert_eq!(report.denied_count(), 1);
166
167        let by_type = report.by_event_type();
168        assert_eq!(by_type.get(&AuditEventType::Authentication), Some(&2));
169        assert_eq!(by_type.get(&AuditEventType::DataAccess), Some(&1));
170    }
171}