Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
opa fmt rego.policy
  • Loading branch information
Emyrk committed Jan 2, 2025
commit 84d2eed81b0dc8d3e2db26f374cb9f9fb6ce41e0
92 changes: 54 additions & 38 deletions coderd/rbac/policy.rego
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package authz

import rego.v1
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opting into rego.v1


# A great playground: https://play.openpolicyagent.org/
Expand Down Expand Up @@ -31,13 +32,13 @@ import rego.v1
# bool_flip lets you assign a value to an inverted bool.
# You cannot do 'x := !false', but you can do 'x := bool_flip(false)'
bool_flip(b) = flipped if {
b
flipped = false
b
flipped = false
}

bool_flip(b) = flipped if {
not b
flipped = true
not b
flipped = true
}

# number is a quick way to get a set of {true, false} and convert it to
Expand All @@ -46,51 +47,58 @@ bool_flip(b) = flipped if {
# 1: {true}
number(set) = c if {
count(set) == 0
c := 0
c := 0
}

number(set) = c if {
false in set
c := -1
c := -1
}

number(set) = c if {
not false in set
set[_]
c := 1
c := 1
}

# site, org, and user rules are all similar. Each rule should return a number
# from [-1, 1]. The number corresponds to "negative", "abstain", and "positive"
# for the given level. See the 'allow' rules for how these numbers are used.
default site = 0

site := site_allow(input.subject.roles)

default scope_site := 0

scope_site := site_allow([input.subject.scope])

site_allow(roles) := num if {
# allow is a set of boolean values without duplicates.
allow := { x |
allow := {x |
# Iterate over all site permissions in all roles
perm := roles[_].site[_]
perm.action in [input.action, "*"]
perm := roles[_].site[_]
perm.action in [input.action, "*"]
perm.resource_type in [input.object.type, "*"]

# x is either 'true' or 'false' if a matching permission exists.
x := bool_flip(perm.negate)
}
num := number(allow)
x := bool_flip(perm.negate)
}
num := number(allow)
}

# org_members is the list of organizations the actor is apart of.
org_members := { orgID |
org_members := {orgID |
input.subject.roles[_].org[orgID]
}

# org is the same as 'site' except we need to iterate over each organization
# that the actor is a member of.
default org = 0

org := org_allow(input.subject.roles)

default scope_org := 0

scope_org := org_allow([input.scope])

# org_allow_set is a helper function that iterates over all orgs that the actor
Expand All @@ -104,9 +112,9 @@ scope_org := org_allow([input.scope])
# is that sometimes the input.object.org_owner is unknown. In those cases
# we have a list of org_ids that can we use in a SQL 'WHERE' clause.
org_allow_set(roles) := allow_set if {
allow_set := { id: num |
allow_set := {id: num |
id := org_members[_]
set := { x |
set := {x |
perm := roles[_].org[id][_]
perm.action in [input.action, "*"]
perm.resource_type in [input.object.type, "*"]
Expand Down Expand Up @@ -140,33 +148,34 @@ org_allow(roles) := num if {
input.object.any_org # if this is false, this code block is not used
allow := org_allow_set(roles)


# allow is a map of {"<org_id>": <number>}. We only care about values
# that are 1, and ignore the rest.
num := number([
keep |
# for every value in the mapping
value := allow[_]
# only keep values > 0.
# 1 = allow, 0 = abstain, -1 = deny
# We only need 1 explicit allow to allow the action.
# deny's and abstains are intentionally ignored.
value > 0
# result set is a set of [true,false,...]
# which "number()" will convert to a number.
keep := true
keep |
# for every value in the mapping
value := allow[_]

# only keep values > 0.
# 1 = allow, 0 = abstain, -1 = deny
# We only need 1 explicit allow to allow the action.
# deny's and abstains are intentionally ignored.
value > 0

# result set is a set of [true,false,...]
# which "number()" will convert to a number.
keep := true
])
}

# 'org_mem' is set to true if the user is an org member
# If 'any_org' is set to true, use the other block to determine org membership.
org_mem := true if {
org_mem if {
not input.object.any_org
input.object.org_owner != ""
input.object.org_owner in org_members
}

org_mem := true if {
org_mem if {
input.object.any_org
count(org_members) > 0
}
Expand All @@ -185,20 +194,23 @@ org_ok if {
# User is the same as the site, except it only applies if the user owns the object and
# the user is apart of the org (if the object has an org).
default user = 0

user := user_allow(input.subject.roles)

default user_scope := 0

scope_user := user_allow([input.scope])

user_allow(roles) := num if {
input.object.owner != ""
input.subject.id = input.object.owner
allow := { x |
perm := roles[_].user[_]
perm.action in [input.action, "*"]
input.object.owner != ""
input.subject.id = input.object.owner
allow := {x |
perm := roles[_].user[_]
perm.action in [input.action, "*"]
perm.resource_type in [input.object.type, "*"]
x := bool_flip(perm.negate)
}
num := number(allow)
x := bool_flip(perm.negate)
}
num := number(allow)
}

# Scope allow_list is a list of resource IDs explicitly allowed by the scope.
Expand Down Expand Up @@ -239,6 +251,7 @@ role_allow if {
role_allow if {
not site = -1
not org = -1

# If we are not a member of an org, and the object has an org, then we are
# not authorized. This is an "implied -1" for not being in the org.
org_ok
Expand All @@ -260,6 +273,7 @@ scope_allow if {
scope_allow_list
not scope_site = -1
not scope_org = -1

# If we are not a member of an org, and the object has an org, then we are
# not authorized. This is an "implied -1" for not being in the org.
org_ok
Expand All @@ -270,6 +284,7 @@ scope_allow if {
acl_allow if {
# Should you have to be a member of the org too?
perms := input.object.acl_user_list[input.subject.id]

# Either the input action or wildcard
[input.action, "*"][_] in perms
}
Expand All @@ -281,6 +296,7 @@ acl_allow if {
org_mem
group := input.subject.groups[_]
perms := input.object.acl_group_list[group]

# Either the input action or wildcard
[input.action, "*"][_] in perms
}
Expand Down
Loading