1use crate::error::Result;
4use crate::models::{AuditLog, AuditResult};
5use chrono::Utc;
6use std::collections::HashMap;
7use std::net::IpAddr;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10use uuid::Uuid;
11
12pub struct AuditManager {
13 logs: Arc<RwLock<Vec<AuditLog>>>,
14 retention_days: u32,
15}
16
17impl AuditManager {
18 pub fn new(retention_days: u32) -> Self {
19 Self {
20 logs: Arc::new(RwLock::new(Vec::new())),
21 retention_days,
22 }
23 }
24
25 pub async fn log(
26 &self,
27 user_id: Option<Uuid>,
28 session_id: Option<Uuid>,
29 action: String,
30 resource: String,
31 result: AuditResult,
32 ip_address: Option<IpAddr>,
33 user_agent: Option<String>,
34 metadata: HashMap<String, serde_json::Value>,
35 risk_score: u8,
36 ) {
37 let log = AuditLog {
38 id: Uuid::new_v4(),
39 user_id,
40 session_id,
41 action,
42 resource,
43 result,
44 ip_address,
45 user_agent,
46 metadata,
47 risk_score,
48 timestamp: Utc::now(),
49 };
50
51 let mut logs = self.logs.write().await;
52 logs.push(log.clone());
53
54 self.persist_log(&log).await;
56
57 self.send_to_telemetry(&log);
59 }
60
61 async fn persist_log(&self, log: &AuditLog) {
62 tracing::debug!("Persisting audit log: {:?}", log.id);
64 }
65
66 fn send_to_telemetry(&self, log: &AuditLog) {
67 tracing::info!(
68 user_id = ?log.user_id,
69 action = %log.action,
70 resource = %log.resource,
71 result = ?log.result,
72 risk_score = log.risk_score,
73 "Audit event"
74 );
75 }
76
77 pub async fn query(
78 &self,
79 user_id: Option<Uuid>,
80 action: Option<String>,
81 start_time: Option<chrono::DateTime<Utc>>,
82 end_time: Option<chrono::DateTime<Utc>>,
83 limit: usize,
84 ) -> Vec<AuditLog> {
85 let logs = self.logs.read().await;
86
87 logs.iter()
88 .filter(|log| {
89 if let Some(uid) = user_id {
90 if log.user_id != Some(uid) {
91 return false;
92 }
93 }
94
95 if let Some(ref act) = action {
96 if &log.action != act {
97 return false;
98 }
99 }
100
101 if let Some(start) = start_time {
102 if log.timestamp < start {
103 return false;
104 }
105 }
106
107 if let Some(end) = end_time {
108 if log.timestamp > end {
109 return false;
110 }
111 }
112
113 true
114 })
115 .take(limit)
116 .cloned()
117 .collect()
118 }
119
120 pub async fn cleanup_old_logs(&self) -> Result<usize> {
121 let cutoff = Utc::now() - chrono::Duration::days(self.retention_days as i64);
122
123 let mut logs = self.logs.write().await;
124 let initial_count = logs.len();
125
126 logs.retain(|log| log.timestamp > cutoff);
127
128 let removed = initial_count - logs.len();
129
130 tracing::info!("Cleaned up {} old audit logs", removed);
131 Ok(removed)
132 }
133
134 pub async fn get_user_activity(&self, user_id: &Uuid, days: u32) -> UserActivity {
135 let since = Utc::now() - chrono::Duration::days(days as i64);
136 let logs = self.logs.read().await;
137
138 let user_logs: Vec<_> = logs
139 .iter()
140 .filter(|log| log.user_id == Some(*user_id) && log.timestamp > since)
141 .collect();
142
143 let total_actions = user_logs.len();
144 let successful = user_logs.iter().filter(|l| l.result == AuditResult::Success).count();
145 let failed = user_logs.iter().filter(|l| l.result == AuditResult::Failure).count();
146 let blocked = user_logs.iter().filter(|l| l.result == AuditResult::Blocked).count();
147
148 let unique_ips: std::collections::HashSet<_> = user_logs
149 .iter()
150 .filter_map(|l| l.ip_address)
151 .collect();
152
153 let action_breakdown: HashMap<String, usize> = user_logs
154 .iter()
155 .fold(HashMap::new(), |mut acc, log| {
156 *acc.entry(log.action.clone()).or_insert(0) += 1;
157 acc
158 });
159
160 UserActivity {
161 user_id: *user_id,
162 total_actions,
163 successful,
164 failed,
165 blocked,
166 unique_ips: unique_ips.len(),
167 action_breakdown,
168 avg_risk_score: if total_actions > 0 {
169 user_logs.iter().map(|l| l.risk_score as f64).sum::<f64>() / total_actions as f64
170 } else {
171 0.0
172 },
173 }
174 }
175
176 pub async fn generate_compliance_report(
178 &self,
179 user_id: &Uuid,
180 ) -> ComplianceReport {
181 let logs = self.logs.read().await;
182
183 let user_logs: Vec<_> = logs
184 .iter()
185 .filter(|log| log.user_id == Some(*user_id))
186 .cloned()
187 .collect();
188
189 let data_accesses = user_logs
190 .iter()
191 .filter(|l| l.action.contains("read") || l.action.contains("access"))
192 .count();
193
194 let data_modifications = user_logs
195 .iter()
196 .filter(|l| l.action.contains("update") || l.action.contains("delete"))
197 .count();
198
199 let data_exports = user_logs
200 .iter()
201 .filter(|l| l.action.contains("export"))
202 .count();
203
204 ComplianceReport {
205 user_id: *user_id,
206 report_date: Utc::now(),
207 total_events: user_logs.len(),
208 data_accesses,
209 data_modifications,
210 data_exports,
211 logs: user_logs,
212 }
213 }
214}
215
216#[derive(Debug, Clone, serde::Serialize)]
217pub struct UserActivity {
218 pub user_id: Uuid,
219 pub total_actions: usize,
220 pub successful: usize,
221 pub failed: usize,
222 pub blocked: usize,
223 pub unique_ips: usize,
224 pub action_breakdown: HashMap<String, usize>,
225 pub avg_risk_score: f64,
226}
227
228#[derive(Debug, Clone, serde::Serialize)]
229pub struct ComplianceReport {
230 pub user_id: Uuid,
231 pub report_date: chrono::DateTime<Utc>,
232 pub total_events: usize,
233 pub data_accesses: usize,
234 pub data_modifications: usize,
235 pub data_exports: usize,
236 pub logs: Vec<AuditLog>,
237}