use corium_protocol::authz::{Access, Principal};
use crate::model::action_name;
#[derive(Clone, Debug)]
pub struct AuditEvent {
pub subject: String,
pub provider: String,
pub action: &'static str,
pub database: Option<String>,
pub object: String,
pub allowed: bool,
pub reason: Option<String>,
pub path: Option<String>,
pub views: Vec<String>,
pub authz_t: u64,
pub source: String,
}
impl AuditEvent {
#[must_use]
pub fn new(
principal: &Principal,
access: &Access,
decision: &crate::AuthzDecision,
source: &str,
) -> Self {
Self {
subject: principal.subject.clone(),
provider: principal.provider.clone(),
action: action_name(access.action),
database: access.database.clone(),
object: decision.object.clone(),
allowed: decision.is_allowed(),
reason: decision.reason.clone(),
path: decision.path.clone(),
views: decision.views.clone(),
authz_t: decision.authz_t,
source: source.to_owned(),
}
}
}
pub trait AuditSink: Send + Sync + 'static {
fn record(&self, event: &AuditEvent);
}
#[derive(Clone, Copy, Debug, Default)]
pub struct TracingAudit;
impl AuditSink for TracingAudit {
fn record(&self, event: &AuditEvent) {
if event.allowed {
tracing::debug!(
target: "corium_authz::audit",
subject = %event.subject,
provider = %event.provider,
action = event.action,
object = %event.object,
path = event.path.as_deref().unwrap_or(""),
views = ?event.views,
authz_t = event.authz_t,
source = %event.source,
"authorization allowed"
);
} else {
tracing::info!(
target: "corium_authz::audit",
subject = %event.subject,
provider = %event.provider,
action = event.action,
object = %event.object,
reason = event.reason.as_deref().unwrap_or(""),
authz_t = event.authz_t,
source = %event.source,
"authorization denied"
);
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct NullAudit;
impl AuditSink for NullAudit {
fn record(&self, _event: &AuditEvent) {}
}