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:
Steven Masley
2025-01-03 11:06:47 -06:00
committed by GitHub
co-authored by dependabot[bot]
parent 4495db8ce8
commit 813270d63a
7 changed files with 127 additions and 107 deletions
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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
View File
@@ -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
}
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"