use std::future::Future;
use crate::actor::Actor;
use crate::config;
#[derive(Clone, Debug, Default)]
pub struct AuditContext {
pub user: Option<Actor>,
pub remote_address: Option<String>,
pub request_uuid: Option<String>,
pub(crate) scope_override: Option<bool>,
}
impl AuditContext {
pub fn new() -> Self {
AuditContext::default()
}
pub fn with_user(mut self, user: impl Into<Actor>) -> Self {
self.user = Some(user.into());
self
}
pub fn with_remote_address(mut self, addr: impl Into<String>) -> Self {
self.remote_address = Some(addr.into());
self
}
pub fn with_request_uuid(mut self, uuid: impl Into<String>) -> Self {
self.request_uuid = Some(uuid.into());
self
}
}
tokio::task_local! {
static CONTEXT: AuditContext;
}
pub fn current_context() -> AuditContext {
CONTEXT.try_with(|c| c.clone()).unwrap_or_default()
}
pub async fn with_context<F>(ctx: AuditContext, f: F) -> F::Output
where
F: Future,
{
CONTEXT.scope(ctx, f).await
}
pub fn with_context_sync<R>(ctx: AuditContext, f: impl FnOnce() -> R) -> R {
CONTEXT.sync_scope(ctx, f)
}
pub async fn as_user<F>(user: impl Into<Actor>, f: F) -> F::Output
where
F: Future,
{
let mut ctx = current_context();
ctx.user = Some(user.into());
with_context(ctx, f).await
}
pub fn as_user_sync<R>(user: impl Into<Actor>, f: impl FnOnce() -> R) -> R {
let mut ctx = current_context();
ctx.user = Some(user.into());
with_context_sync(ctx, f)
}
pub async fn without_auditing<F>(f: F) -> F::Output
where
F: Future,
{
let mut ctx = current_context();
ctx.scope_override = Some(false);
with_context(ctx, f).await
}
pub fn without_auditing_sync<R>(f: impl FnOnce() -> R) -> R {
let mut ctx = current_context();
ctx.scope_override = Some(false);
with_context_sync(ctx, f)
}
pub async fn with_auditing<F>(f: F) -> F::Output
where
F: Future,
{
let mut ctx = current_context();
ctx.scope_override = Some(true);
with_context(ctx, f).await
}
pub fn with_auditing_sync<R>(f: impl FnOnce() -> R) -> R {
let mut ctx = current_context();
ctx.scope_override = Some(true);
with_context_sync(ctx, f)
}
pub(crate) fn type_auditing_enabled(auditable_type: &str, ctx: &AuditContext) -> bool {
if !config::auditing_enabled() {
return false;
}
match ctx.scope_override {
Some(forced) => forced,
None => config::type_enabled(auditable_type),
}
}