ABAC Rules
ABAC rules define access policies across arbitrary attribute dimensions. Each rule specifies matching criteria on one or more dimensions, a rule type (Allow/Deny), and an enabled status.
Creating rules
#![allow(unused)]
fn main() {
use abac_rs::{AbacRule, AttributeType};
let rule = AbacRule::builder("allow_engineers_prod_read")
// User must be in engineers group
.dimension_values("user", vec![
AttributeType::String("group:engineers".into()),
])
// Resource must be a production database
.dimension_values("resource", vec![
AttributeType::String("prod:db-01".into()),
AttributeType::String("prod:db-02".into()),
])
// Action must be read
.dimension_values("action", vec![
AttributeType::String("read".into()),
])
.enabled(true)
.build();
}
Rule types
- Allow (default) – grants access if all dimensions match
- Deny – blocks access if all dimensions match (takes precedence)
#![allow(unused)]
fn main() {
// Allow rule (default)
let rule = AbacRule::builder("allow_rule")
// ... dimensions ...
.enabled(true)
.build();
// Deny rule
let rule = AbacRule::builder("deny_rule")
.deny()
// ... dimensions ...
.enabled(true)
.build();
}
Wildcard matching
Use AttributeValue::All to match any value for a dimension:
#![allow(unused)]
fn main() {
// Allow any user to read public data
let rule = AbacRule::builder("public_read")
.dimension_all("user")
.dimension_values("resource", vec![
AttributeType::String("public:data".into()),
])
.dimension_values("action", vec![
AttributeType::String("read".into()),
])
.enabled(true)
.build();
}
#![allow(unused)]
fn main() {
// Any user can read public data
let rule = AbacRule::builder("public_read")
.dimension_all("user")
.dimension_values("resource", vec![
AttributeType::String("public:data".into()),
])
.dimension_values("action", vec![
AttributeType::String("read".into()),
])
.enabled(true)
.build();
}
Multi-dimensional matching
A rule matches a request only when all dimensions match. This is an AND relationship – every dimension requirement must be satisfied.
Rule identifiers
Rules can carry an optional, stable id – separate from the internal name
key – for correlating rules with external systems such as a CRDT or
database row. The evaluation engine ignores id; it round-trips through
serde and shows up in RuleMatch results from explained evaluation.
#![allow(unused)]
fn main() {
use abac_rs::AbacRule;
let rule = AbacRule::builder("allow_read")
.id("018f2e6a-26f1-7c3e-9b0a-000000000001")
.dimension_all("user")
.enabled(true)
.build();
assert_eq!(rule.id.as_deref(), Some("018f2e6a-26f1-7c3e-9b0a-000000000001"));
}
Temporal rules
Wrap a rule in TemporalAbacRule to restrict it to a validity window using
valid_from / valid_until timestamps (milliseconds since the Unix epoch).
evaluate_at() (and evaluate(), which uses the current time) only
considers a temporal rule while it falls within that window:
#![allow(unused)]
fn main() {
use abac_rs::{AbacPolicy, AbacRule, TemporalAbacRule};
let rule = AbacRule::builder("contractor_access")
.enabled(true)
.build();
// Valid for the next 24 hours
let one_day = 24 * 60 * 60 * 1000;
let temporal = TemporalAbacRule::valid_for_duration(rule, one_day).unwrap();
let mut policy = AbacPolicy::new();
policy.add_temporal_rule(temporal).unwrap();
}
TemporalAbacRule also provides valid_until() and valid_from() for
open-ended windows, and is_valid_at() / is_currently_valid() to query a
rule’s window directly. When a temporal deny rule is active it overrides
every other rule; an active temporal allow rule can override an
otherwise-denied regular decision.