apache / apache/casbin

[Question] Optimization for IAM-like policies

Open
#1,688 0 comments 1 reaction 2 assignees Claimed by @hsluoyz View on GitHub
enhancement
Dominant language
Go
Stars
20.4k
Forks
1.8k
Avg merge
5d 11h
Merged PRs (30d)
2

Description

Hello,
I'm trying to design an AWS IAM like system where we have a set of resources which you can "allow" or "deny" for certain roles and users.

My Initial was this:

Initial Option: Model

```toml
[request_definition]
r = sub, dom, obj, act

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

[role_definition]
g = _, _, _ # Roles permission
g2 = _, _ # Data group
g3 = _, _ # Action group

[policy_effect]
# Allow as soon as there one rule allowing
#e = some(where (p.eft == allow))
#Deny as soon as there is one rule denying
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))

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

Initial Option: Policy

```sh
# sub/perm, resource, action
p, perm:instance:list, /instance, READ, allow
p, perm:instance:delete, /instance/{name}, DELETE, allow
p, perm:instance:describe, /instance/{name}, READ, allow
p, perm:instance:create, /instance/{name}, POST, allow

p, perm:service:list, /service, READ, allow

p, perm:proxy:read, /proxy/{name}/*, READ, allow
p, perm:proxy:write, /proxy/{name}/*, WRITE, allow
p, perm:proxy:crashes, /proxy/{name}/crashes, READ, allow
p, block:proxy:crashes, /proxy/{name}/crashes, READ, deny

# Adding wild cards in the group does seem to add a lot of time when users grow
#
#g, role:instance:admin, perm:instance:*, *
#g, role:service:admin, perm:service:*, *
#g, role:proxy:admin, perm:proxy:*, *
#
g, role:instance:admin, perm:instance:list, *
g, role:instance:admin, perm:instance:delete, *
g, role:instance:admin, perm:instance:describe, *
g, role:instance:admin, perm:instance:create, *

g, role:service:admin, perm:service:list, *

g, role:proxy:admin, perm:proxy:read, *
g, role:proxy:admin, perm:proxy:write, *

g, role:instance:viewer, perm:instance:list, *
g, role:instance:viewer, perm:instance:describe, *

g, role:service:viewer, perm:service:list, *

g, role:proxy:viewer, perm:proxy:read, *
g, role:proxy:viewer, block:proxy:crashes, *

g, user:admin, role:instance:admin, *
g, user:admin, role:service:admin, *
g, user:admin, role:proxy:admin, *

g, user:alice, role:instance:admin, orgA
g, user:alice, role:service:admin, orgA
g, user:alice, role:proxy:admin, orgA

g, user:alice, role:instance:viewer, orgB
g, user:alice, role:service:viewer, orgB
g, user:alice, role:proxy:viewer, orgB

g, user:bob, role:instance:viewer, orgB
g, user:bob, role:service:viewer, orgB
g, user:bob, role:proxy:viewer, orgB

g, user:nancy, role:proxy:admin, orgA

g3, POST, WRITE
g3, PUT, WRITE
g3, PATCH, WRITE
g3, DELETE, WRITE

g3, OPTIONS, READ
g3, HEAD, READ
g3, GET, READ

g3, READ, ANY
g3, WRITE, ANY
```

I find it's not ideal because I would need to duplicate every `p` to have one for `deny` and one for `allow`.

So My second option is this:

Tentative Option; Model

```toml
[request_definition]
r = sub, dom, obj, act

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

[role_definition]
g = _, _, _ # Roles permission
g2 = _, _, _ # Data group
g3 = _, _ # Action group

[policy_effect]
# Allow as soon as there one rule allowing
#e = some(where (p.eft == allow))
#Deny as soon as there is one rule denying
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))

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

Tentative Option; Policy

```sh
#
p, role:instance:admin, perm:instance:list, allow, *
p, role:instance:admin, perm:instance:delete, allow, *
p, role:instance:admin, perm:instance:describe, allow, *
p, role:instance:admin, perm:instance:create, allow, *
p, role:service:admin, perm:service:list, allow, *
p, role:proxy:admin, perm:proxy:read, allow, *
p, role:proxy:admin, perm:proxy:write, allow, *
p, role:instance:viewer, perm:instance:list, allow, *
p, role:instance:viewer, perm:instance:describe, allow, *
p, role:service:viewer, perm:service:list, allow, *
p, role:proxy:viewer, perm:proxy:read, allow, *
p, role:proxy:viewer, perm:proxy:crashes, deny, *
# sub/perm, resource, action
g2, /instance, perm:instance:list, GET
g2, /instance/{name}, perm:instance:delete, DELETE
g2, /instance/{name}, perm:instance:describe, GET
g2, /instance/{name}, perm:instance:create, POST
g2, /service, perm:service:list, GET
g2, /proxy/{name}/*, perm:proxy:read, GET
g2, /proxy/{name}/*, perm:proxy:write, POST
g2, /proxy/{name}/crashes, perm:proxy:crashes, GET

g, user:admin, role:instance:admin, *
g, user:admin, role:service:admin, *
g, user:admin, role:proxy:admin, *

g, user:alice, role:instance:admin, orgA
g, user:alice, role:service:admin, orgA
g, user:alice, role:proxy:admin, orgA

g, user:alice, role:instance:viewer, orgB
g, user:alice, role:service:viewer, orgB
g, user:alice, role:proxy:viewer, orgB

g, user:bob, role:instance:viewer, orgB
g, user:bob, role:service:viewer, orgB
g, user:bob, role:proxy:viewer, orgB

g, user:nancy, role:proxy:admin, orgA
```

Both work, but the first one is significantly (at least 8 times) faster for a few thousand checks. Is there a way for me to optimize option 2 or am I better off going with my initial design?

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.