ricecoder_permissions/audit/
logger.rs1use super::models::{AuditAction, AuditLogEntry, AuditResult};
4use std::sync::{Arc, RwLock};
5
6#[derive(Clone)]
8pub struct AuditLogger {
9 entries: Arc<RwLock<Vec<AuditLogEntry>>>,
10}
11
12impl AuditLogger {
13 pub fn new() -> Self {
15 Self {
16 entries: Arc::new(RwLock::new(Vec::new())),
17 }
18 }
19
20 pub fn log_execution(
22 &self,
23 tool: String,
24 agent: Option<String>,
25 context: Option<String>,
26 ) -> Result<(), String> {
27 let mut entry = AuditLogEntry::new(tool, AuditAction::Allowed, AuditResult::Success);
28
29 if let Some(agent_name) = agent {
30 entry.agent = Some(agent_name);
31 }
32
33 if let Some(ctx) = context {
34 entry.context = Some(ctx);
35 }
36
37 let mut entries = self
38 .entries
39 .write()
40 .map_err(|e| format!("Failed to acquire write lock: {}", e))?;
41 entries.push(entry);
42
43 Ok(())
44 }
45
46 pub fn log_denial(
48 &self,
49 tool: String,
50 agent: Option<String>,
51 context: Option<String>,
52 ) -> Result<(), String> {
53 let mut entry = AuditLogEntry::new(tool, AuditAction::Denied, AuditResult::Blocked);
54
55 if let Some(agent_name) = agent {
56 entry.agent = Some(agent_name);
57 }
58
59 if let Some(ctx) = context {
60 entry.context = Some(ctx);
61 }
62
63 let mut entries = self
64 .entries
65 .write()
66 .map_err(|e| format!("Failed to acquire write lock: {}", e))?;
67 entries.push(entry);
68
69 Ok(())
70 }
71
72 pub fn log_prompt(
74 &self,
75 tool: String,
76 agent: Option<String>,
77 context: Option<String>,
78 ) -> Result<(), String> {
79 let mut entry = AuditLogEntry::new(tool, AuditAction::Prompted, AuditResult::Success);
80
81 if let Some(agent_name) = agent {
82 entry.agent = Some(agent_name);
83 }
84
85 if let Some(ctx) = context {
86 entry.context = Some(ctx);
87 }
88
89 let mut entries = self
90 .entries
91 .write()
92 .map_err(|e| format!("Failed to acquire write lock: {}", e))?;
93 entries.push(entry);
94
95 Ok(())
96 }
97
98 pub fn entries(&self) -> Result<Vec<AuditLogEntry>, String> {
100 let entries = self
101 .entries
102 .read()
103 .map_err(|e| format!("Failed to acquire read lock: {}", e))?;
104 Ok(entries.clone())
105 }
106
107 pub fn len(&self) -> Result<usize, String> {
109 let entries = self
110 .entries
111 .read()
112 .map_err(|e| format!("Failed to acquire read lock: {}", e))?;
113 Ok(entries.len())
114 }
115
116 pub fn is_empty(&self) -> Result<bool, String> {
118 let entries = self
119 .entries
120 .read()
121 .map_err(|e| format!("Failed to acquire read lock: {}", e))?;
122 Ok(entries.is_empty())
123 }
124
125 pub fn clear(&self) -> Result<(), String> {
127 let mut entries = self
128 .entries
129 .write()
130 .map_err(|e| format!("Failed to acquire write lock: {}", e))?;
131 entries.clear();
132 Ok(())
133 }
134}
135
136impl Default for AuditLogger {
137 fn default() -> Self {
138 Self::new()
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_audit_logger_creation() {
148 let logger = AuditLogger::new();
149 assert!(logger.is_empty().unwrap());
150 assert_eq!(logger.len().unwrap(), 0);
151 }
152
153 #[test]
154 fn test_log_execution() {
155 let logger = AuditLogger::new();
156 let result = logger.log_execution("test_tool".to_string(), None, None);
157
158 assert!(result.is_ok());
159 assert_eq!(logger.len().unwrap(), 1);
160
161 let entries = logger.entries().unwrap();
162 assert_eq!(entries[0].tool, "test_tool");
163 assert_eq!(entries[0].action, AuditAction::Allowed);
164 assert_eq!(entries[0].result, AuditResult::Success);
165 }
166
167 #[test]
168 fn test_log_execution_with_agent() {
169 let logger = AuditLogger::new();
170 let result =
171 logger.log_execution("test_tool".to_string(), Some("agent1".to_string()), None);
172
173 assert!(result.is_ok());
174 let entries = logger.entries().unwrap();
175 assert_eq!(entries[0].agent, Some("agent1".to_string()));
176 }
177
178 #[test]
179 fn test_log_execution_with_context() {
180 let logger = AuditLogger::new();
181 let result = logger.log_execution(
182 "test_tool".to_string(),
183 None,
184 Some("User context".to_string()),
185 );
186
187 assert!(result.is_ok());
188 let entries = logger.entries().unwrap();
189 assert_eq!(entries[0].context, Some("User context".to_string()));
190 }
191
192 #[test]
193 fn test_log_denial() {
194 let logger = AuditLogger::new();
195 let result = logger.log_denial("test_tool".to_string(), None, None);
196
197 assert!(result.is_ok());
198 assert_eq!(logger.len().unwrap(), 1);
199
200 let entries = logger.entries().unwrap();
201 assert_eq!(entries[0].tool, "test_tool");
202 assert_eq!(entries[0].action, AuditAction::Denied);
203 assert_eq!(entries[0].result, AuditResult::Blocked);
204 }
205
206 #[test]
207 fn test_log_denial_with_agent() {
208 let logger = AuditLogger::new();
209 let result = logger.log_denial("test_tool".to_string(), Some("agent1".to_string()), None);
210
211 assert!(result.is_ok());
212 let entries = logger.entries().unwrap();
213 assert_eq!(entries[0].agent, Some("agent1".to_string()));
214 }
215
216 #[test]
217 fn test_log_prompt() {
218 let logger = AuditLogger::new();
219 let result = logger.log_prompt("test_tool".to_string(), None, None);
220
221 assert!(result.is_ok());
222 assert_eq!(logger.len().unwrap(), 1);
223
224 let entries = logger.entries().unwrap();
225 assert_eq!(entries[0].tool, "test_tool");
226 assert_eq!(entries[0].action, AuditAction::Prompted);
227 assert_eq!(entries[0].result, AuditResult::Success);
228 }
229
230 #[test]
231 fn test_log_prompt_with_agent() {
232 let logger = AuditLogger::new();
233 let result = logger.log_prompt("test_tool".to_string(), Some("agent1".to_string()), None);
234
235 assert!(result.is_ok());
236 let entries = logger.entries().unwrap();
237 assert_eq!(entries[0].agent, Some("agent1".to_string()));
238 }
239
240 #[test]
241 fn test_multiple_logs() {
242 let logger = AuditLogger::new();
243
244 logger
245 .log_execution("tool1".to_string(), None, None)
246 .unwrap();
247 logger.log_denial("tool2".to_string(), None, None).unwrap();
248 logger.log_prompt("tool3".to_string(), None, None).unwrap();
249
250 assert_eq!(logger.len().unwrap(), 3);
251
252 let entries = logger.entries().unwrap();
253 assert_eq!(entries[0].tool, "tool1");
254 assert_eq!(entries[0].action, AuditAction::Allowed);
255 assert_eq!(entries[1].tool, "tool2");
256 assert_eq!(entries[1].action, AuditAction::Denied);
257 assert_eq!(entries[2].tool, "tool3");
258 assert_eq!(entries[2].action, AuditAction::Prompted);
259 }
260
261 #[test]
262 fn test_clear_entries() {
263 let logger = AuditLogger::new();
264
265 logger
266 .log_execution("tool1".to_string(), None, None)
267 .unwrap();
268 logger
269 .log_execution("tool2".to_string(), None, None)
270 .unwrap();
271
272 assert_eq!(logger.len().unwrap(), 2);
273
274 logger.clear().unwrap();
275 assert_eq!(logger.len().unwrap(), 0);
276 assert!(logger.is_empty().unwrap());
277 }
278
279 #[test]
280 fn test_default_creation() {
281 let logger = AuditLogger::default();
282 assert!(logger.is_empty().unwrap());
283 }
284
285 #[test]
286 fn test_clone() {
287 let logger1 = AuditLogger::new();
288 logger1
289 .log_execution("tool1".to_string(), None, None)
290 .unwrap();
291
292 let logger2 = logger1.clone();
293 assert_eq!(logger2.len().unwrap(), 1);
294
295 logger2
296 .log_execution("tool2".to_string(), None, None)
297 .unwrap();
298 assert_eq!(logger1.len().unwrap(), 2);
299 }
300}