ABAC — Attribute-Based Access Control
Define fine-grained JSON condition trees that are evaluated against namespaced request attributes at decision time.
Edit this page on GitHubABAC policies let you express access rules that depend on arbitrary attributes of the request — user department, resource owner, time-of-day, or any other context — rather than just roles. The ABAC engine sits alongside Casbin RBAC; both are evaluated on every POST /api/v1/check call.
Policy model#
An ABAC policy has these fields:
| Field | Type | Description |
|---|---|---|
name | string | Human-readable policy name |
resource | string | Resource key this policy applies to (e.g. invoice:read) |
effect | string | "allow" or "deny" |
priority | int | Higher priority wins when multiple policies match (default 0) |
enabled | bool | Toggle without deleting |
format | string | "json" (default) or "casbin" |
rule_data | JSON | The condition tree (see below) |
tenant_id | string | Auto-set from the caller's JWT domain claim |
Condition tree format#
Rules are expressed as nested AND / OR / CONDITION nodes. Attributes are referenced in dot-notation namespaces.
{
"type": "AND",
"conditions": [
{
"type": "CONDITION",
"attribute": "user.department",
"operator": "eq",
"value": "Finance"
},
{
"type": "OR",
"conditions": [
{
"type": "CONDITION",
"attribute": "resource.classification",
"operator": "in",
"value": ["internal", "public"]
},
{
"type": "CONDITION",
"attribute": "user.clearance_level",
"operator": "gte",
"value": 3
}
]
}
]
}Supported operators#
| Operator | Description |
|---|---|
eq | Equals |
neq | Not equals |
gt / gte | Greater than / greater-or-equal |
lt / lte | Less than / less-or-equal |
in | Value is in an array |
contains | String contains substring |
startsWith | String starts with prefix |
API — Create a policy#
curl -X POST https://api.permix.dev/api/v1/abac/policies \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Finance read invoices",
"resource": "invoice:read",
"effect": "allow",
"priority": 10,
"rule_data": {
"type": "CONDITION",
"attribute": "user.department",
"operator": "eq",
"value": "Finance"
}
}'Response 201:
{
"id": "3f7a1b2c-...",
"tenant_id": "tenant_prod_42",
"name": "Finance read invoices",
"resource": "invoice:read",
"effect": "allow",
"format": "json",
"priority": 10,
"enabled": true,
"created_by": "user_123",
"created_at": "2026-05-10T08:00:00Z",
"rule_data": { ... }
}API — Check access (combined RBAC + ABAC)#
curl -X POST https://api.permix.dev/api/v1/check \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject": "user_123",
"resource": "invoice:read",
"action": "read",
"domain": "tenant_prod_42",
"attributes": {
"user": {
"department": "Finance",
"clearance_level": 4
},
"resource": {
"classification": "internal"
}
}
}'Attributes are grouped by namespace (e.g. user, resource, time). The engine flattens them to dot-notation internally (user.department).
List and filter policies#
# List all policies for a resource
GET /api/v1/abac/policies?resource=invoice:read
# Filter by effect
GET /api/v1/abac/policies?effect=denyUpdate a policy#
curl -X PUT /api/v1/abac/policies/{id} \
-H "Authorization: Bearer $TOKEN" \
-d '{
"enabled": false,
"priority": 5
}'Immutable resource field
The resource field cannot be changed after creation. To move a policy to a different resource, delete it and create a new one.
Priority and conflict resolution#
When multiple ABAC policies match a request, the engine applies them in descending priority order. A deny effect always overrides an allow at the same priority. If no policy matches, the engine falls through to the Casbin RBAC result.
| Priority | Effect | Decision |
|---|---|---|
| 100 | deny | ❌ Denied immediately |
| 50 | allow | ✅ Allowed (if no deny at higher priority) |
| 10 | allow | Reached only if p=50 didn't match |
Performance & caching#
The ABAC service uses an in-process policy cache per tenant (ABACPolicyCache) invalidated on every write. P95 evaluation latency is under 2ms for up to 500 active policies per tenant.