RBAC/ABAC policy engine for Python with policy sets, condition DSL, and hot reload
Project description
RBACX
Universal RBAC/ABAC/ReBAC policy engine for Python with a clean core, policy sets, a compact condition DSL (including time ops), and adapters for common web frameworks.
Features
- Algorithms:
deny-overrides(default),permit-overrides,first-applicable - Conditions:
==,!=,<,<=,>,>=,contains,in,hasAll,hasAny,startsWith,endsWith,before,after,between - Role shorthand:
"roles": ["admin", "editor"]on any rule — sugar forhasAnyonsubject.roles - Explainability:
decision,reason,rule_id/last_rule_id,obligations - Policy sets: combine multiple policies with the same algorithms
- Hot reload: file/HTTP/S3 sources with ETag and a polling manager
- Types & lint: mypy-friendly core, Ruff-ready
- AI Policy Authoring (
rbacx[ai]): generate, refine, and explain policies using any OpenAI-compatible LLM
Installation
pip install rbacx
Optional extras include adapters, metrics, YAML support, and AI policy authoring.
Quickstart
from rbacx import Action, Context, Guard, Subject, Resource
policy = {
"algorithm": "deny-overrides",
"rules": [
{
"id": "doc_read",
"effect": "permit",
"actions": ["read"],
"resource": {"type": "doc", "attrs": {"visibility": ["public", "internal"]}},
"roles": ["reader", "admin"], # shorthand: hasAny on subject.roles
# Or using the explicit condition syntax:
# "condition": {"hasAny": [ {"attr": "subject.roles"}, ["reader", "admin"] ]},
"obligations": [ {"type": "require_mfa"} ]
},
{"id": "doc_deny_archived", "effect": "deny", "actions": ["*"],
"resource": {"type": "doc", "attrs": {"archived": True}}}
],
}
g = Guard(policy)
d = g.evaluate_sync(
subject=Subject(id="u1", roles=["reader"]),
action=Action("read"),
resource=Resource(type="doc", id="42", attrs={"visibility": "public"}),
context=Context(attrs={"mfa": True}),
)
assert d.allowed is True
assert d.effect == "permit"
print(d.reason, d.rule_id) # "matched", "doc_read"
Decision schema
decision:"permit"or"deny"reason: one of"matched","explicit_deny","action_mismatch","condition_mismatch","condition_type_mismatch","resource_mismatch","no_match","obligation_failed"rule_idandlast_rule_id(both included for compatibility;last_rule_idis the matched rule id)policy_id(present for policy sets;Nonefor single policies)obligations: list passed to the obligation checker (if a permit was gated)- (optional)
challenge: present when an authentication/step-up is required (e.g., for MFA); may be used to return401with the appropriate challenge header
Policy sets
Default algorithm is:
from rbacx.core.policyset import decide as decide_policyset
policyset = {"algorithm":"deny-overrides", "policies":[ policy, {"rules":[...]} ]}
result = decide_policyset(policyset, {"subject":..., "action":"read", "resource":...})
If you want to test, try this:
from rbacx.core.policyset import decide as decide_policyset
# example set of policies
policyset = {
"algorithm": "deny-overrides",
"policies": [
{"rules": [
{"id": "allow_public_read", "effect": "permit", "actions": ["read"],
"resource": {"type": "doc", "attrs": {"visibility": ["public"]}}}
]},
{"rules": [
{"id": "deny_archived", "effect": "deny", "actions": ["*"],
"resource": {"type": "doc", "attrs": {"archived": True}}}
]},
],
}
# example request
req = {
"subject": {"id": "u1", "roles": ["reader"]},
"action": "read",
"resource": {"type": "doc", "id": "42", "attrs": {"visibility": "public", "archived": False}}, # can try: would be `deny` if archived `True`
"context": {},
}
res = decide_policyset(policyset, req)
print(res.get("decision", res)) # -> "permit"
Hot reloading
Default algorithm is:
from rbacx import Guard, HotReloader
from rbacx.store import FilePolicySource
guard = Guard(policy={})
mgr = HotReloader(guard, FilePolicySource("policy.json"), initial_load=...)
mgr.check_and_reload() # initial load
mgr.start(10) # background polling thread
If you want to test, try this:
⚠️ Important: this example creates a file on disk. You also can rewrite it with TempFile (tempfile.NamedTemporaryFile)
import json
import time
from rbacx import Action, Context, Guard, HotReloader, Resource, Subject
from rbacx.store import FilePolicySource
# create a tiny policy file next to the script
policy_path = "policy.json"
json.dump({
"algorithm": "deny-overrides",
"rules": [{
"id": "allow_public_read", "effect": "permit", "actions": ["read"],
"resource": {"type": "doc", "attrs": {"visibility": ["public"]}}
}]
}, open(policy_path, "w", encoding="utf-8"))
guard = Guard({})
mgr = HotReloader(guard, FilePolicySource(policy_path), initial_load=True)
mgr.check_and_reload() # initial load
print(guard.evaluate_sync(
subject=Subject(id="u1", roles=["reader"]),
action=Action("read"),
resource=Resource(type="doc", id="1", attrs={"visibility": "public"}),
context=Context(),
).effect) # -> "permit"
# update policy and wait 3 second for reload
json.dump({
"algorithm": "deny-overrides",
"rules": [{"id": "deny_all", "effect": "deny", "actions": ["*"], "resource": {"type": "doc"}}]
}, open(policy_path, "w", encoding="utf-8"))
mgr.start(3) # starting polling
time.sleep(3)
print(guard.evaluate_sync(
subject=Subject(id="u1", roles=["reader"]),
action=Action("read"),
resource=Resource(type="doc", id="1", attrs={"visibility": "public"}),
context=Context(),
).effect) # -> "deny"
Quick links
Packaging
- We ship
py.typedso type checkers pick up annotations. - Standard PyPA flow:
python -m build, thentwine uploadto (Test)PyPI.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rbacx-1.16.0.tar.gz.
File metadata
- Download URL: rbacx-1.16.0.tar.gz
- Upload date:
- Size: 89.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
354f6d9c7322501381e29c2c8f53e7f905865d937b0ee4b26c529ccc51cdac3a
|
|
| MD5 |
1d1ec482b021ccd7a8f61e7ba5d40c83
|
|
| BLAKE2b-256 |
871da71ee41c3411bd86158a07d8e77d15a8a42e0e6584942657d7e26b4e8586
|
Provenance
The following attestation bundles were made for rbacx-1.16.0.tar.gz:
Publisher:
release.yml on Cheater121/rbacx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rbacx-1.16.0.tar.gz -
Subject digest:
354f6d9c7322501381e29c2c8f53e7f905865d937b0ee4b26c529ccc51cdac3a - Sigstore transparency entry: 1235973294
- Sigstore integration time:
-
Permalink:
Cheater121/rbacx@6a87379dcccad4b0fe90e8a39c503daa1fc33dcb -
Branch / Tag:
refs/tags/v1.16.0 - Owner: https://github.com/Cheater121
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6a87379dcccad4b0fe90e8a39c503daa1fc33dcb -
Trigger Event:
release
-
Statement type:
File details
Details for the file rbacx-1.16.0-py3-none-any.whl.
File metadata
- Download URL: rbacx-1.16.0-py3-none-any.whl
- Upload date:
- Size: 111.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
21488d8c30fa754677282ccbc09d97352a07993963d49133456ba03534a9ff8d
|
|
| MD5 |
94eeb9719ac6563defa06dce4fe13d43
|
|
| BLAKE2b-256 |
6e7393edc98c331a8434fc8eed378d47ad105c80ce810f952a412b65a6836397
|
Provenance
The following attestation bundles were made for rbacx-1.16.0-py3-none-any.whl:
Publisher:
release.yml on Cheater121/rbacx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rbacx-1.16.0-py3-none-any.whl -
Subject digest:
21488d8c30fa754677282ccbc09d97352a07993963d49133456ba03534a9ff8d - Sigstore transparency entry: 1235973340
- Sigstore integration time:
-
Permalink:
Cheater121/rbacx@6a87379dcccad4b0fe90e8a39c503daa1fc33dcb -
Branch / Tag:
refs/tags/v1.16.0 - Owner: https://github.com/Cheater121
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6a87379dcccad4b0fe90e8a39c503daa1fc33dcb -
Trigger Event:
release
-
Statement type: