mirror of https://github.com/hashicorp/boundary
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.
2150 lines
64 KiB
2150 lines
64 KiB
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package iam
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/boundary/globals"
|
|
"github.com/hashicorp/boundary/internal/db"
|
|
"github.com/hashicorp/boundary/internal/db/timestamp"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
"github.com/hashicorp/boundary/internal/iam/store"
|
|
"github.com/hashicorp/boundary/internal/types/action"
|
|
"github.com/hashicorp/boundary/internal/types/resource"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/testing/protocmp"
|
|
)
|
|
|
|
func TestNewRole(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
org, proj := TestScopes(t, repo)
|
|
id := testId(t)
|
|
|
|
type args struct {
|
|
scopePublicId string
|
|
opt []Option
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wantErr bool
|
|
wantErrMsg string
|
|
wantIsErr errors.Code
|
|
wantName string
|
|
wantDescription string
|
|
}{
|
|
{
|
|
name: "valid",
|
|
args: args{
|
|
scopePublicId: org.PublicId,
|
|
opt: []Option{WithName(id), WithDescription("description-" + id)},
|
|
},
|
|
wantErr: false,
|
|
wantName: id,
|
|
wantDescription: "description-" + id,
|
|
},
|
|
{
|
|
name: "valid-proj",
|
|
args: args{
|
|
scopePublicId: proj.PublicId,
|
|
opt: []Option{WithName(id), WithDescription("description-" + id)},
|
|
},
|
|
wantErr: false,
|
|
wantName: id,
|
|
wantDescription: "description-" + id,
|
|
},
|
|
{
|
|
name: "valid-with-no-options" + id,
|
|
args: args{
|
|
scopePublicId: org.PublicId,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "no-scope",
|
|
args: args{
|
|
opt: []Option{WithName(id)},
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: "iam.NewRole: missing scope id: parameter violation: error #100",
|
|
wantIsErr: errors.InvalidParameter,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
got, err := NewRole(ctx, tt.args.scopePublicId, tt.args.opt...)
|
|
if tt.wantErr {
|
|
require.Error(err)
|
|
assert.Contains(err.Error(), tt.wantErrMsg)
|
|
assert.True(errors.Match(errors.T(tt.wantIsErr), err))
|
|
return
|
|
}
|
|
require.NoError(err)
|
|
assert.Equal(tt.wantName, got.Name)
|
|
assert.Equal(tt.wantDescription, got.Description)
|
|
assert.Empty(got.PublicId)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRole_Actions(t *testing.T) {
|
|
assert := assert.New(t)
|
|
r := &Role{}
|
|
a := r.Actions()
|
|
assert.Equal(a[action.Create.String()], action.Create)
|
|
assert.Equal(a[action.Update.String()], action.Update)
|
|
assert.Equal(a[action.Read.String()], action.Read)
|
|
assert.Equal(a[action.Delete.String()], action.Delete)
|
|
assert.Equal(a[action.AddGrants.String()], action.AddGrants)
|
|
assert.Equal(a[action.RemoveGrants.String()], action.RemoveGrants)
|
|
assert.Equal(a[action.SetGrants.String()], action.SetGrants)
|
|
assert.Equal(a[action.AddPrincipals.String()], action.AddPrincipals)
|
|
assert.Equal(a[action.RemovePrincipals.String()], action.RemovePrincipals)
|
|
assert.Equal(a[action.SetPrincipals.String()], action.SetPrincipals)
|
|
}
|
|
|
|
func TestRole_ResourceType(t *testing.T) {
|
|
assert := assert.New(t)
|
|
r := &Role{}
|
|
ty := r.GetResourceType()
|
|
assert.Equal(ty, resource.Role)
|
|
}
|
|
|
|
func TestRole_GetScope(t *testing.T) {
|
|
t.Parallel()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
org, proj := TestScopes(t, repo)
|
|
|
|
t.Run("valid-org", func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
w := db.New(conn)
|
|
role := TestRole(t, conn, org.PublicId)
|
|
scope, err := role.GetScope(context.Background(), w)
|
|
require.NoError(err)
|
|
assert.True(proto.Equal(org, scope))
|
|
})
|
|
t.Run("valid-proj", func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
w := db.New(conn)
|
|
role := TestRole(t, conn, proj.PublicId)
|
|
scope, err := role.GetScope(context.Background(), w)
|
|
require.NoError(err)
|
|
assert.True(proto.Equal(proj, scope))
|
|
})
|
|
}
|
|
|
|
func Test_globalRole_Create(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
type args struct {
|
|
role *globalRole
|
|
}
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
org, proj := TestScopes(t, repo)
|
|
rw := db.New(conn)
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wantErr bool
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "can create valid role in global grants scope individual",
|
|
args: args{
|
|
role: func() *globalRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: grpId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can create valid role in global grants scope descendants",
|
|
args: args{
|
|
role: func() *globalRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: grpId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeDescendants,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can create valid role in global grants scope children",
|
|
args: args{
|
|
role: func() *globalRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: grpId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeChildren,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "cannot create role in org",
|
|
args: args{
|
|
role: func() *globalRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: grpId,
|
|
ScopeId: org.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeDescendants,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: insert or update on table "iam_role_global" violates foreign key constraint "iam_scope_global_fkey": integrity violation: error #1003`,
|
|
},
|
|
{
|
|
name: "cannot create role in project",
|
|
args: args{
|
|
role: func() *globalRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: grpId,
|
|
ScopeId: proj.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeDescendants,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: insert or update on table "iam_role_global" violates foreign key constraint "iam_scope_global_fkey": integrity violation: error #1003`,
|
|
},
|
|
{
|
|
name: "invalid grants scope",
|
|
args: args{
|
|
role: func() *globalRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: grpId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: "invalid-scope",
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: insert or update on table "iam_role_global" violates foreign key constraint "iam_role_global_grant_scope_enm_fkey": integrity violation: error #1003`,
|
|
},
|
|
{
|
|
name: "duplicate name not allowed",
|
|
args: args{
|
|
role: func() *globalRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
// create a role then return the cloned role with different name
|
|
r := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: grpId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeDescendants,
|
|
},
|
|
}
|
|
|
|
require.NoError(t, rw.Create(ctx, r))
|
|
newId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
dupeName := r.Clone().(*globalRole)
|
|
dupeName.PublicId = newId
|
|
dupeName.GrantThisRoleScope = false
|
|
dupeName.GrantScope = globals.GrantScopeChildren
|
|
return dupeName
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: duplicate key value violates unique constraint "iam_role_global_name_scope_id_uq": unique constraint violation: integrity violation: error #1002`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := tt.args.role.Clone().(*globalRole)
|
|
err := rw.Create(ctx, r)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, tt.wantErrMsg)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, tt.args.role.PublicId)
|
|
|
|
foundGrp := allocGlobalRole()
|
|
foundGrp.PublicId = tt.args.role.PublicId
|
|
err = rw.LookupByPublicId(ctx, &foundGrp)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cmp.Diff(r, &foundGrp, protocmp.Transform()))
|
|
|
|
require.NotEmpty(t, foundGrp.GrantScopeUpdateTime)
|
|
require.NotEmpty(t, foundGrp.GrantThisRoleScopeUpdateTime)
|
|
require.NotZero(t, foundGrp.CreateTime.AsTime())
|
|
require.NotZero(t, foundGrp.UpdateTime.AsTime())
|
|
|
|
baseRole := &testBaseRole{PublicId: tt.args.role.PublicId}
|
|
err = rw.LookupByPublicId(ctx, baseRole)
|
|
require.NoError(t, err)
|
|
require.Equal(t, foundGrp.CreateTime.AsTime(), baseRole.CreateTime.AsTime())
|
|
require.Equal(t, foundGrp.UpdateTime.AsTime(), baseRole.UpdateTime.AsTime())
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_globalRole_Update(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
type arg struct {
|
|
updateRole *globalRole
|
|
fieldMask []string
|
|
nullPath []string
|
|
opts []db.Option
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
setupOriginal func(t *testing.T) *globalRole
|
|
createInput func(t *testing.T, original *globalRole) arg
|
|
wantErr bool
|
|
wantRowsUpdate int
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "can update name and description",
|
|
setupOriginal: func(t *testing.T) *globalRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: roleId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: testId(t),
|
|
Description: testId(t),
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *globalRole) arg {
|
|
updated := original.Clone().(*globalRole)
|
|
updated.Name = testId(t)
|
|
updated.Description = testId(t)
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"name", "description"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can update grant_scope",
|
|
setupOriginal: func(t *testing.T) *globalRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: roleId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *globalRole) arg {
|
|
updated := original.Clone().(*globalRole)
|
|
updated.GrantScope = globals.GrantScopeChildren
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"GrantScope"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can set name and description null",
|
|
setupOriginal: func(t *testing.T) *globalRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: roleId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *globalRole) arg {
|
|
updated := original.Clone().(*globalRole)
|
|
updated.Name = ""
|
|
updated.Description = ""
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{},
|
|
nullPath: []string{"name", "description"},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can update grant_this_role_scope",
|
|
setupOriginal: func(t *testing.T) *globalRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: roleId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *globalRole) arg {
|
|
updated := original.Clone().(*globalRole)
|
|
updated.GrantThisRoleScope = false
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"GrantThisRoleScope"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can update version",
|
|
setupOriginal: func(t *testing.T) *globalRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: roleId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *globalRole) arg {
|
|
updated := original.Clone().(*globalRole)
|
|
// version will be overridden by trigger
|
|
updated.Version = 123456
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"version"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
original := tt.setupOriginal(t)
|
|
args := tt.createInput(t, original)
|
|
updatedRows, err := rw.Update(context.Background(), args.updateRole, args.fieldMask, args.nullPath, args.opts...)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, tt.wantErrMsg)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, args.updateRole.PublicId)
|
|
if tt.wantRowsUpdate == 0 {
|
|
require.Zero(t, updatedRows)
|
|
return
|
|
}
|
|
require.Equal(t, tt.wantRowsUpdate, updatedRows)
|
|
foundGrp := allocGlobalRole()
|
|
foundGrp.PublicId = original.PublicId
|
|
err = rw.LookupByPublicId(ctx, &foundGrp)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cmp.Diff(args.updateRole, &foundGrp, protocmp.Transform()))
|
|
baseRole := &testBaseRole{PublicId: original.PublicId}
|
|
err = rw.LookupByPublicId(ctx, baseRole)
|
|
require.NoError(t, err)
|
|
require.Equal(t, original.Version+1, foundGrp.Version)
|
|
require.Equal(t, foundGrp.UpdateTime.AsTime(), baseRole.UpdateTime.AsTime())
|
|
// assert other update time as necessary
|
|
if original.GrantThisRoleScope != args.updateRole.GrantThisRoleScope {
|
|
require.Greater(t, foundGrp.GrantThisRoleScopeUpdateTime.AsTime(), original.GrantThisRoleScopeUpdateTime.AsTime())
|
|
}
|
|
if original.GrantScope != args.updateRole.GrantScope {
|
|
require.Greater(t, foundGrp.GrantScopeUpdateTime.AsTime(), original.GrantScopeUpdateTime.AsTime())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_globalRole_Delete(t *testing.T) {
|
|
t.Parallel()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
tests := []struct {
|
|
name string
|
|
setupRole func(ctx context.Context, t *testing.T) *globalRole
|
|
wantRowsDeleted int
|
|
wantErr bool
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "valid",
|
|
setupRole: func(ctx context.Context, t *testing.T) *globalRole {
|
|
role := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: "test-role",
|
|
Description: "description",
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
id, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
role.PublicId = id
|
|
require.NoError(t, rw.Create(ctx, role))
|
|
require.NotEmpty(t, role.PublicId)
|
|
return role
|
|
},
|
|
wantErr: false,
|
|
wantRowsDeleted: 1,
|
|
},
|
|
{
|
|
name: "role-does-not-exist",
|
|
setupRole: func(ctx context.Context, t *testing.T) *globalRole {
|
|
id, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
role := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: id,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: "test-role",
|
|
Description: "description",
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
return role
|
|
},
|
|
wantErr: false,
|
|
wantRowsDeleted: 0,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
createdRole := tc.setupRole(context.Background(), t)
|
|
deletedRows, err := rw.Delete(context.Background(), &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: createdRole.PublicId,
|
|
},
|
|
})
|
|
if tc.wantErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
if tc.wantRowsDeleted == 0 {
|
|
require.Equal(t, tc.wantRowsDeleted, deletedRows)
|
|
return
|
|
}
|
|
require.Equal(t, tc.wantRowsDeleted, deletedRows)
|
|
foundRole := &globalRole{GlobalRole: &store.GlobalRole{PublicId: createdRole.PublicId}}
|
|
err = rw.LookupByPublicId(context.Background(), foundRole)
|
|
require.Error(t, err)
|
|
require.True(t, errors.IsNotFoundError(err))
|
|
|
|
// also check that base table was deleted
|
|
baseRole := &testBaseRole{PublicId: createdRole.PublicId}
|
|
err = rw.LookupByPublicId(context.Background(), baseRole)
|
|
require.Error(t, err)
|
|
require.True(t, errors.IsNotFoundError(err))
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_globalRole_Actions(t *testing.T) {
|
|
r := allocGlobalRole()
|
|
a := r.Actions()
|
|
assert.Equal(t, a[action.Create.String()], action.Create)
|
|
assert.Equal(t, a[action.Update.String()], action.Update)
|
|
assert.Equal(t, a[action.Read.String()], action.Read)
|
|
assert.Equal(t, a[action.Delete.String()], action.Delete)
|
|
assert.Equal(t, a[action.AddGrants.String()], action.AddGrants)
|
|
assert.Equal(t, a[action.RemoveGrants.String()], action.RemoveGrants)
|
|
assert.Equal(t, a[action.SetGrants.String()], action.SetGrants)
|
|
assert.Equal(t, a[action.AddPrincipals.String()], action.AddPrincipals)
|
|
assert.Equal(t, a[action.RemovePrincipals.String()], action.RemovePrincipals)
|
|
assert.Equal(t, a[action.SetPrincipals.String()], action.SetPrincipals)
|
|
}
|
|
|
|
func Test_globalRole_ResourceType(t *testing.T) {
|
|
t.Parallel()
|
|
role := allocGlobalRole()
|
|
result := role.GetResourceType()
|
|
assert.Equal(t, result, resource.Role)
|
|
}
|
|
|
|
func Test_globalRole_GetScope(t *testing.T) {
|
|
t.Parallel()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
ctx := context.Background()
|
|
rw := db.New(conn)
|
|
role := globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: "test-role",
|
|
Description: "description",
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
id, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
role.PublicId = id
|
|
require.NoError(t, rw.Create(ctx, role))
|
|
require.NotEmpty(t, role.PublicId)
|
|
scope, err := role.GetScope(context.Background(), rw)
|
|
require.NoError(t, err)
|
|
require.Equal(t, scope.GetPublicId(), role.GetScopeId())
|
|
}
|
|
|
|
func Test_globalRole_Clone(t *testing.T) {
|
|
t.Parallel()
|
|
role1 := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: "r_123456",
|
|
ScopeId: "global",
|
|
Name: "test-role",
|
|
Description: "description",
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeChildren,
|
|
GrantThisRoleScopeUpdateTime: timestamp.New(time.Date(2025, 2, 12, 3, 15, 15, 30, time.UTC)),
|
|
GrantScopeUpdateTime: timestamp.New(time.Date(2025, 3, 12, 3, 15, 15, 30, time.UTC)),
|
|
CreateTime: timestamp.New(time.Date(2025, 4, 12, 3, 15, 15, 30, time.UTC)),
|
|
UpdateTime: timestamp.New(time.Date(2025, 5, 12, 3, 15, 15, 30, time.UTC)),
|
|
Version: 1,
|
|
},
|
|
GrantScopes: []*RoleGrantScope{
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2025, 3, 12, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_123456",
|
|
ScopeIdOrSpecial: "children",
|
|
},
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2025, 1, 14, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_123456",
|
|
ScopeIdOrSpecial: "p_123456",
|
|
},
|
|
},
|
|
}
|
|
role2 := &globalRole{
|
|
GlobalRole: &store.GlobalRole{
|
|
PublicId: "r_abcdef",
|
|
ScopeId: "o_123456",
|
|
Name: "another-role",
|
|
Description: "different description",
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeChildren,
|
|
GrantThisRoleScopeUpdateTime: timestamp.New(time.Date(2024, 2, 12, 3, 15, 15, 30, time.UTC)),
|
|
GrantScopeUpdateTime: timestamp.New(time.Date(2024, 3, 12, 3, 15, 15, 30, time.UTC)),
|
|
CreateTime: timestamp.New(time.Date(2024, 4, 12, 3, 15, 15, 30, time.UTC)),
|
|
UpdateTime: timestamp.New(time.Date(2024, 5, 12, 3, 15, 15, 30, time.UTC)),
|
|
Version: 1,
|
|
},
|
|
GrantScopes: []*RoleGrantScope{
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2024, 3, 12, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_abcdef",
|
|
ScopeIdOrSpecial: "children",
|
|
},
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2024, 1, 14, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_abcdef",
|
|
ScopeIdOrSpecial: "p_abcdef",
|
|
},
|
|
},
|
|
}
|
|
require.NotEqual(t, role1, role2)
|
|
|
|
t.Run("valid", func(t *testing.T) {
|
|
clone := role1.Clone()
|
|
assert.True(t, proto.Equal(clone.(*globalRole).GlobalRole, role1.GlobalRole))
|
|
assert.Equal(t, clone.(*globalRole).GrantScopes, role1.GrantScopes)
|
|
})
|
|
t.Run("not-equal", func(t *testing.T) {
|
|
role1Clone := role1.Clone()
|
|
assert.True(t, !proto.Equal(role1Clone.(*globalRole).GlobalRole, role2.GlobalRole))
|
|
assert.NotEqual(t, role1Clone.(*globalRole).GrantScopes, role2.GrantScopes)
|
|
})
|
|
}
|
|
|
|
func Test_globalRole_SetTableName(t *testing.T) {
|
|
defaultTableName := defaultGlobalRoleTableName
|
|
tests := []struct {
|
|
name string
|
|
initialName string
|
|
setNameTo string
|
|
want string
|
|
}{
|
|
{
|
|
name: "new-name",
|
|
initialName: "",
|
|
setNameTo: "new-name",
|
|
want: "new-name",
|
|
},
|
|
{
|
|
name: "reset to default",
|
|
initialName: "initial",
|
|
setNameTo: "",
|
|
want: defaultTableName,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
def := allocGlobalRole()
|
|
require.Equal(t, defaultTableName, def.TableName())
|
|
s := &globalRole{
|
|
GlobalRole: &store.GlobalRole{},
|
|
tableName: tt.initialName,
|
|
}
|
|
s.SetTableName(tt.setNameTo)
|
|
assert.Equal(t, tt.want, s.TableName())
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_orgRole_Create(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
type args struct {
|
|
role *orgRole
|
|
}
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
org, proj := TestScopes(t, repo)
|
|
org2 := TestOrg(t, repo)
|
|
rw := db.New(conn)
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wantErr bool
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "can create valid role in org grants scope individual",
|
|
args: args{
|
|
role: func() *orgRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: grpId,
|
|
ScopeId: org.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can create valid role in org grants scope children",
|
|
args: args{
|
|
role: func() *orgRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: grpId,
|
|
ScopeId: org.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeChildren,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "duplicate name different scope allowed",
|
|
args: args{
|
|
role: func() *orgRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
// create a role then return the cloned role with different name
|
|
r := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: grpId,
|
|
ScopeId: org.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeChildren,
|
|
},
|
|
}
|
|
|
|
require.NoError(t, rw.Create(ctx, r))
|
|
newId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
dupeName := r.Clone().(*orgRole)
|
|
dupeName.PublicId = newId
|
|
dupeName.ScopeId = org2.PublicId
|
|
dupeName.GrantThisRoleScope = false
|
|
dupeName.GrantScope = globals.GrantScopeChildren
|
|
return dupeName
|
|
}(),
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "cannot create valid role in org grants scope descendants",
|
|
args: args{
|
|
role: func() *orgRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: grpId,
|
|
ScopeId: org.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeDescendants,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: insert or update on table "iam_role_org" violates foreign key constraint "iam_role_org_grant_scope_enm_fkey": integrity violation: error #1003`,
|
|
},
|
|
{
|
|
name: "cannot create role in org with descendants grants",
|
|
args: args{
|
|
role: func() *orgRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: grpId,
|
|
ScopeId: org.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeDescendants,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: insert or update on table "iam_role_org" violates foreign key constraint "iam_role_org_grant_scope_enm_fkey": integrity violation: error #1003`,
|
|
},
|
|
{
|
|
name: "cannot create role in global",
|
|
args: args{
|
|
role: func() *orgRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: grpId,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeChildren,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: insert or update on table "iam_role_org" violates foreign key constraint "iam_scope_org_fkey": integrity violation: error #1003`,
|
|
},
|
|
{
|
|
name: "cannot create role in project",
|
|
args: args{
|
|
role: func() *orgRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: grpId,
|
|
ScopeId: proj.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeDescendants,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: insert or update on table "iam_role_org" violates foreign key constraint "iam_scope_org_fkey": integrity violation: error #1003`,
|
|
},
|
|
{
|
|
name: "invalid grants scope",
|
|
args: args{
|
|
role: func() *orgRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: grpId,
|
|
ScopeId: org.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: "invalid-scope",
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: insert or update on table "iam_role_org" violates foreign key constraint "iam_role_org_grant_scope_enm_fkey": integrity violation: error #1003`,
|
|
},
|
|
{
|
|
name: "duplicate name not allowed",
|
|
args: args{
|
|
role: func() *orgRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
// create a role then return the cloned role with different name
|
|
r := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: grpId,
|
|
ScopeId: org.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeChildren,
|
|
},
|
|
}
|
|
|
|
require.NoError(t, rw.Create(ctx, r))
|
|
newId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
dupeName := r.Clone().(*orgRole)
|
|
dupeName.PublicId = newId
|
|
dupeName.GrantThisRoleScope = false
|
|
dupeName.GrantScope = globals.GrantScopeChildren
|
|
return dupeName
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: duplicate key value violates unique constraint "iam_role_org_name_scope_id_uq": unique constraint violation: integrity violation: error #1002`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := tt.args.role.Clone().(*orgRole)
|
|
err := rw.Create(ctx, r)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, tt.wantErrMsg)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, tt.args.role.PublicId)
|
|
|
|
foundGrp := allocOrgRole()
|
|
foundGrp.PublicId = tt.args.role.PublicId
|
|
err = rw.LookupByPublicId(ctx, &foundGrp)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cmp.Diff(r, &foundGrp, protocmp.Transform()))
|
|
|
|
require.NotEmpty(t, foundGrp.GrantScopeUpdateTime)
|
|
require.NotEmpty(t, foundGrp.GrantThisRoleScopeUpdateTime)
|
|
require.NotZero(t, foundGrp.CreateTime.AsTime())
|
|
require.NotZero(t, foundGrp.UpdateTime.AsTime())
|
|
|
|
baseRole := testBaseRole{PublicId: tt.args.role.PublicId}
|
|
err = rw.LookupByPublicId(ctx, &baseRole)
|
|
require.NoError(t, err)
|
|
require.Equal(t, foundGrp.CreateTime.AsTime(), baseRole.CreateTime.AsTime())
|
|
require.Equal(t, foundGrp.UpdateTime.AsTime(), baseRole.UpdateTime.AsTime())
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_orgRole_Update(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 := TestOrg(t, repo)
|
|
type arg struct {
|
|
updateRole *orgRole
|
|
fieldMask []string
|
|
nullPath []string
|
|
opts []db.Option
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
setupOriginal func(t *testing.T) *orgRole
|
|
createInput func(t *testing.T, original *orgRole) arg
|
|
wantErr bool
|
|
wantRowsUpdate int
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "can update name and description",
|
|
setupOriginal: func(t *testing.T) *orgRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: roleId,
|
|
ScopeId: org.PublicId,
|
|
Name: testId(t),
|
|
Description: testId(t),
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *orgRole) arg {
|
|
updated := original.Clone().(*orgRole)
|
|
updated.Name = testId(t)
|
|
updated.Description = testId(t)
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"name", "description"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can update grant_scope",
|
|
setupOriginal: func(t *testing.T) *orgRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: roleId,
|
|
ScopeId: org.PublicId,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *orgRole) arg {
|
|
updated := original.Clone().(*orgRole)
|
|
updated.GrantScope = globals.GrantScopeChildren
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"GrantScope"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can set name and description null",
|
|
setupOriginal: func(t *testing.T) *orgRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: roleId,
|
|
ScopeId: org.PublicId,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *orgRole) arg {
|
|
updated := original.Clone().(*orgRole)
|
|
updated.Name = ""
|
|
updated.Description = ""
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{},
|
|
nullPath: []string{"name", "description"},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can update grant_this_role_scope",
|
|
setupOriginal: func(t *testing.T) *orgRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: roleId,
|
|
ScopeId: org.PublicId,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *orgRole) arg {
|
|
updated := original.Clone().(*orgRole)
|
|
updated.GrantThisRoleScope = false
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"GrantThisRoleScope"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can update version",
|
|
setupOriginal: func(t *testing.T) *orgRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: roleId,
|
|
ScopeId: org.PublicId,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *orgRole) arg {
|
|
updated := original.Clone().(*orgRole)
|
|
updated.Version = uint32(15)
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"version"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
original := tt.setupOriginal(t)
|
|
args := tt.createInput(t, original)
|
|
updatedRows, err := rw.Update(context.Background(), args.updateRole, args.fieldMask, args.nullPath, args.opts...)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, tt.wantErrMsg)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, args.updateRole.PublicId)
|
|
if tt.wantRowsUpdate == 0 {
|
|
require.Zero(t, updatedRows)
|
|
return
|
|
}
|
|
require.Equal(t, tt.wantRowsUpdate, updatedRows)
|
|
|
|
foundGrp := allocOrgRole()
|
|
foundGrp.PublicId = original.PublicId
|
|
err = rw.LookupByPublicId(ctx, &foundGrp)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cmp.Diff(args.updateRole, &foundGrp, protocmp.Transform()))
|
|
// also check that base table was deleted
|
|
baseRole := &testBaseRole{PublicId: original.PublicId}
|
|
err = rw.LookupByPublicId(ctx, baseRole)
|
|
require.NoError(t, err)
|
|
require.Equal(t, foundGrp.UpdateTime.AsTime(), baseRole.UpdateTime.AsTime())
|
|
require.Equal(t, original.Version+1, foundGrp.Version)
|
|
require.Equal(t, foundGrp.UpdateTime.AsTime(), baseRole.UpdateTime.AsTime())
|
|
// assert other update time as necessary
|
|
if original.GrantThisRoleScope != args.updateRole.GrantThisRoleScope {
|
|
require.Greater(t, foundGrp.GrantThisRoleScopeUpdateTime.AsTime(), original.GrantThisRoleScopeUpdateTime.AsTime())
|
|
}
|
|
if original.GrantScope != args.updateRole.GrantScope {
|
|
require.Greater(t, foundGrp.GrantScopeUpdateTime.AsTime(), original.GrantScopeUpdateTime.AsTime())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_orgRole_Delete(t *testing.T) {
|
|
t.Parallel()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
org := TestOrg(t, repo)
|
|
tests := []struct {
|
|
name string
|
|
setupRole func(ctx context.Context, t *testing.T) *orgRole
|
|
wantRowsDeleted int
|
|
wantErr bool
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "valid",
|
|
setupRole: func(ctx context.Context, t *testing.T) *orgRole {
|
|
role := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
ScopeId: org.PublicId,
|
|
Name: "test-role",
|
|
Description: "description",
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
id, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
role.PublicId = id
|
|
require.NoError(t, rw.Create(ctx, role))
|
|
require.NotEmpty(t, role.PublicId)
|
|
return role
|
|
},
|
|
wantErr: false,
|
|
wantRowsDeleted: 1,
|
|
},
|
|
{
|
|
name: "role-does-not-exist",
|
|
setupRole: func(ctx context.Context, t *testing.T) *orgRole {
|
|
id, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
role := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: id,
|
|
ScopeId: org.PublicId,
|
|
Name: "test-role",
|
|
Description: "description",
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
return role
|
|
},
|
|
wantErr: false,
|
|
wantRowsDeleted: 0,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
createdRole := tc.setupRole(context.Background(), t)
|
|
deletedRows, err := rw.Delete(context.Background(), &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: createdRole.PublicId,
|
|
},
|
|
})
|
|
if tc.wantErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
if tc.wantRowsDeleted == 0 {
|
|
require.Equal(t, tc.wantRowsDeleted, deletedRows)
|
|
return
|
|
}
|
|
require.Equal(t, tc.wantRowsDeleted, deletedRows)
|
|
foundRole := &orgRole{OrgRole: &store.OrgRole{PublicId: createdRole.PublicId}}
|
|
err = rw.LookupByPublicId(context.Background(), foundRole)
|
|
require.Error(t, err)
|
|
require.True(t, errors.IsNotFoundError(err))
|
|
|
|
// also check that base table was deleted
|
|
baseRole := &testBaseRole{PublicId: createdRole.PublicId}
|
|
err = rw.LookupByPublicId(context.Background(), baseRole)
|
|
require.Error(t, err)
|
|
require.True(t, errors.IsNotFoundError(err))
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_orgRole_Actions(t *testing.T) {
|
|
r := &orgRole{}
|
|
a := r.Actions()
|
|
assert.Equal(t, a[action.Create.String()], action.Create)
|
|
assert.Equal(t, a[action.Update.String()], action.Update)
|
|
assert.Equal(t, a[action.Read.String()], action.Read)
|
|
assert.Equal(t, a[action.Delete.String()], action.Delete)
|
|
assert.Equal(t, a[action.AddGrants.String()], action.AddGrants)
|
|
assert.Equal(t, a[action.RemoveGrants.String()], action.RemoveGrants)
|
|
assert.Equal(t, a[action.SetGrants.String()], action.SetGrants)
|
|
assert.Equal(t, a[action.AddPrincipals.String()], action.AddPrincipals)
|
|
assert.Equal(t, a[action.RemovePrincipals.String()], action.RemovePrincipals)
|
|
assert.Equal(t, a[action.SetPrincipals.String()], action.SetPrincipals)
|
|
}
|
|
|
|
func Test_orgRole_ResourceType(t *testing.T) {
|
|
t.Parallel()
|
|
role := orgRole{}
|
|
result := role.GetResourceType()
|
|
assert.Equal(t, result, resource.Role)
|
|
}
|
|
|
|
func Test_orgRole_GetScope(t *testing.T) {
|
|
t.Parallel()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
ctx := context.Background()
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
org := TestOrg(t, repo)
|
|
role := orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
ScopeId: org.PublicId,
|
|
Name: "test-role",
|
|
Description: "description",
|
|
GrantThisRoleScope: false,
|
|
GrantScope: globals.GrantScopeIndividual,
|
|
},
|
|
}
|
|
id, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
role.PublicId = id
|
|
require.NoError(t, rw.Create(ctx, role))
|
|
require.NotEmpty(t, role.PublicId)
|
|
scope, err := role.GetScope(context.Background(), rw)
|
|
require.NoError(t, err)
|
|
require.True(t, proto.Equal(org, scope))
|
|
}
|
|
|
|
func Test_orgRole_Clone(t *testing.T) {
|
|
t.Parallel()
|
|
role1 := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: "r_123456",
|
|
ScopeId: "o_123456",
|
|
Name: "test-role",
|
|
Description: "description",
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeChildren,
|
|
GrantThisRoleScopeUpdateTime: timestamp.New(time.Date(2025, 2, 12, 3, 15, 15, 30, time.UTC)),
|
|
GrantScopeUpdateTime: timestamp.New(time.Date(2025, 3, 12, 3, 15, 15, 30, time.UTC)),
|
|
CreateTime: timestamp.New(time.Date(2025, 4, 12, 3, 15, 15, 30, time.UTC)),
|
|
UpdateTime: timestamp.New(time.Date(2025, 5, 12, 3, 15, 15, 30, time.UTC)),
|
|
Version: 1,
|
|
},
|
|
GrantScopes: []*RoleGrantScope{
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2025, 3, 12, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_123456",
|
|
ScopeIdOrSpecial: "children",
|
|
},
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2025, 1, 14, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_123456",
|
|
ScopeIdOrSpecial: "p_123456",
|
|
},
|
|
},
|
|
}
|
|
role2 := &orgRole{
|
|
OrgRole: &store.OrgRole{
|
|
PublicId: "r_abcdef",
|
|
ScopeId: "o_abcdef",
|
|
Name: "another-role",
|
|
Description: "different description",
|
|
GrantThisRoleScope: true,
|
|
GrantScope: globals.GrantScopeChildren,
|
|
GrantThisRoleScopeUpdateTime: timestamp.New(time.Date(2024, 2, 12, 3, 15, 15, 30, time.UTC)),
|
|
GrantScopeUpdateTime: timestamp.New(time.Date(2024, 3, 12, 3, 15, 15, 30, time.UTC)),
|
|
CreateTime: timestamp.New(time.Date(2024, 4, 12, 3, 15, 15, 30, time.UTC)),
|
|
UpdateTime: timestamp.New(time.Date(2024, 5, 12, 3, 15, 15, 30, time.UTC)),
|
|
Version: 1,
|
|
},
|
|
GrantScopes: []*RoleGrantScope{
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2024, 3, 12, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_abcdef",
|
|
ScopeIdOrSpecial: "children",
|
|
},
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2024, 1, 14, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_abcdef",
|
|
ScopeIdOrSpecial: "p_abcdef",
|
|
},
|
|
},
|
|
}
|
|
require.NotEqual(t, role1, role2)
|
|
|
|
t.Run("valid", func(t *testing.T) {
|
|
clone := role1.Clone()
|
|
assert.True(t, proto.Equal(clone.(*orgRole).OrgRole, role1.OrgRole))
|
|
assert.Equal(t, clone.(*orgRole).GrantScopes, role1.GrantScopes)
|
|
})
|
|
t.Run("not-equal", func(t *testing.T) {
|
|
role1Clone := role1.Clone()
|
|
assert.True(t, !proto.Equal(role1Clone.(*orgRole).OrgRole, role2.OrgRole))
|
|
assert.NotEqual(t, role1Clone.(*orgRole).GrantScopes, role2.GrantScopes)
|
|
})
|
|
}
|
|
|
|
func Test_orgRole_SetTableName(t *testing.T) {
|
|
defaultTableName := defaultOrgRoleTableName
|
|
tests := []struct {
|
|
name string
|
|
initialName string
|
|
setNameTo string
|
|
want string
|
|
}{
|
|
{
|
|
name: "new-name",
|
|
initialName: "",
|
|
setNameTo: "new-name",
|
|
want: "new-name",
|
|
},
|
|
{
|
|
name: "reset to default",
|
|
initialName: "initial",
|
|
setNameTo: "",
|
|
want: defaultTableName,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
def := orgRole{}
|
|
require.Equal(t, defaultTableName, def.TableName())
|
|
s := &orgRole{
|
|
OrgRole: &store.OrgRole{},
|
|
tableName: tt.initialName,
|
|
}
|
|
s.SetTableName(tt.setNameTo)
|
|
assert.Equal(t, tt.want, s.TableName())
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_projectRole_Create(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
type args struct {
|
|
role *projectRole
|
|
}
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
org, proj := TestScopes(t, repo)
|
|
proj2 := TestProject(t, repo, org.PublicId)
|
|
rw := db.New(conn)
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wantErr bool
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "can create valid role in project",
|
|
args: args{
|
|
role: func() *projectRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: grpId,
|
|
ScopeId: proj.PublicId,
|
|
GrantThisRoleScope: false,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "duplicate name different scope allowed",
|
|
args: args{
|
|
role: func() *projectRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
// create a role then return the cloned role with different name
|
|
r := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: grpId,
|
|
ScopeId: proj.PublicId,
|
|
GrantThisRoleScope: true,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
},
|
|
}
|
|
|
|
require.NoError(t, rw.Create(ctx, r))
|
|
newId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
dupeName := r.Clone().(*projectRole)
|
|
dupeName.PublicId = newId
|
|
dupeName.ScopeId = proj2.PublicId
|
|
return dupeName
|
|
}(),
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "cannot create role in global",
|
|
args: args{
|
|
role: func() *projectRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: grpId,
|
|
GrantThisRoleScope: true,
|
|
ScopeId: globals.GlobalPrefix,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: insert or update on table "iam_role_project" violates foreign key constraint "iam_scope_project_fkey": integrity violation: error #1003`,
|
|
},
|
|
{
|
|
name: "cannot create role in org",
|
|
args: args{
|
|
role: func() *projectRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
r := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: grpId,
|
|
ScopeId: org.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
},
|
|
}
|
|
return r
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: insert or update on table "iam_role_project" violates foreign key constraint "iam_scope_project_fkey": integrity violation: error #1003`,
|
|
},
|
|
{
|
|
name: "duplicate name same scope not allowed",
|
|
args: args{
|
|
role: func() *projectRole {
|
|
randomUuid := testId(t)
|
|
grpId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
// create a role then return the cloned role with different name
|
|
r := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: grpId,
|
|
GrantThisRoleScope: true,
|
|
ScopeId: proj.PublicId,
|
|
Name: "name" + randomUuid,
|
|
Description: "desc" + randomUuid,
|
|
},
|
|
}
|
|
|
|
require.NoError(t, rw.Create(ctx, r))
|
|
newId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
dupeName := r.Clone().(*projectRole)
|
|
dupeName.PublicId = newId
|
|
return dupeName
|
|
}(),
|
|
},
|
|
wantErr: true,
|
|
wantErrMsg: `db.Create: duplicate key value violates unique constraint "iam_role_project_name_scope_id_uq": unique constraint violation: integrity violation: error #1002`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := tt.args.role.Clone().(*projectRole)
|
|
err := rw.Create(ctx, r)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, tt.wantErrMsg)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, tt.args.role.PublicId)
|
|
|
|
foundGrp := allocProjectRole()
|
|
foundGrp.PublicId = tt.args.role.PublicId
|
|
err = rw.LookupByPublicId(ctx, &foundGrp)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cmp.Diff(r, &foundGrp, protocmp.Transform()))
|
|
require.NotZero(t, foundGrp.CreateTime.AsTime())
|
|
require.NotZero(t, foundGrp.UpdateTime.AsTime())
|
|
baseRole := &testBaseRole{PublicId: tt.args.role.PublicId}
|
|
err = rw.LookupByPublicId(ctx, baseRole)
|
|
require.NoError(t, err)
|
|
require.Equal(t, foundGrp.CreateTime.AsTime(), baseRole.CreateTime.AsTime())
|
|
require.Equal(t, foundGrp.UpdateTime.AsTime(), baseRole.UpdateTime.AsTime())
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_projectRole_Update(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)
|
|
_, proj := TestScopes(t, repo)
|
|
type arg struct {
|
|
updateRole *projectRole
|
|
fieldMask []string
|
|
nullPath []string
|
|
opts []db.Option
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
setupOriginal func(t *testing.T) *projectRole
|
|
createInput func(t *testing.T, original *projectRole) arg
|
|
wantErr bool
|
|
wantRowsUpdate int
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "can update name and description",
|
|
setupOriginal: func(t *testing.T) *projectRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: roleId,
|
|
ScopeId: proj.PublicId,
|
|
Name: testId(t),
|
|
Description: testId(t),
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *projectRole) arg {
|
|
updated := original.Clone().(*projectRole)
|
|
updated.Name = testId(t)
|
|
updated.Description = testId(t)
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"name", "description"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can set name and description null",
|
|
setupOriginal: func(t *testing.T) *projectRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: roleId,
|
|
ScopeId: proj.PublicId,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *projectRole) arg {
|
|
updated := original.Clone().(*projectRole)
|
|
updated.Name = ""
|
|
updated.Description = ""
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{},
|
|
nullPath: []string{"name", "description"},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can update grant_this_role_scope",
|
|
setupOriginal: func(t *testing.T) *projectRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: roleId,
|
|
ScopeId: proj.PublicId,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
GrantThisRoleScope: true,
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *projectRole) arg {
|
|
updated := original.Clone().(*projectRole)
|
|
updated.GrantThisRoleScope = false
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"GrantThisRoleScope"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "can update version",
|
|
setupOriginal: func(t *testing.T) *projectRole {
|
|
roleId, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
original := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: roleId,
|
|
ScopeId: proj.PublicId,
|
|
Name: testId(t),
|
|
Description: "desc",
|
|
},
|
|
}
|
|
require.NoError(t, rw.Create(ctx, original))
|
|
return original
|
|
},
|
|
createInput: func(t *testing.T, original *projectRole) arg {
|
|
updated := original.Clone().(*projectRole)
|
|
updated.Version = uint32(15)
|
|
return arg{
|
|
updateRole: updated,
|
|
fieldMask: []string{"version"},
|
|
nullPath: []string{},
|
|
opts: []db.Option{},
|
|
}
|
|
},
|
|
wantRowsUpdate: 1,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
original := tt.setupOriginal(t)
|
|
args := tt.createInput(t, original)
|
|
updatedRows, err := rw.Update(context.Background(), args.updateRole, args.fieldMask, args.nullPath, args.opts...)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, tt.wantErrMsg)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, args.updateRole.PublicId)
|
|
if tt.wantRowsUpdate == 0 {
|
|
require.Zero(t, updatedRows)
|
|
return
|
|
}
|
|
require.Equal(t, tt.wantRowsUpdate, updatedRows)
|
|
|
|
foundGrp := allocProjectRole()
|
|
foundGrp.PublicId = original.PublicId
|
|
err = rw.LookupByPublicId(ctx, &foundGrp)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cmp.Diff(args.updateRole, &foundGrp, protocmp.Transform()))
|
|
baseRole := &testBaseRole{PublicId: original.PublicId}
|
|
err = rw.LookupByPublicId(ctx, baseRole)
|
|
require.NoError(t, err)
|
|
require.Equal(t, foundGrp.UpdateTime.AsTime(), baseRole.UpdateTime.AsTime())
|
|
require.Equal(t, original.Version+1, foundGrp.Version)
|
|
require.Equal(t, foundGrp.UpdateTime.AsTime(), baseRole.UpdateTime.AsTime())
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_projectRole_Delete(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)
|
|
tests := []struct {
|
|
name string
|
|
setupRole func(ctx context.Context, t *testing.T) *projectRole
|
|
wantRowsDeleted int
|
|
wantErr bool
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "valid",
|
|
setupRole: func(ctx context.Context, t *testing.T) *projectRole {
|
|
role := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
ScopeId: proj.PublicId,
|
|
Name: "test-role",
|
|
Description: "description",
|
|
},
|
|
}
|
|
id, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
role.PublicId = id
|
|
require.NoError(t, rw.Create(ctx, role))
|
|
require.NotEmpty(t, role.PublicId)
|
|
return role
|
|
},
|
|
wantErr: false,
|
|
wantRowsDeleted: 1,
|
|
},
|
|
{
|
|
name: "role-does-not-exist",
|
|
setupRole: func(ctx context.Context, t *testing.T) *projectRole {
|
|
id, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
role := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: id,
|
|
ScopeId: proj.PublicId,
|
|
Name: "test-role",
|
|
Description: "description",
|
|
},
|
|
}
|
|
return role
|
|
},
|
|
wantErr: false,
|
|
wantRowsDeleted: 0,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
createdRole := tc.setupRole(context.Background(), t)
|
|
deletedRows, err := rw.Delete(context.Background(), &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: createdRole.PublicId,
|
|
},
|
|
})
|
|
if tc.wantErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
if tc.wantRowsDeleted == 0 {
|
|
require.Equal(t, tc.wantRowsDeleted, deletedRows)
|
|
return
|
|
}
|
|
require.Equal(t, tc.wantRowsDeleted, deletedRows)
|
|
foundRole := &projectRole{ProjectRole: &store.ProjectRole{PublicId: createdRole.PublicId}}
|
|
err = rw.LookupByPublicId(context.Background(), foundRole)
|
|
require.Error(t, err)
|
|
require.True(t, errors.IsNotFoundError(err))
|
|
|
|
// also check that base table was deleted
|
|
baseRole := &testBaseRole{PublicId: createdRole.PublicId}
|
|
err = rw.LookupByPublicId(context.Background(), baseRole)
|
|
require.Error(t, err)
|
|
require.True(t, errors.IsNotFoundError(err))
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_projectRole_Actions(t *testing.T) {
|
|
r := &projectRole{}
|
|
a := r.Actions()
|
|
assert.Equal(t, a[action.Create.String()], action.Create)
|
|
assert.Equal(t, a[action.Update.String()], action.Update)
|
|
assert.Equal(t, a[action.Read.String()], action.Read)
|
|
assert.Equal(t, a[action.Delete.String()], action.Delete)
|
|
assert.Equal(t, a[action.AddGrants.String()], action.AddGrants)
|
|
assert.Equal(t, a[action.RemoveGrants.String()], action.RemoveGrants)
|
|
assert.Equal(t, a[action.SetGrants.String()], action.SetGrants)
|
|
assert.Equal(t, a[action.AddPrincipals.String()], action.AddPrincipals)
|
|
assert.Equal(t, a[action.RemovePrincipals.String()], action.RemovePrincipals)
|
|
assert.Equal(t, a[action.SetPrincipals.String()], action.SetPrincipals)
|
|
}
|
|
|
|
func Test_projectRole_ResourceType(t *testing.T) {
|
|
t.Parallel()
|
|
role := projectRole{}
|
|
result := role.GetResourceType()
|
|
assert.Equal(t, result, resource.Role)
|
|
}
|
|
|
|
func Test_projectRole_GetScope(t *testing.T) {
|
|
t.Parallel()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
ctx := context.Background()
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
_, proj := TestScopes(t, repo)
|
|
role := projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
ScopeId: proj.PublicId,
|
|
Name: "test-role",
|
|
Description: "description",
|
|
},
|
|
}
|
|
id, err := newRoleId(ctx)
|
|
require.NoError(t, err)
|
|
role.PublicId = id
|
|
require.NoError(t, rw.Create(ctx, role))
|
|
require.NotEmpty(t, role.PublicId)
|
|
scope, err := role.GetScope(context.Background(), rw)
|
|
require.NoError(t, err)
|
|
require.True(t, proto.Equal(proj, scope))
|
|
}
|
|
|
|
func Test_projectRole_Clone(t *testing.T) {
|
|
t.Parallel()
|
|
role1 := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: "r_123456",
|
|
ScopeId: "p_123456",
|
|
Name: "test-role",
|
|
Description: "description",
|
|
CreateTime: timestamp.New(time.Date(2025, 4, 12, 3, 15, 15, 30, time.UTC)),
|
|
UpdateTime: timestamp.New(time.Date(2025, 5, 12, 3, 15, 15, 30, time.UTC)),
|
|
Version: 1,
|
|
},
|
|
GrantScopes: []*RoleGrantScope{
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2025, 3, 12, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_123456",
|
|
ScopeIdOrSpecial: "this",
|
|
},
|
|
},
|
|
}
|
|
role2 := &projectRole{
|
|
ProjectRole: &store.ProjectRole{
|
|
PublicId: "r_abcdef",
|
|
ScopeId: "o_123456",
|
|
Name: "another-role",
|
|
Description: "different description",
|
|
CreateTime: timestamp.New(time.Date(2024, 4, 12, 3, 15, 15, 30, time.UTC)),
|
|
UpdateTime: timestamp.New(time.Date(2024, 5, 12, 3, 15, 15, 30, time.UTC)),
|
|
Version: 1,
|
|
},
|
|
GrantScopes: []*RoleGrantScope{
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2024, 3, 12, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_abcdef",
|
|
ScopeIdOrSpecial: "children",
|
|
},
|
|
{
|
|
CreateTime: timestamp.New(time.Date(2024, 1, 14, 3, 15, 15, 30, time.UTC)),
|
|
RoleId: "r_abcdef",
|
|
ScopeIdOrSpecial: "p_abcdef",
|
|
},
|
|
},
|
|
}
|
|
require.NotEqual(t, role1, role2)
|
|
|
|
t.Run("valid", func(t *testing.T) {
|
|
clone := role1.Clone()
|
|
assert.True(t, proto.Equal(clone.(*projectRole).ProjectRole, role1.ProjectRole))
|
|
assert.Equal(t, clone.(*projectRole).GrantScopes, role1.GrantScopes)
|
|
})
|
|
t.Run("not-equal", func(t *testing.T) {
|
|
role1Clone := role1.Clone()
|
|
assert.True(t, !proto.Equal(role1Clone.(*projectRole).ProjectRole, role2.ProjectRole))
|
|
assert.NotEqual(t, role1Clone.(*projectRole).GrantScopes, role2.GrantScopes)
|
|
})
|
|
}
|
|
|
|
func Test_projectRole_SetTableName(t *testing.T) {
|
|
defaultTableName := defaultProjectRoleTableName
|
|
tests := []struct {
|
|
name string
|
|
initialName string
|
|
setNameTo string
|
|
want string
|
|
}{
|
|
{
|
|
name: "new-name",
|
|
initialName: "",
|
|
setNameTo: "new-name",
|
|
want: "new-name",
|
|
},
|
|
{
|
|
name: "reset to default",
|
|
initialName: "initial",
|
|
setNameTo: "",
|
|
want: defaultTableName,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
def := projectRole{}
|
|
require.Equal(t, defaultTableName, def.TableName())
|
|
s := &projectRole{
|
|
ProjectRole: &store.ProjectRole{},
|
|
tableName: tt.initialName,
|
|
}
|
|
s.SetTableName(tt.setNameTo)
|
|
assert.Equal(t, tt.want, s.TableName())
|
|
})
|
|
}
|
|
}
|
|
|
|
// testBaseRole is used to interact with `iam_role` table for testing purposes
|
|
type testBaseRole struct {
|
|
PublicId string `gorm:"primary_key"`
|
|
ScopeId string
|
|
CreateTime *timestamp.Timestamp
|
|
UpdateTime *timestamp.Timestamp
|
|
}
|
|
|
|
func (t *testBaseRole) GetPublicId() string {
|
|
if t == nil {
|
|
return ""
|
|
}
|
|
return t.PublicId
|
|
}
|
|
|
|
func (t *testBaseRole) TableName() string {
|
|
return "iam_role"
|
|
}
|