ricecoder_permissions/audit/
models.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "lowercase")]
10pub enum AuditAction {
11 Allowed,
13 Denied,
15 Prompted,
17 Approved,
19 Rejected,
21}
22
23impl std::fmt::Display for AuditAction {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 AuditAction::Allowed => write!(f, "allowed"),
27 AuditAction::Denied => write!(f, "denied"),
28 AuditAction::Prompted => write!(f, "prompted"),
29 AuditAction::Approved => write!(f, "approved"),
30 AuditAction::Rejected => write!(f, "rejected"),
31 }
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "lowercase")]
38pub enum AuditResult {
39 Success,
41 Blocked,
43 Cancelled,
45}
46
47impl std::fmt::Display for AuditResult {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 AuditResult::Success => write!(f, "success"),
51 AuditResult::Blocked => write!(f, "blocked"),
52 AuditResult::Cancelled => write!(f, "cancelled"),
53 }
54 }
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct AuditLogEntry {
60 pub id: String,
62 pub timestamp: DateTime<Utc>,
64 pub tool: String,
66 pub action: AuditAction,
68 pub result: AuditResult,
70 pub agent: Option<String>,
72 pub context: Option<String>,
74}
75
76impl AuditLogEntry {
77 pub fn new(tool: String, action: AuditAction, result: AuditResult) -> Self {
79 Self {
80 id: Uuid::new_v4().to_string(),
81 timestamp: Utc::now(),
82 tool,
83 action,
84 result,
85 agent: None,
86 context: None,
87 }
88 }
89
90 pub fn with_agent(
92 tool: String,
93 action: AuditAction,
94 result: AuditResult,
95 agent: String,
96 ) -> Self {
97 Self {
98 id: Uuid::new_v4().to_string(),
99 timestamp: Utc::now(),
100 tool,
101 action,
102 result,
103 agent: Some(agent),
104 context: None,
105 }
106 }
107
108 pub fn with_context(mut self, context: String) -> Self {
110 self.context = Some(context);
111 self
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_audit_action_display() {
121 assert_eq!(AuditAction::Allowed.to_string(), "allowed");
122 assert_eq!(AuditAction::Denied.to_string(), "denied");
123 assert_eq!(AuditAction::Prompted.to_string(), "prompted");
124 assert_eq!(AuditAction::Approved.to_string(), "approved");
125 assert_eq!(AuditAction::Rejected.to_string(), "rejected");
126 }
127
128 #[test]
129 fn test_audit_result_display() {
130 assert_eq!(AuditResult::Success.to_string(), "success");
131 assert_eq!(AuditResult::Blocked.to_string(), "blocked");
132 assert_eq!(AuditResult::Cancelled.to_string(), "cancelled");
133 }
134
135 #[test]
136 fn test_audit_log_entry_creation() {
137 let entry = AuditLogEntry::new(
138 "test_tool".to_string(),
139 AuditAction::Allowed,
140 AuditResult::Success,
141 );
142
143 assert_eq!(entry.tool, "test_tool");
144 assert_eq!(entry.action, AuditAction::Allowed);
145 assert_eq!(entry.result, AuditResult::Success);
146 assert_eq!(entry.agent, None);
147 assert_eq!(entry.context, None);
148 assert!(!entry.id.is_empty());
149 }
150
151 #[test]
152 fn test_audit_log_entry_with_agent() {
153 let entry = AuditLogEntry::with_agent(
154 "test_tool".to_string(),
155 AuditAction::Denied,
156 AuditResult::Blocked,
157 "agent1".to_string(),
158 );
159
160 assert_eq!(entry.tool, "test_tool");
161 assert_eq!(entry.action, AuditAction::Denied);
162 assert_eq!(entry.result, AuditResult::Blocked);
163 assert_eq!(entry.agent, Some("agent1".to_string()));
164 }
165
166 #[test]
167 fn test_audit_log_entry_with_context() {
168 let entry = AuditLogEntry::new(
169 "test_tool".to_string(),
170 AuditAction::Prompted,
171 AuditResult::Success,
172 )
173 .with_context("User approved after 5 seconds".to_string());
174
175 assert_eq!(
176 entry.context,
177 Some("User approved after 5 seconds".to_string())
178 );
179 }
180
181 #[test]
182 fn test_audit_log_entry_serialization() {
183 let entry = AuditLogEntry::new(
184 "test_tool".to_string(),
185 AuditAction::Allowed,
186 AuditResult::Success,
187 );
188
189 let json = serde_json::to_string(&entry).unwrap();
190 let deserialized: AuditLogEntry = serde_json::from_str(&json).unwrap();
191
192 assert_eq!(deserialized.tool, entry.tool);
193 assert_eq!(deserialized.action, entry.action);
194 assert_eq!(deserialized.result, entry.result);
195 }
196
197 #[test]
198 fn test_audit_log_entry_timestamp() {
199 let before = Utc::now();
200 let entry = AuditLogEntry::new(
201 "test_tool".to_string(),
202 AuditAction::Allowed,
203 AuditResult::Success,
204 );
205 let after = Utc::now();
206
207 assert!(entry.timestamp >= before);
208 assert!(entry.timestamp <= after);
209 }
210}