use std::collections::BTreeMap;
use std::convert::Infallible;
use std::error::Error;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::evaluation::DecisionKind;
use crate::model::{ActorRef, ExpiryCause, KeepsakeId, RelationId, SubjectRef};
#[cfg(any(test, feature = "test"))]
mod memory;
#[cfg(any(test, feature = "test"))]
pub use memory::{InMemoryAuditError, InMemoryAuditSink};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuditEvent {
pub event_type: AuditEventType,
pub at: DateTime<Utc>,
pub actor: ActorRef,
pub keepsake_id: KeepsakeId,
pub subject: SubjectRef,
pub relation_id: RelationId,
pub decision: AuditDecision,
pub context: AuditContext,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AuditEventType {
Apply,
DuplicateApply,
Revoke,
TimedExpiry,
FulfillmentExpiry,
}
impl AuditEventType {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Apply => "apply",
Self::DuplicateApply => "duplicate_apply",
Self::Revoke => "revoke",
Self::TimedExpiry => "timed_expiry",
Self::FulfillmentExpiry => "fulfillment_expiry",
}
}
#[must_use]
pub fn from_storage_label(label: &str) -> Option<Self> {
match label {
"apply" => Some(Self::Apply),
"duplicate_apply" => Some(Self::DuplicateApply),
"revoke" => Some(Self::Revoke),
"timed_expiry" => Some(Self::TimedExpiry),
"fulfillment_expiry" => Some(Self::FulfillmentExpiry),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AuditDecision {
Applied {
duplicate_prevented: bool,
},
Revoked,
Expired {
cause: ExpiryCause,
},
Evaluated {
decision: DecisionKind,
},
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuditContext {
pub attributes: BTreeMap<String, String>,
}
pub type AuditResult<T, E> = core::result::Result<T, E>;
pub trait AuditSink: Send + Sync {
type Error: Error + Send + Sync + 'static;
fn record(&self, event: AuditEvent) -> AuditResult<(), Self::Error>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct NoopAuditSink;
impl AuditSink for NoopAuditSink {
type Error = Infallible;
fn record(&self, _event: AuditEvent) -> AuditResult<(), Self::Error> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::AuditEventType;
#[test]
fn event_type_storage_label_round_trips() {
for event_type in [
AuditEventType::Apply,
AuditEventType::DuplicateApply,
AuditEventType::Revoke,
AuditEventType::TimedExpiry,
AuditEventType::FulfillmentExpiry,
] {
assert_eq!(
AuditEventType::from_storage_label(event_type.as_str()),
Some(event_type)
);
}
assert_eq!(AuditEventType::from_storage_label("unknown"), None);
}
}