You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
boundary/internal/iam/repository_role_grant_test.go

1539 lines
47 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package iam
import (
"context"
"fmt"
"math/rand"
"strings"
"testing"
"time"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/hashicorp/boundary/internal/perms"
"github.com/hashicorp/boundary/internal/types/action"
"github.com/hashicorp/boundary/internal/types/resource"
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepository_AddRoleGrants(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
repo := TestRepo(t, conn, wrapper)
_, proj := TestScopes(t, repo)
role := TestRole(t, conn, proj.PublicId)
createGrantsFn := func() []string {
results := []string{}
for i := 0; i < 5; i++ {
g := fmt.Sprintf("ids=hc_%d;actions=*", i)
results = append(results, g)
}
return results
}
type args struct {
roleId string
roleVersion uint32
grants []string
opt []Option
}
tests := []struct {
name string
args args
wantErr bool
wantErrIs error
}{
{
name: "valid",
args: args{
roleId: role.PublicId,
roleVersion: 1,
grants: createGrantsFn(),
},
wantErr: false,
},
{
name: "no-grants",
args: args{
roleId: role.PublicId,
roleVersion: 2,
grants: nil,
},
wantErr: true,
},
{
name: "bad-version",
args: args{
roleId: role.PublicId,
roleVersion: 1000,
grants: createGrantsFn(),
},
wantErr: true,
},
{
name: "zero-version",
args: args{
roleId: role.PublicId,
roleVersion: 0,
grants: createGrantsFn(),
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
db.TestDeleteWhere(t, conn, func() any { rg := allocRoleGrant(); return &rg }(), "1=1")
got, err := repo.AddRoleGrants(context.Background(), tt.args.roleId, tt.args.roleVersion, tt.args.grants, tt.args.opt...)
if tt.wantErr {
require.Error(err)
if tt.wantErrIs != nil {
assert.Truef(errors.Is(err, tt.wantErrIs), "unexpected error %s", err.Error())
}
return
}
require.NoError(err)
gotRoleGrant := map[string]*RoleGrant{}
for _, r := range got {
gotRoleGrant[r.CanonicalGrant] = r
}
err = db.TestVerifyOplog(t, rw, role.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))
assert.NoError(err)
foundRoleGrants, err := repo.ListRoleGrants(context.Background(), role.PublicId)
require.NoError(err)
// Create a map of grants to check against
grantSet := make(map[string]bool, len(tt.args.grants))
for _, grant := range tt.args.grants {
grantSet[grant] = true
}
for _, r := range foundRoleGrants {
roleGrant := gotRoleGrant[r.CanonicalGrant]
assert.NotEmpty(roleGrant)
assert.Equal(roleGrant.GetRoleId(), r.GetRoleId())
assert.NotEmpty(grantSet[roleGrant.CanonicalGrant])
delete(grantSet, roleGrant.CanonicalGrant)
}
assert.Empty(grantSet)
})
}
}
func TestRepository_ListRoleGrants(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
const testLimit = 10
wrapper := db.TestWrapper(t)
repo := TestRepo(t, conn, wrapper, WithLimit(testLimit))
org, proj := TestScopes(t, repo)
type args struct {
withRoleId string
opt []Option
}
tests := []struct {
name string
createCnt int
createGrantScopeId string
args args
wantCnt int
wantErr bool
}{
{
name: "no-limit",
createCnt: repo.defaultLimit + 2,
createGrantScopeId: org.PublicId,
args: args{
opt: []Option{WithLimit(-1)},
},
wantCnt: repo.defaultLimit + 2,
wantErr: false,
},
{
name: "no-limit-proj-group",
createCnt: repo.defaultLimit + 2,
createGrantScopeId: proj.PublicId,
args: args{
opt: []Option{WithLimit(-1)},
},
wantCnt: repo.defaultLimit + 2,
wantErr: false,
},
{
name: "default-limit",
createCnt: repo.defaultLimit + 2,
createGrantScopeId: org.PublicId,
wantCnt: repo.defaultLimit,
wantErr: false,
},
{
name: "custom-limit",
createCnt: repo.defaultLimit + 2,
createGrantScopeId: org.PublicId,
args: args{
opt: []Option{WithLimit(3)},
},
wantCnt: 3,
wantErr: false,
},
{
name: "bad-role-id",
createCnt: 2,
createGrantScopeId: org.PublicId,
args: args{
withRoleId: "bad-id",
},
wantCnt: 0,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
db.TestDeleteWhere(t, conn, func() any { r := allocRole(); return &r }(), "1=1")
role := TestRole(t, conn, tt.createGrantScopeId)
roleGrants := make([]string, 0, tt.createCnt)
for i := 0; i < tt.createCnt; i++ {
roleGrants = append(roleGrants, fmt.Sprintf("ids=h_%d;actions=*", i))
}
testRoles, err := repo.AddRoleGrants(context.Background(), role.PublicId, role.Version, roleGrants, tt.args.opt...)
require.NoError(err)
assert.Equal(tt.createCnt, len(testRoles))
var roleId string
switch {
case tt.args.withRoleId != "":
roleId = tt.args.withRoleId
default:
roleId = role.PublicId
}
got, err := repo.ListRoleGrants(context.Background(), roleId, tt.args.opt...)
if tt.wantErr {
require.Error(err)
return
}
require.NoError(err)
assert.Equal(tt.wantCnt, len(got))
})
}
}
func TestRepository_DeleteRoleGrants(t *testing.T) {
t.Parallel()
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
repo := TestRepo(t, conn, wrapper)
org, _ := TestScopes(t, repo)
type args struct {
role *Role
roleIdOverride *string
roleVersionOverride *uint32
grantStringsOverride []string
createCnt int
deleteCnt int
opt []Option
}
tests := []struct {
name string
args args
wantRowsDeleted int
wantErr bool
wantIsErr errors.Code
}{
{
name: "valid",
args: args{
role: TestRole(t, conn, org.PublicId),
createCnt: 5,
deleteCnt: 5,
},
wantRowsDeleted: 5,
wantErr: false,
},
{
name: "valid-keeping-some",
args: args{
role: TestRole(t, conn, org.PublicId),
createCnt: 5,
deleteCnt: 3,
},
wantRowsDeleted: 3,
wantErr: false,
},
{
name: "no-deletes",
args: args{
role: TestRole(t, conn, org.PublicId),
createCnt: 5,
},
wantRowsDeleted: 0,
wantErr: true,
wantIsErr: errors.InvalidParameter,
},
{
name: "not-found",
args: args{
role: TestRole(t, conn, org.PublicId),
roleIdOverride: func() *string { id := testId(t); return &id }(),
createCnt: 5,
deleteCnt: 5,
},
wantRowsDeleted: 0,
wantErr: true,
},
{
name: "missing-role-id",
args: args{
role: TestRole(t, conn, org.PublicId),
roleIdOverride: func() *string { id := ""; return &id }(),
createCnt: 5,
deleteCnt: 5,
},
wantRowsDeleted: 0,
wantErr: true,
wantIsErr: errors.InvalidParameter,
},
{
name: "invalid-grant-strings",
args: args{
role: TestRole(t, conn, org.PublicId),
grantStringsOverride: []string{"ids=s_87;actions=*"},
createCnt: 5,
deleteCnt: 3,
},
wantRowsDeleted: 2,
},
{
name: "zero-version",
args: args{
role: TestRole(t, conn, org.PublicId),
createCnt: 5,
deleteCnt: 5,
roleVersionOverride: func() *uint32 { v := uint32(0); return &v }(),
},
wantRowsDeleted: 0,
wantErr: true,
},
{
name: "bad-version",
args: args{
role: TestRole(t, conn, org.PublicId),
createCnt: 5,
deleteCnt: 5,
roleVersionOverride: func() *uint32 { v := uint32(1000); return &v }(),
},
wantRowsDeleted: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
db.TestDeleteWhere(t, conn, func() any { rg := allocRoleGrant(); return &rg }(), "1=1")
grants := make([]*RoleGrant, 0, tt.args.createCnt)
grantStrings := make([]string, 0, tt.args.createCnt)
for i := 0; i < tt.args.createCnt; i++ {
g, err := NewRoleGrant(ctx, tt.args.role.PublicId, fmt.Sprintf("actions=*;ids=s_%d", i), tt.args.opt...)
require.NoError(err)
grantStrings = append(grantStrings, g.RawGrant)
grants = append(grants, g)
}
roleGrants, err := repo.AddRoleGrants(context.Background(), tt.args.role.PublicId, 1, grantStrings, tt.args.opt...)
require.NoError(err)
assert.Equal(tt.args.createCnt, len(roleGrants))
deleteCanonicalGrants := make([]string, 0, tt.args.deleteCnt)
deleteGrants := make([]string, 0, tt.args.deleteCnt)
for i := 0; i < tt.args.deleteCnt; i++ {
deleteCanonicalGrants = append(deleteCanonicalGrants, grants[i].CanonicalGrant)
deleteGrants = append(deleteGrants, fmt.Sprintf("ids=s_%d;actions=*", i))
}
for i, override := range tt.args.grantStringsOverride {
deleteCanonicalGrants = deleteCanonicalGrants[1:]
deleteGrants[i] = override
}
var roleId string
switch {
case tt.args.roleIdOverride != nil:
roleId = *tt.args.roleIdOverride
default:
roleId = tt.args.role.PublicId
}
var roleVersion uint32
switch {
case tt.args.roleVersionOverride != nil:
roleVersion = *tt.args.roleVersionOverride
default:
roleVersion = 2
}
deletedRows, err := repo.DeleteRoleGrants(context.Background(), roleId, roleVersion, deleteGrants, tt.args.opt...)
if tt.wantErr {
assert.Error(err)
assert.Equal(0, deletedRows)
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "unexpected error %s", err.Error())
err = db.TestVerifyOplog(t, rw, tt.args.role.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
assert.Error(err)
assert.True(errors.IsNotFoundError(err))
return
}
require.NoError(err)
assert.Equal(tt.wantRowsDeleted, deletedRows)
roleGrants = []*RoleGrant{}
require.NoError(repo.reader.SearchWhere(context.Background(), &roleGrants, "role_id = ?", []any{roleId}))
found := map[string]bool{}
for _, rg := range roleGrants {
found[rg.CanonicalGrant] = true
}
for _, i := range deleteCanonicalGrants {
assert.False(found[i])
}
err = db.TestVerifyOplog(t, rw, tt.args.role.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
assert.NoError(err)
})
}
}
func TestRepository_SetRoleGrants_Randomize(t *testing.T) {
t.Parallel()
ctx := context.Background()
require := require.New(t)
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
repo := TestRepo(t, conn, wrapper)
org, _ := TestScopes(t, repo)
role := TestRole(t, conn, org.PublicId)
db.TestDeleteWhere(t, conn, func() any { i := allocRoleGrant(); return &i }(), "1=1")
type roleGrantWrapper struct {
grantString string
enabled bool
}
totalCnt := 30
grants := make([]*roleGrantWrapper, 0, totalCnt)
for i := 0; i < totalCnt; i++ {
g, err := NewRoleGrant(ctx, role.PublicId, fmt.Sprintf("ids=s_%d;actions=*", i))
require.NoError(err)
grants = append(grants, &roleGrantWrapper{
grantString: g.RawGrant,
})
}
// Each loop will set some number of role grants to enabled or disabled
// randomly and then validate after setting that what's set matches
var grantsToSet []string
var expected map[string]bool
for i := 1; i <= totalCnt; i++ {
grantsToSet = make([]string, 0, totalCnt)
expected = make(map[string]bool, totalCnt)
prng := rand.New(rand.NewSource(time.Now().UnixNano()))
for _, rgw := range grants {
rgw.enabled = prng.Int()%2 == 0
if rgw.enabled {
grantsToSet = append(grantsToSet, rgw.grantString)
expected[rgw.grantString] = true
}
}
// First time, run a couple of error conditions
if i == 1 {
_, _, err := repo.SetRoleGrants(ctx, "", 1, []string{})
require.Error(err)
_, _, err = repo.SetRoleGrants(ctx, role.PublicId, 1, nil)
require.Error(err)
}
_, _, err := repo.SetRoleGrants(ctx, role.PublicId, uint32(i), grantsToSet)
require.NoError(err)
roleGrants, err := repo.ListRoleGrants(ctx, role.PublicId)
require.NoError(err)
require.Equal(len(grantsToSet), len(roleGrants))
for _, rg := range roleGrants {
require.Contains(expected, rg.CanonicalGrant)
delete(expected, rg.CanonicalGrant)
}
require.Empty(expected)
}
// At the end, set to explicitly empty and make sure all are cleared out
_, _, err := repo.SetRoleGrants(context.Background(), role.PublicId, uint32(totalCnt+1), []string{})
require.NoError(err)
roleGrants, err := repo.ListRoleGrants(context.Background(), role.PublicId)
require.NoError(err)
require.Empty(roleGrants)
}
func TestRepository_SetRoleGrants_Parameters(t *testing.T) {
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
repo := TestRepo(t, conn, wrapper)
org, _ := TestScopes(t, repo)
role := TestRole(t, conn, org.PublicId)
type args struct {
roleId string
roleVersion uint32
grants []string
opt []Option
}
tests := []struct {
name string
args args
want []*RoleGrant
wantAffectedRows int
wantErr bool
}{
{
name: "missing-roleid",
args: args{
roleId: "",
roleVersion: 1,
grants: []string{"ids=s_1;actions=*"},
},
want: nil,
wantAffectedRows: 0,
wantErr: true,
},
{
name: "bad-roleid",
args: args{
roleId: "bad-roleid",
roleVersion: 1,
grants: []string{"ids=s_1;actions=*"},
},
want: nil,
wantAffectedRows: 0,
wantErr: true,
},
{
name: "nil-grants",
args: args{
roleId: role.PublicId,
roleVersion: 1,
grants: nil,
},
want: nil,
wantAffectedRows: 0,
wantErr: true,
},
{
name: "zero-version",
args: args{
roleId: role.PublicId,
roleVersion: 0,
grants: []string{"ids=s_1;actions=*"},
},
want: nil,
wantAffectedRows: 0,
wantErr: true,
},
{
name: "bad-version",
args: args{
roleId: role.PublicId,
roleVersion: 1000,
grants: []string{"ids=s_1;actions=*"},
},
want: nil,
wantAffectedRows: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
rg := allocRoleGrant()
db.TestDeleteWhere(t, conn, &rg, "1=1")
got, gotAffectedRows, err := repo.SetRoleGrants(context.Background(), tt.args.roleId, tt.args.roleVersion, tt.args.grants, tt.args.opt...)
if tt.wantErr {
require.Error(err)
} else {
require.NoError(err)
}
assert.Equal(tt.wantAffectedRows, gotAffectedRows)
assert.Equal(tt.want, got)
})
}
}
func TestGrantsForUser(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
repo := TestRepo(t, conn, wrap)
user := TestUser(t, repo, "global")
// Create a series of scopes with roles in each. We'll create two of each
// kind to ensure we're not just picking up the first role in each.
// The first org/project do not have any direct grants to the user. They
// contain roles but the user is not a principal.
noGrantOrg1, noGrantProj1 := TestScopes(
t,
repo,
WithSkipAdminRoleCreation(true),
WithSkipDefaultRoleCreation(true),
)
noGrantOrg1Role := TestRole(t, conn, noGrantOrg1.PublicId)
TestRoleGrant(t, conn, noGrantOrg1Role.PublicId, "ids=*;type=scope;actions=*")
noGrantProj1Role := TestRole(t, conn, noGrantProj1.PublicId)
TestRoleGrant(t, conn, noGrantProj1Role.PublicId, "ids=*;type=*;actions=*")
noGrantOrg2, noGrantProj2 := TestScopes(
t,
repo,
WithSkipAdminRoleCreation(true),
WithSkipDefaultRoleCreation(true),
)
noGrantOrg2Role := TestRole(t, conn, noGrantOrg2.PublicId)
TestRoleGrant(t, conn, noGrantOrg2Role.PublicId, "ids=*;type=scope;actions=*")
noGrantProj2Role := TestRole(t, conn, noGrantProj2.PublicId)
TestRoleGrant(t, conn, noGrantProj2Role.PublicId, "ids=*;type=*;actions=*")
// The second org/project set contains direct grants, but without
// inheritance. We create two roles in each project.
directGrantOrg1, directGrantProj1a := TestScopes(
t,
repo,
WithSkipAdminRoleCreation(true),
WithSkipDefaultRoleCreation(true),
)
directGrantProj1b := TestProject(
t,
repo,
directGrantOrg1.PublicId,
WithSkipAdminRoleCreation(true),
WithSkipDefaultRoleCreation(true),
)
directGrantOrg1Role := TestRole(t, conn, directGrantOrg1.PublicId)
TestUserRole(t, conn, directGrantOrg1Role.PublicId, user.PublicId)
directGrantOrg1RoleGrant1 := "ids=*;type=*;actions=*"
TestRoleGrant(t, conn, directGrantOrg1Role.PublicId, directGrantOrg1RoleGrant1)
directGrantOrg1RoleGrant2 := "ids=*;type=role;actions=list,read"
TestRoleGrant(t, conn, directGrantOrg1Role.PublicId, directGrantOrg1RoleGrant2)
directGrantProj1aRole := TestRole(t, conn, directGrantProj1a.PublicId)
TestUserRole(t, conn, directGrantProj1aRole.PublicId, user.PublicId)
directGrantProj1aRoleGrant := "ids=*;type=target;actions=authorize-session,read"
TestRoleGrant(t, conn, directGrantProj1aRole.PublicId, directGrantProj1aRoleGrant)
directGrantProj1bRole := TestRole(t, conn, directGrantProj1b.PublicId)
TestUserRole(t, conn, directGrantProj1bRole.PublicId, user.PublicId)
directGrantProj1bRoleGrant := "ids=*;type=session;actions=list,read"
TestRoleGrant(t, conn, directGrantProj1bRole.PublicId, directGrantProj1bRoleGrant)
directGrantOrg2, directGrantProj2a := TestScopes(
t,
repo,
WithSkipAdminRoleCreation(true),
WithSkipDefaultRoleCreation(true),
)
directGrantProj2b := TestProject(
t,
repo,
directGrantOrg2.PublicId,
WithSkipAdminRoleCreation(true),
WithSkipDefaultRoleCreation(true),
)
directGrantOrg2Role := TestRole(t, conn, directGrantOrg2.PublicId,
WithGrantScopeIds([]string{
globals.GrantScopeThis,
directGrantProj2a.PublicId,
}))
TestUserRole(t, conn, directGrantOrg2Role.PublicId, user.PublicId)
directGrantOrg2RoleGrant1 := "ids=*;type=user;actions=*"
TestRoleGrant(t, conn, directGrantOrg2Role.PublicId, directGrantOrg2RoleGrant1)
directGrantOrg2RoleGrant2 := "ids=*;type=group;actions=list,read"
TestRoleGrant(t, conn, directGrantOrg2Role.PublicId, directGrantOrg2RoleGrant2)
directGrantProj2aRole := TestRole(t, conn, directGrantProj2a.PublicId)
TestUserRole(t, conn, directGrantProj2aRole.PublicId, user.PublicId)
directGrantProj2aRoleGrant := "ids=hcst_abcd1234,hcst_1234abcd;actions=*"
TestRoleGrant(t, conn, directGrantProj2aRole.PublicId, directGrantProj2aRoleGrant)
directGrantProj2bRole := TestRole(t, conn, directGrantProj2b.PublicId)
TestUserRole(t, conn, directGrantProj2bRole.PublicId, user.PublicId)
directGrantProj2bRoleGrant := "ids=cs_abcd1234;actions=read,update"
TestRoleGrant(t, conn, directGrantProj2bRole.PublicId, directGrantProj2bRoleGrant)
// For the third set we create a couple of orgs/projects and then use
// globals.GrantScopeChildren.
childGrantOrg1, childGrantOrg1Proj := TestScopes(
t,
repo,
WithSkipAdminRoleCreation(true),
WithSkipDefaultRoleCreation(true),
)
childGrantOrg1Role := TestRole(t, conn, childGrantOrg1.PublicId,
WithGrantScopeIds([]string{
globals.GrantScopeChildren,
}))
TestUserRole(t, conn, childGrantOrg1Role.PublicId, user.PublicId)
childGrantOrg1RoleGrant := "ids=*;type=host-set;actions=add-hosts,remove-hosts"
TestRoleGrant(t, conn, childGrantOrg1Role.PublicId, childGrantOrg1RoleGrant)
childGrantOrg2, childGrantOrg2Proj := TestScopes(
t,
repo,
WithSkipAdminRoleCreation(true),
WithSkipDefaultRoleCreation(true),
)
childGrantOrg2Role := TestRole(t, conn, childGrantOrg2.PublicId,
WithGrantScopeIds([]string{
globals.GrantScopeChildren,
}))
TestUserRole(t, conn, childGrantOrg2Role.PublicId, user.PublicId)
childGrantOrg2RoleGrant1 := "ids=*;type=session;actions=cancel:self"
TestRoleGrant(t, conn, childGrantOrg2Role.PublicId, childGrantOrg2RoleGrant1)
childGrantOrg2RoleGrant2 := "ids=*;type=session;actions=read:self"
TestRoleGrant(t, conn, childGrantOrg2Role.PublicId, childGrantOrg2RoleGrant2)
// Finally, let's create some roles at global scope with children and
// descendants grants
childGrantGlobalRole := TestRole(t, conn, scope.Global.String(),
WithGrantScopeIds([]string{
globals.GrantScopeChildren,
}))
TestUserRole(t, conn, childGrantGlobalRole.PublicId, globals.AnyAuthenticatedUserId)
childGrantGlobalRoleGrant := "ids=*;type=account;actions=*"
TestRoleGrant(t, conn, childGrantGlobalRole.PublicId, childGrantGlobalRoleGrant)
descendantGrantGlobalRole := TestRole(t, conn, scope.Global.String(),
WithGrantScopeIds([]string{
globals.GrantScopeDescendants,
}))
TestUserRole(t, conn, descendantGrantGlobalRole.PublicId, globals.AnyAuthenticatedUserId)
descendantGrantGlobalRoleGrant := "ids=*;type=credential;actions=*"
TestRoleGrant(t, conn, descendantGrantGlobalRole.PublicId, descendantGrantGlobalRoleGrant)
t.Run("db-grants", func(t *testing.T) {
// Here we should see exactly what the DB has returned, before we do some
// local exploding of grants and grant scopes
expMultiGrantTuples := []multiGrantTuple{
// No grants from noOrg/noProj
// Direct org1/2:
{
RoleId: directGrantOrg1Role.PublicId,
RoleScopeId: directGrantOrg1.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeIds: globals.GrantScopeThis,
Grants: strings.Join([]string{directGrantOrg1RoleGrant1, directGrantOrg1RoleGrant2}, "^"),
},
{
RoleId: directGrantOrg2Role.PublicId,
RoleScopeId: directGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeIds: strings.Join([]string{globals.GrantScopeThis, directGrantProj2a.PublicId}, "^"),
Grants: strings.Join([]string{directGrantOrg2RoleGrant1, directGrantOrg2RoleGrant2}, "^"),
},
// Proj orgs 1/2:
{
RoleId: directGrantProj1aRole.PublicId,
RoleScopeId: directGrantProj1a.PublicId,
RoleParentScopeId: directGrantOrg1.PublicId,
GrantScopeIds: globals.GrantScopeThis,
Grants: directGrantProj1aRoleGrant,
},
{
RoleId: directGrantProj1bRole.PublicId,
RoleScopeId: directGrantProj1b.PublicId,
RoleParentScopeId: directGrantOrg1.PublicId,
GrantScopeIds: globals.GrantScopeThis,
Grants: directGrantProj1bRoleGrant,
},
{
RoleId: directGrantProj2aRole.PublicId,
RoleScopeId: directGrantProj2a.PublicId,
RoleParentScopeId: directGrantOrg2.PublicId,
GrantScopeIds: globals.GrantScopeThis,
Grants: directGrantProj2aRoleGrant,
},
{
RoleId: directGrantProj2bRole.PublicId,
RoleScopeId: directGrantProj2b.PublicId,
RoleParentScopeId: directGrantOrg2.PublicId,
GrantScopeIds: globals.GrantScopeThis,
Grants: directGrantProj2bRoleGrant,
},
// Child grants from orgs 1/2:
{
RoleId: childGrantOrg1Role.PublicId,
RoleScopeId: childGrantOrg1.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeIds: globals.GrantScopeChildren,
Grants: childGrantOrg1RoleGrant,
},
{
RoleId: childGrantOrg2Role.PublicId,
RoleScopeId: childGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeIds: globals.GrantScopeChildren,
Grants: strings.Join([]string{childGrantOrg2RoleGrant1, childGrantOrg2RoleGrant2}, "^"),
},
// Children of global and descendants of global
{
RoleId: descendantGrantGlobalRole.PublicId,
RoleScopeId: scope.Global.String(),
GrantScopeIds: globals.GrantScopeDescendants,
Grants: descendantGrantGlobalRoleGrant,
},
{
RoleId: childGrantGlobalRole.PublicId,
RoleScopeId: scope.Global.String(),
GrantScopeIds: globals.GrantScopeChildren,
Grants: childGrantGlobalRoleGrant,
},
}
for i, tuple := range expMultiGrantTuples {
tuple.testStableSort()
expMultiGrantTuples[i] = tuple
}
multiGrantTuplesCache := new([]multiGrantTuple)
_, err := repo.GrantsForUser(ctx, user.PublicId, withTestCacheMultiGrantTuples(multiGrantTuplesCache))
require.NoError(t, err)
// log.Println("multiGrantTuplesCache", pretty.Sprint(*multiGrantTuplesCache))
assert.ElementsMatch(t, *multiGrantTuplesCache, expMultiGrantTuples)
})
t.Run("exploded-grants", func(t *testing.T) {
// We expect to see:
//
// * No grants from noOrg/noProj
// * Grants from direct orgs/projs:
// * directGrantOrg1/directGrantOrg2 on org and respective projects (6 grants total per org)
// * directGrantProj on respective projects (4 grants total)
expGrantTuples := []perms.GrantTuple{
// No grants from noOrg/noProj
// Grants from direct org1 to org1/proj1a/proj1b:
{
RoleId: directGrantOrg1Role.PublicId,
RoleScopeId: directGrantOrg1.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantOrg1.PublicId,
Grant: directGrantOrg1RoleGrant1,
},
{
RoleId: directGrantOrg1Role.PublicId,
RoleScopeId: directGrantOrg1.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantOrg1.PublicId,
Grant: directGrantOrg1RoleGrant2,
},
// Grants from direct org 1 proj 1a:
{
RoleId: directGrantProj1aRole.PublicId,
RoleScopeId: directGrantProj1a.PublicId,
RoleParentScopeId: directGrantOrg1.PublicId,
GrantScopeId: directGrantProj1a.PublicId,
Grant: directGrantProj1aRoleGrant,
},
// Grant from direct org 1 proj 1 b:
{
RoleId: directGrantProj1bRole.PublicId,
RoleScopeId: directGrantProj1b.PublicId,
RoleParentScopeId: directGrantOrg1.PublicId,
GrantScopeId: directGrantProj1b.PublicId,
Grant: directGrantProj1bRoleGrant,
},
// Grants from direct org2 to org2/proj2a/proj2b:
{
RoleId: directGrantOrg2Role.PublicId,
RoleScopeId: directGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantOrg2.PublicId,
Grant: directGrantOrg2RoleGrant1,
},
{
RoleId: directGrantOrg2Role.PublicId,
RoleScopeId: directGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantProj2a.PublicId,
Grant: directGrantOrg2RoleGrant1,
},
{
RoleId: directGrantOrg2Role.PublicId,
RoleScopeId: directGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantOrg2.PublicId,
Grant: directGrantOrg2RoleGrant2,
},
{
RoleId: directGrantOrg2Role.PublicId,
RoleScopeId: directGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantProj2a.PublicId,
Grant: directGrantOrg2RoleGrant2,
},
// Grants from direct org 2 proj 2a:
{
RoleId: directGrantProj2aRole.PublicId,
RoleScopeId: directGrantProj2a.PublicId,
RoleParentScopeId: directGrantOrg2.PublicId,
GrantScopeId: directGrantProj2a.PublicId,
Grant: directGrantProj2aRoleGrant,
},
// Grant from direct org 2 proj 2 b:
{
RoleId: directGrantProj2bRole.PublicId,
RoleScopeId: directGrantProj2b.PublicId,
RoleParentScopeId: directGrantOrg2.PublicId,
GrantScopeId: directGrantProj2b.PublicId,
Grant: directGrantProj2bRoleGrant,
},
// Child grants from child org1 to proj1a/proj1b:
{
RoleId: childGrantOrg1Role.PublicId,
RoleScopeId: childGrantOrg1.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: globals.GrantScopeChildren,
Grant: childGrantOrg1RoleGrant,
},
// Child grants from child org2 to proj2a/proj2b:
{
RoleId: childGrantOrg2Role.PublicId,
RoleScopeId: childGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: globals.GrantScopeChildren,
Grant: childGrantOrg2RoleGrant1,
},
{
RoleId: childGrantOrg2Role.PublicId,
RoleScopeId: childGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: globals.GrantScopeChildren,
Grant: childGrantOrg2RoleGrant2,
},
// Grants from global to every org:
{
RoleId: childGrantGlobalRole.PublicId,
RoleScopeId: scope.Global.String(),
GrantScopeId: globals.GrantScopeChildren,
Grant: childGrantGlobalRoleGrant,
},
// Grants from global to every org and project:
{
RoleId: descendantGrantGlobalRole.PublicId,
RoleScopeId: scope.Global.String(),
GrantScopeId: globals.GrantScopeDescendants,
Grant: descendantGrantGlobalRoleGrant,
},
}
multiGrantTuplesCache := new([]multiGrantTuple)
grantTuples, err := repo.GrantsForUser(ctx, user.PublicId, withTestCacheMultiGrantTuples(multiGrantTuplesCache))
require.NoError(t, err)
assert.ElementsMatch(t, grantTuples, expGrantTuples)
})
t.Run("acl-grants", func(t *testing.T) {
grantTuples, err := repo.GrantsForUser(ctx, user.PublicId)
require.NoError(t, err)
grants := make([]perms.Grant, 0, len(grantTuples))
for _, gt := range grantTuples {
grant, err := perms.Parse(ctx, gt)
require.NoError(t, err)
grants = append(grants, grant)
}
acl := perms.NewACL(grants...)
t.Run("descendant-grants", func(t *testing.T) {
descendantGrants := acl.DescendantsGrants()
expDescendantGrants := []perms.AclGrant{
{
RoleScopeId: scope.Global.String(),
GrantScopeId: globals.GrantScopeDescendants,
Id: "*",
Type: resource.Credential,
ActionSet: perms.ActionSet{action.All: true},
},
}
assert.ElementsMatch(t, descendantGrants, expDescendantGrants)
})
t.Run("child-grants", func(t *testing.T) {
childrenGrants := acl.ChildrenScopeGrantMap()
expChildrenGrants := map[string][]perms.AclGrant{
childGrantOrg1.PublicId: {
{
RoleScopeId: childGrantOrg1.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: globals.GrantScopeChildren,
Id: "*",
Type: resource.HostSet,
ActionSet: perms.ActionSet{action.AddHosts: true, action.RemoveHosts: true},
},
},
childGrantOrg2.PublicId: {
{
RoleScopeId: childGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: globals.GrantScopeChildren,
Id: "*",
Type: resource.Session,
ActionSet: perms.ActionSet{action.CancelSelf: true},
},
{
RoleScopeId: childGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: globals.GrantScopeChildren,
Id: "*",
Type: resource.Session,
ActionSet: perms.ActionSet{action.ReadSelf: true},
},
},
scope.Global.String(): {
{
RoleScopeId: scope.Global.String(),
GrantScopeId: globals.GrantScopeChildren,
Id: "*",
Type: resource.Account,
ActionSet: perms.ActionSet{action.All: true},
},
},
}
assert.Len(t, childrenGrants, len(expChildrenGrants))
for k, v := range childrenGrants {
assert.ElementsMatch(t, v, expChildrenGrants[k])
}
})
t.Run("direct-grants", func(t *testing.T) {
directGrants := acl.DirectScopeGrantMap()
expDirectGrants := map[string][]perms.AclGrant{
directGrantOrg1.PublicId: {
{
RoleScopeId: directGrantOrg1.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantOrg1.PublicId,
Id: "*",
Type: resource.All,
ActionSet: perms.ActionSet{action.All: true},
},
{
RoleScopeId: directGrantOrg1.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantOrg1.PublicId,
Id: "*",
Type: resource.Role,
ActionSet: perms.ActionSet{action.List: true, action.Read: true},
},
},
directGrantProj1a.PublicId: {
{
RoleScopeId: directGrantProj1a.PublicId,
RoleParentScopeId: directGrantOrg1.PublicId,
GrantScopeId: directGrantProj1a.PublicId,
Id: "*",
Type: resource.Target,
ActionSet: perms.ActionSet{action.AuthorizeSession: true, action.Read: true},
},
},
directGrantProj1b.PublicId: {
{
RoleScopeId: directGrantProj1b.PublicId,
RoleParentScopeId: directGrantOrg1.PublicId,
GrantScopeId: directGrantProj1b.PublicId,
Id: "*",
Type: resource.Session,
ActionSet: perms.ActionSet{action.List: true, action.Read: true},
},
},
directGrantOrg2.PublicId: {
{
RoleScopeId: directGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantOrg2.PublicId,
Id: "*",
Type: resource.User,
ActionSet: perms.ActionSet{action.All: true},
},
{
RoleScopeId: directGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantOrg2.PublicId,
Id: "*",
Type: resource.Group,
ActionSet: perms.ActionSet{action.List: true, action.Read: true},
},
},
directGrantProj2a.PublicId: {
{
RoleScopeId: directGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantProj2a.PublicId,
Id: "*",
Type: resource.User,
ActionSet: perms.ActionSet{action.All: true},
},
{
RoleScopeId: directGrantOrg2.PublicId,
RoleParentScopeId: scope.Global.String(),
GrantScopeId: directGrantProj2a.PublicId,
Id: "*",
Type: resource.Group,
ActionSet: perms.ActionSet{action.List: true, action.Read: true},
},
{
RoleScopeId: directGrantProj2a.PublicId,
RoleParentScopeId: directGrantOrg2.PublicId,
GrantScopeId: directGrantProj2a.PublicId,
Id: "hcst_abcd1234",
Type: resource.Unknown,
ActionSet: perms.ActionSet{action.All: true},
},
{
RoleScopeId: directGrantProj2a.PublicId,
RoleParentScopeId: directGrantOrg2.PublicId,
GrantScopeId: directGrantProj2a.PublicId,
Id: "hcst_1234abcd",
Type: resource.Unknown,
ActionSet: perms.ActionSet{action.All: true},
},
},
directGrantProj2b.PublicId: {
{
RoleScopeId: directGrantProj2b.PublicId,
RoleParentScopeId: directGrantOrg2.PublicId,
GrantScopeId: directGrantProj2b.PublicId,
Id: "cs_abcd1234",
Type: resource.Unknown,
ActionSet: perms.ActionSet{action.Update: true, action.Read: true},
},
},
}
/*
log.Println("org1", directGrantOrg1.PublicId)
log.Println("proj1a", directGrantProj1a.PublicId)
log.Println("proj1b", directGrantProj1b.PublicId)
log.Println("org2", directGrantOrg2.PublicId)
log.Println("proj2a", directGrantProj2a.PublicId)
log.Println("proj2b", directGrantProj2b.PublicId)
*/
assert.Len(t, directGrants, len(expDirectGrants))
for k, v := range directGrants {
assert.ElementsMatch(t, v, expDirectGrants[k])
}
})
})
t.Run("real-world", func(t *testing.T) {
// These tests cases crib from the initial setup of the grants, and
// include a number of cases to ensure the ones that should work do and
// various that should not do not
type testCase struct {
name string
res perms.Resource
act action.Type
shouldWork bool
}
testCases := []testCase{}
// These test cases should fail because the grants are in roles where
// the user is not a principal
{
testCases = append(testCases, testCase{
name: "nogrant-a",
res: perms.Resource{
ScopeId: noGrantOrg1.PublicId,
Id: "u_abcd1234",
Type: resource.Scope,
ParentScopeId: scope.Global.String(),
},
act: action.Read,
}, testCase{
name: "nogrant-b",
res: perms.Resource{
ScopeId: noGrantProj1.PublicId,
Id: "u_abcd1234",
Type: resource.User,
ParentScopeId: noGrantOrg1.String(),
},
act: action.Read,
}, testCase{
name: "nogrant-c",
res: perms.Resource{
ScopeId: noGrantOrg2.PublicId,
Id: "u_abcd1234",
Type: resource.Scope,
ParentScopeId: scope.Global.String(),
},
act: action.Read,
}, testCase{
name: "nogrant-d",
res: perms.Resource{
ScopeId: noGrantProj2.PublicId,
Id: "u_abcd1234",
Type: resource.User,
ParentScopeId: noGrantOrg2.String(),
},
act: action.Read,
},
)
}
// These test cases are for org1 and its projects where the grants are
// direct, not via children/descendants. They test some actions that
// should work and some that shouldn't.
{
testCases = append(testCases, testCase{
name: "direct-a",
res: perms.Resource{
ScopeId: directGrantOrg1.PublicId,
Id: "u_abcd1234",
Type: resource.User,
ParentScopeId: scope.Global.String(),
},
act: action.Read,
shouldWork: true,
}, testCase{
name: "direct-b",
res: perms.Resource{
ScopeId: directGrantOrg1.PublicId,
Id: "r_abcd1234",
Type: resource.Role,
ParentScopeId: scope.Global.String(),
},
act: action.Read,
shouldWork: true,
}, testCase{
name: "direct-c",
res: perms.Resource{
ScopeId: directGrantProj1a.PublicId,
Id: "ttcp_abcd1234",
Type: resource.Target,
ParentScopeId: directGrantOrg1.PublicId,
},
act: action.AuthorizeSession,
shouldWork: true,
}, testCase{
name: "direct-d",
res: perms.Resource{
ScopeId: directGrantProj1a.PublicId,
Id: "s_abcd1234",
Type: resource.Session,
ParentScopeId: directGrantOrg1.PublicId,
},
act: action.Read,
}, testCase{
name: "direct-e",
res: perms.Resource{
ScopeId: directGrantProj1b.PublicId,
Id: "ttcp_abcd1234",
Type: resource.Target,
ParentScopeId: directGrantOrg1.PublicId,
},
act: action.AuthorizeSession,
}, testCase{
name: "direct-f",
res: perms.Resource{
ScopeId: directGrantProj1b.PublicId,
Id: "s_abcd1234",
Type: resource.Session,
ParentScopeId: directGrantOrg1.PublicId,
},
act: action.Read,
shouldWork: true,
},
)
}
// These test cases are for org2 and its projects where the grants are
// direct, not via children/descendants. They test some actions that
// should work and some that shouldn't.
{
testCases = append(testCases, testCase{
name: "direct-g",
res: perms.Resource{
ScopeId: directGrantOrg2.PublicId,
Id: "u_abcd1234",
Type: resource.User,
ParentScopeId: scope.Global.String(),
},
act: action.Update,
shouldWork: true,
}, testCase{
name: "direct-m",
res: perms.Resource{
ScopeId: directGrantOrg2.PublicId,
Id: "g_abcd1234",
Type: resource.Group,
ParentScopeId: scope.Global.String(),
},
act: action.Update,
}, testCase{
name: "direct-h",
res: perms.Resource{
ScopeId: directGrantOrg2.PublicId,
Id: "acct_abcd1234",
Type: resource.Account,
ParentScopeId: scope.Global.String(),
},
act: action.Delete,
shouldWork: true,
}, testCase{
name: "direct-i",
res: perms.Resource{
ScopeId: directGrantProj2a.PublicId,
Type: resource.Group,
ParentScopeId: directGrantOrg2.PublicId,
},
act: action.List,
shouldWork: true,
}, testCase{
name: "direct-j",
res: perms.Resource{
ScopeId: directGrantProj2a.PublicId,
Id: "r_abcd1234",
Type: resource.Role,
ParentScopeId: directGrantOrg2.PublicId,
},
act: action.Read,
}, testCase{
name: "direct-n",
res: perms.Resource{
ScopeId: directGrantProj2a.PublicId,
Id: "u_abcd1234",
Type: resource.User,
ParentScopeId: directGrantOrg2.PublicId,
},
act: action.Read,
shouldWork: true,
}, testCase{
name: "direct-k",
res: perms.Resource{
ScopeId: directGrantProj2a.PublicId,
Id: "hcst_abcd1234",
Type: resource.HostCatalog,
ParentScopeId: directGrantOrg2.PublicId,
},
act: action.Read,
shouldWork: true,
}, testCase{
name: "direct-l",
res: perms.Resource{
ScopeId: directGrantProj2b.PublicId,
Id: "cs_abcd1234",
Type: resource.CredentialStore,
ParentScopeId: directGrantOrg2.PublicId,
},
act: action.Update,
shouldWork: true,
},
testCase{
name: "direct-m",
res: perms.Resource{
ScopeId: directGrantProj2b.PublicId,
Id: "cl_abcd1234",
Type: resource.CredentialLibrary,
ParentScopeId: directGrantOrg2.PublicId,
},
act: action.Update,
},
)
}
// These test cases are child grants
{
testCases = append(testCases, testCase{
name: "children-a",
res: perms.Resource{
ScopeId: scope.Global.String(),
Id: "a_abcd1234",
Type: resource.Account,
},
act: action.Update,
}, testCase{
name: "children-b",
res: perms.Resource{
ScopeId: noGrantOrg1.PublicId,
Id: "a_abcd1234",
Type: resource.Account,
ParentScopeId: scope.Global.String(),
},
act: action.Update,
shouldWork: true,
}, testCase{
name: "children-c",
res: perms.Resource{
ScopeId: directGrantOrg1.PublicId,
Id: "a_abcd1234",
Type: resource.Account,
ParentScopeId: scope.Global.String(),
},
act: action.Update,
shouldWork: true,
}, testCase{
name: "children-d",
res: perms.Resource{
ScopeId: directGrantOrg2.PublicId,
Id: "a_abcd1234",
Type: resource.Account,
ParentScopeId: scope.Global.String(),
},
act: action.Update,
shouldWork: true,
}, testCase{
name: "children-e",
res: perms.Resource{
ScopeId: childGrantOrg2.PublicId,
Id: "s_abcd1234",
Type: resource.Session,
ParentScopeId: scope.Global.String(),
},
act: action.CancelSelf,
}, testCase{
name: "children-f",
res: perms.Resource{
ScopeId: childGrantOrg1Proj.PublicId,
Id: "s_abcd1234",
Type: resource.Session,
ParentScopeId: childGrantOrg1.PublicId,
},
act: action.CancelSelf,
}, testCase{
name: "children-g",
res: perms.Resource{
ScopeId: childGrantOrg2Proj.PublicId,
Id: "s_abcd1234",
Type: resource.Session,
ParentScopeId: childGrantOrg2.PublicId,
},
act: action.CancelSelf,
shouldWork: true,
}, testCase{
name: "children-h",
res: perms.Resource{
ScopeId: childGrantOrg2Proj.PublicId,
Id: "s_abcd1234",
Type: resource.Session,
ParentScopeId: childGrantOrg2.PublicId,
},
act: action.CancelSelf,
shouldWork: true,
}, testCase{
name: "children-i",
res: perms.Resource{
ScopeId: childGrantOrg1.PublicId,
Id: "hsst_abcd1234",
Type: resource.HostSet,
ParentScopeId: scope.Global.String(),
},
act: action.AddHosts,
}, testCase{
name: "children-j",
res: perms.Resource{
ScopeId: childGrantOrg1Proj.PublicId,
Id: "hsst_abcd1234",
Type: resource.HostSet,
ParentScopeId: childGrantOrg1.PublicId,
},
act: action.AddHosts,
shouldWork: true,
}, testCase{
name: "children-k",
res: perms.Resource{
ScopeId: childGrantOrg2Proj.PublicId,
Id: "hsst_abcd1234",
Type: resource.HostSet,
ParentScopeId: childGrantOrg2.PublicId,
},
act: action.AddHosts,
},
)
}
// These test cases are global descendants grants
{
testCases = append(testCases, testCase{
name: "descendants-a",
res: perms.Resource{
ScopeId: scope.Global.String(),
Id: "cs_abcd1234",
Type: resource.Credential,
},
act: action.Update,
}, testCase{
name: "descendants-b",
res: perms.Resource{
ScopeId: noGrantProj1.PublicId,
Id: "cs_abcd1234",
Type: resource.Credential,
ParentScopeId: noGrantOrg1.PublicId,
},
act: action.Update,
shouldWork: true,
}, testCase{
name: "descendants-c",
res: perms.Resource{
ScopeId: directGrantOrg2.PublicId,
Id: "cs_abcd1234",
Type: resource.Credential,
ParentScopeId: scope.Global.String(),
},
act: action.Update,
shouldWork: true,
}, testCase{
name: "descendants-d",
res: perms.Resource{
ScopeId: directGrantProj1a.PublicId,
Id: "cs_abcd1234",
Type: resource.Credential,
ParentScopeId: directGrantOrg1.PublicId,
},
act: action.Update,
shouldWork: true,
}, testCase{
name: "descendants-e",
res: perms.Resource{
ScopeId: directGrantProj1a.PublicId,
Id: "cs_abcd1234",
Type: resource.Credential,
ParentScopeId: directGrantOrg1.PublicId,
},
act: action.Update,
shouldWork: true,
}, testCase{
name: "descendants-f",
res: perms.Resource{
ScopeId: directGrantProj2b.PublicId,
Id: "cs_abcd1234",
Type: resource.Credential,
ParentScopeId: directGrantOrg2.PublicId,
},
act: action.Update,
shouldWork: true,
},
)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
grantTuples, err := repo.GrantsForUser(ctx, user.PublicId)
require.NoError(t, err)
grants := make([]perms.Grant, 0, len(grantTuples))
for _, gt := range grantTuples {
grant, err := perms.Parse(ctx, gt)
require.NoError(t, err)
grants = append(grants, grant)
}
acl := perms.NewACL(grants...)
assert.True(t, acl.Allowed(tc.res, tc.act, "u_abc123").Authorized == tc.shouldWork)
})
}
})
}