1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! Documented `tracing` schema for authorization decisions.
//!
//! axess emits a structured `tracing` event per Cedar evaluation inside
//! the [`PolicyEvaluator::is_authorized`](super::store::PolicyEvaluator::is_authorized)
//! impl on [`PolicyStore`](super::store::PolicyStore). Apps that need
//! audit logs route those events via `tracing-subscriber::Layer` to
//! wherever the audit needs to go: a file, an OpenTelemetry collector,
//! an Iggy/Kafka/Pulsar producer, a SIEM agent, etc.
//!
//! axess deliberately does **not** ship its own audit transport: the
//! `tracing` ecosystem already solves this and is the de facto Rust
//! pattern. This module documents the event shape so consumers can
//! filter and route confidently.
//!
//! # Event target
//!
//! All authorization-decision events use the target string:
//!
//! ```text
//! axess::authz::decision
//! ```
//!
//! The constant [`DECISION_TARGET`] holds this string; prefer it over
//! re-typing the literal so a future axess release can rename it
//! safely.
//!
//! # Event level
//!
//! `INFO`. Both allow and deny are emitted at the same level: both are
//! audit-relevant. Use `tracing-subscriber::filter::Targets` or a custom
//! `Layer::enabled` to drop allows in environments that only need
//! denies (rarely the right call for compliance regimes; keep both).
//!
//! # Event fields
//!
//! | Field | Type | Notes |
//! |---|---|---|
//! | `principal` | string (`Display` of `cedar_policy::EntityUid`) | e.g. `App::User::"alice"` |
//! | `action` | string (`Display` of `cedar_policy::EntityUid`) | e.g. `App::Action::"ViewLedger"` |
//! | `resource` | string (`Display` of `cedar_policy::EntityUid`) | e.g. `App::Ledger::"ledger-1"` |
//! | `decision` | string | `"allow"` or `"deny"` |
//! | `reasons` | `Debug` of `Vec<String>` | Cedar `PolicyId`s that produced the decision; empty for default-deny |
//! | `latency_us` | u64 | Cedar evaluation latency in microseconds |
//! | `reason` (optional) | string | Set when the decision is forced by a non-policy condition (e.g. `"request_validation_failed"`) |
//!
//! # Example: route audit to a file via tracing-subscriber
//!
//! ```ignore
//! use tracing_subscriber::{filter::Targets, layer::SubscriberExt, prelude::*};
//!
//! let audit_layer = tracing_subscriber::fmt::layer()
//! .json()
//! .with_writer(std::fs::File::create("audit.log").unwrap());
//!
//! let audit_filter = axess_core::authz::audit::tracing_filter();
//!
//! tracing_subscriber::registry()
//! .with(tracing_subscriber::fmt::layer()) // app logs
//! .with(audit_layer.with_filter(audit_filter)) // audit -> file
//! .init();
//! ```
//!
//! # Example: route audit to Iggy (or Kafka, etc.)
//!
//! Application implements its own `tracing_subscriber::Layer` whose
//! `on_event` filters on `axess::authz::decision` and forwards to the
//! desired transport. axess does not ship transport-specific code;
//! consumers pick the crate that fits their stack.
/// Target string used by all axess Cedar-decision events.
///
/// Prefer `DECISION_TARGET` over re-typing the literal so consumers stay
/// aligned with future axess naming.
pub const DECISION_TARGET: &str = "axess::authz::decision";
/// Returns true if `metadata` describes an axess authorization-decision
/// event. Use as a custom filter:
///
/// ```ignore
/// use tracing_subscriber::filter::filter_fn;
/// let audit_only = filter_fn(|m| axess_core::authz::audit::is_decision_event(m));
/// ```