Skip to main content

Priority Model

Casbin can apply priority to policies: when multiple policies match, the one with highest priority wins (and can produce allow or deny).

Implicit priority (policy order)

Policy order defines priority: first in the list has highest priority. Use this effect:

[policy_effect]
e = priority(p.eft) || deny

Explicit priority (numeric field)

Add a priority field to the policy; lower number = higher priority. Non-numeric values are treated as lowest priority. See casbin#550.

Custom priority field

The default priority field name is priority. For another name (e.g. customized_priority), call e.SetFieldIndex("p", constant.PriorityIndex, index) after init and reload policy. Example: TestCustomizedFieldIndex.

Example with explicit priority in policy:

[policy_definition]
p = customized_priority, sub, obj, act, eft

Golang code example:

e, _ := NewEnforcer("./example/priority_model_explicit_customized.conf",
"./example/priority_policy_explicit_customized.csv")
// Due to the customized priority token, the enforcer fails to handle the priority.
ok, err := e.Enforce("bob", "data2", "read") // the result will be `true, nil`
// Set PriorityIndex and reload
e.SetFieldIndex("p", constant.PriorityIndex, 0)
err := e.LoadPolicy()
if err != nil {
log.Fatalf("LoadPolicy: %v", err)
}
ok, err := e.Enforce("bob", "data2", "read") // the result will be `false, nil`

:::

Only AddPolicy and AddPolicies respect explicit priority. Do not change the priority field via UpdatePolicy.

Full model:

[request_definition]
r = sub, obj, act

[policy_definition]
p = priority, sub, obj, act, eft

[role_definition]
g = _, _

[policy_effect]
e = priority(p.eft) || deny

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

policy.csv

p, 10, data1_deny_group, data1, read, deny
p, 10, data1_deny_group, data1, write, deny
p, 10, data2_allow_group, data2, read, allow
p, 10, data2_allow_group, data2, write, allow


p, 1, alice, data1, write, allow
p, 1, alice, data1, read, allow
p, 1, bob, data2, read, deny

g, bob, data2_allow_group
g, alice, data1_deny_group

request:

alice, data1, write --> true // because `p, 1, alice, data1, write, allow` has the highest priority
bob, data2, read --> false
bob, data2, write --> true // because bob has the role of `data2_allow_group` which has the right to write data2, and there's no deny policy with higher priority

Subject priority (role/user hierarchy)

Use subjectPriority(p.eft) so that priority comes from the role hierarchy: leaves (e.g. end users) have higher priority than inner roles. The hierarchy must be trees, not graphs; if a user has multiple roles, they should be at the same depth in each tree. Same-level ties are broken by policy order. See casbin#833, casbin#831.

Model:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act, eft

[role_definition]
g = _, _

[policy_effect]
e = subjectPriority(p.eft) || deny

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

policy.csv

p, root, data1, read, deny
p, admin, data1, read, deny

p, editor, data1, read, deny
p, subscriber, data1, read, deny

p, jane, data1, read, allow
p, alice, data1, read, allow

g, admin, root

g, editor, admin
g, subscriber, admin

g, jane, editor
g, alice, subscriber

Request:

jane, data1, read --> true // because jane is at the bottom, her priority is higher than that of editor, admin, and root
alice, data1, read --> true

The role hierarchy structure:

role: root
└─ role: admin
├─ role editor
│ └─ user: jane

└─ role: subscriber
└─ user: alice

Automatic priority assignment:

role: root                 # auto priority: 30
└─ role: admin # auto priority: 20
├─ role: editor # auto priority: 10
└─ role: subscriber # auto priority: 10