Skip to content

Commit b3536bc

Browse files
committed
chore: Update rego to be partial execution friendly
1 parent f28cd15 commit b3536bc

File tree

1 file changed

+72
-99
lines changed

1 file changed

+72
-99
lines changed

coderd/rbac/policy.rego

Lines changed: 72 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package authz
2-
import future.keywords.in
3-
import future.keywords.every
2+
import future.keywords
3+
# Helpful cli commands to debug.
4+
# opa eval --format=pretty 'data.authz.allow = true' -d policy.rego -i input.json
5+
# opa eval --partial --format=pretty 'data.authz.allow = true' -d policy.rego --unknowns input.object.owner --unknowns input.object.org_owner -i input.json
6+
47

5-
# A great playground: https://play.openpolicyagent.org/
6-
# TODO: Add debug instructions to do in the cli. Running really short on time, the
7-
# playground is sufficient for now imo. In the future we can provide a tidy bash
8-
# script for running this against predefined input.
98

109
# bool_flip lets you assign a value to an inverted bool.
1110
# You cannot do 'x := !false', but you can do 'x := bool_flip(false)'
@@ -19,121 +18,95 @@ bool_flip(b) = flipped {
1918
flipped = true
2019
}
2120

22-
# perms_grant returns a set of boolean values {true, false}.
23-
# True means a positive permission in the set, false is a negative permission.
24-
# It will only return `bool_flip(perm.negate)` for permissions that affect a given
25-
# resource_type, and action.
26-
# The empty set is returned if no relevant permissions are found.
27-
perms_grant(permissions) = grants {
28-
# If there are no permissions, this value is the empty set {}.
29-
grants := { x |
30-
# All permissions ...
31-
perm := permissions[_]
32-
# Such that the permission action, and type matches
33-
perm.action in [input.action, "*"]
34-
perm.resource_type in [input.object.type, "*"]
35-
x := bool_flip(perm.negate)
36-
}
21+
number(set) = c {
22+
count(set) == 0
23+
c := 0
3724
}
3825

39-
# Site & User are both very simple. We default both to the empty set '{}'. If no permissions are present, then the
40-
# result is the default value.
41-
default site = {}
42-
site = grant {
43-
# Boolean set for all site wide permissions.
44-
grant = { v | # Use set comprehension to remove duplicate values
45-
# For each role, grab the site permission.
46-
# Find the grants on this permission list.
47-
v = perms_grant(input.subject.roles[_].site)[_]
48-
}
26+
number(set) = c {
27+
false in set
28+
c := -1
4929
}
5030

51-
default user = {}
52-
user = grant {
53-
# Only apply user permissions if the user owns the resource
54-
input.object.owner != ""
55-
input.object.owner == input.subject.id
56-
grant = { v |
57-
# For each role, grab the user permissions.
58-
# Find the grants on this permission list.
59-
v = perms_grant(input.subject.roles[_].user)[_]
60-
}
31+
number(set) = c {
32+
not false in set
33+
set[_]
34+
c := 1
6135
}
6236

63-
# Organizations are more complex. If the user has no roles that specifically indicate the org_id of the object,
64-
# then we want to block the action. This is because that means the user is not a member of the org.
65-
# A non-member cannot access any org resources.
6637

67-
# org_member returns the set of permissions associated with a user if the user is a member of the
68-
# organization
69-
org_member = grant {
70-
input.object.org_owner != ""
71-
grant = { v |
72-
v = perms_grant(input.subject.roles[_].org[input.object.org_owner])[_]
38+
default site = 0
39+
site := num {
40+
# relevent are all the permissions that affect the given unknown object
41+
allow := { x |
42+
perm := input.subject.roles[_].site[_]
43+
perm.action in [input.action, "*"]
44+
perm.resource_type in [input.object.type, "*"]
45+
x := bool_flip(perm.negate)
7346
}
47+
num := number(allow)
7448
}
7549

76-
# If a user is not part of an organization, 'org_non_member' is set to true
77-
org_non_member {
78-
input.object.org_owner != ""
79-
# Identify if the user is in the org
80-
roles := input.subject.roles
81-
every role in roles {
82-
not role.org[input.object.org_owner]
83-
}
50+
org_members := { orgID |
51+
input.subject.roles[_].org[orgID]
8452
}
8553

86-
# org is two rules that equate to the following
87-
# if org_non_member { return {false} }
88-
# else { org_member }
89-
#
90-
# It is important both rules cannot be true, as the `org` rules cannot produce multiple outputs.
91-
default org = {}
92-
org = set {
93-
# We have to do !org_non_member because rego rules must evaluate to 'true'
94-
# to have a value set.
95-
# So we do "not not-org-member" which means "subject is in org"
96-
not org_non_member
97-
set = org_member
54+
default org = 0
55+
org := num {
56+
orgPerms := { id: num |
57+
id := org_members[_]
58+
set := { x |
59+
perm := input.subject.roles[_].org[id][_]
60+
perm.action in [input.action, "*"]
61+
perm.resource_type in [input.object.type, "*"]
62+
x := bool_flip(perm.negate)
63+
}
64+
num := number(set)
65+
}
66+
67+
num := orgPerms[input.object.org_owner]
9868
}
9969

100-
org = set {
101-
org_non_member
102-
set = {false}
70+
# 'org_mem' is set to true if the user is an org member
71+
# or if the object has no org.
72+
org_mem := true {
73+
input.object.org_owner != ""
74+
input.object.org_owner in org_members
10375
}
10476

105-
# The allow block is quite simple. Any set with `false` cascades down in levels.
106-
# Authorization looks for any `allow` statement that is true. Multiple can be true!
107-
# Note that the absence of `allow` means "unauthorized".
108-
# An explicit `"allow": true` is required.
109-
110-
# site allow
111-
allow {
112-
# No site wide deny
113-
not false in site
114-
# And all permissions are positive
115-
site[_]
77+
org_mem := true {
78+
input.object.org_owner == ""
11679
}
11780

118-
# OR
81+
default user = 0
82+
user := num {
83+
input.subject.id = input.object.owner
84+
# relevent are all the permissions that affect the given unknown object
85+
allow := { x |
86+
perm := input.subject.roles[_].user[_]
87+
perm.action in [input.action, "*"]
88+
perm.resource_type in [input.object.type, "*"]
89+
x := bool_flip(perm.negate)
90+
}
91+
num := number(allow)
92+
}
11993

120-
# org allow
94+
default allow = false
95+
# Site
12196
allow {
122-
# No site or org deny
123-
not false in site
124-
not false in org
125-
# And all permissions are positive
126-
org[_]
97+
site = 1
12798
}
12899

129-
# OR
100+
# Org
101+
allow {
102+
not site = -1
103+
org = 1
104+
}
130105

131-
# user allow
106+
# User
132107
allow {
133-
# No site, org, or user deny
134-
not false in site
135-
not false in org
136-
not false in user
137-
# And all permissions are positive
138-
user[_]
108+
not site = -1
109+
not org = -1
110+
org_mem
111+
user = 1
139112
}

0 commit comments

Comments
 (0)