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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
//! The `AuthzEntityProvider` trait: the application's bridge between its data
//! layer and Cedar's entity graph.
//!
//! # Why this trait exists
//!
//! Cedar evaluates policies against an *entity set*: a graph of typed entities
//! with attributes and parent relationships. The library has no knowledge of
//! your schema: you own your tables, your IDs, and your relationship model.
//! Implementing this trait is how you teach Axess what to load and how to
//! represent it as Cedar entities.
//!
//! # What `entities_for` must return
//!
//! The returned [`cedar_policy::Entities`] should include everything Cedar needs
//! to evaluate the (principal, action, resource) triple:
//!
//! - The **principal** entity (the authenticated user) with its attributes
//! (tenant, any ABAC attributes) and **parents** set to its role entity UIDs.
//! - The **role** entities: one per role the principal holds, with their
//! own parent hierarchy if roles are hierarchical.
//! - The **resource** entity (the specific instance being accessed) with
//! attributes (tenant, owner, status, etc.).
//! - Any other entities referenced by your Cedar policies (e.g. a tenant
//! entity if policies check `principal.tenant == resource.tenant`).
//!
//! The `action` parameter is provided so that read-only actions can skip
//! loading attributes only needed for write policies: purely optional
//! optimisation; loading more than needed is always safe.
//!
//! # Example
//!
//! ```rust,ignore
//! use axess_core::authz::{AuthzEntityProvider, AuthzError};
//! use cedar_policy::{Entities, EntityUid};
//!
//! pub struct MyEntityProvider {
//! db: sqlx::SqlitePool,
//! namespace: String,
//! }
//!
//! impl AuthzEntityProvider for MyEntityProvider {
//! type ResourceId = uuid::Uuid;
//! type Error = sqlx::Error;
//!
//! async fn entities_for(
//! &self,
//! principal: &EntityUid,
//! resource_id: &uuid::Uuid,
//! _action: &EntityUid,
//! ) -> Result<Entities, sqlx::Error> {
//! // load user, roles, document from DB and build Entities
//! todo!()
//! }
//!
//! fn resource_uid(&self, id: &uuid::Uuid) -> Result<EntityUid, AuthzError> {
//! // build Document::"uuid" UID
//! todo!()
//! }
//! }
//! ```
use ;
use AuthzError;
/// Application-supplied bridge between the data layer and Cedar entity graphs.
///
/// Implement this trait once per application (or per resource domain). Axess
/// calls [`entities_for`][Self::entities_for] for every authorization check,
/// passing the result directly to Cedar for evaluation.
///
/// Native `async fn`: no `async-trait` macro required (Rust 1.75+).
///
/// # Tenant-qualified Cedar IDs are mandatory in multi-tenant deployments
///
/// Cedar's `EntityUid` has no built-in awareness of axess tenants. Two
/// tenants both with a user "alice" produce the *same* `EntityUid`
/// `"App::User::\"alice\""` if the implementation forms ids from the
/// raw identifier alone. The Cedar policy engine then evaluates rules
/// against a graph where Alice-from-tenant-A and Alice-from-tenant-B
/// are indistinguishable: a cross-tenant authorisation hole.
///
/// Implementations MUST qualify resource and principal ids by tenant.
/// Three workable conventions:
///
/// * **Composite-id encoding:** `App::User::"<tenant_id>:<user_id>"`,
/// simple, opaque, requires schema awareness in callers that emit
/// Cedar requests.
/// * **Per-tenant Cedar namespace:** `Tenant_<id>::User::"alice"`,
/// isolates each tenant in a separate type cluster; works well when
/// tenants are statically known.
/// * **Tenant attribute on every entity:** `App::User::"<user_id>"` with
/// a `tenant: Tenant::"<id>"` attribute and policies that always
/// constrain `principal.tenant == resource.tenant`. Most flexible;
/// easiest to forget a single policy.
///
/// Pick one and apply it consistently. The convenience helpers
/// [`make_entity_uid`] / [`make_action_uid`] do not enforce a choice;
/// the caller controls the id string. A future axess release may add a
/// schema-validation pass that rejects untenanted entity types; until
/// then this is a load-bearing application invariant.
// ── RequestEntityProvider ───────────────────────────────────────────────────
/// Object-safe entity provider: sibling of [`AuthzEntityProvider`] for
/// runtime polymorphism.
///
/// `AuthzEntityProvider` keeps a typed associated `ResourceId`, which is
/// great for ergonomic handler code that knows its concrete provider but
/// makes the trait non-object-safe. `RequestEntityProvider` erases the
/// resource type to [`EntityUid`] so it can live behind
/// `Arc<dyn RequestEntityProvider>` in axum extensions and be consumed
/// generically by macros and cache decorators that don't know the
/// application's concrete provider type.
///
/// # When to implement
///
/// You implement *both* traits when you want both ergonomic typed APIs
/// and the `require_authz!` route macro. The implementations share their
/// data layer; `RequestEntityProvider::entities_for` is typically a thin
/// adapter calling the typed `AuthzEntityProvider::entities_for` after
/// reconstructing your `ResourceId` from the supplied `EntityUid`.
///
/// Apps that only use `require_authz!` (and not `AuthzStore`/`AuthzSession`
/// directly) can implement `RequestEntityProvider` alone.
///
/// # Wrapping with caches
///
/// The cache decorators in [`crate::authz::cache`] are parameterised over
/// `RequestEntityProvider` so the same cache stack composes regardless of
/// the concrete inner provider:
///
/// ```rust,ignore
/// let provider: PlatformProvider = ...; // typed
/// let provider: Arc<dyn RequestEntityProvider> = Arc::new(
/// MokaEntityCache::new(
/// ValkeyEntityCache::new(provider, valkey, Duration::from_secs(60))
/// )
/// );
/// ```
///
/// # Why `&AuthSession` is in the signature
///
/// Cedar policies frequently key off attributes that depend on the
/// session's current state, most commonly the active tenant in
/// multi-tenant deployments, but also things like the authenticated-at
/// time, MFA recency, or app-defined custom session data. The principal
/// `EntityUid` alone cannot convey these; the session is the natural
/// per-request anchor for them.
///
/// Implementations that don't need any session state should still take
/// the parameter (it's part of the trait contract) and acknowledge the
/// non-use explicitly with `let _ = session;` at the top of the method
/// body, matching the established axess pattern for trait methods with
/// optional inputs (e.g. `SessionStore::find_sessions_for_user`'s
/// `let _ = (user_id, limit);`). Do NOT prefix the parameter with `_`
/// axess forbids `_`-prefixed parameter names in trait impls, since
/// the prefix hides the contract from readers and tooling.
///
/// # Async return shape
///
/// Returns `Pin<Box<dyn Future + Send + 'a>>`: the standard object-safe
/// pattern for async trait methods on stable Rust. axess does not depend
/// on `async-trait`; the boxed-future return is one allocation per call,
/// which is dominated by the underlying DB / network work.
// ── Entity UID helpers ──────────────────────────────────────────────────────
/// Build a Cedar [`EntityUid`] from a namespace, type name, and entity ID.
///
/// Example: `make_entity_uid("MyApp", "User", "alice")` produces `MyApp::User::"alice"`.
///
/// Returns an error if the resulting type or ID string is invalid per Cedar's grammar.
/// Build a Cedar action [`EntityUid`] from a namespace and action name.
///
/// Example: `make_action_uid("MyApp", "ReadDocument")` produces `MyApp::Action::"ReadDocument"`.