// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package targets_test import ( "fmt" "net/http" "strings" "testing" "time" "github.com/hashicorp/boundary/api" "github.com/hashicorp/boundary/api/aliases" "github.com/hashicorp/boundary/api/credentiallibraries" "github.com/hashicorp/boundary/api/credentials" "github.com/hashicorp/boundary/api/credentialstores" "github.com/hashicorp/boundary/api/hostcatalogs" "github.com/hashicorp/boundary/api/hosts" "github.com/hashicorp/boundary/api/hostsets" "github.com/hashicorp/boundary/api/roles" "github.com/hashicorp/boundary/api/targets" "github.com/hashicorp/boundary/globals" aliastar "github.com/hashicorp/boundary/internal/alias/target" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/daemon/controller" "github.com/hashicorp/boundary/internal/daemon/worker" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/iam" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" _ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets/tcp" ) func TestHostSetASD(t *testing.T) { assert, require := assert.New(t), require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() token := tc.Token() _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) client := tc.Client().Clone() client.SetToken(token.Token) hc, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) require.NoError(err) hSetClient := hostsets.NewClient(client) hSet, err := hSetClient.Create(tc.Context(), hc.Item.Id) require.NoError(err) require.NotNil(hSet) hSet2, err := hSetClient.Create(tc.Context(), hc.Item.Id) require.NoError(err) require.NotNil(hSet2) tarClient := targets.NewClient(client) tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) require.NotNil(tar) assert.Empty(tar.Item.HostSourceIds) aliasedTar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("aliased"), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) require.NotNil(aliasedTar) assert.Empty(aliasedTar.Item.HostSourceIds) rw := db.New(tc.DbConn()) al := aliastar.TestAlias(t, rw, "alias.value", aliastar.WithDestinationId(aliasedTar.Item.Id)) cases := []struct { name string id string }{ { name: "target id", id: tar.Item.Id, }, { name: "aliases", id: al.GetValue(), }, } for _, c := range cases { tar, err := tarClient.AddHostSources(tc.Context(), c.id, 1, []string{hSet.Item.Id}) require.NoError(err) require.NotNil(tar) assert.ElementsMatch(tar.Item.HostSourceIds, []string{hSet.Item.Id}) tar, err = tarClient.SetHostSources(tc.Context(), c.id, tar.Item.Version, []string{hSet2.Item.Id}) require.NoError(err) require.NotNil(tar) assert.ElementsMatch(tar.Item.HostSourceIds, []string{hSet2.Item.Id}) tar, err = tarClient.RemoveHostSources(tc.Context(), c.id, tar.Item.Version, []string{hSet2.Item.Id}) require.NoError(err) require.NotNil(tar) assert.Empty(tar.Item.HostSourceIds) } } func TestCredentialSourcesASD(t *testing.T) { assert, require := assert.New(t), require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() vaultServ := vault.NewTestVaultServer(t, vault.WithTestVaultTLS(vault.TestNoTLS)) _, vaultTok := vaultServ.CreateToken(t) token := tc.Token() _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) client := tc.Client().Clone() client.SetToken(token.Token) csVault, err := credentialstores.NewClient(client).Create(tc.Context(), "vault", proj.GetPublicId(), credentialstores.WithVaultCredentialStoreAddress(vaultServ.Addr), credentialstores.WithVaultCredentialStoreToken(vaultTok)) require.NoError(err) require.NotNil(csVault) lClient := credentiallibraries.NewClient(client) lib1, err := lClient.Create(tc.Context(), "vault-generic", csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something1")) require.NoError(err) require.NotNil(lib1) lib2, err := lClient.Create(tc.Context(), "vault-generic", csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something2")) require.NoError(err) require.NotNil(lib2) csStatic, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) require.NoError(err) require.NotNil(csStatic) cClient := credentials.NewClient(client) cOpts := []credentials.Option{credentials.WithUsernamePasswordCredentialUsername("user"), credentials.WithUsernamePasswordCredentialPassword("pass")} cred, err := cClient.Create(tc.Context(), "username_password", csStatic.Item.Id, cOpts...) require.NoError(err) require.NotNil(cred) tarClient := targets.NewClient(client) tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) require.NotNil(tar) assert.Empty(tar.Item.BrokeredCredentialSourceIds) aliasedTar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("aliased"), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) require.NotNil(aliasedTar) assert.Empty(aliasedTar.Item.BrokeredCredentialSourceIds) rw := db.New(tc.DbConn()) al := aliastar.TestAlias(t, rw, "alias.value", aliastar.WithDestinationId(aliasedTar.Item.Id)) cases := []struct { name string id string }{ { name: "target id", id: tar.Item.Id, }, { name: "aliases", id: al.GetValue(), }, } for _, c := range cases { // Add first Vault library tar, err := tarClient.AddCredentialSources(tc.Context(), c.id, 1, targets.WithBrokeredCredentialSourceIds([]string{lib1.Item.Id})) require.NoError(err) require.NotNil(tar) assert.ElementsMatch(tar.Item.BrokeredCredentialSourceIds, []string{lib1.Item.Id}) assert.ElementsMatch(tar.Item.BrokeredCredentialSources, []*targets.CredentialSource{ { Id: lib1.Item.Id, CredentialStoreId: csVault.Item.Id, }, }) // Set second Vault library and a static credential tar, err = tarClient.SetCredentialSources(tc.Context(), c.id, tar.Item.Version, targets.WithBrokeredCredentialSourceIds([]string{lib2.Item.Id, cred.Item.Id})) require.NoError(err) require.NotNil(tar) assert.ElementsMatch(tar.Item.BrokeredCredentialSourceIds, []string{lib2.Item.Id, cred.Item.Id}) assert.ElementsMatch(tar.Item.BrokeredCredentialSources, []*targets.CredentialSource{ { Id: lib2.Item.Id, CredentialStoreId: csVault.Item.Id, }, { Id: cred.Item.Id, CredentialStoreId: csStatic.Item.Id, }, }) // Remove second Vault library tar, err = tarClient.RemoveCredentialSources(tc.Context(), c.id, tar.Item.Version, targets.WithBrokeredCredentialSourceIds([]string{lib2.Item.Id})) require.NoError(err) require.NotNil(tar) assert.ElementsMatch(tar.Item.BrokeredCredentialSourceIds, []string{cred.Item.Id}) assert.ElementsMatch(tar.Item.BrokeredCredentialSources, []*targets.CredentialSource{ { Id: cred.Item.Id, CredentialStoreId: csStatic.Item.Id, }, }) // Set empty credential sources tar, err = tarClient.SetCredentialSources(tc.Context(), c.id, tar.Item.Version, targets.WithBrokeredCredentialSourceIds([]string{})) require.NoError(err) require.NotNil(tar) assert.Empty(tar.Item.BrokeredCredentialSourceIds) } } func TestList(t *testing.T) { assert, require := assert.New(t), require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() token := tc.Token() client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) // Add unpriv user to default role in new project. Also add create grant as // it will be needed later in the test and no-op for listing visibility. rls, err := roles.NewClient(client).List(tc.Context(), proj.GetPublicId()) require.NoError(err) var defaultRoleId string for _, rl := range rls.Items { if strings.Contains(rl.Name, "Default") { defaultRoleId = rl.Id break } } require.NotEmpty(defaultRoleId) unprivToken := tc.UnprivilegedToken() iam.TestUserRole(t, tc.DbConn(), defaultRoleId, unprivToken.UserId) iam.TestRoleGrant(t, tc.DbConn(), defaultRoleId, "type=target;actions=create") iam.TestRoleGrant(t, tc.DbConn(), defaultRoleId, "ids=*;type=target;actions=no-op") client.SetToken(unprivToken.Token) tarClient := targets.NewClient(client) ul, err := tarClient.List(tc.Context(), proj.GetPublicId()) require.NoError(err) assert.Empty(ul.Items) var expected []*targets.Target for i := 0; i < 10; i++ { expected = append(expected, &targets.Target{Name: fmt.Sprint(i)}) } tcr, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName(expected[0].Name), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) expected[0] = tcr.Item ul, err = tarClient.List(tc.Context(), proj.GetPublicId()) require.NoError(err) assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(ul.Items)) for i := 1; i < 10; i++ { tcr, err = tarClient.Create( tc.Context(), "tcp", proj.GetPublicId(), targets.WithName(expected[i].Name), targets.WithTcpTargetDefaultPort(uint32(i)), targets.WithTcpTargetDefaultClientPort(uint32(i+100)), ) require.NoError(err) expected[i] = tcr.Item } filterItem := expected[3] ul, err = tarClient.List(tc.Context(), proj.GetPublicId()) require.NoError(err) assert.ElementsMatch(comparableSlice(expected), comparableSlice(ul.Items)) ul, err = tarClient.List(tc.Context(), proj.GetPublicId(), targets.WithFilter(fmt.Sprintf(`"/item/id"==%q`, filterItem.Id))) require.NoError(err) assert.Len(ul.Items, 1) assert.Equal(filterItem.Id, ul.Items[0].Id) filterstr := fmt.Sprintf(`"/item/attributes/default_port"==%d`, uint32(filterItem.Attributes["default_port"].(float64))) ul, err = tarClient.List(tc.Context(), proj.GetPublicId(), targets.WithFilter(filterstr)) require.NoError(err) assert.Len(ul.Items, 1) assert.Equal(filterItem.Id, ul.Items[0].Id) } func TestListWithListToken(t *testing.T) { // Set database read timeout to avoid duplicates in response oldReadTimeout := globals.RefreshReadLookbackDuration globals.RefreshReadLookbackDuration = 0 t.Cleanup(func() { globals.RefreshReadLookbackDuration = oldReadTimeout }) require := require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() token := tc.Token() client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) tarClient := targets.NewClient(client) _, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("1"), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) _, err = tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("2"), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) // Refresh tokens using project scope res, err := tarClient.List(tc.Context(), proj.GetPublicId(), targets.WithRecursive(true)) require.NoError(err) require.Len(res.Items, 2, "expected the 2 targets created above") refTok := res.ListToken res, err = tarClient.List(tc.Context(), proj.GetPublicId(), targets.WithRecursive(true), targets.WithListToken(refTok)) require.NoError(err) require.Empty(res.Items) // Refresh tokens recursive listing over global scope res, err = tarClient.List(tc.Context(), "global", targets.WithRecursive(true)) require.NoError(err) require.Len(res.Items, 4, "expected the 2 targets created above and the 2 auto created for the test controller") refTok = res.ListToken res, err = tarClient.List(tc.Context(), "global", targets.WithRecursive(true), targets.WithListToken(refTok)) require.NoError(err) require.Empty(res.Items) } func TestListWithPageSize(t *testing.T) { // Set database read timeout to avoid duplicates in response oldReadTimeout := globals.RefreshReadLookbackDuration globals.RefreshReadLookbackDuration = 0 t.Cleanup(func() { globals.RefreshReadLookbackDuration = oldReadTimeout }) require := require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() token := tc.Token() client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) tarClient := targets.NewClient(client) _, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("1"), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) _, err = tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("2"), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) // Refresh tokens recursive listing over global scope res, err := tarClient.List(tc.Context(), "global", targets.WithRecursive(true), targets.WithPageSize(2)) require.NoError(err) require.Len(res.Items, 4, "expected the 2 targets created above and the 2 auto created for the test controller") refTok := res.ListToken res, err = tarClient.List(tc.Context(), "global", targets.WithRecursive(true), targets.WithListToken(refTok)) require.NoError(err) require.Empty(res.Items) } func TestTarget_AddressMutualExclusiveRelationship(t *testing.T) { tc := controller.NewTestController(t, nil) client := tc.Client() at := tc.Token() client.SetToken(at.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(at.UserId)) tClient := targets.NewClient(client) // Create target with a network address association targetResp, err := tClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("test-address"), targets.WithAddress("[::1]"), targets.WithTcpTargetDefaultPort(22)) require.NoError(t, err) require.NotNil(t, targetResp) require.Equal(t, "::1", targetResp.GetItem().Address) // Setup host catalog, host set, & host resources hc, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) require.NoError(t, err) require.NotNil(t, hc) hs, err := hostsets.NewClient(client).Create(tc.Context(), hc.Item.Id) require.NoError(t, err) require.NotNil(t, hs) h, err := hosts.NewClient(client).Create(tc.Context(), hc.Item.Id, hosts.WithStaticHostAddress("[::1]")) require.NoError(t, err) require.NotNil(t, h) hUpdate, err := hostsets.NewClient(client).AddHosts(tc.Context(), hs.Item.Id, hs.Item.Version, []string{h.GetItem().Id}) require.NoError(t, err) require.NotNil(t, hUpdate) // Expect error when associating a host source to the target with a direct network address association targetId := targetResp.GetItem().Id version := targetResp.GetItem().Version updateResp, err := tClient.AddHostSources(tc.Context(), targetId, version, []string{hs.Item.Id}) require.Error(t, err) require.Nil(t, updateResp) apiErr := api.AsServerError(err) require.NotNil(t, apiErr) require.Equal(t, http.StatusBadRequest, apiErr.Response().StatusCode()) // Remove direct network address association. Successfully add a host source to target. targetResp, err = tClient.Update(tc.Context(), targetId, version, targets.DefaultAddress()) require.NoError(t, err) require.NotNil(t, targetResp) require.Empty(t, targetResp.GetItem().Address) version = targetResp.GetItem().Version updateResp, err = tClient.AddHostSources(tc.Context(), targetId, version, []string{hs.Item.Id}) require.NoError(t, err) require.NotNil(t, updateResp) require.Empty(t, updateResp.GetItem().Address) require.Equal(t, []string{hs.Item.Id}, updateResp.GetItem().HostSourceIds) } func TestTarget_HostSourceMutualExclusiveRelationship(t *testing.T) { tc := controller.NewTestController(t, nil) client := tc.Client() at := tc.Token() client.SetToken(at.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(at.UserId)) tClient := targets.NewClient(client) // Setup host catalog, host set, & host resources hc, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) require.NoError(t, err) require.NotNil(t, hc) hs, err := hostsets.NewClient(client).Create(tc.Context(), hc.Item.Id) require.NoError(t, err) require.NotNil(t, hs) h, err := hosts.NewClient(client).Create(tc.Context(), hc.Item.Id, hosts.WithStaticHostAddress("[::1]")) require.NoError(t, err) require.NotNil(t, h) hUpdate, err := hostsets.NewClient(client).AddHosts(tc.Context(), hs.Item.Id, hs.Item.Version, []string{h.GetItem().Id}) require.NoError(t, err) require.NotNil(t, hUpdate) // Create target without a network address association targetResp, err := tClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("test-host-source"), targets.WithTcpTargetDefaultPort(22)) require.NoError(t, err) require.NotNil(t, targetResp) require.Empty(t, targetResp.GetItem().Address) // Expect error when associating a address to the target with a host source association targetId := targetResp.GetItem().Id version := targetResp.GetItem().Version updateResp, err := tClient.AddHostSources(tc.Context(), targetId, version, []string{hs.Item.Id}) require.NoError(t, err) require.NotNil(t, updateResp) require.Empty(t, updateResp.GetItem().Address) require.Equal(t, []string{hs.Item.Id}, updateResp.GetItem().HostSourceIds) version = updateResp.GetItem().Version updateResp, err = tClient.Update(tc.Context(), targetId, version, targets.WithAddress("[::1]")) require.Error(t, err) require.Nil(t, updateResp) apiErr := api.AsServerError(err) require.NotNil(t, apiErr) require.Equal(t, http.StatusBadRequest, apiErr.Response().StatusCode()) // Remove host source association. Successfully assign a network address to the target. updateResp, err = tClient.RemoveHostSources(tc.Context(), targetId, version, []string{hs.Item.Id}) require.NoError(t, err) require.NotNil(t, updateResp) require.Empty(t, updateResp.GetItem().HostSourceIds) version = updateResp.GetItem().Version updateResp, err = tClient.Update(tc.Context(), targetId, version, targets.WithAddress("[::1]")) require.NoError(t, err) require.NotNil(t, updateResp) require.Equal(t, "::1", updateResp.GetItem().Address) require.Empty(t, updateResp.GetItem().HostSourceIds) } func TestCreateTarget_DirectlyAttachedAddress(t *testing.T) { tc := controller.NewTestController(t, nil) client := tc.Client() at := tc.Token() client.SetToken(at.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(at.UserId)) tClient := targets.NewClient(client) tests := []struct { name string address string expectedAddress string }{ { name: "target-ipv4-address", address: "127.0.0.1", expectedAddress: "127.0.0.1", }, { name: "target-ipv6-address", address: "[2001:4860:4860:0:0:0:0:8888]", expectedAddress: "2001:4860:4860:0:0:0:0:8888", }, { name: "target-abbreviated-ipv6-address", address: "[2001:4860:4860::8888]", expectedAddress: "2001:4860:4860::8888", }, { name: "target-dns-address", address: "www.google.com", expectedAddress: "www.google.com", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) createResp, err := tClient.Create(tc.Context(), "tcp", proj.PublicId, targets.WithName(tt.name), targets.WithAddress(tt.address), targets.WithTcpTargetDefaultPort(22)) require.NoError(err) require.NotNil(createResp) assert.Equal(tt.expectedAddress, createResp.GetItem().Address) targetId := createResp.GetItem().Id version := createResp.GetItem().Version readResp, err := tClient.Read(tc.Context(), targetId) require.NoError(err) require.NotNil(readResp) assert.Equal(tt.expectedAddress, readResp.GetItem().Address) updateResp, err := tClient.Update(tc.Context(), targetId, version, targets.DefaultAddress()) require.NoError(err) require.NotNil(updateResp) assert.Empty(updateResp.GetItem().Address) readResp, err = tClient.Read(tc.Context(), targetId) require.NoError(err) require.NotNil(readResp) assert.Empty(readResp.GetItem().Address) }) } } func TestUpdateTarget_DeleteAddress(t *testing.T) { tc := controller.NewTestController(t, nil) client := tc.Client() at := tc.Token() client.SetToken(at.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(at.UserId)) tClient := targets.NewClient(client) assert, require := assert.New(t), require.New(t) // Create Target with address addr := "127.0.0.1" createResp, err := tClient.Create(tc.Context(), "tcp", proj.PublicId, targets.WithName("test_target_addr_delete"), targets.WithAddress(addr), targets.WithTcpTargetDefaultPort(22)) require.NoError(err) require.NotNil(createResp) assert.Equal(addr, createResp.GetItem().Address) aliasedTar, err := tClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("aliased"), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) require.NotNil(aliasedTar) rw := db.New(tc.DbConn()) al := aliastar.TestAlias(t, rw, "alias.value", aliastar.WithDestinationId(aliasedTar.Item.Id)) cases := []struct { name string id string }{ { name: "target id", id: createResp.Item.Id, }, { name: "aliases", id: al.GetValue(), }, } for _, c := range cases { // Update to delete address (set to null) updateResp, err := tClient.Update(tc.Context(), c.id, 1, targets.DefaultAddress()) require.NoError(err) require.NotNil(updateResp) assert.Empty(updateResp.GetItem().Address) // Do it again with same version, should error _, err = tClient.Update(tc.Context(), c.id, 1, targets.DefaultAddress()) require.Error(err) // Do it again with the correct version, ensure this is not an error. secondUpdateResp, err := tClient.Update(tc.Context(), c.id, updateResp.Item.Version, targets.DefaultAddress()) require.NoError(err) require.NotNil(secondUpdateResp) assert.Empty(secondUpdateResp.GetItem().Address) assert.NotEqual(updateResp.Item.Version, secondUpdateResp.Item.Version) } } func comparableSlice(in []*targets.Target) []targets.Target { var filtered []targets.Target for _, i := range in { p := targets.Target{ Id: i.Id, Name: i.Name, Description: i.Description, CreatedTime: i.CreatedTime, UpdatedTime: i.UpdatedTime, } filtered = append(filtered, p) } return filtered } func TestCreateWithAliases(t *testing.T) { assert, require := assert.New(t), require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() token := tc.Token() client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) tarClient := targets.NewClient(client) hostId := "hst_1234567890" tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"), targets.WithTcpTargetDefaultPort(2), targets.WithAliases([]targets.Alias{ { Value: "alias1", ScopeId: "global", Attributes: &targets.TargetAliasAttributes{ AuthorizeSessionArguments: &targets.AuthorizeSessionArguments{ HostId: hostId, }, }, }, { Value: "alias2", ScopeId: "global", }, })) require.NoError(err) assert.NotNil(tar) assert.Len(tar.GetItem().Aliases, 2) assert.NotEmpty(tar.GetItem().Aliases[0].Id) assert.NotEmpty(tar.GetItem().Aliases[1].Id) assert.NotEqual(tar.GetItem().Aliases[0].Value, tar.GetItem().Aliases[1].Value) aliasClient := aliases.NewClient(client) a1, err := aliasClient.Read(tc.Context(), tar.GetItem().Aliases[0].Id) require.NoError(err) assert.Equal("alias1", a1.Item.Value) } func TestCrud(t *testing.T) { assert, require := assert.New(t), require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() token := tc.Token() client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) checkResource := func(t *testing.T, step string, h *targets.Target, err error, wantedName string, wantVersion uint32) { t.Helper() require.NoError(err, step) assert.NotNil(h, "returned no resource", step) gotName := "" if h.Name != "" { gotName = h.Name } assert.Equal(wantedName, gotName, step) assert.Equal(wantVersion, h.Version) } tarClient := targets.NewClient(client) tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"), targets.WithTcpTargetDefaultPort(2)) checkResource(t, "create", tar.Item, err, "foo", 1) aliasedTar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("aliased"), targets.WithTcpTargetDefaultPort(2)) checkResource(t, "create", aliasedTar.Item, err, "aliased", 1) rw := db.New(tc.DbConn()) al := aliastar.TestAlias(t, rw, "alias.value", aliastar.WithDestinationId(aliasedTar.Item.Id)) cases := []struct { name string id string initialName string }{ { name: "target id", id: tar.Item.Id, initialName: "foo", }, { name: "aliases", id: al.GetValue(), initialName: "aliased", }, } for _, c := range cases { tar, err := tarClient.Read(tc.Context(), c.id) checkResource(t, "read", tar.Item, err, c.initialName, 1) tar, err = tarClient.Update(tc.Context(), c.id, tar.Item.Version, targets.WithName("bar")) checkResource(t, "update", tar.Item, err, "bar", 2) _, err = tarClient.Delete(tc.Context(), c.id) assert.NoError(err) } t.Run("deleting unknown target is not found", func(t *testing.T) { _, err = tarClient.Delete(tc.Context(), tar.Item.Id) assert.Error(err) apiErr := api.AsServerError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) }) t.Run("deleting unknown alias is not found", func(t *testing.T) { _, err = tarClient.Delete(tc.Context(), al.GetValue()) assert.Error(err) apiErr := api.AsServerError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) }) } func TestSet_Errors(t *testing.T) { assert, require := assert.New(t), require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() token := tc.Token() client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) tarClient := targets.NewClient(client) tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"), targets.WithTcpTargetDefaultPort(2)) require.NoError(err) assert.NotNil(tar) validTargetId := tar.GetItem().Id // A malformed id is processed as the id and not a different path to the api. _, err = tarClient.Read(tc.Context(), fmt.Sprintf("%s/../", tar.Item.Id)) require.Error(err) apiErr := api.AsServerError(err) require.NotNil(apiErr) assert.EqualValues(http.StatusBadRequest, apiErr.Response().StatusCode()) require.Len(apiErr.Details.RequestFields, 1) assert.Equal(apiErr.Details.RequestFields[0].Name, "id") // Updating using WithAliases should fail. _, err = tarClient.Update(tc.Context(), tar.Item.Id, tar.Item.Version, targets.WithAliases([]targets.Alias{{Value: "alias1", ScopeId: "global"}})) require.Error(err) apiErr = api.AsServerError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusBadRequest, apiErr.Response().StatusCode()) // Updating the wrong version should fail. _, err = tarClient.Update(tc.Context(), tar.Item.Id, 73, targets.WithName("anything")) require.Error(err) apiErr = api.AsServerError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) tar, err = tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo")) require.Error(err) apiErr = api.AsServerError(err) assert.NotNil(apiErr) assert.Nil(tar) _, err = tarClient.Read(tc.Context(), globals.TcpTargetPrefix+"_doesntexis") require.Error(err) apiErr = api.AsServerError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) _, err = tarClient.Read(tc.Context(), "invalid id") require.Error(err) apiErr = api.AsServerError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) // reading by alias with no destination id should fail rw := db.New(tc.DbConn()) al := aliastar.TestAlias(t, rw, "alias.value") _, err = tarClient.Read(tc.Context(), al.GetValue()) require.Error(err) apiErr = api.AsServerError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) client.SetToken("at_1234567890_madeupinvalidtoken") tarClient = targets.NewClient(client) _, err = tarClient.Read(tc.Context(), validTargetId) apiErr = api.AsServerError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusUnauthorized, apiErr.Response().StatusCode()) } func TestCreateTarget_WhitespaceInAddress(t *testing.T) { require := require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() token := tc.Token() client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) tarClient := targets.NewClient(client) tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"), targets.WithTcpTargetDefaultPort(2), targets.WithAddress(" 127.0.0.1 ")) require.NoError(err) require.NotNil(tar) require.Equal("127.0.0.1", tar.GetItem().Address) } func TestUpdateTarget_WhitespaceInAddress(t *testing.T) { require := require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() token := tc.Token() client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) tarClient := targets.NewClient(client) tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"), targets.WithTcpTargetDefaultPort(2), targets.WithAddress("127.0.0.1")) require.NoError(err) require.NotNil(tar) updateResult, err := tarClient.Update(tc.Context(), tar.Item.Id, tar.Item.Version, targets.WithAddress(" 10.0.0.1 ")) require.NoError(err) require.NotNil(updateResult) require.Equal("10.0.0.1", updateResult.Item.Address) } // TestCreateTarget_SlashesInName verifies that we can prop'ly route when using // a target name for authorizing a session and the name contains slashes func TestCreateTarget_SlashesInName(t *testing.T) { require := require.New(t) tc := controller.NewTestController(t, nil) tw, _ := worker.NewAuthorizedPkiTestWorker(t, tc.ServersRepo(), "test", tc.ClusterAddrs()) require.NotNil(t, tw) // Wait for worker to become ready time.Sleep(10 * time.Second) client := tc.Client() token := tc.Token() client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) tarClient := targets.NewClient(client) tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo/bar"), targets.WithTcpTargetDefaultPort(2), targets.WithAddress("127.0.0.1")) require.NoError(err) require.NotNil(tar) authzResult, err := tarClient.AuthorizeSession(tc.Context(), "", targets.WithName("foo/bar"), targets.WithScopeId(proj.PublicId)) require.NoError(err) require.NotNil(authzResult) }