Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

See also