RBACX¶
RBAC + ABAC authorization for Python with a clean architecture, declarative JSON policies, framework adapters, and optional hot reloading.
Inspiration & Philosophy¶
RBACX is inspired by: - OWASP-aligned practices like deny by default and least privilege. - The recurring pain for Python developers switching between projects with inconsistent authorization stacks. - The XACML model (policies, rules, effects, combining algorithms, obligations), simplified and made friendlier for web developers using JSON and a Python-first API.
Our philosophy: security should be understandable and ergonomic for developers so that correct authorization becomes the path of least resistance.
What you get¶
- RBAC + ABAC in a single engine (role checks + attribute conditions).
- Declarative policies (JSON/YAML or Python dict) with compact operators, including time operators.
- Secure defaults:
deny-overrides
by default; alsopermit-overrides
,first-applicable
. - Role hierarchy via resolvers (
StaticRoleResolver
and custom implementations). - Explainable decisions (
allowed
,effect
,reason
,rule_id
) and obligations (e.g., require MFA). - Hot reload from file/HTTP/S3 using ETag checks.
- Adapters for FastAPI/Starlette, Flask, Django/DRF, Litestar.
- Observability: logging hooks and metrics sinks (Prometheus/OpenTelemetry).
- CLI & linting:
rbacx validate
to validate policies. - Test coverage around 82% across core decision paths.
Quick start¶
1) Install¶
pip install rbacx
2) Define a minimal policy (JSON)¶
{
"algorithm": "deny-overrides",
"rules": [
{
"id": "allow_read_public",
"target": { "resource": { "type": "document" }, "action": "read" },
"condition": { "==": [ { "attr": "resource.visibility" }, "public" ] },
"effect": "permit",
"obligations": [{ "type": "require_mfa", "when": true }]
}
]
}
3) Evaluate in Python¶
from rbacx import Guard
policy = {...} # load JSON as a dict
g = Guard(policy)
decision = g.evaluate_sync(
subject={"id": "u1", "roles": ["reader"]},
action="read",
resource={"type": "document", "visibility": "public"},
context={"mfa": True},
)
assert decision.allowed is True
print(decision.effect, decision.reason, decision.rule_id, decision.obligations)
4) (Optional) Use a web adapter¶
FastAPI
from fastapi import FastAPI
from rbacx.adapters.fastapi import require_access
from rbacx import Guard
app = FastAPI()
policy = {...} # reuse the policy from above or define one here
guard = Guard(policy)
def build_env(request):
# map request -> (subject, action, resource, context)
return {
"subject": {"id": request.headers.get("X-User"), "roles": ["reader"]},
"action": request.method.lower(),
"resource": {"type": "document", "path": request.url.path},
"context": {"mfa": True},
}
@app.get("/docs")
@require_access(guard, build_env)
def docs():
return {"ok": True}
Documentation map¶
Start here - Quickstart - Why choose RBACX - Highlights
Core concepts - Architecture - Security model - Explainability (reasons & obligations) - Role hierarchy - Audit mode
Policy - Policy authoring - Policy catalog - Time operators - Policy loading (hot reload) - Policy stores - HTTP mapping
Integration - Web adapters - Adapters (API) - Try examples
Observability - Metrics - OpenTelemetry - Logging - Diagnostics - Observability stack
Performance & operations - Performance guide - Benchmarks - CI - Migration (RBAC→ABAC)
Reference - Public API