casbin
casbin copied to clipboard
How can I design a matcher that looks for membership in two (g & g2) role_definitions?
Want to prioritize this issue? Try:
What's your scenario? What do you want to achieve?
I would like to model a set of roles and entitlements to match when a user is granted both the role & entitlement offering a specific permission.
In other words:
- to model a single set of roles that cover everything the app offers
- to model a set of entitlements to group the permissions needed for various features
- and only grant a user access if they have the appropriate role and have the respective entitlement (i.e. have purchased the relevant feature)
e.g. the "admin" role offers access to everything in the app, but an actual admin user only has access to premium_feature if they have the premium_entitlement as well
I'm pretty sure I am just using the matchers wrong... I can us g to determine match for role access & g2 to match for entitlement access successfully, however I am having trouble putting those two matchers together to match only when both evaluate to true
Your model:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
g2 = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = (g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act) && (g2(r.sub, p.sub) && r.obj == p.obj && r.act == p.act)
# match for roles
#m = (g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act)
# match for entitlements
#m = (g2(r.sub, p.sub) && r.obj == p.obj && r.act == p.act)
Your policy:
p, basic_entitlement, basic_feature, read
p, basic_entitlement, basic_feature, write
p, premium_entitlement, premium_feature, read
p, premium_entitlement, premium_feature, write
p, admin_role, basic_feature, read
p, admin_role, basic_feature, write
p, admin_role, premium_feature, read
p, admin_role, premium_feature, write
p, auditor_role, basic_feature, read
p, auditor_role, premium_feature, read
# alice is an admin that has purchased the premium feature
g, alice, admin_role
g2, alice, basic_entitlement
g2, alice, premium_entitlement
# bob is an admin on a free account
g, bob, admin_role
g2, bob, basic_entitlement
# charlie is an auditor that has purchased the premium feature
g, charlie, auditor_role
g2, charlie, basic_entitlement
g2, charlie, premium_entitlement
# derek is an auditor on a free account
g, derek, auditor_role
g2, derek, basic_entitlement
Your request(s):
alice, basic_feature, read ---> false (expected: true)
alice, basic_feature, write ---> false (expected: true)
alice, premium_feature, read ---> false (expected: true)
alice, premium_feature, write ---> false (expected: true)
bob, basic_feature, read ---> false (expected: true)
bob, basic_feature, write ---> false (expected: true)
bob, premium_feature, read ---> false (expected: false)
bob, premium_feature, write ---> false (expected: false)
charlie, basic_feature, read ---> false (expected: true)
charlie, basic_feature, write ---> false (expected: false)
charlie, premium_feature, read ---> false (expected: true)
charlie, premium_feature, write ---> false (expected: false)
derek, basic_feature, read ---> false (expected: true)
derek, basic_feature, write ---> false (expected: false)
derek, premium_feature, read ---> false (expected: false)
derek, premium_feature, write ---> false (expected: false)
@tangyang9464 @JalinWang
Sadly that's not so simple as you expect.
The problem is that enforcer would try to find only one role from g or g2 which meet reqirement, not one from g and one from g2.
Your model is more likely a RBAC with Domains model, since there is no relationship between roles and entitlements. Currently casbin is not good at implementing multi-positive check. And I didn't find a way to perfectly implement your model.
Anyway, here is a working solution.
model:
[request_definition]
r = sub, dom, obj, act
[policy_definition]
p = sub, dom, obj, act
[role_definition]
g = _, _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub, r.dom) && r.obj == p.obj && r.act == p.act
policy:
p, basic, entitlement, basic_feature, read
p, basic, entitlement, basic_feature, write
p, premium, entitlement, premium_feature, read
p, premium, entitlement, premium_feature, write
p, admin, role, basic_feature, read
p, admin, role, basic_feature, write
p, admin, role, premium_feature, read
p, admin, role, premium_feature, write
p, auditor, role, basic_feature, read
p, auditor, role, premium_feature, read
# alice is an admin that has purchased the premium feature
g, alice, admin, role
g, alice, basic, entitlement
g, alice, premium, entitlement
# bob is an admin on a free account
g, bob, admin, role
g, bob, basic, entitlement
# charlie is an auditor that has purchased the premium feature
g, charlie, auditor, role
g, charlie, basic, entitlement
g, charlie, premium, entitlement
# derek is an auditor on a free account
g, derek, auditor, role
g, derek, basic, entitlement
code:
func Test_1384(t *testing.T) {
e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")
testMultiDomainEnforce := func(t *testing.T, e *Enforcer, sub, obj, act string, res bool) {
t.Helper()
res1, err := e.Enforce(sub, "role", obj, act)
if err != nil {
t.Errorf("Enforce Error: %s", err)
return
}
res2, err := e.Enforce(sub, "entitlement", obj, act)
if err != nil {
t.Errorf("Enforce Error: %s", err)
return
}
if res != res1 && res2 {
t.Errorf("%s, %s, %s: %t %t, supposed to be %t", sub, obj, act, res1, res2, res)
}
// Pass
}
testMultiDomainEnforce(t, e, "alice", "basic_feature", "read", true)
testMultiDomainEnforce(t, e, "alice", "basic_feature", "write", true)
testMultiDomainEnforce(t, e, "alice", "premium_feature", "read", true)
testMultiDomainEnforce(t, e, "alice", "premium_feature", "write", true)
testMultiDomainEnforce(t, e, "bob", "basic_feature", "read", true)
testMultiDomainEnforce(t, e, "bob", "basic_feature", "write", true)
testMultiDomainEnforce(t, e, "bob", "premium_feature", "read", false)
testMultiDomainEnforce(t, e, "bob", "premium_feature", "write", false)
testMultiDomainEnforce(t, e, "charlie", "basic_feature", "read", true)
testMultiDomainEnforce(t, e, "charlie", "basic_feature", "write", false)
testMultiDomainEnforce(t, e, "charlie", "premium_feature", "read", true)
testMultiDomainEnforce(t, e, "charlie", "premium_feature", "write", false)
testMultiDomainEnforce(t, e, "derek", "basic_feature", "read", true)
testMultiDomainEnforce(t, e, "derek", "basic_feature", "write", false)
testMultiDomainEnforce(t, e, "derek", "premium_feature", "read", false)
testMultiDomainEnforce(t, e, "derek", "premium_feature", "write", false)
}
appreciated!