mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
chore: bump github.com/open-policy-agent/opa from 0.70.0 to 1.0.0 (#16013)
Opting into rego v1. Rego v1 requires `if` for all rule statements. This PR updates the dependencies and the rego policy itself. Golang imports upgraded for opa/rego --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
dependabot[bot]
parent
4495db8ce8
commit
813270d63a
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
"github.com/ammario/tlru"
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
"github.com/open-policy-agent/opa/v1/rego"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
"github.com/open-policy-agent/opa/topdown"
|
||||
"github.com/open-policy-agent/opa/v1/rego"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/httpapi/httpapiconstraints"
|
||||
|
||||
+84
-67
@@ -1,5 +1,7 @@
|
||||
package authz
|
||||
import future.keywords
|
||||
|
||||
import rego.v1
|
||||
|
||||
# A great playground: https://play.openpolicyagent.org/
|
||||
# Helpful cli commands to debug.
|
||||
# opa eval --format=pretty 'data.authz.allow' -d policy.rego -i input.json
|
||||
@@ -29,67 +31,74 @@ import future.keywords
|
||||
|
||||
# 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 {
|
||||
b
|
||||
flipped = false
|
||||
bool_flip(b) := flipped if {
|
||||
b
|
||||
flipped = false
|
||||
}
|
||||
|
||||
bool_flip(b) = flipped {
|
||||
not b
|
||||
flipped = true
|
||||
bool_flip(b) := flipped if {
|
||||
not b
|
||||
flipped = true
|
||||
}
|
||||
|
||||
# number is a quick way to get a set of {true, false} and convert it to
|
||||
# -1: {false, true} or {false}
|
||||
# 0: {}
|
||||
# 1: {true}
|
||||
number(set) = c {
|
||||
number(set) := c if {
|
||||
count(set) == 0
|
||||
c := 0
|
||||
c := 0
|
||||
}
|
||||
|
||||
number(set) = c {
|
||||
number(set) := c if {
|
||||
false in set
|
||||
c := -1
|
||||
c := -1
|
||||
}
|
||||
|
||||
number(set) = c {
|
||||
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
|
||||
default site := 0
|
||||
|
||||
site := site_allow(input.subject.roles)
|
||||
|
||||
default scope_site := 0
|
||||
|
||||
scope_site := site_allow([input.subject.scope])
|
||||
|
||||
site_allow(roles) := num {
|
||||
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
|
||||
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
|
||||
@@ -102,10 +111,10 @@ scope_org := org_allow([input.scope])
|
||||
# The reason we calculate this for all orgs, and not just the input.object.org_owner
|
||||
# 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 {
|
||||
allow_set := { id: num |
|
||||
org_allow_set(roles) := allow_set if {
|
||||
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, "*"]
|
||||
@@ -115,7 +124,7 @@ org_allow_set(roles) := allow_set {
|
||||
}
|
||||
}
|
||||
|
||||
org_allow(roles) := num {
|
||||
org_allow(roles) := num if {
|
||||
# If the object has "any_org" set to true, then use the other
|
||||
# org_allow block.
|
||||
not input.object.any_org
|
||||
@@ -135,78 +144,82 @@ org_allow(roles) := num {
|
||||
# This is useful for UI elements when we want to conclude, "Can the user create
|
||||
# a new template in any organization?"
|
||||
# It is easier than iterating over every organization the user is apart of.
|
||||
org_allow(roles) := num {
|
||||
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 {
|
||||
org_mem if {
|
||||
not input.object.any_org
|
||||
input.object.org_owner != ""
|
||||
input.object.org_owner in org_members
|
||||
}
|
||||
|
||||
org_mem := true {
|
||||
org_mem if {
|
||||
input.object.any_org
|
||||
count(org_members) > 0
|
||||
}
|
||||
|
||||
org_ok {
|
||||
org_ok if {
|
||||
org_mem
|
||||
}
|
||||
|
||||
# If the object has no organization, then the user is also considered part of
|
||||
# the non-existent org.
|
||||
org_ok {
|
||||
org_ok if {
|
||||
input.object.org_owner == ""
|
||||
not input.object.any_org
|
||||
}
|
||||
|
||||
# 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
|
||||
default user := 0
|
||||
|
||||
user := user_allow(input.subject.roles)
|
||||
|
||||
default user_scope := 0
|
||||
|
||||
scope_user := user_allow([input.scope])
|
||||
|
||||
user_allow(roles) := num {
|
||||
input.object.owner != ""
|
||||
input.subject.id = input.object.owner
|
||||
allow := { x |
|
||||
perm := roles[_].user[_]
|
||||
perm.action in [input.action, "*"]
|
||||
user_allow(roles) := num if {
|
||||
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.
|
||||
# If the list is '*', then all resources are allowed.
|
||||
scope_allow_list {
|
||||
scope_allow_list if {
|
||||
"*" in input.subject.scope.allow_list
|
||||
}
|
||||
|
||||
scope_allow_list {
|
||||
scope_allow_list if {
|
||||
# If the wildcard is listed in the allow_list, we do not care about the
|
||||
# object.id. This line is included to prevent partial compilations from
|
||||
# ever needing to include the object.id.
|
||||
@@ -226,39 +239,41 @@ scope_allow_list {
|
||||
# Allow query:
|
||||
# data.authz.role_allow = true data.authz.scope_allow = true
|
||||
|
||||
role_allow {
|
||||
role_allow if {
|
||||
site = 1
|
||||
}
|
||||
|
||||
role_allow {
|
||||
role_allow if {
|
||||
not site = -1
|
||||
org = 1
|
||||
}
|
||||
|
||||
role_allow {
|
||||
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
|
||||
user = 1
|
||||
}
|
||||
|
||||
scope_allow {
|
||||
scope_allow if {
|
||||
scope_allow_list
|
||||
scope_site = 1
|
||||
}
|
||||
|
||||
scope_allow {
|
||||
scope_allow if {
|
||||
scope_allow_list
|
||||
not scope_site = -1
|
||||
scope_org = 1
|
||||
}
|
||||
|
||||
scope_allow {
|
||||
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
|
||||
@@ -266,26 +281,28 @@ scope_allow {
|
||||
}
|
||||
|
||||
# ACL for users
|
||||
acl_allow {
|
||||
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
|
||||
}
|
||||
|
||||
# ACL for groups
|
||||
acl_allow {
|
||||
acl_allow if {
|
||||
# If there is no organization owner, the object cannot be owned by an
|
||||
# org_scoped team.
|
||||
org_mem
|
||||
group := input.subject.groups[_]
|
||||
perms := input.object.acl_group_list[group]
|
||||
|
||||
# Either the input action or wildcard
|
||||
[input.action, "*"][_] in perms
|
||||
}
|
||||
|
||||
# ACL for 'all_users' special group
|
||||
acl_allow {
|
||||
acl_allow if {
|
||||
org_mem
|
||||
perms := input.object.acl_group_list[input.object.org_owner]
|
||||
[input.action, "*"][_] in perms
|
||||
@@ -296,13 +313,13 @@ acl_allow {
|
||||
# The role or the ACL must allow the action. Scopes can be used to limit,
|
||||
# so scope_allow must always be true.
|
||||
|
||||
allow {
|
||||
allow if {
|
||||
role_allow
|
||||
scope_allow
|
||||
}
|
||||
|
||||
# ACL list must also have the scope_allow to pass
|
||||
allow {
|
||||
allow if {
|
||||
acl_allow
|
||||
scope_allow
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
"github.com/open-policy-agent/opa/v1/rego"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/rbac/regosql/sqltypes"
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
"github.com/open-policy-agent/opa/v1/rego"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/rbac/regosql"
|
||||
|
||||
Reference in New Issue
Block a user