Skip to main content

corium_authz/
lib.rs

1//! Self-hosted relationship-based authorization (`ReBAC`) over a Corium
2//! database.
3//!
4//! `corium-protocol`'s [`Authorizer`](corium_protocol::authz::Authorizer) seam
5//! was built so a deployment could point authorization at an external policy
6//! service. This crate is the answer for deployments that would rather not run
7//! one: Corium's own data model — entities, reference attributes, immutable
8//! history — is a good fit for the tuple shape `ReBAC` needs, so the policy
9//! lives in an ordinary Corium database and the check is a bounded in-memory
10//! graph walk over a compiled snapshot of it.
11//!
12//! ```text
13//! Check(subject, action, object, database, at_authz_t)
14//!   action  -> required relation(s)          (permission map)
15//!   subject --relation/derived--> object     (tuples + rewrites)
16//!   optional binding -> ViewFilter           (bindings + views)
17//! ```
18//!
19//! # The pieces
20//!
21//! * [`schema`] — the reserved attributes of the authz database, and the EDN
22//!   an operator installs to create one.
23//! * [`model`] — the policy vocabulary: objects, tuples, permissions,
24//!   rewrites, views, bindings. Relation names are *data*, not Rust enums.
25//! * [`policy::Policy`] — an immutable compiled snapshot keyed by the authz
26//!   database's basis `t`, with the indexes a check needs.
27//! * [`eval`] — the bounded, cycle-safe relationship search.
28//! * [`subject`] — how a request [`Principal`](corium_protocol::authz::Principal)
29//!   becomes the subjects the search starts from.
30//! * [`source::PolicySource`] — where snapshots come from; each surface
31//!   supplies the database value it already holds.
32//! * [`SystemDbAuthorizer`] — the `Authorizer` itself: snapshot caching,
33//!   consistency, result caching, fail-closed behaviour, break-glass, audit.
34//! * [`bootstrap`] — building an authz database in process, and the EDN an
35//!   operator's grant/revoke sends to a real one.
36//!
37//! # Fail closed
38//!
39//! Every failure that leaves the authorizer without a policy — a missing authz
40//! database, an unreadable snapshot, a policy that does not compile — denies.
41//! The one exception is an explicit [`BreakGlass`] configuration for operator
42//! recovery, and it is audited at `warn` every time it grants. A snapshot that
43//! stops compiling never *replaces* the last good one either: the process
44//! keeps deciding from the policy it already has.
45//!
46//! # Example
47//!
48//! ```
49//! use std::sync::Arc;
50//!
51//! use corium_authz::{SystemDbAuthorizer, bootstrap, source::MemoryPolicySource};
52//! use corium_protocol::authz::{Access, Action, Authorizer, Decision, Principal};
53//!
54//! # #[tokio::main(flavor = "current_thread")]
55//! # async fn main() {
56//! let db = bootstrap::policy_db(r#"
57//! [{:db/id "read" :authz.permission/object-type "database"
58//!   :authz.permission/action "read" :authz.permission/relation ["viewer"]}
59//!  {:db/id "t1" :authz.tuple/subject "group:eng#member"
60//!   :authz.tuple/relation "viewer" :authz.tuple/object "database:music"}
61//!  {:db/id "t2" :authz.tuple/subject "user:alice"
62//!   :authz.tuple/relation "member" :authz.tuple/object "group:eng"}]
63//! "#).expect("policy compiles");
64//!
65//! let authorizer = SystemDbAuthorizer::new(Arc::new(MemoryPolicySource::new("authz", db)));
66//! let alice = Principal::new("oidc", "alice");
67//! let bob = Principal::new("oidc", "bob");
68//!
69//! assert!(matches!(
70//!     authorizer.authorize(&alice, &Access::on(Action::Query, "music")).await,
71//!     Decision::Allow
72//! ));
73//! assert!(matches!(
74//!     authorizer.authorize(&bob, &Access::on(Action::Query, "music")).await,
75//!     Decision::Deny(_)
76//! ));
77//! # }
78//! ```
79
80pub mod audit;
81pub mod authorizer;
82pub mod bootstrap;
83pub mod eval;
84pub mod model;
85pub mod policy;
86pub mod schema;
87pub mod source;
88pub mod subject;
89pub mod view;
90
91pub use authorizer::{
92    AuthzConfig, AuthzDecision, BreakGlass, Consistency, RefreshError, SystemDbAuthorizer,
93};
94pub use eval::{Denial, Limits, Match, Outcome};
95pub use model::{ObjectRef, SubjectRef};
96pub use policy::{Policy, PolicyError, PolicyStats};
97pub use schema::DEFAULT_AUTHZ_DB;
98pub use source::{MemoryPolicySource, PolicySource, SourceError};
99pub use subject::SubjectMapping;