This removes the Cartesian product in the DB for GrantsForUser in favor of returning the actual grant scope information and dealing with it in application code. (cherry picked from commit 913cd536823d73994acc4f4d833208c1cc05f91b)
4660 lines
172 KiB
Go
4660 lines
172 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package tcp_test
|
|
|
|
import (
|
|
"context"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
"path"
|
|
"slices"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/hashicorp/boundary/globals"
|
|
talias "github.com/hashicorp/boundary/internal/alias/target"
|
|
"github.com/hashicorp/boundary/internal/auth/ldap"
|
|
"github.com/hashicorp/boundary/internal/auth/oidc"
|
|
"github.com/hashicorp/boundary/internal/auth/password"
|
|
"github.com/hashicorp/boundary/internal/authtoken"
|
|
"github.com/hashicorp/boundary/internal/credential"
|
|
credstatic "github.com/hashicorp/boundary/internal/credential/static"
|
|
"github.com/hashicorp/boundary/internal/credential/vault"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentials"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets"
|
|
"github.com/hashicorp/boundary/internal/db"
|
|
"github.com/hashicorp/boundary/internal/event"
|
|
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
|
authpb "github.com/hashicorp/boundary/internal/gen/controller/auth"
|
|
hostplugin "github.com/hashicorp/boundary/internal/host/plugin"
|
|
"github.com/hashicorp/boundary/internal/host/static"
|
|
"github.com/hashicorp/boundary/internal/iam"
|
|
"github.com/hashicorp/boundary/internal/kms"
|
|
"github.com/hashicorp/boundary/internal/plugin"
|
|
"github.com/hashicorp/boundary/internal/plugin/loopback"
|
|
"github.com/hashicorp/boundary/internal/requests"
|
|
"github.com/hashicorp/boundary/internal/scheduler"
|
|
"github.com/hashicorp/boundary/internal/server"
|
|
"github.com/hashicorp/boundary/internal/session"
|
|
"github.com/hashicorp/boundary/internal/target"
|
|
"github.com/hashicorp/boundary/internal/target/tcp"
|
|
"github.com/hashicorp/boundary/internal/target/tcp/store"
|
|
"github.com/hashicorp/boundary/internal/types/scope"
|
|
credlibpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries"
|
|
credpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
|
|
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
|
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
|
|
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
|
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
|
"github.com/hashicorp/go-uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/crypto/ssh/testdata"
|
|
"google.golang.org/genproto/protobuf/field_mask"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/testing/protocmp"
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
|
|
|
_ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets/tcp"
|
|
)
|
|
|
|
var testAuthorizedActions = []string{
|
|
"no-op",
|
|
"read",
|
|
"update",
|
|
"delete",
|
|
"add-host-sources",
|
|
"set-host-sources",
|
|
"remove-host-sources",
|
|
"add-credential-sources",
|
|
"set-credential-sources",
|
|
"remove-credential-sources",
|
|
"authorize-session",
|
|
}
|
|
|
|
// Create a variable that we can overwrite in enterprise tests
|
|
var expectedDeprecatedWorkerFilterError = "Use egress_worker_filter instead"
|
|
|
|
func testService(t *testing.T, ctx context.Context, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapper) (targets.Service, error) {
|
|
rw := db.New(conn)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
statusGracePeriod := new(atomic.Int64)
|
|
statusGracePeriod.Store(int64(server.DefaultLiveness))
|
|
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
|
return target.NewRepository(ctx, rw, rw, kms, o...)
|
|
}
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iam.TestRepo(t, conn, wrapper), nil
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
|
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
|
}
|
|
staticHostRepoFn := func() (*static.Repository, error) {
|
|
return static.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
pluginHostRepoFn := func() (*hostplugin.Repository, error) {
|
|
return hostplugin.NewRepository(ctx, rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
|
}
|
|
vaultCredRepoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
staticCredRepoFn := func() (*credstatic.Repository, error) {
|
|
return credstatic.NewRepository(context.Background(), rw, rw, kms)
|
|
}
|
|
targetAliasRepoFn := func() (*talias.Repository, error) {
|
|
return talias.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
return targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn, targetAliasRepoFn, nil, statusGracePeriod, 1000, nil)
|
|
}
|
|
|
|
func TestGet(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
o, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, o.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
hs := static.TestSets(t, conn, hc.GetPublicId(), 2)
|
|
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()}))
|
|
|
|
tarAddr := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test address", target.WithAddress("8.8.8.8"))
|
|
|
|
al := talias.TestAlias(t, rw, "test.alias", talias.WithDestinationId(tar.GetPublicId()), talias.WithHostId("hsht_1234567890"))
|
|
|
|
pAlias := &pb.Alias{
|
|
Id: al.GetPublicId(),
|
|
Value: al.GetValue(),
|
|
}
|
|
|
|
pTar := &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String("test"),
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: tar.GetUpdateTime().GetTimestamp(),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: o.GetPublicId()},
|
|
Type: tcp.Subtype.String(),
|
|
HostSourceIds: []string{hs[0].GetPublicId(), hs[1].GetPublicId()},
|
|
Attrs: &pb.Target_TcpTargetAttributes{},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
Aliases: []*pb.Alias{pAlias},
|
|
}
|
|
for _, ihs := range hs {
|
|
pTar.HostSources = append(pTar.HostSources, &pb.HostSource{Id: ihs.GetPublicId(), HostCatalogId: ihs.GetCatalogId()})
|
|
}
|
|
|
|
pTarAddr := &pb.Target{
|
|
Id: tarAddr.GetPublicId(),
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String("test address"),
|
|
CreatedTime: tarAddr.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: tarAddr.GetUpdateTime().GetTimestamp(),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: o.GetPublicId()},
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{Value: "8.8.8.8"},
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.GetTargetRequest
|
|
res *pbs.GetTargetResponse
|
|
err error
|
|
}{
|
|
{
|
|
name: "Get an Existing Target",
|
|
req: &pbs.GetTargetRequest{Id: tar.GetPublicId()},
|
|
res: &pbs.GetTargetResponse{Item: pTar},
|
|
},
|
|
{
|
|
name: "Get an Existing Target w/ address",
|
|
req: &pbs.GetTargetRequest{Id: tarAddr.GetPublicId()},
|
|
res: &pbs.GetTargetResponse{Item: pTarAddr},
|
|
},
|
|
{
|
|
name: "Get a non existing Target",
|
|
req: &pbs.GetTargetRequest{Id: globals.TcpTargetPrefix + "_DoesntExis"},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.NotFound),
|
|
},
|
|
{
|
|
name: "Wrong id prefix",
|
|
req: &pbs.GetTargetRequest{Id: "j_1234567890"},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "space in id",
|
|
req: &pbs.GetTargetRequest{Id: globals.TcpTargetPrefix + "_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)
|
|
|
|
s, err := testService(t, ctx, conn, kms, wrapper)
|
|
require.NoError(err, "Couldn't create a new host set service.")
|
|
|
|
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, kms, &requestInfo)
|
|
got, gErr := s.GetTarget(ctx, tc.req)
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "GetTarget(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
return
|
|
}
|
|
require.NoError(gErr)
|
|
|
|
if tc.res != nil {
|
|
tc.res.Item.Version = 1
|
|
}
|
|
assert.Empty(cmp.Diff(
|
|
got,
|
|
tc.res,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
), "GetTarget(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestList(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
sqlDB, err := conn.SqlDB(ctx)
|
|
require.NoError(t, err)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
_, projNoTar := iam.TestScopes(t, iamRepo)
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
ar := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, ar.GetPublicId(), globals.AnonymousUserId)
|
|
_ = iam.TestRoleGrant(t, conn, ar.GetPublicId(), "ids=*;type=target;actions=*")
|
|
|
|
otherOrg, otherProj := iam.TestScopes(t, iamRepo)
|
|
r = iam.TestRole(t, conn, otherProj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
otherHc := static.TestCatalogs(t, conn, otherProj.GetPublicId(), 1)[0]
|
|
hss := static.TestSets(t, conn, hc.GetPublicId(), 2)
|
|
otherHss := static.TestSets(t, conn, otherHc.GetPublicId(), 2)
|
|
|
|
var wantTars []*pb.Target
|
|
var totalTars []*pb.Target
|
|
for i := 0; i < 5; i++ {
|
|
name := fmt.Sprintf("tar%d", i)
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), name, target.WithHostSources([]string{hss[0].GetPublicId(), hss[1].GetPublicId()}))
|
|
wantTars = append(wantTars, &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String(name),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: tar.GetUpdateTime().GetTimestamp(),
|
|
Version: tar.GetVersion(),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
})
|
|
totalTars = append(totalTars, wantTars[i])
|
|
tar = tcp.TestTarget(ctx, t, conn, otherProj.GetPublicId(), name, target.WithHostSources([]string{otherHss[0].GetPublicId(), otherHss[1].GetPublicId()}))
|
|
totalTars = append(totalTars, &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: otherProj.GetPublicId(),
|
|
Name: wrapperspb.String(name),
|
|
Scope: &scopes.ScopeInfo{Id: otherProj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: otherOrg.GetPublicId()},
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: tar.GetUpdateTime().GetTimestamp(),
|
|
Version: tar.GetVersion(),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
})
|
|
}
|
|
|
|
// Run analyze to update postgres estimates
|
|
_, err = sqlDB.ExecContext(ctx, "analyze")
|
|
require.NoError(t, err)
|
|
|
|
// Reverse slices since response is ordered by created_time descending (newest first)
|
|
slices.Reverse(wantTars)
|
|
slices.Reverse(totalTars)
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.ListTargetsRequest
|
|
res *pbs.ListTargetsResponse
|
|
err error
|
|
}{
|
|
{
|
|
name: "List Many Targets",
|
|
req: &pbs.ListTargetsRequest{ScopeId: proj.GetPublicId()},
|
|
res: &pbs.ListTargetsResponse{
|
|
Items: wantTars,
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
EstItemCount: 5,
|
|
},
|
|
},
|
|
{
|
|
name: "List No Targets",
|
|
req: &pbs.ListTargetsRequest{ScopeId: projNoTar.GetPublicId()},
|
|
res: &pbs.ListTargetsResponse{},
|
|
},
|
|
{
|
|
name: "List Targets Recursively",
|
|
req: &pbs.ListTargetsRequest{ScopeId: scope.Global.String(), Recursive: true},
|
|
res: &pbs.ListTargetsResponse{
|
|
Items: totalTars,
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
EstItemCount: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "Paginate listing",
|
|
req: &pbs.ListTargetsRequest{ScopeId: scope.Global.String(), Recursive: true, PageSize: 2},
|
|
res: &pbs.ListTargetsResponse{
|
|
Items: totalTars[:2],
|
|
ResponseType: "delta",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
EstItemCount: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "Filter To Many Targets",
|
|
req: &pbs.ListTargetsRequest{ScopeId: scope.Global.String(), Recursive: true, Filter: fmt.Sprintf(`"/item/scope/id"==%q`, proj.GetPublicId())},
|
|
res: &pbs.ListTargetsResponse{
|
|
Items: wantTars,
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
EstItemCount: 5,
|
|
},
|
|
},
|
|
{
|
|
name: "Filter To No Targets",
|
|
req: &pbs.ListTargetsRequest{ScopeId: proj.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
|
|
res: &pbs.ListTargetsResponse{
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
},
|
|
},
|
|
{
|
|
name: "Filter Bad Format",
|
|
req: &pbs.ListTargetsRequest{ScopeId: proj.GetPublicId(), Filter: `"/badformat/"`},
|
|
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 := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(err, "Couldn't create new host set service.")
|
|
|
|
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, kms, &requestInfo)
|
|
got, gErr := s.ListTargets(ctx, tc.req)
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "ListTargets(%q) got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err)
|
|
return
|
|
}
|
|
require.NoError(gErr)
|
|
assert.Equal(len(tc.res.Items), len(got.Items))
|
|
wantById := make(map[string]*pb.Target, len(tc.res.Items))
|
|
for _, t := range tc.res.Items {
|
|
wantById[t.Id] = t
|
|
}
|
|
for _, t := range got.Items {
|
|
want, ok := wantById[t.Id]
|
|
assert.True(ok, "Got unexpected target with id: %s", t.Id)
|
|
assert.Empty(cmp.Diff(
|
|
t,
|
|
want,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
), "got %v, wanted %v", t, want)
|
|
}
|
|
|
|
// Test with anon user
|
|
requestInfo = authpb.RequestInfo{
|
|
TokenFormat: uint32(auth.AuthTokenTypeUnknown),
|
|
}
|
|
requestContext = context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
|
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
|
_, gErr = s.ListTargets(ctx, tc.req)
|
|
require.Error(gErr)
|
|
|
|
// For now, due to how recursive checks the additional scopes,
|
|
// it gets a 403 while a non-recursive expects a 401
|
|
if tc.req.GetRecursive() {
|
|
assert.ErrorIs(gErr, handlers.ForbiddenError())
|
|
} else {
|
|
assert.ErrorIs(gErr, handlers.UnauthenticatedError())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListGrantScopes(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
sqlDB, err := conn.SqlDB(ctx)
|
|
require.NoError(t, err)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
at := authtoken.TestAuthToken(t, conn, kms, scope.Global.String())
|
|
|
|
var projects []*iam.Scope
|
|
org1, proj1 := iam.TestScopes(t, iamRepo)
|
|
projects = append(projects, proj1)
|
|
org2, proj2 := iam.TestScopes(t, iamRepo)
|
|
projects = append(projects, proj2)
|
|
|
|
var totalTars []*pb.Target
|
|
for i, proj := range projects {
|
|
for j := 0; j < 5; j++ {
|
|
name := fmt.Sprintf("tar-%d-%d", i, j)
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), name, target.WithAddress(fmt.Sprintf("1.1.%d.%d", i, j)))
|
|
totalTars = append(totalTars, &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String(name),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: proj.ParentId},
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: tar.GetUpdateTime().GetTimestamp(),
|
|
Version: tar.GetVersion(),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{Value: fmt.Sprintf("1.1.%d.%d", i, j)},
|
|
})
|
|
}
|
|
}
|
|
|
|
// Run analyze to update postgres estimates
|
|
_, err = sqlDB.ExecContext(ctx, "analyze")
|
|
require.NoError(t, err)
|
|
|
|
_ = org1
|
|
_ = org2
|
|
|
|
cases := []struct {
|
|
name string
|
|
pageSize uint32
|
|
setupFunc func(t *testing.T)
|
|
res *pbs.ListTargetsResponse
|
|
err error
|
|
}{
|
|
{
|
|
name: "global-with-direct-grants-wildcard",
|
|
setupFunc: func(t *testing.T) {
|
|
globalRole := iam.TestRole(t, conn, scope.Global.String(), iam.WithGrantScopeIds([]string{proj1.GetPublicId(), proj2.GetPublicId()}))
|
|
_ = iam.TestUserRole(t, conn, globalRole.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, globalRole.GetPublicId(), "ids=*;type=*;actions=*")
|
|
},
|
|
res: &pbs.ListTargetsResponse{
|
|
Items: totalTars,
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
EstItemCount: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "global-with-direct-grants-non-wildcard",
|
|
setupFunc: func(t *testing.T) {
|
|
globalRole := iam.TestRole(t, conn, scope.Global.String(), iam.WithGrantScopeIds([]string{proj1.GetPublicId(), proj2.GetPublicId()}))
|
|
_ = iam.TestUserRole(t, conn, globalRole.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, globalRole.GetPublicId(), "ids=*;type=target;actions=list")
|
|
_ = iam.TestRoleGrant(t, conn, globalRole.GetPublicId(), fmt.Sprintf("ids=%s,%s;actions=*", totalTars[0].Id, totalTars[1].Id))
|
|
},
|
|
res: &pbs.ListTargetsResponse{
|
|
Items: totalTars[0:2],
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
EstItemCount: 2,
|
|
},
|
|
},
|
|
{
|
|
name: "global-with-descendants-wildcard",
|
|
setupFunc: func(t *testing.T) {
|
|
globalRole := iam.TestRole(t, conn, scope.Global.String(), iam.WithGrantScopeIds([]string{globals.GrantScopeDescendants}))
|
|
_ = iam.TestUserRole(t, conn, globalRole.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, globalRole.GetPublicId(), "ids=*;type=*;actions=*")
|
|
},
|
|
res: &pbs.ListTargetsResponse{
|
|
Items: totalTars,
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
EstItemCount: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "org-with-direct-grants-wildcard",
|
|
setupFunc: func(t *testing.T) {
|
|
org1Role := iam.TestRole(t, conn, org1.GetPublicId(), iam.WithGrantScopeIds([]string{proj1.GetPublicId()}))
|
|
_ = iam.TestUserRole(t, conn, org1Role.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, org1Role.GetPublicId(), "ids=*;type=*;actions=*")
|
|
org2Role := iam.TestRole(t, conn, org2.GetPublicId(), iam.WithGrantScopeIds([]string{proj2.GetPublicId()}))
|
|
_ = iam.TestUserRole(t, conn, org2Role.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, org2Role.GetPublicId(), "ids=*;type=*;actions=*")
|
|
},
|
|
res: &pbs.ListTargetsResponse{
|
|
Items: totalTars,
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
EstItemCount: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "org-with-direct-grants-non-wildcard",
|
|
setupFunc: func(t *testing.T) {
|
|
org1Role := iam.TestRole(t, conn, org1.GetPublicId(), iam.WithGrantScopeIds([]string{proj1.GetPublicId()}))
|
|
_ = iam.TestUserRole(t, conn, org1Role.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, org1Role.GetPublicId(), "ids=*;type=target;actions=list")
|
|
_ = iam.TestRoleGrant(t, conn, org1Role.GetPublicId(), fmt.Sprintf("ids=%s,%s;actions=*", totalTars[0].Id, totalTars[1].Id))
|
|
org2Role := iam.TestRole(t, conn, org2.GetPublicId(), iam.WithGrantScopeIds([]string{proj2.GetPublicId()}))
|
|
_ = iam.TestUserRole(t, conn, org2Role.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, org2Role.GetPublicId(), "ids=*;type=target;actions=list")
|
|
_ = iam.TestRoleGrant(t, conn, org2Role.GetPublicId(), fmt.Sprintf("ids=%s,%s;actions=*", totalTars[5].Id, totalTars[6].Id))
|
|
},
|
|
res: &pbs.ListTargetsResponse{
|
|
Items: append([]*pb.Target{}, append(append([]*pb.Target{}, totalTars[0:2]...), totalTars[5:7]...)...),
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
EstItemCount: 4,
|
|
},
|
|
},
|
|
{
|
|
name: "org-with-children-wildcard",
|
|
setupFunc: func(t *testing.T) {
|
|
org1Role := iam.TestRole(t, conn, org1.GetPublicId(), iam.WithGrantScopeIds([]string{globals.GrantScopeChildren}))
|
|
_ = iam.TestUserRole(t, conn, org1Role.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, org1Role.GetPublicId(), "ids=*;type=*;actions=*")
|
|
},
|
|
res: &pbs.ListTargetsResponse{
|
|
Items: totalTars[0:5],
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
EstItemCount: 5,
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
_, err := sqlDB.Exec("delete from iam_role")
|
|
require.NoError(err)
|
|
tc.setupFunc(t)
|
|
|
|
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(err, "Couldn't create new target service.")
|
|
|
|
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, kms, &requestInfo)
|
|
got, gErr := s.ListTargets(ctx, &pbs.ListTargetsRequest{
|
|
ScopeId: scope.Global.String(),
|
|
Recursive: true,
|
|
PageSize: tc.pageSize,
|
|
})
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "got error %v, wanted %v", gErr, tc.err)
|
|
return
|
|
}
|
|
require.NoError(gErr)
|
|
assert.Equal(len(tc.res.Items), len(got.Items))
|
|
wantById := make(map[string]*pb.Target, len(tc.res.Items))
|
|
for _, t := range tc.res.Items {
|
|
wantById[t.Id] = t
|
|
}
|
|
for _, t := range got.Items {
|
|
want, ok := wantById[t.Id]
|
|
assert.True(ok, "Got unexpected target with id: %s", t.Id)
|
|
assert.Empty(cmp.Diff(
|
|
t,
|
|
want,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
), "got %v, wanted %v", t, want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListPagination(t *testing.T) {
|
|
testListPagination := func(t *testing.T, useDescendants bool) {
|
|
// 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)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
repo, err := target.NewRepository(ctx, rw, rw, kms)
|
|
require.NoError(t, err)
|
|
|
|
// We're going to run the same test in two projects; one with
|
|
// descendants and one with direct grants in one project and a child
|
|
// grant from org in another project
|
|
org1, proj1 := iam.TestScopes(t, iamRepo)
|
|
org2, proj2 := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, scope.Global.String())
|
|
if useDescendants {
|
|
r := iam.TestRole(t, conn, scope.Global.String(), iam.WithGrantScopeIds([]string{globals.GrantScopeDescendants}))
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
} else {
|
|
r1 := iam.TestRole(t, conn, proj1.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r1.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r1.GetPublicId(), "ids=*;type=*;actions=*")
|
|
r2 := iam.TestRole(t, conn, org2.GetPublicId(), iam.WithGrantScopeIds([]string{globals.GrantScopeChildren}))
|
|
_ = iam.TestUserRole(t, conn, r2.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r2.GetPublicId(), "ids=*;type=*;actions=*")
|
|
}
|
|
hc := static.TestCatalogs(t, conn, proj1.GetPublicId(), 1)[0]
|
|
hss := static.TestSets(t, conn, hc.GetPublicId(), 2)
|
|
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(t, err)
|
|
|
|
var allTargets []*pb.Target
|
|
for i := 0; i < 10; i++ {
|
|
tar := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), fmt.Sprintf("tar-1-%d", i), target.WithHostSources([]string{hss[0].GetPublicId(), hss[1].GetPublicId()}))
|
|
allTargets = append(allTargets, &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: proj1.GetPublicId(),
|
|
Name: wrapperspb.String(tar.GetName()),
|
|
Scope: &scopes.ScopeInfo{Id: proj1.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org1.GetPublicId()},
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: tar.GetUpdateTime().GetTimestamp(),
|
|
Version: tar.GetVersion(),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
})
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
tar := tcp.TestTarget(ctx, t, conn, proj2.GetPublicId(), fmt.Sprintf("tar-2-%d", i), target.WithAddress(fmt.Sprintf("127.0.0.%d", i)))
|
|
allTargets = append(allTargets, &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: proj2.GetPublicId(),
|
|
Name: wrapperspb.String(tar.GetName()),
|
|
Scope: &scopes.ScopeInfo{Id: proj2.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org2.GetPublicId()},
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: tar.GetUpdateTime().GetTimestamp(),
|
|
Version: tar.GetVersion(),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{Value: fmt.Sprintf("127.0.0.%d", i)},
|
|
})
|
|
}
|
|
// Reverse since we read items in descending order (newest first)
|
|
slices.Reverse(allTargets)
|
|
|
|
// Run analyze to update postgres estimates
|
|
_, err = sqlDB.ExecContext(ctx, "analyze")
|
|
require.NoError(t, err)
|
|
|
|
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, kms, &requestInfo)
|
|
|
|
// Start paginating, recursively
|
|
req := &pbs.ListTargetsRequest{
|
|
ScopeId: "global",
|
|
Recursive: true,
|
|
Filter: "",
|
|
ListToken: "",
|
|
PageSize: 2,
|
|
}
|
|
got, err := s.ListTargets(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.ListTargetsResponse{
|
|
Items: allTargets[0:2],
|
|
ResponseType: "delta",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 20,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListTargetsResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Request second page
|
|
req.ListToken = got.ListToken
|
|
got, err = s.ListTargets(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.ListTargetsResponse{
|
|
Items: allTargets[2:4],
|
|
ResponseType: "delta",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 20,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListTargetsResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Request rest of results
|
|
req.ListToken = got.ListToken
|
|
req.PageSize = 20
|
|
got, err = s.ListTargets(ctx, req)
|
|
require.NoError(t, err)
|
|
require.Len(t, got.GetItems(), 16)
|
|
// Compare without comparing the list token
|
|
assert.Empty(t,
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListTargetsResponse{
|
|
Items: allTargets[4:],
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 20,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListTargetsResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Create another target
|
|
tar := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), "test-target-1", target.WithHostSources([]string{hss[0].GetPublicId(), hss[1].GetPublicId()}))
|
|
newTarget := &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: proj1.GetPublicId(),
|
|
Name: wrapperspb.String(tar.GetName()),
|
|
Scope: &scopes.ScopeInfo{Id: proj1.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org1.GetPublicId()},
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: tar.GetUpdateTime().GetTimestamp(),
|
|
Version: tar.GetVersion(),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
}
|
|
// Add to the front since it's most recently updated
|
|
allTargets = append([]*pb.Target{newTarget}, allTargets...)
|
|
tar = tcp.TestTarget(ctx, t, conn, proj2.GetPublicId(), "test-target-2", target.WithAddress(fmt.Sprintf("127.0.0.11")))
|
|
newTarget = &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: proj2.GetPublicId(),
|
|
Name: wrapperspb.String(tar.GetName()),
|
|
Scope: &scopes.ScopeInfo{Id: proj2.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org2.GetPublicId()},
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
UpdatedTime: tar.GetUpdateTime().GetTimestamp(),
|
|
Version: tar.GetVersion(),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{Value: fmt.Sprintf("127.0.0.11")},
|
|
}
|
|
allTargets = append([]*pb.Target{newTarget}, allTargets...)
|
|
|
|
// Leaving this function here as it is very useful if test objects change
|
|
/*
|
|
printNames := func(step string, tars []*pb.Target) {
|
|
names := make([]string, len(tars))
|
|
for i, t := range tars {
|
|
names[i] = t.GetName().GetValue()
|
|
}
|
|
log.Println(step, pretty.Sprint(strings.Join(names, ", ")))
|
|
}
|
|
*/
|
|
|
|
// printNames("before delete ", allTargets)
|
|
|
|
// Delete one of the other targets in each project
|
|
_, err = repo.DeleteTarget(ctx, allTargets[len(allTargets)-11].Id)
|
|
require.NoError(t, err)
|
|
deletedTarget1 := allTargets[len(allTargets)-11]
|
|
allTargets = append(allTargets[:len(allTargets)-11], allTargets[len(allTargets)-11+1:]...)
|
|
// printNames("after first delete ", allTargets)
|
|
|
|
_, err = repo.DeleteTarget(ctx, allTargets[len(allTargets)-1].Id)
|
|
require.NoError(t, err)
|
|
deletedTarget2 := allTargets[len(allTargets)-1]
|
|
allTargets = allTargets[:len(allTargets)-1]
|
|
// printNames("after second delete", allTargets)
|
|
|
|
// Update two of the other targets
|
|
allTargets[2].Name = wrapperspb.String("new-name-1")
|
|
allTargets[2].Version = 2
|
|
updatedTarget := &tcp.Target{
|
|
Target: &store.Target{
|
|
PublicId: allTargets[2].Id,
|
|
Name: allTargets[2].Name.GetValue(),
|
|
ProjectId: allTargets[2].ScopeId,
|
|
},
|
|
}
|
|
tg, _, err := repo.UpdateTarget(ctx, updatedTarget, 1, []string{"name"})
|
|
require.NoError(t, err)
|
|
allTargets[2].UpdatedTime = tg.GetUpdateTime().GetTimestamp()
|
|
allTargets[2].Version = tg.GetVersion()
|
|
// Add to the front since it's most recently updated
|
|
newAllTargets := append([]*pb.Target{allTargets[2]}, allTargets[0:2]...)
|
|
newAllTargets = append(newAllTargets, allTargets[3:]...)
|
|
allTargets = newAllTargets
|
|
// printNames("after first update ", allTargets)
|
|
allTargets[11].Name = wrapperspb.String("new-name-11")
|
|
allTargets[11].Version = 2
|
|
updatedTarget = &tcp.Target{
|
|
Target: &store.Target{
|
|
PublicId: allTargets[11].Id,
|
|
Name: allTargets[11].Name.GetValue(),
|
|
ProjectId: allTargets[11].ScopeId,
|
|
},
|
|
}
|
|
tg, _, err = repo.UpdateTarget(ctx, updatedTarget, 1, []string{"name"})
|
|
require.NoError(t, err)
|
|
allTargets[11].UpdatedTime = tg.GetUpdateTime().GetTimestamp()
|
|
allTargets[11].Version = tg.GetVersion()
|
|
// Add to the front since it's most recently updated
|
|
newAllTargets = append([]*pb.Target{allTargets[11]}, allTargets[0:11]...)
|
|
newAllTargets = append(newAllTargets, allTargets[12:]...)
|
|
allTargets = newAllTargets
|
|
// printNames("after second update", allTargets)
|
|
|
|
// Run analyze to update postgres estimates
|
|
_, err = sqlDB.ExecContext(ctx, "analyze")
|
|
require.NoError(t, err)
|
|
|
|
// Request updated results
|
|
req.ListToken = got.ListToken
|
|
req.PageSize = 2
|
|
got, err = s.ListTargets(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.ListTargetsResponse{
|
|
Items: []*pb.Target{allTargets[0], allTargets[1]},
|
|
ResponseType: "delta",
|
|
SortBy: "updated_time",
|
|
SortDir: "desc",
|
|
// Should contain the deleted target
|
|
RemovedIds: []string{deletedTarget1.Id, deletedTarget2.Id},
|
|
EstItemCount: 20,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListTargetsResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Get next page
|
|
req.ListToken = got.ListToken
|
|
got, err = s.ListTargets(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.ListTargetsResponse{
|
|
Items: []*pb.Target{allTargets[2], allTargets[3]},
|
|
ResponseType: "complete",
|
|
SortBy: "updated_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 20,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListTargetsResponse{}, "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`, allTargets[len(allTargets)-2].Id, allTargets[len(allTargets)-1].Id)
|
|
got, err = s.ListTargets(ctx, req)
|
|
require.NoError(t, err)
|
|
require.Len(t, got.GetItems(), 1)
|
|
assert.Empty(t,
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListTargetsResponse{
|
|
Items: []*pb.Target{allTargets[len(allTargets)-2]},
|
|
ResponseType: "delta",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
// Should be empty again
|
|
RemovedIds: nil,
|
|
EstItemCount: 20,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListTargetsResponse{}, "list_token"),
|
|
),
|
|
)
|
|
req.ListToken = got.ListToken
|
|
// Get the second page
|
|
got, err = s.ListTargets(ctx, req)
|
|
require.NoError(t, err)
|
|
require.Len(t, got.GetItems(), 1)
|
|
assert.Empty(t,
|
|
cmp.Diff(
|
|
got,
|
|
&pbs.ListTargetsResponse{
|
|
Items: []*pb.Target{allTargets[len(allTargets)-1]},
|
|
ResponseType: "complete",
|
|
SortBy: "created_time",
|
|
SortDir: "desc",
|
|
RemovedIds: nil,
|
|
EstItemCount: 20,
|
|
},
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pbs.ListTargetsResponse{}, "list_token"),
|
|
),
|
|
)
|
|
|
|
// Create unauthenticated user
|
|
unauthAt := authtoken.TestAuthToken(t, conn, kms, org1.GetPublicId())
|
|
unauthR := iam.TestRole(t, conn, proj1.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, unauthR.GetPublicId(), unauthAt.GetIamUserId())
|
|
|
|
// Make a request with the unauthenticated user,
|
|
// ensure the response contains the pagination parameters.
|
|
requestInfo = authpb.RequestInfo{
|
|
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
|
PublicId: unauthAt.GetPublicId(),
|
|
Token: unauthAt.GetToken(),
|
|
}
|
|
requestContext = context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
|
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
|
|
|
got, err = s.ListTargets(ctx, &pbs.ListTargetsRequest{
|
|
ScopeId: "global",
|
|
Recursive: true,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Empty(t, got.Items)
|
|
assert.Equal(t, "created_time", got.SortBy)
|
|
assert.Equal(t, "desc", got.SortDir)
|
|
assert.Equal(t, "complete", got.ResponseType)
|
|
}
|
|
|
|
t.Run("with-descendants", func(t *testing.T) {
|
|
testListPagination(t, true)
|
|
})
|
|
t.Run("without-descendants", func(t *testing.T) {
|
|
testListPagination(t, false)
|
|
})
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test")
|
|
|
|
s, err := testService(t, ctx, conn, kms, wrapper)
|
|
require.NoError(t, err, "Couldn't create a new target service.")
|
|
|
|
cases := []struct {
|
|
name string
|
|
scopeId string
|
|
req *pbs.DeleteTargetRequest
|
|
res *pbs.DeleteTargetResponse
|
|
err error
|
|
}{
|
|
{
|
|
name: "Delete an Existing Target",
|
|
scopeId: proj.GetPublicId(),
|
|
req: &pbs.DeleteTargetRequest{
|
|
Id: tar.GetPublicId(),
|
|
},
|
|
},
|
|
{
|
|
name: "Delete Not Existing Target",
|
|
scopeId: proj.GetPublicId(),
|
|
req: &pbs.DeleteTargetRequest{
|
|
Id: globals.TcpTargetPrefix + "_doesntexis",
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.NotFound),
|
|
},
|
|
{
|
|
name: "Bad target id formatting",
|
|
scopeId: proj.GetPublicId(),
|
|
req: &pbs.DeleteTargetRequest{
|
|
Id: globals.TcpTargetPrefix + "_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)
|
|
requestInfo := authpb.RequestInfo{
|
|
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
|
PublicId: at.GetPublicId(),
|
|
Token: at.GetToken(),
|
|
}
|
|
requestContext := context.WithValue(ctx, requests.ContextRequestInformationKey, &requests.RequestContext{})
|
|
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
|
got, gErr := s.DeleteTarget(ctx, tc.req)
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "DeleteTarget(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
}
|
|
assert.Empty(cmp.Diff(
|
|
tc.res,
|
|
got,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
), "DeleteTarget(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDelete_twice(t *testing.T) {
|
|
t.Parallel()
|
|
assert, require := assert.New(t), require.New(t)
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
ctx := context.Background()
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test")
|
|
|
|
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(err, "Couldn't create a new target service.")
|
|
req := &pbs.DeleteTargetRequest{
|
|
Id: tar.GetPublicId(),
|
|
}
|
|
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, kms, &requestInfo)
|
|
_, gErr := s.DeleteTarget(ctx, req)
|
|
assert.NoError(gErr, "First attempt")
|
|
_, gErr = s.DeleteTarget(ctx, req)
|
|
assert.Error(gErr, "Second attempt")
|
|
assert.True(errors.Is(gErr, handlers.ApiErrorWithCode(codes.NotFound)), "Expected permission denied for the second delete.")
|
|
}
|
|
|
|
func TestCreate(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, "global")
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
_ = iam.TestRoleGrantScope(t, conn, r.GetPublicId(), globals.GrantScopeDescendants)
|
|
|
|
// Ensure we are using the OSS worker filter function
|
|
workerFilterFn := targets.AuthorizeSessionWorkerFilterFn
|
|
targets.AuthorizeSessionWorkerFilterFn = targets.AuthorizeSessionWithWorkerFilter
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.CreateTargetRequest
|
|
res *pbs.CreateTargetResponse
|
|
err error
|
|
errStr string
|
|
}{
|
|
{
|
|
name: "Create a valid target",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String("name"),
|
|
Description: wrapperspb.String("desc"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
DefaultClientPort: wrapperspb.UInt32(3),
|
|
},
|
|
},
|
|
EgressWorkerFilter: wrapperspb.String(`type == "bar"`),
|
|
}},
|
|
res: &pbs.CreateTargetResponse{
|
|
Uri: fmt.Sprintf("targets/%s_", globals.TcpTargetPrefix),
|
|
Item: &pb.Target{
|
|
ScopeId: proj.GetPublicId(),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
|
Name: wrapperspb.String("name"),
|
|
Description: wrapperspb.String("desc"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
DefaultClientPort: wrapperspb.UInt32(3),
|
|
},
|
|
},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
EgressWorkerFilter: wrapperspb.String(`type == "bar"`),
|
|
Address: &wrapperspb.StringValue{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Create a valid target with two aliases",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String("target_with_aliases"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
},
|
|
},
|
|
WithAliases: []*pb.Alias{
|
|
{
|
|
Value: "create-two-aliasses1",
|
|
ScopeId: "global",
|
|
},
|
|
{
|
|
Value: "create-two-aliasses2",
|
|
ScopeId: "global",
|
|
},
|
|
},
|
|
}},
|
|
res: &pbs.CreateTargetResponse{
|
|
Uri: fmt.Sprintf("targets/%s_", globals.TcpTargetPrefix),
|
|
Item: &pb.Target{
|
|
ScopeId: proj.GetPublicId(),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
|
Name: wrapperspb.String("target_with_aliases"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
},
|
|
},
|
|
Aliases: []*pb.Alias{
|
|
{
|
|
Value: "create-two-aliasses1",
|
|
},
|
|
{
|
|
Value: "create-two-aliasses2",
|
|
},
|
|
},
|
|
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
|
SessionConnectionLimit: wrapperspb.Int32(-1),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Create a target with alias specifying the id",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String("target_with_invalid_alias"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
},
|
|
},
|
|
WithAliases: []*pb.Alias{
|
|
{
|
|
Id: "alt_1234567890",
|
|
Value: "id-specified",
|
|
ScopeId: "global",
|
|
},
|
|
},
|
|
}},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Create a target with invalid alias",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String("target_with_invalid_alias"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
},
|
|
},
|
|
WithAliases: []*pb.Alias{
|
|
{
|
|
Value: "invalid alias",
|
|
ScopeId: "global",
|
|
},
|
|
},
|
|
}},
|
|
errStr: "unable to create target alias",
|
|
},
|
|
{
|
|
name: "Create a target with invalid scope",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String("target_with_invalid_alias"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
},
|
|
},
|
|
WithAliases: []*pb.Alias{
|
|
{
|
|
Value: "alias.invalid.scope",
|
|
ScopeId: proj.GetPublicId(),
|
|
},
|
|
},
|
|
}},
|
|
errStr: "unable to create target alias",
|
|
},
|
|
{
|
|
name: "Create a target with duplicate aliasses",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String("target_with_invalid_alias"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
},
|
|
},
|
|
WithAliases: []*pb.Alias{
|
|
{
|
|
Value: "duplicate-alias",
|
|
ScopeId: "global",
|
|
},
|
|
{
|
|
Value: "duplicate-alias",
|
|
ScopeId: "global",
|
|
},
|
|
},
|
|
}},
|
|
errStr: "duplicate key value violates unique constraint",
|
|
},
|
|
{
|
|
name: "Create a target with no port",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String("name"),
|
|
Description: wrapperspb.String("desc"),
|
|
Type: tcp.Subtype.String(),
|
|
EgressWorkerFilter: wrapperspb.String(`type == "bar"`),
|
|
}},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Create with default port 0",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
Name: wrapperspb.String("name"),
|
|
Description: wrapperspb.String("desc"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
DefaultClientPort: wrapperspb.UInt32(3),
|
|
},
|
|
},
|
|
}},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Create with unknown type",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
Name: wrapperspb.String("name"),
|
|
Description: wrapperspb.String("desc"),
|
|
Type: "ThisIsMadeUp",
|
|
}},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Create with no type",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
Name: wrapperspb.String("name"),
|
|
Description: wrapperspb.String("desc"),
|
|
}},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Can't specify Id",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
Id: "not allowed to be set",
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Can't specify Created Time",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
CreatedTime: timestamppb.Now(),
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Can't specify Update Time",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
UpdatedTime: timestamppb.Now(),
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid worker filter expression",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
EgressWorkerFilter: wrapperspb.String("bad expression"),
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Deprecated worker filter",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
WorkerFilter: wrapperspb.String(`"/name" matches "test-worker"`),
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errStr: expectedDeprecatedWorkerFilterError,
|
|
},
|
|
{
|
|
name: "Ingress filter unsupported on OSS",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
IngressWorkerFilter: wrapperspb.String(`"/name" matches "test-worker"`),
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid address length",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
Address: wrapperspb.String("ab"),
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid address w/ port",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
Address: wrapperspb.String("8.8.8.8:80"),
|
|
}},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid address not parsable",
|
|
req: &pbs.CreateTargetRequest{Item: &pb.Target{
|
|
Address: wrapperspb.String("aaa.8bc.8.8:80:abc"),
|
|
}},
|
|
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)
|
|
|
|
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(err, "Failed to create a new host set service.")
|
|
|
|
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, kms, &requestInfo)
|
|
|
|
got, gErr := s.CreateTarget(ctx, tc.req)
|
|
if tc.err != nil || tc.errStr != "" {
|
|
require.Error(gErr)
|
|
if tc.err != nil {
|
|
assert.True(errors.Is(gErr, tc.err), "CreateTarget(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
}
|
|
if tc.errStr != "" {
|
|
assert.ErrorContains(gErr, tc.errStr)
|
|
}
|
|
} else {
|
|
assert.Nil(gErr, "Unexpected err: %v", gErr)
|
|
}
|
|
|
|
if got != nil {
|
|
assert.Contains(got.GetUri(), tc.res.GetUri())
|
|
assert.True(strings.HasPrefix(got.GetItem().GetId(), globals.TcpTargetPrefix), got.GetItem().GetId())
|
|
|
|
// 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
|
|
}
|
|
if tc.res != nil {
|
|
tc.res.Item.Version = 1
|
|
}
|
|
assert.Empty(cmp.Diff(
|
|
got,
|
|
tc.res,
|
|
protocmp.Transform(),
|
|
// These are generated, so we don't know what they are when
|
|
// specifying the expected case.
|
|
protocmp.IgnoreFields(&pb.Alias{}, "id"),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
), "CreateTarget(%q)\n got response %q\n, wanted %q\n", tc.req, got, tc.res)
|
|
})
|
|
}
|
|
// Reset worker filter func
|
|
targets.AuthorizeSessionWorkerFilterFn = workerFilterFn
|
|
}
|
|
|
|
func TestCreate_AliasAuthCheck(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
// Both users can create targets
|
|
allProjectR := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, allProjectR.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, allProjectR.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
req := &pb.Target{
|
|
ScopeId: proj.GetPublicId(),
|
|
Name: wrapperspb.String("name"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
},
|
|
},
|
|
WithAliases: []*pb.Alias{
|
|
{
|
|
Value: "alias-allowed",
|
|
ScopeId: "global",
|
|
},
|
|
},
|
|
}
|
|
|
|
// Ensure we are using the OSS worker filter function
|
|
workerFilterFn := targets.AuthorizeSessionWorkerFilterFn
|
|
targets.AuthorizeSessionWorkerFilterFn = targets.AuthorizeSessionWithWorkerFilter
|
|
t.Cleanup(func() {
|
|
targets.AuthorizeSessionWorkerFilterFn = workerFilterFn
|
|
})
|
|
|
|
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(t, err, "Failed to create a new host set service.")
|
|
|
|
t.Run("disallowed user creating target", func(t *testing.T) {
|
|
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, kms, &requestInfo)
|
|
resp, err := s.CreateTarget(ctx, &pbs.CreateTargetRequest{Item: req})
|
|
require.Error(t, err)
|
|
require.Nil(t, resp)
|
|
assert.ErrorContains(t, err, "PermissionDenied")
|
|
})
|
|
|
|
t.Run("allowed user creating a target", func(t *testing.T) {
|
|
// only aliasAllowedAt can create aliases
|
|
aliasR := iam.TestRole(t, conn, "global")
|
|
_ = iam.TestUserRole(t, conn, aliasR.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, aliasR.GetPublicId(), "ids=*;type=alias;actions=*")
|
|
|
|
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, kms, &requestInfo)
|
|
resp, err := s.CreateTarget(ctx, &pbs.CreateTargetRequest{Item: req})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
})
|
|
}
|
|
|
|
func TestUpdate(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
|
return target.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
repo, err := repoFn()
|
|
require.NoError(t, err, "Couldn't create new target repo.")
|
|
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
hs := static.TestSets(t, conn, hc.GetPublicId(), 2)
|
|
hostSourceIds := []string{hs[0].GetPublicId(), hs[1].GetPublicId()}
|
|
hostSources := []*pb.HostSource{
|
|
{Id: hs[0].GetPublicId(), HostCatalogId: hs[0].GetCatalogId()},
|
|
{Id: hs[1].GetPublicId(), HostCatalogId: hs[1].GetCatalogId()},
|
|
}
|
|
|
|
ttar, err := target.New(ctx, tcp.Subtype, proj.GetPublicId(),
|
|
target.WithName("default"),
|
|
target.WithDescription("default"),
|
|
target.WithSessionMaxSeconds(1),
|
|
target.WithSessionConnectionLimit(1),
|
|
target.WithDefaultPort(2),
|
|
target.WithDefaultClientPort(3))
|
|
require.NoError(t, err)
|
|
tar, err := repo.CreateTarget(context.Background(), ttar)
|
|
require.NoError(t, err)
|
|
tar, err = repo.AddTargetHostSources(context.Background(), tar.GetPublicId(), tar.GetVersion(), []string{hs[0].GetPublicId(), hs[1].GetPublicId()})
|
|
require.NoError(t, err)
|
|
|
|
resetTarget := func() {
|
|
itar, err := repo.LookupTarget(context.Background(), tar.GetPublicId())
|
|
require.NoError(t, err)
|
|
|
|
tar, _, err = repo.UpdateTarget(context.Background(), tar, itar.GetVersion(),
|
|
[]string{"Name", "Description", "SessionMaxSeconds", "SessionConnectionLimit", "DefaultPort", "DefaultClientPort"})
|
|
require.NoError(t, err, "Failed to reset target.")
|
|
}
|
|
|
|
hCreated := tar.GetCreateTime().GetTimestamp().AsTime()
|
|
toMerge := &pbs.UpdateTargetRequest{
|
|
Id: tar.GetPublicId(),
|
|
}
|
|
|
|
tested, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(t, err, "Failed to create a new host set service.")
|
|
|
|
// Ensure we are using the OSS worker filter functions
|
|
workerFilterFn := targets.AuthorizeSessionWorkerFilterFn
|
|
targets.AuthorizeSessionWorkerFilterFn = targets.AuthorizeSessionWithWorkerFilter
|
|
validateIngressFn := targets.ValidateIngressWorkerFilterFn
|
|
targets.ValidateIngressWorkerFilterFn = targets.IngressWorkerFilterUnsupported
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.UpdateTargetRequest
|
|
res *pbs.UpdateTargetResponse
|
|
err error
|
|
}{
|
|
{
|
|
name: "Update an Existing Target",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name", "description", "session_max_seconds", "session_connection_limit", "type"},
|
|
},
|
|
Item: &pb.Target{
|
|
Name: wrapperspb.String("name"),
|
|
Description: wrapperspb.String("desc"),
|
|
SessionMaxSeconds: wrapperspb.UInt32(3600),
|
|
SessionConnectionLimit: wrapperspb.Int32(5),
|
|
Type: tcp.Subtype.String(),
|
|
},
|
|
},
|
|
res: &pbs.UpdateTargetResponse{
|
|
Item: &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: tar.GetProjectId(),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
|
Name: wrapperspb.String("name"),
|
|
Description: wrapperspb.String("desc"),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
DefaultClientPort: wrapperspb.UInt32(3),
|
|
},
|
|
},
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
HostSourceIds: hostSourceIds,
|
|
HostSources: hostSources,
|
|
SessionMaxSeconds: wrapperspb.UInt32(3600),
|
|
SessionConnectionLimit: wrapperspb.Int32(5),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple Paths in single string",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name,description,type"},
|
|
},
|
|
Item: &pb.Target{
|
|
Name: wrapperspb.String("name"),
|
|
Description: wrapperspb.String("desc"),
|
|
Type: tcp.Subtype.String(),
|
|
},
|
|
},
|
|
res: &pbs.UpdateTargetResponse{
|
|
Item: &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: tar.GetProjectId(),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
|
Name: wrapperspb.String("name"),
|
|
Description: wrapperspb.String("desc"),
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
DefaultClientPort: wrapperspb.UInt32(3),
|
|
},
|
|
},
|
|
HostSourceIds: hostSourceIds,
|
|
HostSources: hostSources,
|
|
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
|
|
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "No Update Mask",
|
|
req: &pbs.UpdateTargetRequest{
|
|
Item: &pb.Target{
|
|
Name: wrapperspb.String("updated name"),
|
|
Description: wrapperspb.String("updated desc"),
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Empty Path",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{}},
|
|
Item: &pb.Target{
|
|
Name: wrapperspb.String("updated name"),
|
|
Description: wrapperspb.String("updated desc"),
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Update port to 0",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{"attributes.default_port"}},
|
|
Item: &pb.Target{
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(0),
|
|
DefaultClientPort: wrapperspb.UInt32(3),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Clear port",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{"attributes.default_port"}},
|
|
Item: &pb.Target{},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Only non-existent paths in Mask",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{"nonexistent_field"}},
|
|
Item: &pb.Target{
|
|
Name: wrapperspb.String("updated name"),
|
|
Description: wrapperspb.String("updated desc"),
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Unset Name",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name"},
|
|
},
|
|
Item: &pb.Target{
|
|
Description: wrapperspb.String("ignored"),
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Unset Description",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"description"},
|
|
},
|
|
Item: &pb.Target{
|
|
Name: wrapperspb.String("ignored"),
|
|
},
|
|
},
|
|
res: &pbs.UpdateTargetResponse{
|
|
Item: &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: tar.GetProjectId(),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
|
Name: wrapperspb.String("default"),
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
DefaultClientPort: wrapperspb.UInt32(3),
|
|
},
|
|
},
|
|
HostSourceIds: hostSourceIds,
|
|
HostSources: hostSources,
|
|
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
|
|
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Update Only Name",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name"},
|
|
},
|
|
Item: &pb.Target{
|
|
Name: wrapperspb.String("updated"),
|
|
Description: wrapperspb.String("ignored"),
|
|
},
|
|
},
|
|
res: &pbs.UpdateTargetResponse{
|
|
Item: &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: tar.GetProjectId(),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
|
Name: wrapperspb.String("updated"),
|
|
Description: wrapperspb.String("default"),
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
Type: tcp.Subtype.String(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
DefaultClientPort: wrapperspb.UInt32(3),
|
|
},
|
|
},
|
|
HostSourceIds: hostSourceIds,
|
|
HostSources: hostSources,
|
|
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
|
|
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Update Only Description",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"description"},
|
|
},
|
|
Item: &pb.Target{
|
|
Name: wrapperspb.String("ignored"),
|
|
Description: wrapperspb.String("notignored"),
|
|
},
|
|
},
|
|
res: &pbs.UpdateTargetResponse{
|
|
Item: &pb.Target{
|
|
Id: tar.GetPublicId(),
|
|
ScopeId: tar.GetProjectId(),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
|
Name: wrapperspb.String("default"),
|
|
Description: wrapperspb.String("notignored"),
|
|
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
|
Attrs: &pb.Target_TcpTargetAttributes{
|
|
TcpTargetAttributes: &pb.TcpTargetAttributes{
|
|
DefaultPort: wrapperspb.UInt32(2),
|
|
DefaultClientPort: wrapperspb.UInt32(3),
|
|
},
|
|
},
|
|
Type: tcp.Subtype.String(),
|
|
HostSourceIds: hostSourceIds,
|
|
HostSources: hostSources,
|
|
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
|
|
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
|
|
AuthorizedActions: testAuthorizedActions,
|
|
Address: &wrapperspb.StringValue{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Update WithAlias",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"with_alias"},
|
|
},
|
|
Item: &pb.Target{
|
|
Name: wrapperspb.String("ignored"),
|
|
WithAliases: []*pb.Alias{{Value: "new-alias", ScopeId: "global"}},
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Update a Non Existing Target",
|
|
req: &pbs.UpdateTargetRequest{
|
|
Id: globals.TcpTargetPrefix + "_DoesntExis",
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"description"},
|
|
},
|
|
Item: &pb.Target{
|
|
Name: wrapperspb.String("new"),
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
|
Description: wrapperspb.String("desc"),
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.NotFound),
|
|
},
|
|
{
|
|
name: "Cant change Id",
|
|
req: &pbs.UpdateTargetRequest{
|
|
Id: hc.GetPublicId(),
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"id"},
|
|
},
|
|
Item: &pb.Target{
|
|
Id: "p_somethinge",
|
|
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()},
|
|
Name: wrapperspb.String("new"),
|
|
Description: wrapperspb.String("new desc"),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Cant specify Created Time",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"created_time"},
|
|
},
|
|
Item: &pb.Target{
|
|
CreatedTime: timestamppb.Now(),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Cant specify Updated Time",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"updated_time"},
|
|
},
|
|
Item: &pb.Target{
|
|
UpdatedTime: timestamppb.Now(),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Ingress filter unsupported",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"ingress_worker_filter"},
|
|
},
|
|
Item: &pb.Target{
|
|
IngressWorkerFilter: wrapperspb.String(`type == "bar"`),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Can't update worker filter and egress filter",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"worker_filter", "egress_worker_filter"},
|
|
},
|
|
Item: &pb.Target{
|
|
WorkerFilter: wrapperspb.String(`type == "bar"`),
|
|
EgressWorkerFilter: wrapperspb.String(`type == "bar"`),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid address length",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"address"},
|
|
},
|
|
Item: &pb.Target{
|
|
Address: wrapperspb.String("ab"),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid address w/ port",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"address"},
|
|
},
|
|
Item: &pb.Target{
|
|
Address: wrapperspb.String("8.8.8.8:80"),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid address not parsable",
|
|
req: &pbs.UpdateTargetRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"address"},
|
|
},
|
|
Item: &pb.Target{
|
|
Address: wrapperspb.String("aaa.8bc.8.8:80:abc"),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
defer resetTarget()
|
|
assert, require := assert.New(t), require.New(t)
|
|
tc.req.Item.Version = tar.GetVersion()
|
|
|
|
req := proto.Clone(toMerge).(*pbs.UpdateTargetRequest)
|
|
proto.Merge(req, tc.req)
|
|
|
|
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, kms, &requestInfo)
|
|
got, gErr := tested.UpdateTarget(ctx, req)
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "UpdateTarget(%+v) got error %v, wanted %v", req, gErr, tc.err)
|
|
return
|
|
}
|
|
require.NoError(gErr)
|
|
|
|
if got != nil {
|
|
assert.NotNilf(tc.res, "Expected UpdateHost response to be nil, but was %v", got)
|
|
gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime()
|
|
// Verify it is a set updated after it was created
|
|
// TODO: This is currently failing.
|
|
assert.True(gotUpdateTime.After(hCreated), "Updated target should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, hCreated)
|
|
|
|
// Clear all values which are hard to compare against.
|
|
got.Item.UpdatedTime, tc.res.Item.UpdatedTime = nil, nil
|
|
}
|
|
if tc.res != nil {
|
|
tc.res.Item.Version = tc.req.Item.Version + 1
|
|
}
|
|
assert.Empty(cmp.Diff(
|
|
got,
|
|
tc.res,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
), "UpdateTarget(%q) got response %q, wanted %q", req, got, tc.res)
|
|
})
|
|
}
|
|
// Reset worker filter funcs
|
|
targets.AuthorizeSessionWorkerFilterFn = workerFilterFn
|
|
targets.ValidateIngressWorkerFilterFn = validateIngressFn
|
|
}
|
|
|
|
func TestUpdate_BadVersion(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
|
return target.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
repo, err := repoFn()
|
|
require.NoError(t, err, "Couldn't create new target repo.")
|
|
|
|
ttar, err := target.New(ctx, tcp.Subtype, proj.GetPublicId(), target.WithName("default"), target.WithDescription("default"))
|
|
tar := ttar.(*tcp.Target)
|
|
tar.DefaultPort = 2
|
|
tar.DefaultClientPort = 3
|
|
require.NoError(t, err)
|
|
gtar, err := repo.CreateTarget(context.Background(), tar)
|
|
require.NoError(t, err)
|
|
|
|
tested, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(t, err, "Failed to create a new host set service.")
|
|
|
|
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, kms, &requestInfo)
|
|
upTar, err := tested.UpdateTarget(ctx, &pbs.UpdateTargetRequest{
|
|
Id: gtar.GetPublicId(),
|
|
Item: &pb.Target{
|
|
Description: wrapperspb.String("updated"),
|
|
Version: 72,
|
|
},
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{"description"}},
|
|
})
|
|
assert.Nil(t, upTar)
|
|
assert.Error(t, err)
|
|
assert.True(t, errors.Is(err, handlers.NotFoundError()), "Got %v, wanted not found error.", err)
|
|
}
|
|
|
|
func TestAddTargetHostSources(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(t, err, "Error when getting new target service.")
|
|
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
hs := static.TestSets(t, conn, hc.GetPublicId(), 2)
|
|
|
|
plg := plugin.TestPlugin(t, conn, "test")
|
|
pluginHc := hostplugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
|
pluginHs := hostplugin.TestSet(t, conn, kms, sche, pluginHc, map[string]plgpb.HostPluginServiceClient{
|
|
plg.GetPublicId(): loopback.NewWrappingPluginHostClient(&loopback.TestPluginServer{}),
|
|
})
|
|
|
|
addCases := []struct {
|
|
name string
|
|
tar target.Target
|
|
addHostSources []string
|
|
resultHostSources []string
|
|
}{
|
|
{
|
|
name: "Add set on empty target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty"),
|
|
addHostSources: []string{hs[1].GetPublicId()},
|
|
resultHostSources: []string{hs[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add set on populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated", target.WithHostSources([]string{hs[0].GetPublicId()})),
|
|
addHostSources: []string{hs[1].GetPublicId()},
|
|
resultHostSources: []string{hs[0].GetPublicId(), hs[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add duplicated sets on populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated", target.WithHostSources([]string{hs[0].GetPublicId()})),
|
|
addHostSources: []string{hs[1].GetPublicId(), hs[1].GetPublicId()},
|
|
resultHostSources: []string{hs[0].GetPublicId(), hs[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add plugin set on empty target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "plugin empty"),
|
|
addHostSources: []string{pluginHs.GetPublicId()},
|
|
resultHostSources: []string{pluginHs.GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add plugin set on populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})),
|
|
addHostSources: []string{pluginHs.GetPublicId()},
|
|
resultHostSources: []string{hs[0].GetPublicId(), pluginHs.GetPublicId()},
|
|
},
|
|
}
|
|
|
|
for _, tc := range addCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
req := &pbs.AddTargetHostSourcesRequest{
|
|
Id: tc.tar.GetPublicId(),
|
|
Version: tc.tar.GetVersion(),
|
|
HostSourceIds: tc.addHostSources,
|
|
}
|
|
|
|
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, kms, &requestInfo)
|
|
got, err := s.AddTargetHostSources(ctx, req)
|
|
s, ok := status.FromError(err)
|
|
require.True(t, ok)
|
|
require.NoError(t, err, "Got error: %v", s)
|
|
|
|
assert.ElementsMatch(t, tc.resultHostSources, got.GetItem().GetHostSourceIds())
|
|
})
|
|
}
|
|
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test")
|
|
|
|
failCases := []struct {
|
|
name string
|
|
req *pbs.AddTargetHostSourcesRequest
|
|
err error
|
|
}{
|
|
{
|
|
name: "Bad Target Id",
|
|
req: &pbs.AddTargetHostSourcesRequest{
|
|
Id: "bad id",
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{hs[0].GetPublicId()},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Bad version",
|
|
req: &pbs.AddTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion() + 2,
|
|
HostSourceIds: []string{hs[0].GetPublicId()},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.Internal),
|
|
},
|
|
{
|
|
name: "Empty host set list",
|
|
req: &pbs.AddTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Incorrect host set ids",
|
|
req: &pbs.AddTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{"incorrect"},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
}
|
|
for _, tc := range failCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
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, kms, &requestInfo)
|
|
_, gErr := s.AddTargetHostSources(ctx, tc.req)
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "AddTargetHostSources(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetTargetHostSources(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(t, err, "Error when getting new host set service.")
|
|
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
hs := static.TestSets(t, conn, hc.GetPublicId(), 2)
|
|
|
|
plg := plugin.TestPlugin(t, conn, "test")
|
|
pluginHc := hostplugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
|
pluginHs := hostplugin.TestSet(t, conn, kms, sche, pluginHc, map[string]plgpb.HostPluginServiceClient{
|
|
plg.GetPublicId(): loopback.NewWrappingPluginHostClient(&loopback.TestPluginServer{}),
|
|
})
|
|
|
|
setCases := []struct {
|
|
name string
|
|
tar target.Target
|
|
setHostSources []string
|
|
resultHostSources []string
|
|
}{
|
|
{
|
|
name: "Set on empty target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty"),
|
|
setHostSources: []string{hs[1].GetPublicId()},
|
|
resultHostSources: []string{hs[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set on populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated", target.WithHostSources([]string{hs[0].GetPublicId()})),
|
|
setHostSources: []string{hs[1].GetPublicId()},
|
|
resultHostSources: []string{hs[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set plugin set on populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})),
|
|
setHostSources: []string{pluginHs.GetPublicId()},
|
|
resultHostSources: []string{pluginHs.GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set duplicate host set on populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicate", target.WithHostSources([]string{hs[0].GetPublicId()})),
|
|
setHostSources: []string{hs[1].GetPublicId(), hs[1].GetPublicId()},
|
|
resultHostSources: []string{hs[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set empty on populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "another populated", target.WithHostSources([]string{hs[0].GetPublicId()})),
|
|
setHostSources: []string{},
|
|
resultHostSources: nil,
|
|
},
|
|
}
|
|
for _, tc := range setCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
req := &pbs.SetTargetHostSourcesRequest{
|
|
Id: tc.tar.GetPublicId(),
|
|
Version: tc.tar.GetVersion(),
|
|
HostSourceIds: tc.setHostSources,
|
|
}
|
|
|
|
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, kms, &requestInfo)
|
|
got, err := s.SetTargetHostSources(ctx, req)
|
|
require.NoError(t, err, "Got error: %v", s)
|
|
assert.ElementsMatch(t, tc.resultHostSources, got.GetItem().GetHostSourceIds())
|
|
})
|
|
}
|
|
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test name")
|
|
|
|
failCases := []struct {
|
|
name string
|
|
req *pbs.SetTargetHostSourcesRequest
|
|
err error
|
|
}{
|
|
{
|
|
name: "Bad target Id",
|
|
req: &pbs.SetTargetHostSourcesRequest{
|
|
Id: "bad id",
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{hs[0].GetPublicId()},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Bad version",
|
|
req: &pbs.SetTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion() + 3,
|
|
HostSourceIds: []string{hs[0].GetPublicId()},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.Internal),
|
|
},
|
|
{
|
|
name: "Bad host set id",
|
|
req: &pbs.SetTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{"invalid"},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
}
|
|
for _, tc := range failCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
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, kms, &requestInfo)
|
|
_, gErr := s.SetTargetHostSources(ctx, tc.req)
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "SetTargetHostSources(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRemoveTargetHostSources(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(t, err, "Error when getting new host set service.")
|
|
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
hs := static.TestSets(t, conn, hc.GetPublicId(), 2)
|
|
|
|
plg := plugin.TestPlugin(t, conn, "test")
|
|
pluginHc := hostplugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
|
pluginHs := hostplugin.TestSet(t, conn, kms, sche, pluginHc, map[string]plgpb.HostPluginServiceClient{
|
|
plg.GetPublicId(): loopback.NewWrappingPluginHostClient(&loopback.TestPluginServer{}),
|
|
})
|
|
|
|
removeCases := []struct {
|
|
name string
|
|
tar target.Target
|
|
removeHostSources []string
|
|
resultHostSources []string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "Remove from empty",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty"),
|
|
removeHostSources: []string{hs[1].GetPublicId()},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Remove 1 of 2 sets",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove partial", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})),
|
|
removeHostSources: []string{hs[1].GetPublicId()},
|
|
resultHostSources: []string{hs[0].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Remove 1 plugin set of 2 sets",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove plugin partial", target.WithHostSources([]string{hs[0].GetPublicId(), pluginHs.GetPublicId()})),
|
|
removeHostSources: []string{pluginHs.GetPublicId()},
|
|
resultHostSources: []string{hs[0].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Remove 1 duplicate set of 2 sets",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove duplicate", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})),
|
|
removeHostSources: []string{hs[1].GetPublicId(), hs[1].GetPublicId()},
|
|
resultHostSources: []string{hs[0].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Remove all hosts from set",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove all", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})),
|
|
removeHostSources: []string{hs[0].GetPublicId(), hs[1].GetPublicId()},
|
|
resultHostSources: []string{},
|
|
},
|
|
}
|
|
|
|
for _, tc := range removeCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
req := &pbs.RemoveTargetHostSourcesRequest{
|
|
Id: tc.tar.GetPublicId(),
|
|
Version: tc.tar.GetVersion(),
|
|
HostSourceIds: tc.removeHostSources,
|
|
}
|
|
|
|
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, kms, &requestInfo)
|
|
got, err := s.RemoveTargetHostSources(ctx, req)
|
|
if tc.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
s, ok := status.FromError(err)
|
|
require.True(t, ok)
|
|
require.NoError(t, err, "Got error: %v", s)
|
|
|
|
assert.ElementsMatch(t, tc.resultHostSources, got.GetItem().GetHostSourceIds())
|
|
})
|
|
}
|
|
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "testing")
|
|
|
|
failCases := []struct {
|
|
name string
|
|
req *pbs.RemoveTargetHostSourcesRequest
|
|
err error
|
|
}{
|
|
{
|
|
name: "Bad version",
|
|
req: &pbs.RemoveTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion() + 3,
|
|
HostSourceIds: []string{hs[0].GetPublicId()},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.Internal),
|
|
},
|
|
{
|
|
name: "Bad target Id",
|
|
req: &pbs.RemoveTargetHostSourcesRequest{
|
|
Id: "bad id",
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{hs[0].GetPublicId()},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "empty sets",
|
|
req: &pbs.RemoveTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid set ids",
|
|
req: &pbs.RemoveTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{"invalid"},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
}
|
|
for _, tc := range failCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
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, kms, &requestInfo)
|
|
_, gErr := s.RemoveTargetHostSources(ctx, tc.req)
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "RemoveTargetHostSets(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAddTargetCredentialSources(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
|
require.NoError(t, err, "Error when getting new target service.")
|
|
|
|
storeVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
|
cls := vault.TestCredentialLibraries(t, conn, wrapper, storeVault.GetPublicId(), 2)
|
|
|
|
storeStatic := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId())
|
|
creds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", storeStatic.GetPublicId(), proj.GetPublicId(), 2)
|
|
|
|
addCases := []struct {
|
|
name string
|
|
tar target.Target
|
|
addSources []string
|
|
resultSourceIds []string
|
|
}{
|
|
{
|
|
name: "Add library on empty target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty for lib sources"),
|
|
addSources: []string{cls[1].GetPublicId()},
|
|
resultSourceIds: []string{cls[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add static cred on empty target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty for static sources"),
|
|
addSources: []string{creds[1].GetPublicId()},
|
|
resultSourceIds: []string{creds[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add library on library populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for lib-lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
addSources: []string{cls[1].GetPublicId()},
|
|
resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add library on static cred populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for lib-static sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
addSources: []string{cls[1].GetPublicId()},
|
|
resultSourceIds: []string{creds[0].GetPublicId(), cls[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add static cred on library populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for static-lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
addSources: []string{creds[1].GetPublicId()},
|
|
resultSourceIds: []string{cls[0].GetPublicId(), creds[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add static cred on static cred populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for static-static sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
addSources: []string{creds[1].GetPublicId()},
|
|
resultSourceIds: []string{creds[0].GetPublicId(), creds[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add duplicated sources on library populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated for lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId(), creds[1].GetPublicId()},
|
|
resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Add duplicated sources on static cred populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated for static sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId(), creds[1].GetPublicId()},
|
|
resultSourceIds: []string{creds[0].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId()},
|
|
},
|
|
}
|
|
|
|
for _, tc := range addCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
req := &pbs.AddTargetCredentialSourcesRequest{
|
|
Id: tc.tar.GetPublicId(),
|
|
Version: tc.tar.GetVersion(),
|
|
BrokeredCredentialSourceIds: tc.addSources,
|
|
}
|
|
|
|
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, kms, &requestInfo)
|
|
got, err := s.AddTargetCredentialSources(ctx, req)
|
|
require.NoError(t, err, "Got error: %v", s)
|
|
|
|
assert.ElementsMatch(t, tc.resultSourceIds, got.GetItem().GetBrokeredCredentialSourceIds())
|
|
assert.Equal(t, len(tc.resultSourceIds), len(got.GetItem().GetBrokeredCredentialSources()))
|
|
})
|
|
}
|
|
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test")
|
|
|
|
failCases := []struct {
|
|
name string
|
|
req *pbs.AddTargetCredentialSourcesRequest
|
|
err error
|
|
}{
|
|
{
|
|
name: "Bad target id",
|
|
req: &pbs.AddTargetCredentialSourcesRequest{
|
|
Id: "bad id",
|
|
Version: tar.GetVersion(),
|
|
BrokeredCredentialSourceIds: []string{
|
|
cls[0].GetPublicId(),
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Bad version",
|
|
req: &pbs.AddTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion() + 2,
|
|
BrokeredCredentialSourceIds: []string{
|
|
cls[0].GetPublicId(),
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.Internal),
|
|
},
|
|
{
|
|
name: "Empty source list",
|
|
req: &pbs.AddTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Incorrect source id",
|
|
req: &pbs.AddTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
BrokeredCredentialSourceIds: []string{"incorrect"},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
}
|
|
for _, tc := range failCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
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, kms, &requestInfo)
|
|
_, gErr := s.AddTargetCredentialSources(ctx, tc.req)
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "AddTargetCredentialSources(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetTargetCredentialSources(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
s, err := testService(t, ctx, conn, kms, wrapper)
|
|
require.NoError(t, err, "Error when getting new target service.")
|
|
|
|
storeVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
|
cls := vault.TestCredentialLibraries(t, conn, wrapper, storeVault.GetPublicId(), 2)
|
|
|
|
storeStatic := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId())
|
|
creds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", storeStatic.GetPublicId(), proj.GetPublicId(), 2)
|
|
|
|
setCases := []struct {
|
|
name string
|
|
tar target.Target
|
|
setCredentialSources []string
|
|
resultCredentialSourceIds []string
|
|
}{
|
|
{
|
|
name: "Set library on empty target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty library"),
|
|
setCredentialSources: []string{cls[1].GetPublicId()},
|
|
resultCredentialSourceIds: []string{cls[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set static on empty target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty static"),
|
|
setCredentialSources: []string{creds[1].GetPublicId()},
|
|
resultCredentialSourceIds: []string{creds[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set library on library populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated library-library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
setCredentialSources: []string{cls[1].GetPublicId()},
|
|
resultCredentialSourceIds: []string{cls[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set static on library populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
setCredentialSources: []string{creds[1].GetPublicId()},
|
|
resultCredentialSourceIds: []string{creds[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set library on static populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated library-static", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
setCredentialSources: []string{cls[1].GetPublicId()},
|
|
resultCredentialSourceIds: []string{cls[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set static on static populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-static", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
setCredentialSources: []string{creds[1].GetPublicId()},
|
|
resultCredentialSourceIds: []string{creds[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set duplicate library on populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicate library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
setCredentialSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId()},
|
|
resultCredentialSourceIds: []string{cls[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set duplicate static on populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicate static", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
setCredentialSources: []string{creds[1].GetPublicId(), creds[1].GetPublicId()},
|
|
resultCredentialSourceIds: []string{creds[1].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Set empty on populated target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "another populated",
|
|
target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose)}),
|
|
target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose)})),
|
|
setCredentialSources: []string{},
|
|
resultCredentialSourceIds: nil,
|
|
},
|
|
}
|
|
for _, tc := range setCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
req := &pbs.SetTargetCredentialSourcesRequest{
|
|
Id: tc.tar.GetPublicId(),
|
|
Version: tc.tar.GetVersion(),
|
|
BrokeredCredentialSourceIds: tc.setCredentialSources,
|
|
}
|
|
|
|
requestInfo := authpb.RequestInfo{
|
|
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
|
PublicId: at.GetPublicId(),
|
|
Token: at.GetToken(),
|
|
}
|
|
requestContext := context.WithValue(ctx, requests.ContextRequestInformationKey, &requests.RequestContext{})
|
|
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
|
got, err := s.SetTargetCredentialSources(ctx, req)
|
|
require.NoError(t, err, "Got error: %v", s)
|
|
assert.ElementsMatch(t, tc.resultCredentialSourceIds, got.GetItem().GetBrokeredCredentialSourceIds())
|
|
assert.Equal(t, len(tc.resultCredentialSourceIds), len(got.GetItem().GetBrokeredCredentialSources()))
|
|
})
|
|
}
|
|
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test name")
|
|
|
|
failCases := []struct {
|
|
name string
|
|
req *pbs.SetTargetCredentialSourcesRequest
|
|
err error
|
|
}{
|
|
{
|
|
name: "Bad target Id",
|
|
req: &pbs.SetTargetCredentialSourcesRequest{
|
|
Id: "bad id",
|
|
Version: tar.GetVersion(),
|
|
BrokeredCredentialSourceIds: []string{cls[0].GetPublicId()},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Bad version",
|
|
req: &pbs.SetTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion() + 3,
|
|
BrokeredCredentialSourceIds: []string{cls[0].GetPublicId()},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.Internal),
|
|
},
|
|
{
|
|
name: "Bad source id",
|
|
req: &pbs.SetTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
BrokeredCredentialSourceIds: []string{"invalid"},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
}
|
|
for _, tc := range failCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
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, kms, &requestInfo)
|
|
_, gErr := s.SetTargetCredentialSources(ctx, tc.req)
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "SetTargetCredentialSources(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRemoveTargetCredentialSources(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
rw := db.New(conn)
|
|
|
|
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, kms)
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
s, err := testService(t, ctx, conn, kms, wrapper)
|
|
require.NoError(t, err, "Error when getting new target service.")
|
|
|
|
csVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
|
cls := vault.TestCredentialLibraries(t, conn, wrapper, csVault.GetPublicId(), 2)
|
|
|
|
csStatic := credstatic.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
|
creds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "u", "p", csStatic.GetPublicId(), proj.GetPublicId(), 2)
|
|
|
|
removeCases := []struct {
|
|
name string
|
|
tar target.Target
|
|
removeCredentialSources []string
|
|
resultCredentialSourceIds []string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "Remove library from empty",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty lib"),
|
|
removeCredentialSources: []string{cls[1].GetPublicId()},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Remove static from empty",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty static"),
|
|
removeCredentialSources: []string{creds[1].GetPublicId()},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Remove 1 of 2 libraries",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove partial lib",
|
|
target.WithCredentialLibraries([]*target.CredentialLibrary{
|
|
target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose),
|
|
target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.BrokeredPurpose),
|
|
})),
|
|
removeCredentialSources: []string{cls[1].GetPublicId()},
|
|
resultCredentialSourceIds: []string{cls[0].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Remove 1 of 2 static credentials",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove partial static",
|
|
target.WithStaticCredentials([]*target.StaticCredential{
|
|
target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose),
|
|
target.TestNewStaticCredential("", creds[1].GetPublicId(), credential.BrokeredPurpose),
|
|
})),
|
|
removeCredentialSources: []string{creds[1].GetPublicId()},
|
|
resultCredentialSourceIds: []string{creds[0].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Remove 1 duplicate set of 2 libraries",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove duplicate lib",
|
|
target.WithCredentialLibraries([]*target.CredentialLibrary{
|
|
target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose),
|
|
target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.BrokeredPurpose),
|
|
})),
|
|
removeCredentialSources: []string{
|
|
cls[1].GetPublicId(), cls[1].GetPublicId(),
|
|
},
|
|
resultCredentialSourceIds: []string{cls[0].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Remove 1 duplicate set of 2 static credentials",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove duplicate static",
|
|
target.WithStaticCredentials([]*target.StaticCredential{
|
|
target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose),
|
|
target.TestNewStaticCredential("", creds[1].GetPublicId(), credential.BrokeredPurpose),
|
|
})),
|
|
removeCredentialSources: []string{
|
|
creds[1].GetPublicId(), creds[1].GetPublicId(),
|
|
},
|
|
resultCredentialSourceIds: []string{creds[0].GetPublicId()},
|
|
},
|
|
{
|
|
name: "Remove mixed sources from target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove mixed",
|
|
target.WithCredentialLibraries([]*target.CredentialLibrary{
|
|
target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose),
|
|
target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.BrokeredPurpose),
|
|
}),
|
|
target.WithStaticCredentials([]*target.StaticCredential{
|
|
target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose),
|
|
target.TestNewStaticCredential("", creds[1].GetPublicId(), credential.BrokeredPurpose),
|
|
})),
|
|
removeCredentialSources: []string{
|
|
cls[1].GetPublicId(), creds[0].GetPublicId(),
|
|
},
|
|
resultCredentialSourceIds: []string{
|
|
cls[0].GetPublicId(), creds[1].GetPublicId(),
|
|
},
|
|
},
|
|
{
|
|
name: "Remove all sources from target",
|
|
tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove all",
|
|
target.WithCredentialLibraries([]*target.CredentialLibrary{
|
|
target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.BrokeredPurpose),
|
|
target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.BrokeredPurpose),
|
|
}),
|
|
target.WithStaticCredentials([]*target.StaticCredential{
|
|
target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.BrokeredPurpose),
|
|
target.TestNewStaticCredential("", creds[1].GetPublicId(), credential.BrokeredPurpose),
|
|
})),
|
|
removeCredentialSources: []string{
|
|
cls[0].GetPublicId(), cls[1].GetPublicId(),
|
|
creds[0].GetPublicId(), creds[1].GetPublicId(),
|
|
},
|
|
resultCredentialSourceIds: []string{},
|
|
},
|
|
}
|
|
|
|
for _, tc := range removeCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
req := &pbs.RemoveTargetCredentialSourcesRequest{
|
|
Id: tc.tar.GetPublicId(),
|
|
Version: tc.tar.GetVersion(),
|
|
BrokeredCredentialSourceIds: tc.removeCredentialSources,
|
|
}
|
|
|
|
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, kms, &requestInfo)
|
|
got, err := s.RemoveTargetCredentialSources(ctx, req)
|
|
if tc.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err, "Got error: %v", s)
|
|
|
|
assert.ElementsMatch(t, tc.resultCredentialSourceIds, got.GetItem().GetBrokeredCredentialSourceIds())
|
|
assert.Equal(t, len(tc.resultCredentialSourceIds), len(got.GetItem().GetBrokeredCredentialSources()))
|
|
})
|
|
}
|
|
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "testing")
|
|
|
|
failCases := []struct {
|
|
name string
|
|
req *pbs.RemoveTargetCredentialSourcesRequest
|
|
err error
|
|
}{
|
|
{
|
|
name: "Bad version",
|
|
req: &pbs.RemoveTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion() + 3,
|
|
BrokeredCredentialSourceIds: []string{
|
|
cls[0].GetPublicId(),
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.Internal),
|
|
},
|
|
{
|
|
name: "Bad target Id",
|
|
req: &pbs.RemoveTargetCredentialSourcesRequest{
|
|
Id: "bad id",
|
|
Version: tar.GetVersion(),
|
|
BrokeredCredentialSourceIds: []string{
|
|
cls[0].GetPublicId(),
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Empty sources",
|
|
req: &pbs.RemoveTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
BrokeredCredentialSourceIds: []string{},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "Invalid source ids",
|
|
req: &pbs.RemoveTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
BrokeredCredentialSourceIds: []string{
|
|
"invalid",
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
}
|
|
for _, tc := range failCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
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, kms, &requestInfo)
|
|
_, gErr := s.RemoveTargetCredentialSources(ctx, tc.req)
|
|
if tc.err != nil {
|
|
require.Error(gErr)
|
|
assert.True(errors.Is(gErr, tc.err), "RemoveTargetCredentialSources(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthorizeSession(t *testing.T) {
|
|
ctx := context.Background()
|
|
// This prevents us from running tests in parallel.
|
|
targets.SetupSuiteTargetFilters(t)
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
|
|
require.NoError(t, err)
|
|
|
|
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
|
return target.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
|
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
|
}
|
|
staticRepo, err := static.NewRepository(ctx, rw, rw, kms)
|
|
require.NoError(t, err)
|
|
staticHostRepoFn := func() (*static.Repository, error) {
|
|
return staticRepo, nil
|
|
}
|
|
vaultCredRepoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
staticCredRepoFn := func() (*credstatic.Repository, error) {
|
|
return credstatic.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
atRepoFn := func() (*authtoken.Repository, error) {
|
|
return authtoken.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
passwordAuthRepoFn := func() (*password.Repository, error) {
|
|
return password.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
oidcAuthRepoFn := func() (*oidc.Repository, error) {
|
|
return oidc.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
ldapAuthRepoFn := func() (*ldap.Repository, error) {
|
|
return ldap.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
targetAliasRepoFn := func() (*talias.Repository, error) {
|
|
return talias.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
plg := plugin.TestPlugin(t, conn, "test")
|
|
plgm := map[string]plgpb.HostPluginServiceClient{
|
|
plg.GetPublicId(): loopback.NewWrappingPluginHostClient(loopback.TestPluginHostServer{
|
|
ListHostsFn: func(_ context.Context, req *plgpb.ListHostsRequest) (*plgpb.ListHostsResponse, error) {
|
|
var setIds []string
|
|
for _, set := range req.GetSets() {
|
|
setIds = append(setIds, set.GetId())
|
|
}
|
|
return &plgpb.ListHostsResponse{Hosts: []*plgpb.ListHostsResponseHost{
|
|
{
|
|
SetIds: setIds,
|
|
ExternalId: "test",
|
|
IpAddresses: []string{"10.0.0.1", "192.168.0.1"},
|
|
DnsNames: []string{"example.com"},
|
|
},
|
|
{
|
|
SetIds: setIds,
|
|
ExternalId: "test2",
|
|
IpAddresses: []string{"10.1.1.1", "192.168.1.1"},
|
|
DnsNames: []string{"another-example.com"},
|
|
},
|
|
}}, nil
|
|
},
|
|
}),
|
|
}
|
|
pluginHostRepoFn := func() (*hostplugin.Repository, error) {
|
|
return hostplugin.NewRepository(ctx, rw, rw, kms, sche, plgm)
|
|
}
|
|
|
|
loginName := "foo@bar.com"
|
|
accountName := "passname"
|
|
userName := "username"
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t,
|
|
conn,
|
|
kms,
|
|
org.GetPublicId(),
|
|
authtoken.WithPasswordOptions(password.WithLoginName(loginName), password.WithName(accountName)),
|
|
authtoken.WithIamOptions(iam.WithName(userName)))
|
|
ctx = auth.NewVerifierContextWithAccounts(requests.NewRequestContext(ctx),
|
|
iamRepoFn,
|
|
atRepoFn,
|
|
serversRepoFn,
|
|
passwordAuthRepoFn,
|
|
oidcAuthRepoFn,
|
|
ldapAuthRepoFn,
|
|
kms,
|
|
&authpb.RequestInfo{
|
|
Token: at.GetToken(),
|
|
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
|
PublicId: at.GetPublicId(),
|
|
})
|
|
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
|
shs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
|
_ = static.TestSetMembers(t, conn, shs.GetPublicId(), []*static.Host{h})
|
|
|
|
hcWithPort := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
hWithPort := static.TestHosts(t, conn, hcWithPort.GetPublicId(), 1)[0]
|
|
shsWithPort := static.TestSets(t, conn, hcWithPort.GetPublicId(), 1)[0]
|
|
_ = static.TestSetMembers(t, conn, shsWithPort.GetPublicId(), []*static.Host{hWithPort})
|
|
hWithPortBareAddress := hWithPort.GetAddress()
|
|
hWithPort, _, err = staticRepo.UpdateHost(ctx, hcWithPort.GetProjectId(), hWithPort, hWithPort.GetVersion(), []string{"address"})
|
|
require.NoError(t, err)
|
|
|
|
phc := hostplugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
|
|
phs := hostplugin.TestSet(t, conn, kms, sche, phc, plgm, hostplugin.WithPreferredEndpoints([]string{"cidr:10.0.0.0/24"}))
|
|
|
|
// Sync the boundary db from the plugins
|
|
hostplugin.TestRunSetSync(t, conn, kms, plgm)
|
|
|
|
v := vault.NewTestVaultServer(t)
|
|
v.MountPKI(t, vault.WithTestMountPath("pki/"+userName))
|
|
sec, tok := v.CreateToken(t, vault.WithPolicies([]string{"default", "boundary-controller", "pki"}))
|
|
|
|
vaultStore := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor)
|
|
credService, err := credentiallibraries.NewService(ctx, iamRepoFn, vaultCredRepoFn, 1000)
|
|
require.NoError(t, err)
|
|
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Name: wrapperspb.String("Library Name"),
|
|
Description: wrapperspb.String("Library Description"),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: &wrapperspb.StringValue{
|
|
Value: path.Join("pki/{{ .User.Name}}", "issue", "boundary"),
|
|
},
|
|
HttpMethod: &wrapperspb.StringValue{
|
|
Value: "POST",
|
|
},
|
|
HttpRequestBody: &wrapperspb.StringValue{
|
|
Value: `{"common_name":"boundary.com", "alt_names": "{{.User.Name}},{{.Account.Name}},{{.Account.LoginName}},{{ truncateFrom .Account.LoginName "@" }}"}`,
|
|
},
|
|
},
|
|
},
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
const defaultPort = 2
|
|
cases := []struct {
|
|
name string
|
|
hostSourceId string
|
|
credSourceId string
|
|
wantedHostId string
|
|
wantedEndpoint string
|
|
}{
|
|
{
|
|
name: "static host",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: clsResp.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: fmt.Sprintf("%s:%d", h.GetAddress(), defaultPort),
|
|
},
|
|
{
|
|
name: "static host with port defined",
|
|
hostSourceId: shsWithPort.GetPublicId(),
|
|
credSourceId: clsResp.GetItem().GetId(),
|
|
wantedHostId: hWithPort.GetPublicId(),
|
|
wantedEndpoint: fmt.Sprintf("%s:%d", hWithPortBareAddress, defaultPort),
|
|
},
|
|
{
|
|
name: "plugin host",
|
|
hostSourceId: phs.GetPublicId(),
|
|
credSourceId: clsResp.GetItem().GetId(),
|
|
wantedHostId: "?",
|
|
wantedEndpoint: fmt.Sprintf("10.0.0.1:%d", defaultPort),
|
|
},
|
|
}
|
|
|
|
statusGracePeriod := new(atomic.Int64)
|
|
statusGracePeriod.Store(int64(server.DefaultLiveness))
|
|
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn, targetAliasRepoFn, nil, statusGracePeriod, 1000, nil)
|
|
require.NoError(t, err)
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Generate correlation Id and add it to the context
|
|
corId, err := uuid.GenerateUUID()
|
|
require.NoError(t, err)
|
|
ctx, err = event.NewCorrelationIdContext(ctx, corId)
|
|
require.NoError(t, err)
|
|
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), tc.name, target.WithDefaultPort(defaultPort))
|
|
apiTar, err := s.AddTargetHostSources(ctx, &pbs.AddTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{tc.hostSourceId},
|
|
})
|
|
require.NoError(t, err)
|
|
_, err = s.AddTargetCredentialSources(ctx,
|
|
&pbs.AddTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
BrokeredCredentialSourceIds: []string{clsResp.GetItem().GetId()},
|
|
Version: apiTar.GetItem().GetVersion(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Tell our DB that there is a worker ready to serve the data
|
|
server.TestKmsWorker(t, conn, wrapper)
|
|
|
|
asRes1, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
|
|
Id: tar.GetPublicId(),
|
|
})
|
|
require.NoError(t, err)
|
|
asRes2, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
|
|
Id: tar.GetPublicId(),
|
|
})
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, cmp.Diff(
|
|
asRes1.GetItem().GetCredentials(),
|
|
asRes2.GetItem().GetCredentials(),
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
),
|
|
"the credentials aren't unique per request authorized session")
|
|
|
|
_, err = s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
|
|
Id: tar.GetPublicId(),
|
|
HostId: asRes2.GetItem().GetHostId(),
|
|
})
|
|
require.NoError(t, err, "session must authorize with explicit host ID")
|
|
|
|
wantedHostId := tc.wantedHostId
|
|
if tc.wantedHostId == "?" {
|
|
wantedHostId = asRes2.GetItem().GetHostId()
|
|
}
|
|
|
|
want := &pb.SessionAuthorization{
|
|
Scope: &scopes.ScopeInfo{
|
|
Id: proj.GetPublicId(),
|
|
Type: proj.GetType(),
|
|
Name: proj.GetName(),
|
|
Description: proj.GetDescription(),
|
|
ParentScopeId: proj.GetParentId(),
|
|
},
|
|
TargetId: tar.GetPublicId(),
|
|
UserId: at.GetIamUserId(),
|
|
HostSetId: tc.hostSourceId,
|
|
HostId: wantedHostId,
|
|
Type: "tcp",
|
|
Endpoint: fmt.Sprintf("tcp://%s", tc.wantedEndpoint),
|
|
Credentials: []*pb.SessionCredential{
|
|
{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: clsResp.GetItem().GetId(),
|
|
Name: clsResp.GetItem().GetName().GetValue(),
|
|
Description: clsResp.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
},
|
|
},
|
|
},
|
|
EndpointPort: uint32(defaultPort),
|
|
// TODO: validate the contents of the authorization token is what is expected
|
|
}
|
|
wantSecret := map[string]any{
|
|
"certificate": "-----BEGIN CERTIFICATE-----\n",
|
|
"issuing_ca": "-----BEGIN CERTIFICATE-----\n",
|
|
"private_key": "-----BEGIN RSA PRIVATE KEY-----\n",
|
|
"private_key_type": "rsa",
|
|
}
|
|
got := asRes1.GetItem()
|
|
|
|
require.Len(t, got.GetCredentials(), 1)
|
|
|
|
gotCred := got.Credentials[0]
|
|
require.NotNil(t, gotCred.Secret)
|
|
assert.NotEmpty(t, gotCred.Secret.Raw)
|
|
dSec := decodeJsonSecret(t, gotCred.Secret.Raw)
|
|
require.NoError(t, err)
|
|
require.Equal(t, dSec, gotCred.Secret.Decoded.AsMap())
|
|
for k, v := range wantSecret {
|
|
gotV, ok := dSec[k]
|
|
require.True(t, ok)
|
|
assert.Truef(t, strings.HasPrefix(gotV.(string), v.(string)), "%q:%q doesn't have prefix %q", k, gotV, v)
|
|
}
|
|
|
|
b, _ := pem.Decode([]byte(dSec["certificate"].(string)))
|
|
require.NotNil(t, b)
|
|
cert, err := x509.ParseCertificate(b.Bytes)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, cert.DNSNames, userName)
|
|
assert.Contains(t, cert.DNSNames, accountName)
|
|
assert.Contains(t, cert.DNSNames, strings.Split(loginName, "@")[0])
|
|
assert.Contains(t, cert.EmailAddresses, loginName)
|
|
|
|
gotCred.Secret = nil
|
|
|
|
// CorrelationId is not part of the authSession resp, query the session directly
|
|
sessRepo, err := sessionRepoFn()
|
|
require.NoError(t, err)
|
|
gotSess, _, err := sessRepo.LookupSession(ctx, got.SessionId)
|
|
require.NoError(t, err)
|
|
require.Equal(t, corId, gotSess.CorrelationId)
|
|
|
|
got.AuthorizationToken, got.SessionId, got.CreatedTime = "", "", nil
|
|
assert.Empty(t, cmp.Diff(
|
|
got,
|
|
want,
|
|
protocmp.Transform(),
|
|
protocmp.IgnoreFields(&pb.SessionAuthorization{}, "expiration"),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
// This prevents us from running tests in parallel.
|
|
targets.SetupSuiteTargetFilters(t)
|
|
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
|
|
require.NoError(t, err)
|
|
|
|
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
|
return target.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
|
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
|
}
|
|
staticHostRepoFn := func() (*static.Repository, error) {
|
|
return static.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
vaultCredRepoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
staticCredRepoFn := func() (*credstatic.Repository, error) {
|
|
return credstatic.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
atRepoFn := func() (*authtoken.Repository, error) {
|
|
return authtoken.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
pluginHostRepoFn := func() (*hostplugin.Repository, error) {
|
|
return hostplugin.NewRepository(ctx, rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
|
}
|
|
targetAliasRepoFn := func() (*talias.Repository, error) {
|
|
return talias.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
ctx = auth.NewVerifierContext(requests.NewRequestContext(ctx),
|
|
iamRepoFn,
|
|
atRepoFn,
|
|
serversRepoFn,
|
|
kms,
|
|
&authpb.RequestInfo{
|
|
Token: at.GetToken(),
|
|
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
|
PublicId: at.GetPublicId(),
|
|
})
|
|
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
statusGracePeriod := new(atomic.Int64)
|
|
statusGracePeriod.Store(int64(server.DefaultLiveness))
|
|
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn, targetAliasRepoFn, nil, statusGracePeriod, 1000, nil)
|
|
require.NoError(t, err)
|
|
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
|
shs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
|
_ = static.TestSetMembers(t, conn, shs.GetPublicId(), []*static.Host{h})
|
|
|
|
v := vault.NewTestVaultServer(t)
|
|
v.AddKVPolicy(t)
|
|
sec, tok := v.CreateToken(t, vault.WithPolicies([]string{"default", "boundary-controller", "secret"}))
|
|
|
|
vaultStore := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor)
|
|
credLibService, err := credentiallibraries.NewService(ctx, iamRepoFn, vaultCredRepoFn, 1000)
|
|
require.NoError(t, err)
|
|
|
|
// Create secret in vault with default username and password fields
|
|
defaultUserPass := v.CreateKVSecret(t, "default-userpass", []byte(`{"data": {"username": "my-user", "password": "my-pass"}}`))
|
|
require.NotNil(t, defaultUserPass)
|
|
|
|
clsRespUsernamePassword, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Name: wrapperspb.String("Usernamepassword Library"),
|
|
Description: wrapperspb.String("Usernamepassword Library Description"),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(path.Join("secret", "data", "default-userpass")),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: "username_password",
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
clsRespUnspecified, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Name: wrapperspb.String("Unspecified Library"),
|
|
Description: wrapperspb.String("Unspecified Library Description"),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(path.Join("secret", "data", "default-userpass")),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
// Create secret in vault with non default username and password fields
|
|
nonDefaultUserPass := v.CreateKVSecret(t, "non-default-userpass", []byte(`{"data": {"non-default-user": "my-user", "non-default-pass": "my-pass"}}`))
|
|
require.NotNil(t, nonDefaultUserPass)
|
|
|
|
clsRespUsernamePasswordWithMapping, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Name: wrapperspb.String("Usernamepassword Mapping Library"),
|
|
Description: wrapperspb.String("Usernamepassword Mapping Library Description"),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(path.Join("secret", "data", "non-default-userpass")),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: "username_password",
|
|
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
|
|
"username_attribute": structpb.NewStringValue("non-default-user"),
|
|
"password_attribute": structpb.NewStringValue("non-default-pass"),
|
|
}},
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId())
|
|
credService, err := credentials.NewService(ctx, iamRepoFn, staticCredRepoFn, 1000)
|
|
require.NoError(t, err)
|
|
upCredResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
|
|
CredentialStoreId: staticStore.GetPublicId(),
|
|
Type: credential.UsernamePasswordSubtype.String(),
|
|
Name: wrapperspb.String("Cred Name"),
|
|
Description: wrapperspb.String("Cred Description"),
|
|
Attrs: &credpb.Credential_UsernamePasswordAttributes{
|
|
UsernamePasswordAttributes: &credpb.UsernamePasswordAttributes{
|
|
Username: wrapperspb.String("static-username"),
|
|
Password: wrapperspb.String("static-password"),
|
|
},
|
|
},
|
|
}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, upCredResp)
|
|
|
|
sshPkCredResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
|
|
CredentialStoreId: staticStore.GetPublicId(),
|
|
Type: credential.SshPrivateKeySubtype.String(),
|
|
Name: wrapperspb.String("Cred SSH Name"),
|
|
Description: wrapperspb.String("Cred SSH Description"),
|
|
Attrs: &credpb.Credential_SshPrivateKeyAttributes{
|
|
SshPrivateKeyAttributes: &credpb.SshPrivateKeyAttributes{
|
|
Username: wrapperspb.String("static-username"),
|
|
PrivateKey: wrapperspb.String(string(testdata.PEMBytes["ed25519"])),
|
|
},
|
|
},
|
|
}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, sshPkCredResp)
|
|
|
|
sshPkWithPassCredResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
|
|
CredentialStoreId: staticStore.GetPublicId(),
|
|
Type: credential.SshPrivateKeySubtype.String(),
|
|
Name: wrapperspb.String("Cred SSH With Pass Name"),
|
|
Description: wrapperspb.String("Cred SSH Description"),
|
|
Attrs: &credpb.Credential_SshPrivateKeyAttributes{
|
|
SshPrivateKeyAttributes: &credpb.SshPrivateKeyAttributes{
|
|
Username: wrapperspb.String("static-username"),
|
|
PrivateKey: wrapperspb.String(string(testdata.PEMEncryptedKeys[0].PEMBytes)),
|
|
PrivateKeyPassphrase: wrapperspb.String(testdata.PEMEncryptedKeys[0].EncryptionKey),
|
|
},
|
|
},
|
|
}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, sshPkWithPassCredResp)
|
|
|
|
// Create secret in vault with default username and private key fields
|
|
defaultSshPrivateKey := v.CreateKVSecret(t, "default-sshpk", []byte(`{"data": {"username": "my-user", "private_key": "my-pk"}}`))
|
|
require.NotNil(t, defaultSshPrivateKey)
|
|
|
|
clsRespSshPrivateKey, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Name: wrapperspb.String("SSH Private Key Library"),
|
|
Description: wrapperspb.String("SSH Private Key Library Description"),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(path.Join("secret", "data", "default-sshpk")),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: "ssh_private_key",
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
// Create secret in vault with non default username and private key fields
|
|
nonDefaultSshPrivateKey := v.CreateKVSecret(t, "non-default-sshpk", []byte(`{"data": {"non-default-user": "my-user", "non-default-pk": "my-special-pk"}}`))
|
|
require.NotNil(t, nonDefaultSshPrivateKey)
|
|
|
|
clsRespSshPrivateKeyWithMapping, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Name: wrapperspb.String("SSH Private Key Mapping Library"),
|
|
Description: wrapperspb.String("SSH Private Key Mapping Library Description"),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(path.Join("secret", "data", "non-default-sshpk")),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: "ssh_private_key",
|
|
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
|
|
"username_attribute": structpb.NewStringValue("non-default-user"),
|
|
"private_key_attribute": structpb.NewStringValue("non-default-pk"),
|
|
}},
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
// Create secret in vault with default username, private key and passphrase fields
|
|
defaultSshPrivateKeyWithPass := v.CreateKVSecret(t, "default-sshpk-with-pass", []byte(`{"data": {"username": "my-user", "private_key": "my-pk", "private_key_passphrase": "my-pass"}}`))
|
|
require.NotNil(t, defaultSshPrivateKeyWithPass)
|
|
|
|
clsRespSshPrivateKeyWithPass, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Name: wrapperspb.String("SSH Private Key With Passphrase Library"),
|
|
Description: wrapperspb.String("SSH Private Key Library Description"),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(path.Join("secret", "data", "default-sshpk-with-pass")),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: "ssh_private_key",
|
|
}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, clsRespSshPrivateKeyWithPass)
|
|
|
|
// Create secret in vault with non default username, private key and private key passphrase fields
|
|
nonDefaultSshPrivateKeyWithPass := v.CreateKVSecret(t, "non-default-sshpk-with-pass", []byte(`{"data": {"non-default-user": "my-user", "non-default-pk": "my-special-pk", "embedded": {"secret-pass": "my-special-pass"}}}}`))
|
|
require.NotNil(t, nonDefaultSshPrivateKeyWithPass)
|
|
|
|
clsRespSshPrivateKeyWithPassWithMapping, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Name: wrapperspb.String("SSH Private Key With Passphrase Mapping Library"),
|
|
Description: wrapperspb.String("SSH Private Key Mapping Library Description"),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(path.Join("secret", "data", "non-default-sshpk-with-pass")),
|
|
HttpMethod: wrapperspb.String("GET"),
|
|
},
|
|
},
|
|
CredentialType: "ssh_private_key",
|
|
CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{
|
|
"username_attribute": structpb.NewStringValue("/data/non-default-user"),
|
|
"private_key_attribute": structpb.NewStringValue("/data/non-default-pk"),
|
|
"private_key_passphrase_attribute": structpb.NewStringValue("/data/embedded/secret-pass"),
|
|
}},
|
|
}})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, clsRespSshPrivateKeyWithPassWithMapping)
|
|
|
|
cases := []struct {
|
|
name string
|
|
hostSourceId string
|
|
credSourceId string
|
|
wantedHostId string
|
|
wantedEndpoint string
|
|
wantedCred *pb.SessionCredential
|
|
}{
|
|
{
|
|
name: "vault-unspecified",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: clsRespUnspecified.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: h.GetAddress(),
|
|
wantedCred: &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: clsRespUnspecified.GetItem().GetId(),
|
|
Name: clsRespUnspecified.GetItem().GetName().GetValue(),
|
|
Description: clsRespUnspecified.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "vault-usernamepassword",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: clsRespUsernamePassword.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: h.GetAddress(),
|
|
wantedCred: &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: clsRespUsernamePassword.GetItem().GetId(),
|
|
Name: clsRespUsernamePassword.GetItem().GetName().GetValue(),
|
|
Description: clsRespUsernamePassword.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
CredentialType: string(globals.UsernamePasswordCredentialType),
|
|
},
|
|
Credential: func() *structpb.Struct {
|
|
data := map[string]any{
|
|
"password": "my-pass",
|
|
"username": "my-user",
|
|
}
|
|
st, err := structpb.NewStruct(data)
|
|
require.NoError(t, err)
|
|
return st
|
|
}(),
|
|
},
|
|
},
|
|
{
|
|
name: "vault-UsernamePassword-with-mapping",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: clsRespUsernamePasswordWithMapping.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: h.GetAddress(),
|
|
wantedCred: &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: clsRespUsernamePasswordWithMapping.GetItem().GetId(),
|
|
Name: clsRespUsernamePasswordWithMapping.GetItem().GetName().GetValue(),
|
|
Description: clsRespUsernamePasswordWithMapping.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
CredentialType: string(globals.UsernamePasswordCredentialType),
|
|
},
|
|
Credential: func() *structpb.Struct {
|
|
data := map[string]any{
|
|
"password": "my-pass",
|
|
"username": "my-user",
|
|
}
|
|
st, err := structpb.NewStruct(data)
|
|
require.NoError(t, err)
|
|
return st
|
|
}(),
|
|
},
|
|
},
|
|
{
|
|
name: "static-UsernamePassword",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: upCredResp.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: h.GetAddress(),
|
|
wantedCred: &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: upCredResp.GetItem().GetId(),
|
|
Name: upCredResp.GetItem().GetName().GetValue(),
|
|
Description: upCredResp.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: staticStore.GetPublicId(),
|
|
Type: credstatic.Subtype.String(),
|
|
CredentialType: string(globals.UsernamePasswordCredentialType),
|
|
},
|
|
Credential: func() *structpb.Struct {
|
|
data := map[string]any{
|
|
"password": "static-password",
|
|
"username": "static-username",
|
|
}
|
|
st, err := structpb.NewStruct(data)
|
|
require.NoError(t, err)
|
|
return st
|
|
}(),
|
|
},
|
|
},
|
|
{
|
|
name: "static-ssh-private-key",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: sshPkCredResp.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: h.GetAddress(),
|
|
wantedCred: &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: sshPkCredResp.GetItem().GetId(),
|
|
Name: sshPkCredResp.GetItem().GetName().GetValue(),
|
|
Description: sshPkCredResp.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: staticStore.GetPublicId(),
|
|
Type: credstatic.Subtype.String(),
|
|
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
|
},
|
|
Credential: func() *structpb.Struct {
|
|
data := map[string]any{
|
|
"private_key": string(testdata.PEMBytes["ed25519"]),
|
|
"username": "static-username",
|
|
}
|
|
st, err := structpb.NewStruct(data)
|
|
require.NoError(t, err)
|
|
return st
|
|
}(),
|
|
},
|
|
},
|
|
{
|
|
name: "vault-ssh-private-key",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: clsRespSshPrivateKey.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: h.GetAddress(),
|
|
wantedCred: &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: clsRespSshPrivateKey.GetItem().GetId(),
|
|
Name: clsRespSshPrivateKey.GetItem().GetName().GetValue(),
|
|
Description: clsRespSshPrivateKey.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
|
},
|
|
Credential: func() *structpb.Struct {
|
|
data := map[string]any{
|
|
"private_key": "my-pk",
|
|
"username": "my-user",
|
|
}
|
|
st, err := structpb.NewStruct(data)
|
|
require.NoError(t, err)
|
|
return st
|
|
}(),
|
|
},
|
|
},
|
|
{
|
|
name: "vault-ssh-private-key-with-mapping",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: clsRespSshPrivateKeyWithMapping.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: h.GetAddress(),
|
|
wantedCred: &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: clsRespSshPrivateKeyWithMapping.GetItem().GetId(),
|
|
Name: clsRespSshPrivateKeyWithMapping.GetItem().GetName().GetValue(),
|
|
Description: clsRespSshPrivateKeyWithMapping.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
|
},
|
|
Credential: func() *structpb.Struct {
|
|
data := map[string]any{
|
|
"username": "my-user",
|
|
"private_key": "my-special-pk",
|
|
}
|
|
st, err := structpb.NewStruct(data)
|
|
require.NoError(t, err)
|
|
return st
|
|
}(),
|
|
},
|
|
},
|
|
{
|
|
name: "static-ssh-private-key-with-pass",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: sshPkWithPassCredResp.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: h.GetAddress(),
|
|
wantedCred: &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: sshPkWithPassCredResp.GetItem().GetId(),
|
|
Name: sshPkWithPassCredResp.GetItem().GetName().GetValue(),
|
|
Description: sshPkWithPassCredResp.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: staticStore.GetPublicId(),
|
|
Type: credstatic.Subtype.String(),
|
|
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
|
},
|
|
Credential: func() *structpb.Struct {
|
|
data := map[string]any{
|
|
"private_key_passphrase": testdata.PEMEncryptedKeys[0].EncryptionKey,
|
|
"private_key": string(testdata.PEMEncryptedKeys[0].PEMBytes),
|
|
"username": "static-username",
|
|
}
|
|
st, err := structpb.NewStruct(data)
|
|
require.NoError(t, err)
|
|
return st
|
|
}(),
|
|
},
|
|
},
|
|
{
|
|
name: "vault-ssh-private-key-with-pass",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: clsRespSshPrivateKeyWithPass.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: h.GetAddress(),
|
|
wantedCred: &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: clsRespSshPrivateKeyWithPass.GetItem().GetId(),
|
|
Name: clsRespSshPrivateKeyWithPass.GetItem().GetName().GetValue(),
|
|
Description: clsRespSshPrivateKeyWithPass.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
|
},
|
|
Credential: func() *structpb.Struct {
|
|
data := map[string]any{
|
|
"username": "my-user",
|
|
"private_key": "my-pk",
|
|
"private_key_passphrase": "my-pass",
|
|
}
|
|
st, err := structpb.NewStruct(data)
|
|
require.NoError(t, err)
|
|
return st
|
|
}(),
|
|
},
|
|
},
|
|
{
|
|
name: "vault-ssh-private-key-with-pass-with-mapping",
|
|
hostSourceId: shs.GetPublicId(),
|
|
credSourceId: clsRespSshPrivateKeyWithPassWithMapping.GetItem().GetId(),
|
|
wantedHostId: h.GetPublicId(),
|
|
wantedEndpoint: h.GetAddress(),
|
|
wantedCred: &pb.SessionCredential{
|
|
CredentialSource: &pb.CredentialSource{
|
|
Id: clsRespSshPrivateKeyWithPassWithMapping.GetItem().GetId(),
|
|
Name: clsRespSshPrivateKeyWithPassWithMapping.GetItem().GetName().GetValue(),
|
|
Description: clsRespSshPrivateKeyWithPassWithMapping.GetItem().GetDescription().GetValue(),
|
|
CredentialStoreId: vaultStore.GetPublicId(),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
CredentialType: string(globals.SshPrivateKeyCredentialType),
|
|
},
|
|
Credential: func() *structpb.Struct {
|
|
data := map[string]any{
|
|
"username": "my-user",
|
|
"private_key": "my-special-pk",
|
|
"private_key_passphrase": "my-special-pass",
|
|
}
|
|
st, err := structpb.NewStruct(data)
|
|
require.NoError(t, err)
|
|
return st
|
|
}(),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
const defaultPort = 2
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), tc.name, target.WithDefaultPort(defaultPort))
|
|
apiTar, err := s.AddTargetHostSources(ctx, &pbs.AddTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{tc.hostSourceId},
|
|
})
|
|
require.NoError(t, err)
|
|
_, err = s.AddTargetCredentialSources(ctx,
|
|
&pbs.AddTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
BrokeredCredentialSourceIds: []string{tc.credSourceId},
|
|
Version: apiTar.GetItem().GetVersion(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Tell our DB that there is a worker ready to serve the data
|
|
server.TestKmsWorker(t, conn, wrapper)
|
|
|
|
// Generate correlation Id and add it to the context
|
|
corId, err := uuid.GenerateUUID()
|
|
require.NoError(t, err)
|
|
ctx, err = event.NewCorrelationIdContext(ctx, corId)
|
|
require.NoError(t, err)
|
|
|
|
asRes, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
|
|
Id: tar.GetPublicId(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
want := &pb.SessionAuthorization{
|
|
Scope: &scopes.ScopeInfo{
|
|
Id: proj.GetPublicId(),
|
|
Type: proj.GetType(),
|
|
Name: proj.GetName(),
|
|
Description: proj.GetDescription(),
|
|
ParentScopeId: proj.GetParentId(),
|
|
},
|
|
TargetId: tar.GetPublicId(),
|
|
UserId: at.GetIamUserId(),
|
|
HostSetId: tc.hostSourceId,
|
|
HostId: tc.wantedHostId,
|
|
Type: "tcp",
|
|
Endpoint: fmt.Sprintf("tcp://%s:%d", tc.wantedEndpoint, defaultPort),
|
|
Credentials: []*pb.SessionCredential{tc.wantedCred},
|
|
EndpointPort: uint32(defaultPort),
|
|
Expiration: asRes.Item.Expiration,
|
|
// TODO: validate the contents of the authorization token is what is expected
|
|
}
|
|
got := asRes.GetItem()
|
|
|
|
require.Len(t, got.GetCredentials(), 1)
|
|
|
|
gotCred := got.Credentials[0]
|
|
require.NotNil(t, gotCred.Secret)
|
|
assert.NotEmpty(t, gotCred.Secret.Raw)
|
|
|
|
gotCred.Secret = nil
|
|
got.AuthorizationToken, got.SessionId, got.CreatedTime = "", "", nil
|
|
assert.Empty(t, cmp.Diff(
|
|
got,
|
|
want,
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthorizeSession_Errors(t *testing.T) {
|
|
ctx := context.Background()
|
|
// This prevents us from running tests in parallel.
|
|
targets.SetupSuiteTargetFilters(t)
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
|
|
sche := scheduler.TestScheduler(t, conn, wrapper)
|
|
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
|
|
require.NoError(t, err)
|
|
|
|
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
|
return target.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iamRepo, nil
|
|
}
|
|
serversRepoFn := func() (*server.Repository, error) {
|
|
return server.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
|
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
|
}
|
|
staticHostRepoFn := func() (*static.Repository, error) {
|
|
return static.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
pluginHostRepoFn := func() (*hostplugin.Repository, error) {
|
|
return hostplugin.NewRepository(ctx, rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{})
|
|
}
|
|
vaultCredRepoFn := func() (*vault.Repository, error) {
|
|
return vault.NewRepository(ctx, rw, rw, kms, sche)
|
|
}
|
|
staticCredRepoFn := func() (*credstatic.Repository, error) {
|
|
return credstatic.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
atRepoFn := func() (*authtoken.Repository, error) {
|
|
return authtoken.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
targetAliasRepoFn := func() (*talias.Repository, error) {
|
|
return talias.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
org, proj := iam.TestScopes(t, iamRepo)
|
|
|
|
statusGracePeriod := new(atomic.Int64)
|
|
statusGracePeriod.Store(int64(server.DefaultLiveness))
|
|
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn, targetAliasRepoFn, nil, statusGracePeriod, 1000, nil)
|
|
require.NoError(t, err)
|
|
|
|
// Authorized user gets full permissions
|
|
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
|
ctx = auth.NewVerifierContext(requests.NewRequestContext(context.Background()),
|
|
iamRepoFn,
|
|
atRepoFn,
|
|
serversRepoFn,
|
|
kms,
|
|
&authpb.RequestInfo{
|
|
Token: at.GetToken(),
|
|
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
|
PublicId: at.GetPublicId(),
|
|
})
|
|
r := iam.TestRole(t, conn, proj.GetPublicId())
|
|
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
|
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
|
|
|
|
v := vault.NewTestVaultServer(t, vault.WithDockerNetwork(true))
|
|
v.MountDatabase(t)
|
|
sec, tok := v.CreateToken(t, vault.WithPolicies([]string{"default", "database"}))
|
|
store := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor)
|
|
|
|
sec1, tok1 := v.CreateToken(t, vault.WithPolicies([]string{"default", "database"}))
|
|
expiredStore := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok1, sec1.Auth.Accessor)
|
|
|
|
// Set previous token to expired in the database and revoke in Vault to validate a
|
|
// credential store with an expired token is correctly returned over the API
|
|
num, err := rw.Exec(context.Background(), "update credential_vault_token set status = ? where store_id = ?",
|
|
[]any{vault.ExpiredToken, expiredStore.PublicId})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, num)
|
|
v.RevokeToken(t, tok1)
|
|
|
|
workerExists := func(tar target.Target) (version uint32) {
|
|
server.TestKmsWorker(t, conn, wrapper)
|
|
return tar.GetVersion()
|
|
}
|
|
|
|
hostSetNoHostExists := func(tar target.Target) (version uint32) {
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
|
|
|
tr, err := s.AddTargetHostSources(ctx, &pbs.AddTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{hs.GetPublicId()},
|
|
})
|
|
require.NoError(t, err)
|
|
return tr.GetItem().GetVersion()
|
|
}
|
|
|
|
hostExists := func(tar target.Target) (version uint32) {
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
|
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
|
_ = static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
|
|
apiTar, err := s.AddTargetHostSources(ctx, &pbs.AddTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{hs.GetPublicId()},
|
|
})
|
|
require.NoError(t, err)
|
|
repo, err := staticHostRepoFn()
|
|
require.NoError(t, err)
|
|
_, _, err = repo.UpdateHost(ctx, hc.GetProjectId(), h, h.GetVersion(), []string{"address"})
|
|
require.NoError(t, err)
|
|
return apiTar.GetItem().GetVersion()
|
|
}
|
|
|
|
hostWithoutPort := func(tar target.Target) (version uint32) {
|
|
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
|
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
|
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
|
_ = static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
|
|
apiTar, err := s.AddTargetHostSources(ctx, &pbs.AddTargetHostSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
Version: tar.GetVersion(),
|
|
HostSourceIds: []string{hs.GetPublicId()},
|
|
})
|
|
require.NoError(t, err)
|
|
return apiTar.GetItem().GetVersion()
|
|
}
|
|
|
|
libraryExists := func(tar target.Target) (version uint32) {
|
|
credService, err := credentiallibraries.NewService(ctx, iamRepoFn, vaultCredRepoFn, 1000)
|
|
require.NoError(t, err)
|
|
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Description: wrapperspb.String(fmt.Sprintf("Library Description for target %q", tar.GetName())),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(path.Join("database", "creds", "opened")),
|
|
},
|
|
},
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
tr, err := s.AddTargetCredentialSources(ctx,
|
|
&pbs.AddTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
BrokeredCredentialSourceIds: []string{clsResp.GetItem().GetId()},
|
|
Version: tar.GetVersion(),
|
|
})
|
|
require.NoError(t, err)
|
|
return tr.GetItem().GetVersion()
|
|
}
|
|
|
|
misConfiguredlibraryExists := func(tar target.Target) (version uint32) {
|
|
credService, err := credentiallibraries.NewService(ctx, iamRepoFn, vaultCredRepoFn, 1000)
|
|
require.NoError(t, err)
|
|
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: store.GetPublicId(),
|
|
Description: wrapperspb.String(fmt.Sprintf("Library Description for target %q", tar.GetName())),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String("bad path"),
|
|
},
|
|
},
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
tr, err := s.AddTargetCredentialSources(ctx,
|
|
&pbs.AddTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
BrokeredCredentialSourceIds: []string{clsResp.GetItem().GetId()},
|
|
Version: tar.GetVersion(),
|
|
})
|
|
require.NoError(t, err)
|
|
return tr.GetItem().GetVersion()
|
|
}
|
|
|
|
expiredTokenLibrary := func(tar target.Target) (version uint32) {
|
|
credService, err := credentiallibraries.NewService(ctx, iamRepoFn, vaultCredRepoFn, 1000)
|
|
require.NoError(t, err)
|
|
clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{
|
|
CredentialStoreId: expiredStore.GetPublicId(),
|
|
Description: wrapperspb.String(fmt.Sprintf("Library Description for target %q", tar.GetName())),
|
|
Type: vault.GenericLibrarySubtype.String(),
|
|
Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{
|
|
VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{
|
|
Path: wrapperspb.String(path.Join("database", "creds", "opened")),
|
|
},
|
|
},
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
tr, err := s.AddTargetCredentialSources(ctx,
|
|
&pbs.AddTargetCredentialSourcesRequest{
|
|
Id: tar.GetPublicId(),
|
|
BrokeredCredentialSourceIds: []string{clsResp.GetItem().GetId()},
|
|
Version: tar.GetVersion(),
|
|
})
|
|
require.NoError(t, err)
|
|
return tr.GetItem().GetVersion()
|
|
}
|
|
|
|
// Generate correlation Id and add it to the context
|
|
corId, err := uuid.GenerateUUID()
|
|
require.NoError(t, err)
|
|
ctxWithCor, err := event.NewCorrelationIdContext(ctx, corId)
|
|
require.NoError(t, err)
|
|
|
|
cases := []struct {
|
|
name string
|
|
ctx context.Context
|
|
setup []func(target.Target) uint32
|
|
useTargetId bool
|
|
wantErr bool
|
|
wantErrContains string
|
|
}{
|
|
{
|
|
// This one must be run first since it relies on the DB not having any worker details
|
|
name: "no worker",
|
|
ctx: ctxWithCor,
|
|
setup: []func(tcpTarget target.Target) uint32{hostExists, libraryExists},
|
|
useTargetId: true,
|
|
wantErrContains: "No workers are available to handle this session",
|
|
},
|
|
{
|
|
name: "success",
|
|
ctx: ctxWithCor,
|
|
setup: []func(tcpTarget target.Target) uint32{workerExists, hostExists, libraryExists},
|
|
useTargetId: true,
|
|
},
|
|
{
|
|
name: "no target",
|
|
ctx: ctxWithCor,
|
|
setup: []func(tcpTarget target.Target) uint32{workerExists, hostExists, libraryExists},
|
|
useTargetId: false,
|
|
wantErrContains: "Resource not found",
|
|
},
|
|
{
|
|
name: "no host port",
|
|
ctx: ctxWithCor,
|
|
setup: []func(tcpTarget target.Target) uint32{workerExists, hostWithoutPort, libraryExists},
|
|
useTargetId: true,
|
|
},
|
|
{
|
|
name: "host port",
|
|
ctx: ctxWithCor,
|
|
setup: []func(tcpTarget target.Target) uint32{
|
|
workerExists, func(tcpTarget target.Target) uint32 {
|
|
tcpTarget.SetAddress("127.0.0.1:22")
|
|
repo, err := repoFn()
|
|
require.NoError(t, err)
|
|
tcpTarget, _, err = repo.UpdateTarget(ctx, tcpTarget, tcpTarget.GetVersion(), []string{"address"})
|
|
require.NoError(t, err)
|
|
return tcpTarget.GetVersion()
|
|
},
|
|
},
|
|
wantErrContains: "Address specified for use unexpectedly contains a port",
|
|
useTargetId: true,
|
|
},
|
|
{
|
|
name: "no hosts",
|
|
ctx: ctxWithCor,
|
|
setup: []func(tcpTarget target.Target) uint32{workerExists, hostSetNoHostExists, libraryExists},
|
|
useTargetId: true,
|
|
wantErrContains: "No host sources or address found for given target",
|
|
},
|
|
{
|
|
name: "bad library configuration",
|
|
ctx: ctxWithCor,
|
|
setup: []func(tcpTarget target.Target) uint32{workerExists, hostExists, misConfiguredlibraryExists},
|
|
useTargetId: true,
|
|
wantErrContains: "external system issue: error #3014: Error making API request",
|
|
},
|
|
{
|
|
name: "expired token library",
|
|
ctx: ctxWithCor,
|
|
setup: []func(tcpTarget target.Target) uint32{workerExists, hostExists, expiredTokenLibrary},
|
|
useTargetId: true,
|
|
wantErrContains: "vault.newClient: invalid configuration",
|
|
},
|
|
{
|
|
name: "no correaltion id",
|
|
ctx: ctx,
|
|
setup: []func(tcpTarget target.Target) uint32{workerExists, hostExists, libraryExists},
|
|
useTargetId: true,
|
|
wantErrContains: "authorize session: missing correlation id",
|
|
},
|
|
}
|
|
for i, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ctx := tc.ctx
|
|
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), fmt.Sprintf("test-%d", i), target.WithDefaultPort(22))
|
|
|
|
for _, fn := range tc.setup {
|
|
ver := fn(tar)
|
|
tar.SetVersion(ver)
|
|
}
|
|
|
|
id := tar.GetPublicId()
|
|
if !tc.useTargetId {
|
|
id = "ttcp_bogusid"
|
|
}
|
|
|
|
res, err := s.AuthorizeSession(ctx, &pbs.AuthorizeSessionRequest{
|
|
Id: id,
|
|
})
|
|
if tc.wantErrContains != "" {
|
|
require.Error(t, err)
|
|
require.Nil(t, res)
|
|
require.ErrorContains(t, err, tc.wantErrContains)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func decodeJsonSecret(t *testing.T, in string) map[string]any {
|
|
t.Helper()
|
|
ret := make(map[string]any)
|
|
dec := json.NewDecoder(base64.NewDecoder(base64.StdEncoding, strings.NewReader(in)))
|
|
require.NoError(t, dec.Decode(&ret))
|
|
return ret
|
|
}
|