typesec_odrl/audit.rs
1//! Audit log types for ODRL policy decisions.
2
3use tracing::info;
4
5use crate::model::{ConstraintOperand, OdrlRuleType};
6
7/// A structured audit record for a single ODRL policy check.
8#[derive(Debug)]
9pub struct OdrlAuditEvent {
10 /// The policy UID that produced this verdict.
11 pub policy_uid: String,
12 /// The matched rule type (or `None` if no rule matched).
13 pub matched_rule: Option<OdrlRuleType>,
14 /// The subject making the request.
15 pub subject: String,
16 /// The action requested.
17 pub action: String,
18 /// The target resource.
19 pub target: String,
20 /// The final verdict.
21 pub verdict: OdrlVerdict,
22 /// Constraint evaluation results.
23 pub constraint_results: Vec<ConstraintEval>,
24}
25
26/// Verdict of an ODRL check.
27#[derive(Debug, Clone)]
28#[non_exhaustive]
29pub enum OdrlVerdict {
30 /// A permission rule matched and all constraints passed.
31 Permitted,
32 /// A prohibition rule matched and all constraints passed (action blocked).
33 Prohibited {
34 /// Human-readable reason explaining why the action is prohibited.
35 reason: String,
36 },
37 /// A permission rule matched but was overridden by a prohibition.
38 Overridden {
39 /// The policy UID containing the prohibition that took priority.
40 by_policy: String,
41 /// Human-readable reason explaining the override.
42 reason: String,
43 },
44 /// No matching rule found.
45 NotApplicable,
46 /// A permission rule matched but one or more constraints failed.
47 ConstraintFailed {
48 /// Description of the constraint that failed.
49 constraint: String,
50 },
51}
52
53/// Record of a single constraint's evaluation.
54#[derive(Debug, Clone)]
55pub struct ConstraintEval {
56 /// The left operand (e.g., `"purpose"`).
57 pub operand: ConstraintOperand,
58 /// Whether it passed.
59 pub passed: bool,
60}
61
62impl OdrlAuditEvent {
63 /// Emit this event to the `tracing` subscriber.
64 pub fn log(&self) {
65 let verdict_str = match &self.verdict {
66 OdrlVerdict::Permitted => "permitted".to_owned(),
67 OdrlVerdict::Prohibited { reason } => format!("prohibited: {reason}"),
68 OdrlVerdict::Overridden { by_policy, reason } => {
69 format!("overridden by {by_policy}: {reason}")
70 }
71 OdrlVerdict::NotApplicable => "not_applicable".to_owned(),
72 OdrlVerdict::ConstraintFailed { constraint } => {
73 format!("constraint_failed: {constraint}")
74 }
75 };
76
77 info!(
78 policy = %self.policy_uid,
79 subject = %self.subject,
80 action = %self.action,
81 target = %self.target,
82 verdict = %verdict_str,
83 "odrl policy decision"
84 );
85 }
86}