Expand description
An in-process authorization engine for Rust.
Gatehouse keeps authorization logic in Rust while giving policy code a
request-scoped fact session for relationship and backend-loaded data. The
public API is centered on one PolicyDomain marker per authorization
domain, a PermissionChecker that owns that domain’s policy stack, and a
BoundEvaluator created for one request/session/subject/action/context.
§Overview
A Policy is an asynchronous decision unit for one PolicyDomain. The
domain names the four Rust types involved in a decision:
Subject: the caller.Action: the operation being attempted.Resource: the target resource or scope resource.Context: request-scoped inputs such as current time, MFA freshness, network zone, tenant config, or feature flags.
Relationship data and other backend-loaded authorization facts do not
belong in Context; expose them as FactKey values loaded by an
EvaluationSession. The session batches, deduplicates, caches, and
coalesces fact loads for one request.
§Quick Start
The fastest way to define a synchronous predicate policy is
PolicyBuilder:
#[derive(Debug, Clone)]
struct User {
id: u64,
roles: Vec<&'static str>,
}
#[derive(Debug, Clone)]
struct Document {
owner_id: u64,
}
#[derive(Debug, Clone)]
struct ReadAction;
struct Documents;
impl PolicyDomain for Documents {
type Subject = User;
type Action = ReadAction;
type Resource = Document;
type Context = ();
}
let admin_policy = PolicyBuilder::<Documents>::new("AdminOnly")
.subjects(|user: &User| user.roles.contains(&"admin"))
.build();
let owner_policy = PolicyBuilder::<Documents>::new("OwnerOnly")
.when(|user: &User, _action: &ReadAction, document: &Document, _ctx: &()| {
user.id == document.owner_id
})
.build();
let mut checker = PermissionChecker::<Documents>::new();
checker.add_policy(admin_policy);
checker.add_policy(owner_policy);
let session = EvaluationSession::empty();
let document = Document { owner_id: 7 };
let admin = User { id: 1, roles: vec!["admin"] };
let owner = User { id: 7, roles: vec!["user"] };
let guest = User { id: 2, roles: vec!["user"] };
assert!(checker.bind(&session, &admin, &ReadAction, &()).check(&document).await.is_granted());
assert!(checker.bind(&session, &owner, &ReadAction, &()).check(&document).await.is_granted());
assert!(!checker.bind(&session, &guest, &ReadAction, &()).check(&document).await.is_granted());§Core Flows
Bind request-wide inputs once, then evaluate resources through the bound evaluator:
let session = registry.session();
let bound = checker.bind(&session, &subject, &action, &request_context);
let decision = bound.check(&resource).await;
let decisions = bound.evaluate(resources.clone()).await;
let authorized = bound.try_filter(resources).await?;
let authorized_rows = bound.try_filter_by(rows, |row| &row.authz_resource).await?;
let page = bound.try_lookup_page(&lookup, &hydrator, cursor.as_deref(), limit).await?;Use EvaluationSession::empty for fact-free decisions. Use a session from
FactRegistry::session when any policy loads facts — via ctx.fact(...)
(which also records provenance) or the raw session — such as RebacPolicy
or a custom fact-backed policy.
BoundEvaluator::evaluate preserves input order and retains every item
and decision. BoundEvaluator::try_filter excludes definite denials but
fails on indeterminate decisions; BoundEvaluator::try_filter_by applies
the same rule to projected caller-owned rows. Errors retain all input items
and evaluations. The explicit filter_lossy / filter_by_lossy helpers deliberately omit outages.
The original filter / filter_by names are deprecated aliases.
BoundEvaluator::try_lookup_page enumerates candidate IDs, hydrates them,
and strictly authorizes one page. LookupSource must cover every grant
path, including admin overrides. On evaluation failure, no next cursor is
returned: retry the same cursor with a fresh session after recovery.
§Decision Semantics
Policy returns GrantResult: grant, abstention, or indeterminate.
VetoPolicy returns VetoResult: veto, pass, or indeterminate. Register
them with PermissionChecker::add_policy and PermissionChecker::add_veto.
These types prevent grant policies from returning veto authority.
A definite veto denies access. An unresolved veto blocks grants. After
vetoes pass, any grant authorizes, including a grant after another grant
policy failed. With no grant, uncertainty yields AccessEvaluation::Indeterminate;
otherwise access is denied. An empty checker denies access.
AndPolicy, OrPolicy, and NotPolicy compose grants only. A definite
abstention settles AND; a definite grant settles OR; NOT retains uncertainty.
Use grant.and(blocked.not()) for a local exclusion. Vetoes use
AllOfVeto or AnyOfVeto; a pass settles all-of and a veto settles any-of,
otherwise uncertainty remains. Veto negation is not supported.
PolicyBuilder::build produces a grant policy;
PolicyBuilder::build_veto produces a veto when its predicate matches.
Repeated predicate calls accumulate: every predicate on every axis must hold,
so a later call narrows the match and never widens it. A policy with no
predicate must be spelled out with PolicyBuilder::allow_all or
PolicyBuilder::forbid_all.
Register DelegatingPolicy once with PermissionChecker::add_delegate
to preserve both child capabilities and their distinct failure semantics.
PolicyEvalResult is the inspectable audit tree, not a policy return type.
Typed result constructors compute aggregate decisions; arbitrary raw trees
cannot be promoted into typed authority. GrantResult::all and
GrantResult::any support custom grant aggregates.
Use AccessEvaluation::into_result to distinguish AccessError::Denied
from AccessError::Indeterminate without parsing reasons. Both errors retain
the full trace and classified fact errors. The original to_result combines
both outcomes into one reason-only callback.
Recorded successful, missing, and failed facts are retained on every node,
including aggregates. Recorded errors upgrade abstention/pass to indeterminate;
decisive grants and vetoes remain decisive. Attaching evidence has the same
behavior for leaves and aggregate nodes. Call ctx.finish(result) when
evaluating a policy directly; the checker and built-in combinators do this
automatically.
§Cargo Features
tracing is enabled by default. Disable default features to remove its
instrumentation and dependency; decisions, returned EvalTrace, and fact
provenance remain available. No telemetry events or contract warnings are
emitted without tracing. serde independently enables audit serialization.
§Fact-Loaded Authorization
FactSource::load_many receives unique fact keys and must return exactly
one result per key in the same order. EvaluationSession expands
duplicate caller inputs, preserves caller order, caches results for the
request, chunks loads according to FactSource::max_batch_size, and joins
concurrent in-flight loads for the same key.
RebacPolicy is the built-in fact-backed policy. It extracts flat
subject/resource IDs, builds RelationshipQuery keys, and grants only
when the request session loads a Found(true) relationship fact. A
Found(false) or Missing fact is an ordinary non-grant; missing
sources, backend errors, and fact-source contract violations fail closed
as an indeterminate GrantResult — never a grant, and structurally
distinguishable from a policy denial.
§Long-Lived Streams
EvaluationSession caches are scoped to one authorization pass. For SSE,
WebSocket, and other long-lived streams, do not hold one fact-backed session
for the stream lifetime.
If your product contract authorizes once at stream open, create a fresh
session, compute the visible ID set with BoundEvaluator::try_filter or
BoundEvaluator::try_filter_by, drop the session, and only emit frames for
that set. If the stream must observe mid-stream permission revocation, run
periodic reauthorization with a fresh FactRegistry::session and re-bind
the checker for that pass.
§Built-In Policies
RbacPolicy: role-based access control from caller roles and required roles for the(action, resource)pair.RebacPolicy: relationship-based access control backed byFactSourceandEvaluationSession.DelegatingPolicy: maps the current inputs into anotherPolicyDomainand delegates to a childPermissionChecker.
Use PolicyBuilder::when for attribute-style predicates that compare
subject, action, resource, and context in one synchronous closure.
§Custom Policies
Implement Policy directly when a rule needs async work, custom batching,
or custom telemetry metadata. Implement VetoPolicy for custom vetoes.
A grant policy looks like this:
struct OwnerPolicy;
#[async_trait]
impl Policy<Documents> for OwnerPolicy {
async fn evaluate(&self, ctx: &EvalCtx<'_, Documents>) -> GrantResult {
if ctx.subject.id == ctx.resource.owner_id {
ctx.grant("subject owns the document")
} else {
ctx.not_applicable("subject does not own the document")
}
}
fn policy_type(&self) -> Cow<'static, str> {
Cow::Borrowed("OwnerPolicy")
}
}§Tracing
When trace-level events are enabled, checker evaluation records spans for
single-resource and batch evaluation, and each evaluated policy records a
trace! event on the gatehouse::security target. Batch evaluation also
records per-policy counts on nested gatehouse.batch_policy spans.
Structs§
- AllOf
Veto - Forbids when all veto children forbid.
- AndPolicy
- Requires every child policy to grant.
- AnyOf
Veto - Forbids when any veto child forbids.
- Batch
Eval Ctx - Batch policy evaluation context.
- Bound
Evaluator - A request-bound evaluator for one checker, subject, action, context, and evaluation session.
- Conditional
- Builder state: at least one predicate has been supplied.
- Delegating
Policy - Maps inputs into a child checker. Install with
PermissionChecker::add_delegate. - Empty
Policies Error - Error returned when no policies are provided to a combinator.
- EvalCtx
- Per-item policy evaluation context.
- Eval
Trace - A tree of
PolicyEvalResultnodes capturing every policy decision made during an access evaluation. - Evaluation
Session - Request-scoped fact loading and caching state.
- Fact
Provenance - A record that a policy consulted a fact while reaching its decision.
- Fact
Registry - Reusable fact-source registry for creating request-scoped sessions.
- Fact
Registry Builder - Builder for declaring fact sources once at application setup.
- Filter
Error - An authorization batch containing at least one indeterminate decision.
- Grant
Result - A grant policy’s authority-bearing result. Its audit tree is read-only.
- Lookup
Authorized Page - One page of authorized resources, paired with the next candidate-page cursor.
- Lookup
Page - One page of enumerated candidate IDs.
- NotPolicy
- Inverts a grant policy, preserving uncertainty.
- OrPolicy
- Grants when any child policy grants.
- Permission
Checker - Grant and veto policies for one authorization domain.
- Policy
Batch Item - A borrowed resource passed to batch policy evaluators.
- Policy
Builder - Fluent builder for synchronous predicate policies.
- Rbac
Policy - Role-based access control policy.
- Rebac
Policy - Relationship-based access control backed by request-scoped fact loading.
- Relationship
Query - Canonical fact key for relationship (ReBAC) lookups.
- Security
Rule Metadata - Metadata describing the security rule associated with a
crate::Policy. - Unconditional
- Builder state: no predicate has been supplied yet.
- Veto
Result - A veto policy’s authority-bearing result. It cannot grant access.
Enums§
- Access
Error - A non-grant decision with its full audit evidence.
- Access
Evaluation - The complete result of a permission evaluation. Contains both the final decision and a detailed trace for debugging.
- Combine
Op - The type of boolean combining operation a policy might represent.
- Decision
- The decision carried by one node of a
PolicyEvalResulttree. - Fact
Load Error - Error raised while loading a fact.
- Fact
Load Error Kind - Stable, value-erased classification of a
FactLoadError. - Fact
Load Result - Result of loading one fact.
- Fact
Outcome - How a fact load that informed a policy decision resolved.
- Lookup
Authorized Error - Failure modes for lookup, hydration, and authorization of one candidate page.
- Policy
Eval Result - The inspectable audit representation of a policy decision or aggregate.
Traits§
- Builder
State - Marker trait for the
PolicyBuildertype-state parameter. - FactKey
- A typed fact key that can be loaded through an
crate::EvaluationSession. - Fact
Source - A batched source for one fact key type.
- Hydrator
- Resolves enumerated IDs to caller-owned resources.
- Lookup
Source - Enumerates a candidate superset of resources for a subject.
- Policy
- A grant policy for one
PolicyDomain. - Policy
Domain - Names the four Rust types that make up one authorization domain.
- Policy
Ext - Boolean composition for grant policies only.
- Policy
Result - Results accepted by recording contexts. Implementations are sealed.
- Veto
Policy - A policy that can veto or pass, but cannot grant.
- Veto
Policy Ext - Composition of veto predicates. Passing never grants permission.