Add alias resolution to target service (#4277)

* Add aliases to target resource and alias resolution to target service

* Wire up the target alias domain package to the target service handler

* Adding alias resolution to target api tests
This commit is contained in:
Todd committed 2024-02-29 15:51:24 -08:00
1 parent 92181e272a
commit 3f3b624b54
12 files changed
+1359 -631

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
// Code generated by "make api"; DO NOT EDIT.
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package targets
type Alias struct {
Id string `json:"id,omitempty"`
Value string `json:"value,omitempty"`
}
+1
View File
@@ -41,6 +41,7 @@ type Target struct {
Attributes map[string]interface{} `json:"attributes,omitempty"`
AuthorizedActions []string `json:"authorized_actions,omitempty"`
Address string `json:"address,omitempty"`
Aliases []*Alias `json:"aliases,omitempty"`
response *api.Response
}
+17
View File
@@ -207,6 +207,23 @@ func (r *Repository) LookupAlias(ctx context.Context, id string, opt ...Option)
return a, nil
}
// LookupAliasByValue returns the Alias for the provided value. Returns nil, nil
// if no Alias is found for the provided value.
func (r *Repository) LookupAliasByValue(ctx context.Context, value string, opt ...Option) (*Alias, error) {
const op = "target.(Repository).LookupAliasByValue"
if value == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "value is empty")
}
a := allocAlias()
if err := r.reader.LookupWhere(ctx, a, "value = $1", []any{value}); err != nil {
if errors.IsNotFoundError(err) {
return nil, nil
}
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %q", value)))
}
return a, nil
}
// DeleteAlias deletes id from the repository returning a count of the
// number of records deleted.
func (r *Repository) DeleteAlias(ctx context.Context, id string, opt ...Option) (int, error) {
+4
View File
@@ -979,6 +979,10 @@ var inputStructs = []*structInfo{
inProto: &targets.CredentialSource{},
outFile: "targets/credential_source.gen.go",
},
{
inProto: &targets.Alias{},
outFile: "targets/alias.gen.go",
},
{
inProto: &targets.SessionSecret{},
outFile: "targets/session_secret.gen.go",
+1
View File
@@ -262,6 +262,7 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
c.StaticHostRepoFn,
c.VaultCredentialRepoFn,
c.StaticCredentialRepoFn,
c.TargetAliasRepoFn,
c.downstreamWorkers,
c.workerStatusGracePeriod,
c.conf.RawConfig.Controller.MaxPageSize,
+10 -10
View File
@@ -136,7 +136,7 @@ func TestHandleImplementedPaths(t *testing.T) {
"v1/storage-buckets",
"v1/storage-buckets/someid",
"v1/targets",
"v1/targets/someid",
"v1/targets/some_id",
"v1/users",
"v1/users/someid",
"v1/billing:monthly-active-users",
@@ -174,13 +174,13 @@ func TestHandleImplementedPaths(t *testing.T) {
"v1/roles/someid:set-principals",
"v1/roles/someid:remove-principals",
"v1/sessions/someid:cancel",
"v1/targets/someid:authorize-session",
"v1/targets/someid:add-host-sources",
"v1/targets/someid:set-host-sources",
"v1/targets/someid:remove-host-sources",
"v1/targets/someid:add-credential-sources",
"v1/targets/someid:set-credential-sources",
"v1/targets/someid:remove-credential-sources",
"v1/targets/some_id:authorize-session",
"v1/targets/some_id:add-host-sources",
"v1/targets/some_id:set-host-sources",
"v1/targets/some_id:remove-host-sources",
"v1/targets/some_id:add-credential-sources",
"v1/targets/some_id:set-credential-sources",
"v1/targets/some_id:remove-credential-sources",
"v1/users/someid:add-accounts",
"v1/users/someid:set-accounts",
"v1/users/someid:remove-accounts",
@@ -198,7 +198,7 @@ func TestHandleImplementedPaths(t *testing.T) {
"v1/roles/someid",
"v1/scopes/someid",
"v1/storage-buckets/someid",
"v1/targets/someid",
"v1/targets/some_id",
"v1/users/someid",
},
"PATCH": {
@@ -213,7 +213,7 @@ func TestHandleImplementedPaths(t *testing.T) {
"v1/roles/someid",
"v1/scopes/someid",
"v1/storage-buckets/someid",
"v1/targets/someid",
"v1/targets/some_id",
"v1/users/someid",
},
} {
@@ -16,6 +16,7 @@ import (
"time"
"github.com/hashicorp/boundary/globals"
talias "github.com/hashicorp/boundary/internal/alias/target"
"github.com/hashicorp/boundary/internal/credential"
wl "github.com/hashicorp/boundary/internal/daemon/common"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
@@ -118,6 +119,7 @@ type Service struct {
pbs.UnsafeTargetServiceServer
repoFn target.RepositoryFactory
aliasRepoFn common.TargetAliasRepoFactory
iamRepoFn common.IamRepoFactory
serversRepoFn common.ServersRepoFactory
sessionRepoFn session.RepositoryFactory
@@ -146,38 +148,34 @@ func NewService(
staticHostRepoFn common.StaticRepoFactory,
vaultCredRepoFn common.VaultCredentialRepoFactory,
staticCredRepoFn common.StaticCredentialRepoFactory,
aliasRepoFn common.TargetAliasRepoFactory,
downstreams common.Downstreamers,
workerStatusGracePeriod *atomic.Int64,
maxPageSize uint,
controllerExt intglobals.ControllerExtension,
) (Service, error) {
const op = "targets.NewService"
if kmsCache == nil {
switch {
case kmsCache == nil:
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing kms repo")
}
if repoFn == nil {
case repoFn == nil:
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing target repository")
}
if iamRepoFn == nil {
case iamRepoFn == nil:
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing iam repository")
}
if serversRepoFn == nil {
case serversRepoFn == nil:
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing server repository")
}
if sessionRepoFn == nil {
case sessionRepoFn == nil:
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing session repository")
}
if pluginHostRepoFn == nil {
case pluginHostRepoFn == nil:
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing plugin host repository")
}
if staticHostRepoFn == nil {
case staticHostRepoFn == nil:
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static host repository")
}
if vaultCredRepoFn == nil {
case vaultCredRepoFn == nil:
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing vault credential repository")
}
if staticCredRepoFn == nil {
case staticCredRepoFn == nil:
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static credential repository")
case aliasRepoFn == nil:
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing target alias repository")
}
if maxPageSize == 0 {
maxPageSize = uint(globals.DefaultMaxPageSize)
@@ -191,6 +189,7 @@ func NewService(
staticHostRepoFn: staticHostRepoFn,
vaultCredRepoFn: vaultCredRepoFn,
staticCredRepoFn: staticCredRepoFn,
aliasRepoFn: aliasRepoFn,
downstreams: downstreams,
kmsCache: kmsCache,
workerStatusGracePeriod: workerStatusGracePeriod,
@@ -356,6 +355,14 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
func (s Service) GetTarget(ctx context.Context, req *pbs.GetTargetRequest) (*pbs.GetTargetResponse, error) {
const op = "targets.(Service).GetTarget"
if maybeAlias(req.GetId()) {
if a, err := s.resolveAlias(ctx, req.GetId()); err != nil {
return nil, errors.Wrap(ctx, err, op)
} else {
req.Id = a.DestinationId
}
}
if err := validateGetRequest(req); err != nil {
return nil, err
}
@@ -436,6 +443,14 @@ func (s Service) CreateTarget(ctx context.Context, req *pbs.CreateTargetRequest)
func (s Service) UpdateTarget(ctx context.Context, req *pbs.UpdateTargetRequest) (*pbs.UpdateTargetResponse, error) {
const op = "targets.(Service).UpdateTarget"
if maybeAlias(req.GetId()) {
if a, err := s.resolveAlias(ctx, req.GetId()); err != nil {
return nil, errors.Wrap(ctx, err, op)
} else {
req.Id = a.DestinationId
}
}
if err := validateUpdateRequest(req); err != nil {
return nil, err
}
@@ -474,6 +489,16 @@ func (s Service) UpdateTarget(ctx context.Context, req *pbs.UpdateTargetRequest)
// DeleteTarget implements the interface pbs.TargetServiceServer.
func (s Service) DeleteTarget(ctx context.Context, req *pbs.DeleteTargetRequest) (*pbs.DeleteTargetResponse, error) {
const op = "targets.(Service).DeleteTarget"
if maybeAlias(req.GetId()) {
if a, err := s.resolveAlias(ctx, req.GetId()); err != nil {
return nil, errors.Wrap(ctx, err, op)
} else {
req.Id = a.DestinationId
}
}
if err := validateDeleteRequest(req); err != nil {
return nil, err
}
@@ -492,6 +517,14 @@ func (s Service) DeleteTarget(ctx context.Context, req *pbs.DeleteTargetRequest)
func (s Service) AddTargetHostSources(ctx context.Context, req *pbs.AddTargetHostSourcesRequest) (*pbs.AddTargetHostSourcesResponse, error) {
const op = "targets.(Service).AddTargetHostSources"
if maybeAlias(req.GetId()) {
if a, err := s.resolveAlias(ctx, req.GetId()); err != nil {
return nil, errors.Wrap(ctx, err, op)
} else {
req.Id = a.DestinationId
}
}
if err := validateAddHostSourcesRequest(req); err != nil {
return nil, err
}
@@ -534,6 +567,14 @@ func (s Service) AddTargetHostSources(ctx context.Context, req *pbs.AddTargetHos
func (s Service) SetTargetHostSources(ctx context.Context, req *pbs.SetTargetHostSourcesRequest) (*pbs.SetTargetHostSourcesResponse, error) {
const op = "targets.(Service).SetTargetHostSources"
if maybeAlias(req.GetId()) {
if a, err := s.resolveAlias(ctx, req.GetId()); err != nil {
return nil, errors.Wrap(ctx, err, op)
} else {
req.Id = a.DestinationId
}
}
if err := validateSetHostSourcesRequest(req); err != nil {
return nil, err
}
@@ -574,6 +615,14 @@ func (s Service) SetTargetHostSources(ctx context.Context, req *pbs.SetTargetHos
func (s Service) RemoveTargetHostSources(ctx context.Context, req *pbs.RemoveTargetHostSourcesRequest) (*pbs.RemoveTargetHostSourcesResponse, error) {
const op = "targets.(Service).RemoveTargetHostSources"
if maybeAlias(req.GetId()) {
if a, err := s.resolveAlias(ctx, req.GetId()); err != nil {
return nil, errors.Wrap(ctx, err, op)
} else {
req.Id = a.DestinationId
}
}
if err := validateRemoveHostSourcesRequest(req); err != nil {
return nil, err
}
@@ -614,6 +663,14 @@ func (s Service) RemoveTargetHostSources(ctx context.Context, req *pbs.RemoveTar
func (s Service) AddTargetCredentialSources(ctx context.Context, req *pbs.AddTargetCredentialSourcesRequest) (*pbs.AddTargetCredentialSourcesResponse, error) {
const op = "targets.(Service).AddTargetCredentialSources"
if maybeAlias(req.GetId()) {
if a, err := s.resolveAlias(ctx, req.GetId()); err != nil {
return nil, errors.Wrap(ctx, err, op)
} else {
req.Id = a.DestinationId
}
}
if err := validateAddCredentialSourcesRequest(req); err != nil {
return nil, err
}
@@ -655,6 +712,14 @@ func (s Service) AddTargetCredentialSources(ctx context.Context, req *pbs.AddTar
func (s Service) SetTargetCredentialSources(ctx context.Context, req *pbs.SetTargetCredentialSourcesRequest) (*pbs.SetTargetCredentialSourcesResponse, error) {
const op = "targets.(Service).SetTargetCredentialSources"
if maybeAlias(req.GetId()) {
if a, err := s.resolveAlias(ctx, req.GetId()); err != nil {
return nil, errors.Wrap(ctx, err, op)
} else {
req.Id = a.DestinationId
}
}
if err := validateSetCredentialSourcesRequest(req); err != nil {
return nil, err
}
@@ -696,6 +761,14 @@ func (s Service) SetTargetCredentialSources(ctx context.Context, req *pbs.SetTar
func (s Service) RemoveTargetCredentialSources(ctx context.Context, req *pbs.RemoveTargetCredentialSourcesRequest) (*pbs.RemoveTargetCredentialSourcesResponse, error) {
const op = "targets.(Service).RemoveTargetCredentialSources"
if maybeAlias(req.GetId()) {
if a, err := s.resolveAlias(ctx, req.GetId()); err != nil {
return nil, errors.Wrap(ctx, err, op)
} else {
req.Id = a.DestinationId
}
}
if err := validateRemoveCredentialSourcesRequest(req); err != nil {
return nil, err
}
@@ -781,6 +854,27 @@ func DefaultPostSessionAuthorizationCallback(context.Context, intglobals.Control
func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSessionRequest) (_ *pbs.AuthorizeSessionResponse, retErr error) {
const op = "targets.(Service).AuthorizeSession"
if maybeAlias(req.GetId()) && len(req.GetScopeId()) == 0 && len(req.GetScopeName()) == 0 {
// AuthorizeSession allows passing the target name in the place of an id but
// requires that the scope id or scope name is also provided in the request.
// If the scope id or the scope name is provided, we will not attempt to
// resolve the alias.
if a, err := s.resolveAlias(ctx, req.GetId()); err != nil {
return nil, errors.Wrap(ctx, err, op)
} else {
req.Id = a.DestinationId
if a.HostId != "" {
if req.GetHostId() != "" && req.GetHostId() != a.HostId {
return nil, handlers.InvalidArgumentErrorf("Errors in provided fields.", map[string]string{
"host_id": "The host id specified in the request does not match the one provided by the alias. Consider omiting the host id in the request.",
})
}
req.HostId = a.HostId
}
}
}
if err := validateAuthorizeSessionRequest(req); err != nil {
return nil, err
}
@@ -1230,6 +1324,38 @@ func (s Service) getFromRepo(ctx context.Context, id string) (target.Target, []t
return u, hs, cl, nil
}
// maybeAlias returns true if the input string is a candidate for being an alias.
func maybeAlias(s string) bool {
return !strings.Contains(s, "_") &&
len(s) > 0
}
// resolveAlias returns the alias resource with the specified value.
// If the alias does not have a destination id or there is no alias with a
// matching value, an error is returned.
func (s Service) resolveAlias(ctx context.Context, value string) (*talias.Alias, error) {
const op = "targets.(Service).resolveAlias"
if value == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "input string is empty")
}
r, err := s.aliasRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
a, err := r.LookupAliasByValue(ctx, value)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if a == nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.NotFound, "alias with value %q not found", value)
}
if a.DestinationId == "" {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.NotFound, "target not found for alias value %q", value)
}
return a, nil
}
func (s Service) createInRepo(ctx context.Context, item *pb.Target) (target.Target, []target.HostSource, []target.CredentialSource, error) {
const op = "targets.(Service).createInRepo"
opts := []target.Option{target.WithName(item.GetName().GetValue())}
File diff suppressed because it is too large. Load diff
+23
View File
@@ -6909,6 +6909,20 @@
},
"title": "StorageBucket manages external object stores"
},
"controller.api.resources.targets.v1.Alias": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Output only. The ID of the alias referencing this target.",
"readOnly": true
},
"value": {
"type": "string",
"description": "Ouput only. The value of the alias referencing this target."
}
}
},
"controller.api.resources.targets.v1.CredentialSource": {
"type": "object",
"properties": {
@@ -7213,6 +7227,15 @@
"address": {
"type": "string",
"description": "Optional string value that represents a network resource and is used when establishing a session."
},
"aliases": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/controller.api.resources.targets.v1.Alias"
},
"description": "Output only. The aliases that point to this Target.",
"readOnly": true
}
},
"title": "Target contains all fields related to a Target resource"
@@ -14,6 +14,14 @@ import "google/protobuf/wrappers.proto";
option go_package = "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets;targets";
message Alias {
// Output only. The ID of the alias referencing this target.
string id = 10; // @gotags: `class:"public" eventstream:"observation"`
// Ouput only. The value of the alias referencing this target.
string value = 20; // @gotags: `class:"public" eventstream:"observation"`
}
message HostSource {
// Output only. The ID of the Host Set.
string id = 10; // @gotags: `class:"public" eventstream:"observation"`
@@ -211,6 +219,9 @@ message Target {
}
]; // @gotags: `class:"public"`
// Output only. The aliases that point to this Target.
repeated Alias aliases = 550 [json_name = "aliases"]; // @gotags: `class:"public"`
// Deprecated fields
reserved "application_credential_library_ids", "application_credential_libraries";
reserved 150, 180;
+187 -81
View File
@@ -20,9 +20,11 @@ import (
"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"
@@ -57,20 +59,44 @@ func TestHostSetASD(t *testing.T) {
require.NotNil(tar)
assert.Empty(tar.Item.HostSourceIds)
tar, err = tarClient.AddHostSources(tc.Context(), tar.Item.Id, tar.Item.Version, []string{hSet.Item.Id})
aliasedTar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("aliased"), targets.WithTcpTargetDefaultPort(2))
require.NoError(err)
require.NotNil(tar)
assert.ElementsMatch(tar.Item.HostSourceIds, []string{hSet.Item.Id})
require.NotNil(aliasedTar)
assert.Empty(aliasedTar.Item.HostSourceIds)
tar, err = tarClient.SetHostSources(tc.Context(), tar.Item.Id, tar.Item.Version, []string{hSet2.Item.Id})
require.NoError(err)
require.NotNil(tar)
assert.ElementsMatch(tar.Item.HostSourceIds, []string{hSet2.Item.Id})
rw := db.New(tc.DbConn())
al := aliastar.TestAlias(t, rw, "alias.value", aliastar.WithDestinationId(aliasedTar.Item.Id))
tar, err = tarClient.RemoveHostSources(tc.Context(), tar.Item.Id, tar.Item.Version, []string{hSet2.Item.Id})
require.NoError(err)
require.NotNil(tar)
assert.Empty(tar.Item.HostSourceIds)
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) {
@@ -115,54 +141,78 @@ func TestCredentialSourcesASD(t *testing.T) {
require.NotNil(tar)
assert.Empty(tar.Item.BrokeredCredentialSourceIds)
// Add first Vault library
tar, err = tarClient.AddCredentialSources(tc.Context(), tar.Item.Id, tar.Item.Version,
targets.WithBrokeredCredentialSourceIds([]string{lib1.Item.Id}))
aliasedTar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("aliased"), targets.WithTcpTargetDefaultPort(2))
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,
},
})
require.NotNil(aliasedTar)
assert.Empty(aliasedTar.Item.BrokeredCredentialSourceIds)
// Set second Vault library and a static credential
tar, err = tarClient.SetCredentialSources(tc.Context(), tar.Item.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(), tar.Item.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,
},
})
rw := db.New(tc.DbConn())
al := aliastar.TestAlias(t, rw, "alias.value", aliastar.WithDestinationId(aliasedTar.Item.Id))
// Set empty credential sources
tar, err = tarClient.SetCredentialSources(tc.Context(), tar.Item.Id, tar.Item.Version,
targets.WithBrokeredCredentialSourceIds([]string{}))
require.NoError(err)
require.NotNil(tar)
assert.Empty(tar.Item.BrokeredCredentialSourceIds)
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) {
@@ -467,23 +517,46 @@ func TestUpdateTarget_DeleteAddress(t *testing.T) {
require.NotNil(createResp)
assert.Equal(addr, createResp.GetItem().Address)
// Update to delete address (set to null)
updateResp, err := tClient.Update(tc.Context(), createResp.Item.Id, createResp.Item.Version, targets.DefaultAddress())
aliasedTar, err := tClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("aliased"), targets.WithTcpTargetDefaultPort(2))
require.NoError(err)
require.NotNil(updateResp)
assert.Empty(updateResp.GetItem().Address)
require.NotNil(aliasedTar)
// Do it again with same version, should error
_, err = tClient.Update(tc.Context(), createResp.Item.Id, createResp.Item.Version, targets.DefaultAddress())
require.Error(err)
rw := db.New(tc.DbConn())
al := aliastar.TestAlias(t, rw, "alias.value", aliastar.WithDestinationId(aliasedTar.Item.Id))
// Do it again with the correct version, ensure this is not an error.
secondUpdateResp, err := tClient.Update(tc.Context(), createResp.Item.Id, updateResp.Item.Version, targets.DefaultAddress())
require.NoError(err)
require.NotNil(secondUpdateResp)
assert.Empty(secondUpdateResp.GetItem().Address)
cases := []struct {
name string
id string
}{
{
name: "target id",
id: createResp.Item.Id,
},
{
name: "aliases",
id: al.GetValue(),
},
}
assert.NotEqual(updateResp.Item.Version, secondUpdateResp.Item.Version)
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 {
@@ -528,20 +601,44 @@ func TestCrud(t *testing.T) {
tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"), targets.WithTcpTargetDefaultPort(2))
checkResource(t, "create", tar.Item, err, "foo", 1)
tar, err = tarClient.Read(tc.Context(), tar.Item.Id)
checkResource(t, "read", 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)
tar, err = tarClient.Update(tc.Context(), tar.Item.Id, tar.Item.Version, targets.WithName("bar"))
checkResource(t, "update", tar.Item, err, "bar", 2)
rw := db.New(tc.DbConn())
al := aliastar.TestAlias(t, rw, "alias.value", aliastar.WithDestinationId(aliasedTar.Item.Id))
_, err = tarClient.Delete(tc.Context(), tar.Item.Id)
assert.NoError(err)
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)
_, 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())
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)
_, err = tarClient.Delete(tc.Context(), c.id)
assert.Error(err)
apiErr := api.AsServerError(err)
assert.NotNil(apiErr)
assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode())
}
}
func TestSet_Errors(t *testing.T) {
@@ -592,7 +689,16 @@ func TestSet_Errors(t *testing.T) {
require.Error(err)
apiErr = api.AsServerError(err)
assert.NotNil(apiErr)
assert.EqualValues(http.StatusBadRequest, apiErr.Response().StatusCode())
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())
}
func TestCreateTarget_WhitespaceInAddress(t *testing.T) {
File diff suppressed because it is too large. Load diff