use std::sync::{Arc, Mutex};
use nodedb_types::DatabaseId;
use crate::types::TenantId;
use super::auth::AuditAuth;
use super::event::AuditEvent;
use super::log::AuditLog;
pub trait AuditEmitter: Send + Sync {
fn emit(&self, event: AuditEvent, source: &str, detail: &str, auth: AuditEmitContext<'_>);
}
#[derive(Clone, Copy)]
pub struct AuditEmitContext<'a> {
pub tenant_id: Option<TenantId>,
pub database_id: Option<DatabaseId>,
pub auth_user_id: &'a str,
pub auth_user_name: &'a str,
}
impl<'a> AuditEmitContext<'a> {
pub fn new(
tenant_id: Option<TenantId>,
auth_user_id: &'a str,
auth_user_name: &'a str,
) -> Self {
Self {
tenant_id,
database_id: None,
auth_user_id,
auth_user_name,
}
}
}
pub struct ArcAuditEmitter(pub Arc<Mutex<AuditLog>>);
impl AuditEmitter for ArcAuditEmitter {
fn emit(&self, event: AuditEvent, source: &str, detail: &str, ctx: AuditEmitContext<'_>) {
let auth = AuditAuth {
user_id: ctx.auth_user_id.to_string(),
user_name: ctx.auth_user_name.to_string(),
session_id: String::new(),
};
let mut log = match self.0.lock() {
Ok(l) => l,
Err(p) => p.into_inner(),
};
log.record_with_auth(event, ctx.tenant_id, ctx.database_id, source, detail, &auth);
}
}
pub struct NoopAuditEmitter;
impl AuditEmitter for NoopAuditEmitter {
fn emit(&self, _event: AuditEvent, _source: &str, _detail: &str, _ctx: AuditEmitContext<'_>) {}
}
#[cfg(test)]
pub mod test_helpers {
use std::sync::Mutex;
use super::*;
pub struct CapturingEmitter {
pub events: Mutex<Vec<(AuditEvent, String, String)>>,
}
impl Default for CapturingEmitter {
fn default() -> Self {
Self {
events: Mutex::new(Vec::new()),
}
}
}
impl CapturingEmitter {
pub fn new() -> Self {
Self::default()
}
pub fn recorded(&self) -> Vec<(AuditEvent, String, String)> {
self.events
.lock()
.unwrap_or_else(|p| p.into_inner())
.clone()
}
}
impl AuditEmitter for CapturingEmitter {
fn emit(&self, event: AuditEvent, source: &str, detail: &str, _ctx: AuditEmitContext<'_>) {
let mut events = self.events.lock().unwrap_or_else(|p| p.into_inner());
events.push((event, source.to_string(), detail.to_string()));
}
}
}