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/daemon/controller/handlers/aliases/alias_service_test.go

1525 lines
47 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package aliases_test
import (
"context"
"errors"
"fmt"
"slices"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/alias/target"
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/aliases"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
authpb "github.com/hashicorp/boundary/internal/gen/controller/auth"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/requests"
"github.com/hashicorp/boundary/internal/server"
"github.com/hashicorp/boundary/internal/target/tcp"
"github.com/hashicorp/boundary/internal/types/scope"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/aliases"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
func TestGet(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrap)
iamRepo := iam.TestRepo(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repoFn := func() (*target.Repository, error) {
return target.NewRepository(ctx, rw, rw, kmsCache)
}
_, p := iam.TestScopes(t, iamRepo)
tar := tcp.TestTarget(ctx, t, conn, p.GetPublicId(), "testTarget")
og := target.TestAlias(t, rw, "test.get", target.WithName("alias name"), target.WithDescription("default"), target.WithDestinationId(tar.GetPublicId()), target.WithHostId("hst_1234567890"))
toMerge := &pbs.GetAliasRequest{
Id: og.GetPublicId(),
}
globalScopeInfo := &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String(), Name: "global", Description: "Global Scope"}
wantAlias := &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
ScopeId: "global",
Scope: globalScopeInfo,
Name: wrapperspb.String(og.GetName()),
Description: wrapperspb.String(og.GetDescription()),
CreatedTime: og.CreateTime.GetTimestamp(),
UpdatedTime: og.UpdateTime.GetTimestamp(),
Value: og.GetValue(),
Version: 1,
DestinationId: wrapperspb.String(og.GetDestinationId()),
Attrs: &pb.Alias_TargetAliasAttributes{
TargetAliasAttributes: &pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: og.GetHostId(),
},
},
},
AuthorizedActions: testAuthorizedActions,
}
cases := []struct {
name string
scopeId string
req *pbs.GetAliasRequest
res *pbs.GetAliasResponse
err error
}{
{
name: "Get an Existing Alias",
scopeId: og.GetScopeId(),
req: &pbs.GetAliasRequest{Id: og.GetPublicId()},
res: &pbs.GetAliasResponse{Item: wantAlias},
},
{
name: "Get a non existent Alias",
req: &pbs.GetAliasRequest{Id: globals.TargetAliasPrefix + "_DoesntExis"},
res: nil,
err: handlers.ApiErrorWithCode(codes.NotFound),
},
{
name: "Wrong id prefix",
req: &pbs.GetAliasRequest{Id: "j_1234567890"},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "space in id",
req: &pbs.GetAliasRequest{Id: globals.TargetAliasPrefix + "_1 23456789"},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
req := proto.Clone(toMerge).(*pbs.GetAliasRequest)
proto.Merge(req, tc.req)
s, err := aliases.NewService(ctx, repoFn, iamRepoFn, 1000)
require.NoError(err, "Couldn't create new alias service.")
got, gErr := s.GetAlias(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "GetAlias(%+v) got error %v, wanted %v", req, gErr, tc.err)
}
assert.Empty(cmp.Diff(
got,
tc.res,
protocmp.Transform(),
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
), "GetAlias(%q) got response\n%q, wanted\n%q", req, got, tc.res)
})
}
}
func TestList(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrap)
iamRepo := iam.TestRepo(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repo, err := target.NewRepository(ctx, rw, rw, kmsCache)
require.NoError(t, err)
repoFn := func() (*target.Repository, error) {
return repo, nil
}
var wantGlobalAliases []*pb.Alias
for i := 0; i < 10; i++ {
gg := target.TestAlias(t, rw, fmt.Sprintf("test%d.alias", i))
wantGlobalAliases = append(wantGlobalAliases, &pb.Alias{
Type: "target",
Id: gg.GetPublicId(),
ScopeId: gg.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String(), Name: "global", Description: "Global Scope"},
CreatedTime: gg.GetCreateTime().GetTimestamp(),
UpdatedTime: gg.GetUpdateTime().GetTimestamp(),
Version: 1,
Value: gg.GetValue(),
AuthorizedActions: testAuthorizedActions,
})
}
slices.Reverse(wantGlobalAliases)
cases := []struct {
name string
req *pbs.ListAliasesRequest
res *pbs.ListAliasesResponse
err error
}{
{
name: "List Global Aliases",
req: &pbs.ListAliasesRequest{ScopeId: "global"},
res: &pbs.ListAliasesResponse{
Items: wantGlobalAliases,
EstItemCount: uint32(len(wantGlobalAliases)),
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
},
},
{
name: "List global aliases recursively",
req: &pbs.ListAliasesRequest{ScopeId: "global", Recursive: true},
res: &pbs.ListAliasesResponse{
Items: wantGlobalAliases,
EstItemCount: uint32(len(wantGlobalAliases)),
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
},
},
{
name: "Filter to no aliases",
req: &pbs.ListAliasesRequest{ScopeId: "global", Recursive: true, Filter: `"/item/id"=="doesntmatch"`},
res: &pbs.ListAliasesResponse{
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
},
},
{
name: "Filter Bad Format",
req: &pbs.ListAliasesRequest{ScopeId: "global", Filter: `"//id/"=="bad"`},
err: handlers.InvalidArgumentErrorf("bad format", nil),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
s, err := aliases.NewService(ctx, repoFn, iamRepoFn, 1000)
require.NoError(err, "Couldn't create new alias service.")
// Test with a non-anon user
got, gErr := s.ListAliases(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId()), tc.req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "ListAliases(%q) got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err)
return
}
require.NoError(gErr)
assert.Empty(cmp.Diff(
got,
tc.res,
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListAliasesResponse{}, "list_token"),
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
), "ListAliases(%q) got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res)
// Test the anon case
got, gErr = s.ListAliases(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId(), auth.WithUserId(globals.AnonymousUserId)), tc.req)
require.NoError(gErr)
assert.Len(got.Items, len(tc.res.Items))
for _, item := range got.GetItems() {
require.Nil(item.CreatedTime)
require.Nil(item.UpdatedTime)
require.Zero(item.Version)
}
})
}
}
func aliasToProto(u *target.Alias, si *scopes.ScopeInfo, authorizedActions []string) *pb.Alias {
pu := &pb.Alias{
Type: "target",
Id: u.GetPublicId(),
Value: u.GetValue(),
ScopeId: u.GetScopeId(),
Scope: si,
CreatedTime: u.GetCreateTime().GetTimestamp(),
UpdatedTime: u.GetUpdateTime().GetTimestamp(),
Version: u.GetVersion(),
AuthorizedActions: testAuthorizedActions,
}
if u.GetName() != "" {
pu.Name = wrapperspb.String(u.GetName())
}
if u.GetDestinationId() != "" {
pu.DestinationId = wrapperspb.String(u.GetDestinationId())
}
if u.GetHostId() != "" {
pu.Attrs = &pb.Alias_TargetAliasAttributes{
TargetAliasAttributes: &pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: u.GetHostId(),
},
},
}
}
if u.GetDescription() != "" {
pu.Description = wrapperspb.String(u.GetDescription())
}
return pu
}
func TestListPagination(t *testing.T) {
// Set database read timeout to avoid duplicates in response
oldReadTimeout := globals.RefreshReadLookbackDuration
globals.RefreshReadLookbackDuration = 0
t.Cleanup(func() {
globals.RefreshReadLookbackDuration = oldReadTimeout
})
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
sqlDB, err := conn.SqlDB(ctx)
require.NoError(t, err)
wrapper := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrapper)
rw := db.New(conn)
repo, err := target.NewRepository(ctx, rw, rw, kmsCache)
require.NoError(t, err)
repoFn := func() (*target.Repository, error) {
return repo, nil
}
iamRepo := iam.TestRepo(t, conn, wrapper)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
tokenRepoFn := func() (*authtoken.Repository, error) {
return authtoken.NewRepository(ctx, rw, rw, kmsCache)
}
serversRepoFn := func() (*server.Repository, error) {
return server.NewRepository(ctx, rw, rw, kmsCache)
}
tokenRepo, err := tokenRepoFn()
require.NoError(t, err)
// Run analyze to update postgres meta tables
_, err = sqlDB.ExecContext(ctx, "analyze")
require.NoError(t, err)
authMethod := password.TestAuthMethods(t, conn, "global", 1)[0]
acct := password.TestAccount(t, conn, authMethod.GetPublicId(), "test_user")
u := iam.TestUser(t, iamRepo, "global", iam.WithAccountIds(acct.PublicId))
// add roles to be able to see all users
allowedRole := iam.TestRole(t, conn, "global")
iam.TestRoleGrant(t, conn, allowedRole.GetPublicId(), "id=*;type=*;actions=*")
iam.TestUserRole(t, conn, allowedRole.GetPublicId(), u.GetPublicId())
at, err := tokenRepo.CreateAuthToken(ctx, u, acct.GetPublicId())
require.NoError(t, err)
// Test without anon user
requestInfo := authpb.RequestInfo{
TokenFormat: uint32(auth.AuthTokenTypeBearer),
PublicId: at.GetPublicId(),
Token: at.GetToken(),
}
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kmsCache, &requestInfo)
var globalAliases []*pb.Alias
globalScopeInfo := &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String(), Name: "global", Description: "Global Scope"}
var safeToDeleteAlias string
for i := 0; i < 10; i++ {
gg := target.TestAlias(t, rw, fmt.Sprintf("test%d.alias", i))
globalAliases = append(globalAliases, aliasToProto(gg, globalScopeInfo, testAuthorizedActions))
safeToDeleteAlias = gg.GetPublicId()
}
slices.Reverse(globalAliases)
a, err := aliases.NewService(ctx, repoFn, iamRepoFn, 1000)
require.NoError(t, err, "Couldn't create new alias service.")
// Run analyze to update postgres estimates
_, err = sqlDB.ExecContext(context.Background(), "analyze")
require.NoError(t, err)
itemCount := uint32(len(globalAliases))
testPageSize := int((itemCount - 2) / 2)
// Start paginating, recursively
req := &pbs.ListAliasesRequest{
ScopeId: "global",
Recursive: true,
Filter: "",
ListToken: "",
PageSize: uint32(testPageSize),
}
got, err := a.ListAliases(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), testPageSize)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListAliasesResponse{
Items: globalAliases[0:testPageSize],
ResponseType: "delta",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
// In addition to the added aliases, there are the aliases added
// by the test setup when specifying the permissions of the
// requester
EstItemCount: itemCount,
},
protocmp.SortRepeated(func(a, b string) bool {
return strings.Compare(a, b) < 0
}),
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListAliasesResponse{}, "list_token"),
),
)
// Request second page
req.ListToken = got.ListToken
got, err = a.ListAliases(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), testPageSize)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListAliasesResponse{
Items: globalAliases[testPageSize : testPageSize*2],
ResponseType: "delta",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: itemCount,
},
protocmp.Transform(),
protocmp.SortRepeated(func(a, b string) bool {
return strings.Compare(a, b) < 0
}),
protocmp.IgnoreFields(&pbs.ListAliasesResponse{}, "list_token"),
),
)
// Request rest of results
req.ListToken = got.ListToken
req.PageSize = 10
got, err = a.ListAliases(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 2)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListAliasesResponse{
Items: globalAliases[testPageSize*2:],
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: itemCount,
},
protocmp.Transform(),
protocmp.SortRepeated(func(a, b string) bool {
return strings.Compare(a, b) < 0
}),
protocmp.IgnoreFields(&pbs.ListAliasesResponse{}, "list_token"),
),
)
// Update 2 aliases and see them in the refresh
g1 := globalAliases[len(globalAliases)-1]
g1.Description = wrapperspb.String("updated1")
resp1, err := a.UpdateAlias(ctx, &pbs.UpdateAliasRequest{
Id: g1.GetId(),
Item: &pb.Alias{Description: g1.GetDescription(), Version: g1.GetVersion()},
UpdateMask: &field_mask.FieldMask{Paths: []string{"description"}},
})
require.NoError(t, err)
g1.UpdatedTime = resp1.GetItem().GetUpdatedTime()
g1.Version = resp1.GetItem().GetVersion()
globalAliases = append([]*pb.Alias{g1}, globalAliases[:len(globalAliases)-1]...)
g2 := globalAliases[len(globalAliases)-1]
g2.Description = wrapperspb.String("updated2")
resp2, err := a.UpdateAlias(ctx, &pbs.UpdateAliasRequest{
Id: g2.GetId(),
Item: &pb.Alias{Description: g2.GetDescription(), Version: g2.GetVersion()},
UpdateMask: &field_mask.FieldMask{Paths: []string{"description"}},
})
require.NoError(t, err)
g2.UpdatedTime = resp2.GetItem().GetUpdatedTime()
g2.Version = resp2.GetItem().GetVersion()
globalAliases = append([]*pb.Alias{g2}, globalAliases[:len(globalAliases)-1]...)
// Run analyze to update postgres estimates
_, err = sqlDB.ExecContext(ctx, "analyze")
require.NoError(t, err)
// Request updated results
req.ListToken = got.ListToken
req.PageSize = 1
got, err = a.ListAliases(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListAliasesResponse{
Items: []*pb.Alias{globalAliases[0]},
ResponseType: "delta",
SortBy: "updated_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: itemCount,
},
protocmp.Transform(),
protocmp.SortRepeated(func(a, b string) bool {
return strings.Compare(a, b) < 0
}),
protocmp.IgnoreFields(&pbs.ListAliasesResponse{}, "list_token"),
),
)
// Get next page
req.ListToken = got.ListToken
got, err = a.ListAliases(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
// Compare without comparing the list token
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListAliasesResponse{
Items: []*pb.Alias{globalAliases[1]},
ResponseType: "complete",
SortBy: "updated_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: itemCount,
},
protocmp.Transform(),
protocmp.SortRepeated(func(a, b string) bool {
return strings.Compare(a, b) < 0
}),
protocmp.IgnoreFields(&pbs.ListAliasesResponse{}, "list_token"),
),
)
// Request new page with filter requiring looping
// to fill the page.
req.ListToken = ""
req.PageSize = 1
req.Filter = fmt.Sprintf(`"/item/id"==%q or "/item/id"==%q`, globalAliases[len(globalAliases)-2].Id, globalAliases[len(globalAliases)-1].Id)
got, err = a.ListAliases(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListAliasesResponse{
Items: []*pb.Alias{globalAliases[len(globalAliases)-2]},
ResponseType: "delta",
SortBy: "created_time",
SortDir: "desc",
// Should be empty again
RemovedIds: nil,
EstItemCount: itemCount,
},
protocmp.Transform(),
protocmp.SortRepeated(func(a, b string) bool {
return strings.Compare(a, b) < 0
}),
protocmp.IgnoreFields(&pbs.ListAliasesResponse{}, "list_token"),
),
)
req.ListToken = got.ListToken
// Get the second page
got, err = a.ListAliases(ctx, req)
require.NoError(t, err)
require.Len(t, got.GetItems(), 1)
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListAliasesResponse{
Items: []*pb.Alias{globalAliases[len(globalAliases)-1]},
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: itemCount,
},
protocmp.Transform(),
protocmp.SortRepeated(func(a, b string) bool {
return strings.Compare(a, b) < 0
}),
protocmp.IgnoreFields(&pbs.ListAliasesResponse{}, "list_token"),
),
)
_, err = repo.DeleteAlias(ctx, safeToDeleteAlias)
require.NoError(t, err)
req.ListToken = got.ListToken
got, err = a.ListAliases(ctx, req)
require.NoError(t, err)
assert.Empty(t,
cmp.Diff(
got,
&pbs.ListAliasesResponse{
Items: nil,
ResponseType: "complete",
SortBy: "updated_time",
SortDir: "desc",
RemovedIds: []string{safeToDeleteAlias},
EstItemCount: itemCount,
},
protocmp.Transform(),
protocmp.SortRepeated(func(a, b string) bool {
return strings.Compare(a, b) < 0
}),
protocmp.IgnoreFields(&pbs.ListAliasesResponse{}, "list_token"),
),
)
}
func TestDelete(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrap)
iamRepo := iam.TestRepo(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
_, p := iam.TestScopes(t, iamRepo)
repoFn := func() (*target.Repository, error) {
return target.NewRepository(ctx, rw, rw, kmsCache)
}
tar := tcp.TestTarget(ctx, t, conn, p.GetPublicId(), "testTarget")
og := target.TestAlias(t, rw, "test.delete", target.WithDescription("default"), target.WithDestinationId(tar.GetPublicId()), target.WithHostId("hst_1234567890"))
s, err := aliases.NewService(context.Background(), repoFn, iamRepoFn, 1000)
require.NoError(t, err, "Error when getting new alias service.")
cases := []struct {
name string
scopeId string
req *pbs.DeleteAliasRequest
res *pbs.DeleteAliasResponse
err error
}{
{
name: "Delete an Existing Alias",
scopeId: og.GetScopeId(),
req: &pbs.DeleteAliasRequest{
Id: og.GetPublicId(),
},
},
{
name: "Delete bad alias id",
scopeId: og.GetScopeId(),
req: &pbs.DeleteAliasRequest{
Id: globals.TargetAliasPrefix + "_doesntexis",
},
err: handlers.ApiErrorWithCode(codes.NotFound),
},
{
name: "Bad Alias Id formatting",
scopeId: og.GetScopeId(),
req: &pbs.DeleteAliasRequest{
Id: "bad_format",
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, gErr := s.DeleteAlias(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), tc.req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "DeleteAlias(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
}
assert.EqualValuesf(tc.res, got, "DeleteAlias(%+v) got response %q, wanted %q", tc.req, got, tc.res)
})
}
}
func TestDelete_twice(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrap)
iamRepo := iam.TestRepo(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repoFn := func() (*target.Repository, error) {
return target.NewRepository(ctx, rw, rw, kmsCache)
}
og := target.TestAlias(t, rw, "test.delete", target.WithDescription("default"))
s, err := aliases.NewService(context.Background(), repoFn, iamRepoFn, 1000)
require.NoError(t, err, "Error when getting new alias service")
scopeId := og.GetScopeId()
req := &pbs.DeleteAliasRequest{
Id: og.GetPublicId(),
}
ctx = auth.DisabledAuthTestContext(iamRepoFn, scopeId)
_, gErr := s.DeleteAlias(ctx, req)
assert.NoError(t, gErr, "First attempt")
_, gErr = s.DeleteAlias(ctx, req)
assert.Error(t, gErr, "Second attempt")
assert.True(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.NotFound)), "Expected permission denied for the second delete.")
}
func TestCreate(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrap)
iamRepo := iam.TestRepo(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
_, p := iam.TestScopes(t, iamRepo)
repoFn := func() (*target.Repository, error) {
return target.NewRepository(ctx, rw, rw, kmsCache)
}
tar := tcp.TestTarget(ctx, t, conn, p.GetPublicId(), "testTarget")
al := target.TestAlias(t, rw, "test.duplicated.create",
target.WithName("alias name"),
target.WithDestinationId(tar.GetPublicId()))
globalScopeInfo := &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String(), Name: "global", Description: "Global Scope"}
cases := []struct {
name string
req *pbs.CreateAliasRequest
res *pbs.CreateAliasResponse
errContains string
}{
{
name: "Create a valid Alias",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Value: "valid.alias",
}},
res: &pbs.CreateAliasResponse{
Uri: fmt.Sprintf("aliases/%s_", globals.TargetAliasPrefix),
Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Scope: globalScopeInfo,
Value: "valid.alias",
Version: 1,
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Alias to existing target",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Value: "target-assigned.valid.alias",
DestinationId: wrapperspb.String(tar.GetPublicId()),
}},
res: &pbs.CreateAliasResponse{
Uri: fmt.Sprintf("aliases/%s_", globals.TargetAliasPrefix),
Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Scope: globalScopeInfo,
Value: "target-assigned.valid.alias",
DestinationId: wrapperspb.String(tar.GetPublicId()),
Version: 1,
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Alias to poorly formatted target id",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Value: "target-assigned.valid.alias",
DestinationId: wrapperspb.String("this is not a valid target id"),
}},
errContains: `Incorrectly formatted identifier.`,
},
{
name: "Alias to existing target with static host id",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Value: "target-assigned.valid.alias.two",
Attrs: &pb.Alias_TargetAliasAttributes{
TargetAliasAttributes: &pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "hst_1234567890",
},
},
},
DestinationId: wrapperspb.String(tar.GetPublicId()),
}},
res: &pbs.CreateAliasResponse{
Uri: fmt.Sprintf("aliases/%s_", globals.TargetAliasPrefix),
Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Scope: globalScopeInfo,
Value: "target-assigned.valid.alias.two",
Attrs: &pb.Alias_TargetAliasAttributes{
TargetAliasAttributes: &pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "hst_1234567890",
},
},
},
DestinationId: wrapperspb.String(tar.GetPublicId()),
Version: 1,
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Alias to existing target with dynamic host id",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Value: "target-assigned.valid.alias.three",
Attrs: &pb.Alias_TargetAliasAttributes{
TargetAliasAttributes: &pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "hplg_1234567890",
},
},
},
DestinationId: wrapperspb.String(tar.GetPublicId()),
}},
res: &pbs.CreateAliasResponse{
Uri: fmt.Sprintf("aliases/%s_", globals.TargetAliasPrefix),
Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Scope: globalScopeInfo,
Value: "target-assigned.valid.alias.three",
Attrs: &pb.Alias_TargetAliasAttributes{
TargetAliasAttributes: &pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "hplg_1234567890",
},
},
},
DestinationId: wrapperspb.String(tar.GetPublicId()),
Version: 1,
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Omitting the alias type",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
ScopeId: scope.Global.String(),
Value: "target-assigned.valid.alias",
DestinationId: wrapperspb.String(tar.GetPublicId()),
}},
errContains: `This field is required`,
},
{
name: "host id with no destination target",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Value: "host-id.no-destination.alias",
Attrs: &pb.Alias_TargetAliasAttributes{
TargetAliasAttributes: &pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "hst_1234567890",
},
},
},
}},
errContains: `This field is required when 'attributes.authorize_session_arguments.host_id' is specified.`,
},
{
name: "improperly formatted host id",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Value: "bad-host-id.alias",
DestinationId: wrapperspb.String(tar.GetPublicId()),
Attrs: &pb.Alias_TargetAliasAttributes{
TargetAliasAttributes: &pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "badid_1234567890",
},
},
},
}},
errContains: `Incorrectly formatted identifier.`,
},
{
name: "Alias to non existing target",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Value: "unknowntarget.alias",
DestinationId: wrapperspb.String("ttcp_1234567890"),
}},
errContains: `target with specified destination id "ttcp_1234567890" was not found`,
},
{
name: "Duplicate Alias Value",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Value: al.GetValue(),
}},
errContains: `alias value "test.duplicated.create" is already in use`,
},
{
name: "Duplicate Alias Name",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Value: "duplicate.alias.name",
Name: wrapperspb.String(al.GetName()),
}},
errContains: `name "alias name" is already in use`,
},
{
name: "must be in global scope",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: p.GetPublicId(),
Value: "must.be.in.global.scope",
}},
errContains: `{name: "scope_id", desc: "This field is missing or improperly formatted."}`,
},
{
name: "Can't specify Id",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
Id: globals.TargetAliasPrefix + "_notallowed",
}},
res: nil,
errContains: "This is a read only field.",
},
{
name: "Can't specify Created Time",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
CreatedTime: timestamppb.Now(),
}},
res: nil,
errContains: "This is a read only field.",
},
{
name: "Can't specify Update Time",
req: &pbs.CreateAliasRequest{Item: &pb.Alias{
Type: "target",
ScopeId: scope.Global.String(),
UpdatedTime: timestamppb.Now(),
}},
res: nil,
errContains: "This is a read only field.",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
s, err := aliases.NewService(context.Background(), repoFn, iamRepoFn, 1000)
require.NoError(err, "Error when getting new alias service.")
got, gErr := s.CreateAlias(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetItem().GetScopeId()), tc.req)
if tc.errContains != "" {
require.Error(gErr)
assert.ErrorContains(gErr, tc.errContains)
return
}
require.NoError(gErr)
assert.Contains(got.GetUri(), tc.res.Uri)
assert.True(strings.HasPrefix(got.GetItem().GetId(), globals.TargetAliasPrefix+"_"))
// Clear all values which are hard to compare against.
got.Uri, tc.res.Uri = "", ""
got.Item.Id, tc.res.Item.Id = "", ""
got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil
assert.Empty(cmp.Diff(
got,
tc.res,
protocmp.Transform(),
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
), "CreateAlias(%q) got response %q, wanted %q", tc.req, got, tc.res)
})
}
}
func TestUpdate(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrap)
iamRepo := iam.TestRepo(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
_, p := iam.TestScopes(t, iamRepo)
tar := tcp.TestTarget(ctx, t, conn, p.GetPublicId(), "testTarget")
tar2 := tcp.TestTarget(ctx, t, conn, p.GetPublicId(), "testTarget2")
repoFn := func() (*target.Repository, error) {
return target.NewRepository(ctx, rw, rw, kmsCache)
}
og := target.TestAlias(t, rw, "default",
target.WithDestinationId(tar.GetPublicId()),
target.WithName("default"),
target.WithDescription("default"))
var ogVersion uint32 = 1
resetAlias := func(version uint32) {
repo, err := repoFn()
require.NoError(t, err, "Couldn't get a new repo")
og, _, err = repo.UpdateAlias(ctx, og, version, []string{"Name", "Description", "DestinationId", "Value"})
require.NoError(t, err, "Failed to reset the alias")
ogVersion = og.GetVersion()
}
created := og.GetCreateTime().GetTimestamp().AsTime()
toMerge := &pbs.UpdateAliasRequest{
Id: og.GetPublicId(),
}
globalScopeInfo := &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String(), Name: "global", Description: "Global Scope"}
tested, err := aliases.NewService(ctx, repoFn, iamRepoFn, 1000)
require.NoError(t, err, "Error creating new service")
cases := []struct {
name string
scopeId string
req *pbs.UpdateAliasRequest
res *pbs.UpdateAliasResponse
err error
}{
{
name: "Update an Existing Alias",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name", "description"},
},
Item: &pb.Alias{
Name: wrapperspb.String("updated"),
Description: wrapperspb.String("updated"),
},
},
res: &pbs.UpdateAliasResponse{
Item: &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
ScopeId: og.GetScopeId(),
Scope: globalScopeInfo,
Value: "default",
DestinationId: wrapperspb.String(tar.GetPublicId()),
Name: wrapperspb.String("updated"),
Description: wrapperspb.String("updated"),
CreatedTime: og.GetCreateTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Multiple Paths in single string",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name,description"},
},
Item: &pb.Alias{
Name: wrapperspb.String("updated"),
Description: wrapperspb.String("updated"),
},
},
res: &pbs.UpdateAliasResponse{
Item: &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
ScopeId: og.GetScopeId(),
Scope: globalScopeInfo,
Value: "default",
DestinationId: wrapperspb.String(tar.GetPublicId()),
Name: wrapperspb.String("updated"),
Description: wrapperspb.String("updated"),
CreatedTime: og.GetCreateTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "No Update Mask",
req: &pbs.UpdateAliasRequest{
Item: &pb.Alias{
Description: wrapperspb.String("updated desc"),
},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "No Paths in Mask",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{Paths: []string{}},
Item: &pb.Alias{
Description: wrapperspb.String("updated desc"),
},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Only non-existent paths in Mask",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{Paths: []string{"nonexistant_field"}},
Item: &pb.Alias{
Description: wrapperspb.String("updated desc"),
},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Unset Name",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name"},
},
Item: &pb.Alias{
Description: wrapperspb.String("ignored"),
},
},
res: &pbs.UpdateAliasResponse{
Item: &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
ScopeId: og.GetScopeId(),
Scope: globalScopeInfo,
Value: "default",
DestinationId: wrapperspb.String(tar.GetPublicId()),
Description: wrapperspb.String("default"),
CreatedTime: og.GetCreateTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Update Only Name",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name"},
},
Item: &pb.Alias{
Name: wrapperspb.String("updated"),
Description: wrapperspb.String("ignored"),
},
},
res: &pbs.UpdateAliasResponse{
Item: &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
Name: wrapperspb.String("updated"),
ScopeId: og.GetScopeId(),
Scope: globalScopeInfo,
Value: "default",
DestinationId: wrapperspb.String(tar.GetPublicId()),
Description: wrapperspb.String("default"),
CreatedTime: og.GetCreateTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Update Only value",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"value"},
},
Item: &pb.Alias{
Name: wrapperspb.String("ignored"),
Value: "updated",
},
},
res: &pbs.UpdateAliasResponse{
Item: &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
ScopeId: og.GetScopeId(),
Scope: globalScopeInfo,
Value: "updated",
DestinationId: wrapperspb.String(tar.GetPublicId()),
Name: wrapperspb.String("default"),
Description: wrapperspb.String("default"),
CreatedTime: og.GetCreateTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Update destination id",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"destination_id"},
},
Item: &pb.Alias{
Name: wrapperspb.String("ignored"),
DestinationId: wrapperspb.String("invalid format for targets"),
},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "unset value",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"value"},
},
Item: &pb.Alias{
Name: wrapperspb.String("ignored"),
},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Update Only destination id",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"destination_id"},
},
Item: &pb.Alias{
Value: "ignored",
DestinationId: wrapperspb.String(tar2.GetPublicId()),
},
},
res: &pbs.UpdateAliasResponse{
Item: &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
ScopeId: og.GetScopeId(),
Scope: globalScopeInfo,
Value: "default",
DestinationId: wrapperspb.String(tar2.GetPublicId()),
Name: wrapperspb.String("default"),
Description: wrapperspb.String("default"),
CreatedTime: og.GetCreateTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Unset destination id",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"destination_id"},
},
Item: &pb.Alias{
Value: "ignored",
},
},
res: &pbs.UpdateAliasResponse{
Item: &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
ScopeId: og.GetScopeId(),
Scope: globalScopeInfo,
Value: "default",
Name: wrapperspb.String("default"),
Description: wrapperspb.String("default"),
CreatedTime: og.GetCreateTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Update Only Description",
scopeId: og.GetScopeId(),
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"description"},
},
Item: &pb.Alias{
Description: wrapperspb.String("updated"),
},
},
res: &pbs.UpdateAliasResponse{
Item: &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
ScopeId: og.GetScopeId(),
Scope: globalScopeInfo,
Value: "default",
DestinationId: wrapperspb.String(tar.GetPublicId()),
Name: wrapperspb.String("default"),
Description: wrapperspb.String("updated"),
CreatedTime: og.GetCreateTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Update a Non Existing Alias",
req: &pbs.UpdateAliasRequest{
Id: globals.TargetAliasPrefix + "_DoesntExis",
UpdateMask: &field_mask.FieldMask{
Paths: []string{"description"},
},
Item: &pb.Alias{
Description: wrapperspb.String("desc"),
},
},
err: handlers.ApiErrorWithCode(codes.NotFound),
},
{
name: "Cant change Id",
req: &pbs.UpdateAliasRequest{
Id: og.GetPublicId(),
UpdateMask: &field_mask.FieldMask{
Paths: []string{"id"},
},
Item: &pb.Alias{
Id: globals.TargetAliasPrefix + "_somethinge",
Description: wrapperspb.String("new desc"),
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Cant use invalid host id",
req: &pbs.UpdateAliasRequest{
Id: og.GetPublicId(),
UpdateMask: &field_mask.FieldMask{
Paths: []string{"host_id"},
},
Item: &pb.Alias{
Description: wrapperspb.String("new desc"),
Attrs: &pb.Alias_TargetAliasAttributes{
&pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "badid_1234567890",
},
},
},
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Update with static host id",
req: &pbs.UpdateAliasRequest{
Id: og.GetPublicId(),
UpdateMask: &field_mask.FieldMask{
Paths: []string{"attributes.authorize_session_arguments.host_id"},
},
Item: &pb.Alias{
Description: wrapperspb.String("new desc"),
Attrs: &pb.Alias_TargetAliasAttributes{
&pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "hst_1234567890",
},
},
},
},
},
res: &pbs.UpdateAliasResponse{
Item: &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
Name: wrapperspb.String("default"),
ScopeId: og.GetScopeId(),
Scope: globalScopeInfo,
Value: "default",
DestinationId: wrapperspb.String(tar.GetPublicId()),
Description: wrapperspb.String("default"),
CreatedTime: og.GetCreateTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Attrs: &pb.Alias_TargetAliasAttributes{
&pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "hst_1234567890",
},
},
},
},
},
},
{
name: "Update with dynamic host id",
req: &pbs.UpdateAliasRequest{
Id: og.GetPublicId(),
UpdateMask: &field_mask.FieldMask{
Paths: []string{"attributes.authorize_session_arguments.host_id"},
},
Item: &pb.Alias{
Description: wrapperspb.String("new desc"),
Attrs: &pb.Alias_TargetAliasAttributes{
&pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "hplg_1234567890",
},
},
},
},
},
res: &pbs.UpdateAliasResponse{
Item: &pb.Alias{
Type: "target",
Id: og.GetPublicId(),
Name: wrapperspb.String("default"),
ScopeId: og.GetScopeId(),
Scope: globalScopeInfo,
Value: "default",
DestinationId: wrapperspb.String(tar.GetPublicId()),
Description: wrapperspb.String("default"),
CreatedTime: og.GetCreateTime().GetTimestamp(),
AuthorizedActions: testAuthorizedActions,
Attrs: &pb.Alias_TargetAliasAttributes{
&pb.TargetAliasAttributes{
AuthorizeSessionArguments: &pb.AuthorizeSessionArguments{
HostId: "hplg_1234567890",
},
},
},
},
},
},
{
name: "Cant specify Created Time",
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"created_time"},
},
Item: &pb.Alias{
CreatedTime: timestamppb.Now(),
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Cant specify Updated Time",
req: &pbs.UpdateAliasRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"updated_time"},
},
Item: &pb.Alias{
UpdatedTime: timestamppb.Now(),
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
ver := ogVersion
tc.req.Item.Version = ver
assert, require := assert.New(t), require.New(t)
req := proto.Clone(toMerge).(*pbs.UpdateAliasRequest)
proto.Merge(req, tc.req)
// Test with bad version (too high, too low)
req.Item.Version = ver + 2
_, gErr := tested.UpdateAlias(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), req)
require.Error(gErr)
req.Item.Version = ver - 1
_, gErr = tested.UpdateAlias(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), req)
require.Error(gErr)
req.Item.Version = ver
got, gErr := tested.UpdateAlias(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "UpdateAlias(%+v) got error %v, wanted %v", req, gErr, tc.err)
return
}
require.NoError(gErr)
defer resetAlias(got.GetItem().GetVersion())
assert.NotNilf(tc.res, "Expected UpdateAlias response to be nil, but was %v", got)
gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime()
// Verify it is a alias updated after it was created
assert.True(gotUpdateTime.After(created), "Updated alias should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
// Clear all values which are hard to compare against.
got.Item.UpdatedTime, tc.res.Item.UpdatedTime = nil, nil
assert.Equal(ver+1, got.GetItem().GetVersion())
tc.res.Item.Version = ver + 1
assert.Empty(cmp.Diff(
got,
tc.res,
protocmp.Transform(),
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
), "UpdateAlias(%q) got response %q, wanted %q", req, got, tc.res)
})
}
}