chore: update rego policy to respect user and organisation scopes (#19741)

Prior to this change, user and org scopes were always rejected
This commit is contained in:
Steven Masley
2025-09-09 05:50:08 -05:00
committed by GitHub
parent f3b152bb06
commit d527f91f47
2 changed files with 60 additions and 2 deletions
+51
View File
@@ -1110,6 +1110,57 @@ func TestAuthorizeScope(t *testing.T) {
{resource: ResourceOrganization.WithID(defOrg)},
}),
)
// Test setting a scope on the org and the user level
// This is a bit of a contrived example that would not exist in practice.
// It combines a specific organization scope with a user scope to verify
// that both are applied.
// The test uses the `Owner` role, so by default the user can do everything.
user = Subject{
ID: "me",
Roles: Roles{
must(RoleByName(RoleOwner())),
// TODO: There is a __bug__ in the policy.rego. If the user is not a
// member of the organization, the org_scope fails. This happens because
// the org_allow_set uses "org_members".
// This is odd behavior, as without this membership role, the test for
// the workspace fails. Maybe scopes should just assume the user
// is a member.
must(RoleByName(ScopedRoleOrgMember(defOrg))),
},
Scope: Scope{
Role: Role{
Identifier: RoleIdentifier{
Name: "org-and-user-scope",
OrganizationID: defOrg,
},
DisplayName: "OrgAndUserScope",
Site: nil,
Org: map[string][]Permission{
defOrg.String(): Permissions(map[string][]policy.Action{
ResourceWorkspace.Type: {policy.ActionRead},
}),
},
User: Permissions(map[string][]policy.Action{
ResourceUser.Type: {policy.ActionRead},
}),
},
AllowIDList: []string{policy.WildcardSymbol},
},
}
testAuthorize(t, "OrgAndUserScope", user,
// Allowed by scope:
[]authTestCase{
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner(user.ID), allow: true, actions: []policy.Action{policy.ActionRead}},
{resource: ResourceUser.WithOwner(user.ID), allow: true, actions: []policy.Action{policy.ActionRead}},
},
// Not allowed by scope:
[]authTestCase{
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner(user.ID), allow: false, actions: []policy.Action{policy.ActionCreate}},
{resource: ResourceUser.WithOwner(user.ID), allow: false, actions: []policy.Action{policy.ActionUpdate}},
},
)
}
// cases applies a given function to all test cases. This makes generalities easier to create.
+9 -2
View File
@@ -106,6 +106,13 @@ site_allow(roles) := num if {
# -------------------
# org_members is the list of organizations the actor is apart of.
# TODO: Should there be an org_members for the scope too? Without it,
# the membership is determined by the user's roles, not their scope permissions.
# So if an owner (who is not an org member) has an org scope, that org scope
# will fail to return '1'. Since we assume all non members return '-1' for org
# level permissions.
# Adding a second org_members set might affect the partial evaluation.
# This is being left until org scopes are used.
org_members := {orgID |
input.subject.roles[_].org[orgID]
}
@@ -116,7 +123,7 @@ default org := 0
org := org_allow(input.subject.roles)
default scope_org := 0
scope_org := org_allow([input.scope])
scope_org := org_allow([input.subject.scope])
# org_allow_set is a helper function that iterates over all orgs that the actor
# is a member of. For each organization it sets the numerical allow value
@@ -221,7 +228,7 @@ default user := 0
user := user_allow(input.subject.roles)
default scope_user := 0
scope_user := user_allow([input.scope])
scope_user := user_allow([input.subject.scope])
user_allow(roles) := num if {
input.object.owner != ""