This adds several relationships into the resource package that are then made use of to improve the grant validation that runs during Parse. By understanding mappings between ID and resource type the validation checks can far better align with actual allowed grant formats so we can find several common misunderstandings before they simply result in an authorization failure. In the future some of these functions could be used in the logic in the actual ACL checks but it's not currently necessary and I don't want to lump that change in with this. Note that the tests have not meaningfully changed, but they _have_ had to be updated because the stricter validation is actually catching issues.
301 lines
7.2 KiB
Go
301 lines
7.2 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package iam
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/boundary/internal/db"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
"github.com/hashicorp/boundary/internal/iam/store"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func TestRoleGrant_Create(t *testing.T) {
|
|
t.Parallel()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
_, proj := TestScopes(t, repo)
|
|
projRole := TestRole(t, conn, proj.PublicId)
|
|
|
|
type args struct {
|
|
roleId string
|
|
grant string
|
|
opt []Option
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want *RoleGrant
|
|
wantErr bool
|
|
wantIsErr errors.Code
|
|
create bool
|
|
}{
|
|
{
|
|
name: "nil-role",
|
|
args: args{
|
|
roleId: "",
|
|
grant: "id=*;type=*;actions=*",
|
|
},
|
|
wantErr: true,
|
|
wantIsErr: errors.InvalidParameter,
|
|
},
|
|
{
|
|
name: "nil-grant",
|
|
args: args{
|
|
roleId: projRole.PublicId,
|
|
grant: "",
|
|
},
|
|
wantErr: true,
|
|
wantIsErr: errors.InvalidParameter,
|
|
create: true,
|
|
},
|
|
{
|
|
name: "valid",
|
|
args: args{
|
|
roleId: projRole.PublicId,
|
|
grant: "id=*;type=*;actions=*",
|
|
},
|
|
want: func() *RoleGrant {
|
|
g := allocRoleGrant()
|
|
g.RoleId = projRole.PublicId
|
|
g.RawGrant = "id=*;type=*;actions=*"
|
|
g.CanonicalGrant = "id=*;type=*;actions=*"
|
|
return &g
|
|
}(),
|
|
create: true,
|
|
},
|
|
{
|
|
name: "valid-reversed-grant",
|
|
args: args{
|
|
roleId: projRole.PublicId,
|
|
grant: "type=*;actions=*;id=*",
|
|
},
|
|
want: func() *RoleGrant {
|
|
g := allocRoleGrant()
|
|
g.RoleId = projRole.PublicId
|
|
g.RawGrant = "type=*;actions=*;id=*"
|
|
g.CanonicalGrant = "id=*;type=*;actions=*"
|
|
return &g
|
|
}(),
|
|
create: 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 { a := allocRoleGrant(); return &a }(), "1=1")
|
|
got, err := NewRoleGrant(tt.args.roleId, tt.args.grant, tt.args.opt...)
|
|
if tt.wantErr {
|
|
require.Error(err)
|
|
assert.True(errors.Match(errors.T(tt.wantIsErr), err))
|
|
return
|
|
}
|
|
require.NoError(err)
|
|
assert.Equal(tt.want, got)
|
|
if tt.create {
|
|
err = db.New(conn).Create(context.Background(), got)
|
|
if tt.wantErr {
|
|
assert.Error(err)
|
|
return
|
|
} else {
|
|
assert.NoError(err)
|
|
}
|
|
assert.Equal(tt.want.CanonicalGrant, got.CanonicalGrant)
|
|
|
|
// also ensure duplicate grants aren't allowed
|
|
g2 := got.Clone().(*RoleGrant)
|
|
assert.Error(db.New(conn).Create(context.Background(), g2))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRoleGrant_Update(t *testing.T) {
|
|
t.Parallel()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
org, _ := TestScopes(t, repo)
|
|
rw := db.New(conn)
|
|
|
|
t.Run("updates not allowed", func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
r := TestRole(t, conn, org.PublicId)
|
|
roleGrant := TestRoleGrant(t, conn, r.PublicId, "id=*;type=*;actions=*")
|
|
updateRoleGrant := roleGrant.Clone().(*RoleGrant)
|
|
updateRoleGrant.RawGrant = "type=*;actions=*;id=*"
|
|
updatedRows, err := rw.Update(context.Background(), updateRoleGrant, []string{"RawGrant"}, nil)
|
|
require.Error(err)
|
|
assert.Equal(0, updatedRows)
|
|
})
|
|
}
|
|
|
|
func TestRoleGrant_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)
|
|
projRole := TestRole(t, conn, proj.PublicId)
|
|
|
|
type args struct {
|
|
roleId string
|
|
grant string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want *RoleGrant
|
|
wantErr bool
|
|
wantsErrStr string
|
|
deletedRows int
|
|
}{
|
|
{
|
|
name: "nil-role",
|
|
args: args{
|
|
roleId: "",
|
|
grant: "id=*;type=*;actions=*",
|
|
},
|
|
wantErr: true,
|
|
wantsErrStr: "is not set",
|
|
},
|
|
{
|
|
name: "nil-grant",
|
|
args: args{
|
|
roleId: projRole.PublicId,
|
|
grant: "",
|
|
},
|
|
wantErr: true,
|
|
wantsErrStr: "is not set",
|
|
},
|
|
{
|
|
name: "invalid",
|
|
args: args{
|
|
roleId: projRole.PublicId,
|
|
grant: "id=u_edcb;actions=read,update",
|
|
},
|
|
},
|
|
{
|
|
name: "valid",
|
|
args: args{
|
|
roleId: projRole.PublicId,
|
|
grant: "id=u_bcde;actions=read,update",
|
|
},
|
|
deletedRows: 1,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
r := allocRoleGrant()
|
|
db.TestDeleteWhere(t, conn, &r, "1=1")
|
|
rg, err := NewRoleGrant(projRole.PublicId, "id=u_bcde;actions=read,update")
|
|
require.NoError(err)
|
|
require.NoError(rw.Create(context.Background(), rg))
|
|
rg, err = NewRoleGrant(projRole.PublicId, "id=u_cdef;actions=read,update")
|
|
require.NoError(err)
|
|
require.NoError(rw.Create(context.Background(), rg))
|
|
|
|
rg = &RoleGrant{
|
|
RoleGrant: &store.RoleGrant{
|
|
RoleId: tt.args.roleId,
|
|
RawGrant: tt.args.grant,
|
|
CanonicalGrant: tt.args.grant,
|
|
},
|
|
}
|
|
deleted, err := rw.Delete(context.Background(), rg)
|
|
if tt.wantErr {
|
|
require.Error(err)
|
|
assert.Contains(err.Error(), tt.wantsErrStr)
|
|
return
|
|
}
|
|
require.NoError(err)
|
|
assert.Equal(tt.deletedRows, deleted)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRoleGrant_Clone(t *testing.T) {
|
|
t.Parallel()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
repo := TestRepo(t, conn, wrapper)
|
|
t.Run("valid", func(t *testing.T) {
|
|
assert := assert.New(t)
|
|
s := testOrg(t, repo, "", "")
|
|
role := TestRole(t, conn, s.PublicId)
|
|
|
|
g, err := NewRoleGrant(role.PublicId, "id=*;type=*;actions=*")
|
|
assert.NoError(err)
|
|
assert.NotNil(g)
|
|
assert.Equal(g.RoleId, role.PublicId)
|
|
assert.Equal(g.RawGrant, "id=*;type=*;actions=*")
|
|
|
|
cp := g.Clone()
|
|
assert.True(proto.Equal(cp.(*RoleGrant).RoleGrant, g.RoleGrant))
|
|
})
|
|
t.Run("not-equal", func(t *testing.T) {
|
|
require, assert := require.New(t), assert.New(t)
|
|
s := testOrg(t, repo, "", "")
|
|
role := TestRole(t, conn, s.PublicId)
|
|
|
|
g, err := NewRoleGrant(role.PublicId, "id=*;type=*;actions=*")
|
|
assert.NoError(err)
|
|
require.NotNil(g)
|
|
assert.Equal(g.RoleId, role.PublicId)
|
|
assert.Equal(g.RawGrant, "id=*;type=*;actions=*")
|
|
|
|
g2, err := NewRoleGrant(role.PublicId, "id=u_foo;actions=read")
|
|
assert.NoError(err)
|
|
require.NotNil(g2)
|
|
assert.Equal(g2.RoleId, role.PublicId)
|
|
assert.Equal(g2.RawGrant, "id=u_foo;actions=read")
|
|
|
|
cp := g.Clone()
|
|
assert.True(!proto.Equal(cp.(*RoleGrant).RoleGrant, g2.RoleGrant))
|
|
})
|
|
}
|
|
|
|
func TestRoleGrant_SetTableName(t *testing.T) {
|
|
defaultTableName := defaultRoleGrantTable
|
|
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) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
def := allocRoleGrant()
|
|
require.Equal(defaultTableName, def.TableName())
|
|
s := &RoleGrant{
|
|
RoleGrant: &store.RoleGrant{},
|
|
tableName: tt.initialName,
|
|
}
|
|
s.SetTableName(tt.setNameTo)
|
|
assert.Equal(tt.want, s.TableName())
|
|
})
|
|
}
|
|
}
|