oxigdal_security/audit/
query.rs1use crate::audit::{AuditEventType, AuditLogEntry, AuditResult, storage::StorageQuery};
4
5pub struct AuditQuery {
7 query: StorageQuery,
8}
9
10impl AuditQuery {
11 pub fn new() -> Self {
13 Self {
14 query: StorageQuery::new(),
15 }
16 }
17
18 pub fn subject(mut self, subject: String) -> Self {
20 self.query = self.query.with_subject(subject);
21 self
22 }
23
24 pub fn resource(mut self, resource: String) -> Self {
26 self.query = self.query.with_resource(resource);
27 self
28 }
29
30 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 pub fn result(mut self, result: AuditResult) -> Self {
38 self.query = self.query.with_result(result);
39 self
40 }
41
42 pub fn limit(mut self, limit: usize) -> Self {
44 self.query = self.query.with_limit(limit);
45 self
46 }
47
48 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
60pub struct AuditReport {
62 entries: Vec<AuditLogEntry>,
63}
64
65impl AuditReport {
66 pub fn new(entries: Vec<AuditLogEntry>) -> Self {
68 Self { entries }
69 }
70
71 pub fn total_count(&self) -> usize {
73 self.entries.len()
74 }
75
76 pub fn success_count(&self) -> usize {
78 self.entries
79 .iter()
80 .filter(|e| e.result == AuditResult::Success)
81 .count()
82 }
83
84 pub fn failure_count(&self) -> usize {
86 self.entries
87 .iter()
88 .filter(|e| e.result == AuditResult::Failure)
89 .count()
90 }
91
92 pub fn denied_count(&self) -> usize {
94 self.entries
95 .iter()
96 .filter(|e| e.result == AuditResult::Denied)
97 .count()
98 }
99
100 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 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 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}