Policy Evaluation
The ABAC policy engine evaluates access requests through a multi-layer optimization pipeline. Each layer short-circuits on success, so most requests never reach the sequential evaluation fallback.
Basic evaluation
#![allow(unused)]
fn main() {
use abac_rs::{AbacPolicy, AbacRequest, AttributeType};
let mut policy = AbacPolicy::new();
// ... add rules ...
let mut request = AbacRequest::new();
request.add_attribute("user", AttributeType::String("alice".into()), vec![]);
request.add_attribute("resource", AttributeType::String("db-01".into()), vec![]);
request.add_attribute("action", AttributeType::String("read".into()), vec![]);
let decision = policy.evaluate(&request);
if decision.is_allowed() {
// Grant access
}
}
Optimization pipeline
- Layer 0: Constant result (~15 ns) – universal allow/deny detection
- Layer 1: Bitmap deny index – u64 bitmask intersection when universal allow exists
- Layer 2: LRU cache (sub-μs) – memoization of recent evaluations
- Layer 3: AHash – 2-3x faster than SipHash for non-cryptographic use
- Layer 4: Compiled evaluator – pre-extracted attributes + array indexing
- Layer 5: Composite index (O(log n)) – candidate selection fallback
- Layer 6: Deny-only indexing – skip allow rules when universal allow exists
The compiled evaluator (Layer 4) is automatically detected and applied when all rules use consistent dimensions. It pre-extracts request attributes once (3 HashMap lookups) instead of per-candidate (600+ lookups), providing a 62x speedup at large scales.
Deny-only indexing: When a universal allow rule exists, only deny rules are indexed. This is valid because:
- Deny rules are checked first (deny-override semantics)
- If no deny matches, universal allow guarantees Allow decision
- Individual allow rules become irrelevant
This optimization reduces index size and evaluation time dramatically in typical deployments where most rules are allow and only a few are deny exceptions.
See ABAC Performance for detailed layer breakdown.
Lazy index building
Indexes are built on first evaluation after rule changes, not during
add_rule(). This eliminates O(n²) overhead during batch rule loading.
Deny-override semantics
- Check all deny rules – if any match, return
Deny - Check all allow rules – if any match, return
Allow - Default to
Denyif no rules match
Denials always take precedence over allows.