Require default ports to be defined for newly created tcp targets. (#2148)

This commit is contained in:
Todd authored and GitHub committed 2022-06-13 08:26:24 -07:00
1 parent 9663404f1a
commit 06a2f4ce9c
12 files changed
+282 -81

No files matched your search

@@ -17,9 +17,15 @@ type Attributes interface {
// Options create target.Options to be used to create a target.Target.
Options() []target.Option
// Vet validates the Attributes and returns a map of fields to error messages
// if any fields are invalid.
Vet() map[string]string
// VetForUpdate validates the Attributes for an updated resource with the
// provided mask paths and returns a map of fields to error messages if any
// fields are invalid.
VetForUpdate([]string) map[string]string
}
type attributeFunc func(interface{}) Attributes
@@ -7,7 +7,9 @@ import (
stderrors "errors"
"fmt"
"math/rand"
"net"
"net/url"
"strconv"
"strings"
"github.com/hashicorp/boundary/globals"
@@ -47,8 +49,9 @@ import (
)
const (
credentialDomain = "credential"
hostDomain = "host"
credentialDomain = "credential"
hostDomain = "host"
missingPortErrStr = "missing port in address"
)
var (
@@ -1028,15 +1031,21 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession
chosenEndpoint = endpoints[rand.Intn(len(endpoints))]
}
h, p, err := net.SplitHostPort(chosenEndpoint.Address)
switch {
case err != nil && strings.Contains(err.Error(), missingPortErrStr):
if t.GetDefaultPort() == 0 {
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("neither the selected host %q nor the target provides a port to use", chosenEndpoint.HostId))
}
h = chosenEndpoint.Address
p = strconv.FormatUint(uint64(t.GetDefaultPort()), 10)
case err != nil:
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error when parsing the chosen endpoints host address"))
}
// Generate the endpoint URL
endpointUrl := &url.URL{
Scheme: t.GetType().String(),
}
defaultPort := t.GetDefaultPort()
if defaultPort != 0 {
endpointUrl.Host = fmt.Sprintf("%s:%d", chosenEndpoint.Address, defaultPort)
} else {
endpointUrl.Host = chosenEndpoint.Address
Host: net.JoinHostPort(h, p),
}
var reqs []credential.Request
@@ -1752,7 +1761,8 @@ func validateCreateRequest(req *pbs.CreateTargetRequest) error {
func validateUpdateRequest(req *pbs.UpdateTargetRequest) error {
return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
badFields := map[string]string{}
if handlers.MaskContains(req.GetUpdateMask().GetPaths(), globals.NameField) && req.GetItem().GetName().GetValue() == "" {
paths := req.GetUpdateMask().GetPaths()
if handlers.MaskContains(paths, globals.NameField) && req.GetItem().GetName().GetValue() == "" {
badFields[globals.NameField] = "This field cannot be set to empty."
}
if req.GetItem().GetSessionConnectionLimit() != nil {
@@ -1785,7 +1795,7 @@ func validateUpdateRequest(req *pbs.UpdateTargetRequest) error {
if err != nil {
badFields[globals.AttributesField] = "Attribute fields do not match the expected format."
} else {
for k, v := range a.Vet() {
for k, v := range a.VetForUpdate(paths) {
badFields[k] = v
}
}
@@ -474,6 +474,17 @@ func TestCreate(t *testing.T) {
},
},
},
{
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(),
WorkerFilter: wrapperspb.String(`type == "bar"`),
}},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Create with default port 0",
req: &pbs.CreateTargetRequest{Item: &pb.Target{
@@ -604,24 +615,25 @@ func TestUpdate(t *testing.T) {
}
ctx := context.Background()
ttar, err := target.New(ctx, tcp.Subtype, proj.GetPublicId(), target.WithName("default"), target.WithDescription("default"))
ttar, err := target.New(ctx, tcp.Subtype, proj.GetPublicId(),
target.WithName("default"),
target.WithDescription("default"),
target.WithSessionMaxSeconds(1),
target.WithSessionConnectionLimit(1),
target.WithDefaultPort(2))
require.NoError(t, err)
tar := ttar.(*tcp.Target)
tar.DefaultPort = 2
tar, _, _, err := repo.CreateTarget(context.Background(), ttar)
require.NoError(t, err)
gtar, _, _, err := repo.CreateTarget(context.Background(), tar)
tar, _, _, err = repo.AddTargetHostSources(context.Background(), tar.GetPublicId(), tar.GetVersion(), []string{hs[0].GetPublicId(), hs[1].GetPublicId()})
require.NoError(t, err)
gtar, _, _, err = repo.AddTargetHostSources(context.Background(), gtar.GetPublicId(), gtar.GetVersion(), []string{hs[0].GetPublicId(), hs[1].GetPublicId()})
require.NoError(t, err)
tar = gtar.(*tcp.Target)
var version uint32 = gtar.GetVersion()
resetTarget := func() {
version++
_, _, _, _, err = repo.UpdateTarget(context.Background(), tar, version, []string{"Name", "Description"})
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"})
require.NoError(t, err, "Failed to reset target.")
version++
}
hCreated := tar.GetCreateTime().GetTimestamp().AsTime()
@@ -706,8 +718,8 @@ func TestUpdate(t *testing.T) {
HostSets: hostSets,
HostSourceIds: hostSourceIds,
HostSources: hostSources,
SessionMaxSeconds: wrapperspb.UInt32(3600),
SessionConnectionLimit: wrapperspb.Int32(5),
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
AuthorizedActions: testAuthorizedActions,
},
},
@@ -736,7 +748,7 @@ func TestUpdate(t *testing.T) {
{
name: "Update port to 0",
req: &pbs.UpdateTargetRequest{
UpdateMask: &field_mask.FieldMask{Paths: []string{"default_port"}},
UpdateMask: &field_mask.FieldMask{Paths: []string{"attributes.default_port"}},
Item: &pb.Target{
Attrs: &pb.Target_TcpTargetAttributes{
TcpTargetAttributes: &pb.TcpTargetAttributes{
@@ -747,6 +759,14 @@ func TestUpdate(t *testing.T) {
},
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-existant paths in Mask",
req: &pbs.UpdateTargetRequest{
@@ -797,8 +817,8 @@ func TestUpdate(t *testing.T) {
HostSets: hostSets,
HostSourceIds: hostSourceIds,
HostSources: hostSources,
SessionMaxSeconds: wrapperspb.UInt32(3600),
SessionConnectionLimit: wrapperspb.Int32(5),
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
AuthorizedActions: testAuthorizedActions,
},
},
@@ -832,8 +852,8 @@ func TestUpdate(t *testing.T) {
HostSets: hostSets,
HostSourceIds: hostSourceIds,
HostSources: hostSources,
SessionMaxSeconds: wrapperspb.UInt32(3600),
SessionConnectionLimit: wrapperspb.Int32(5),
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
AuthorizedActions: testAuthorizedActions,
},
},
@@ -867,8 +887,8 @@ func TestUpdate(t *testing.T) {
HostSets: hostSets,
HostSourceIds: hostSourceIds,
HostSources: hostSources,
SessionMaxSeconds: wrapperspb.UInt32(3600),
SessionConnectionLimit: wrapperspb.Int32(5),
SessionMaxSeconds: wrapperspb.UInt32(tar.GetSessionMaxSeconds()),
SessionConnectionLimit: wrapperspb.Int32(tar.GetSessionConnectionLimit()),
AuthorizedActions: testAuthorizedActions,
},
},
@@ -934,8 +954,9 @@ func TestUpdate(t *testing.T) {
}
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 = version
tc.req.Item.Version = tar.GetVersion()
req := proto.Clone(toMerge).(*pbs.UpdateTargetRequest)
proto.Merge(req, tc.req)
@@ -944,11 +965,9 @@ func TestUpdate(t *testing.T) {
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
}
if tc.err == nil {
defer resetTarget()
}
require.NoError(gErr)
if got != nil {
assert.NotNilf(tc.res, "Expected UpdateHost response to be nil, but was %v", got)
@@ -961,7 +980,7 @@ func TestUpdate(t *testing.T) {
got.Item.UpdatedTime, tc.res.Item.UpdatedTime = nil, nil
}
if tc.res != nil {
tc.res.Item.Version = version + 1
tc.res.Item.Version = tc.req.Item.Version + 1
}
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "UpdateTarget(%q) got response %q, wanted %q", req, got, tc.res)
})
@@ -2649,8 +2668,10 @@ func TestAuthorizeSession(t *testing.T) {
connectionRepoFn := func() (*session.ConnectionRepository, error) {
return session.NewConnectionRepository(ctx, rw, rw, kms)
}
staticRepo, err := static.NewRepository(rw, rw, kms)
require.NoError(t, err)
staticHostRepoFn := func() (*static.Repository, error) {
return static.NewRepository(rw, rw, kms)
return staticRepo, nil
}
credentialRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(rw, rw, kms, sche)
@@ -2705,14 +2726,19 @@ func TestAuthorizeSession(t *testing.T) {
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn)
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})
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})
hWithPort.Address = fmt.Sprintf("%s:54321", hWithPort.GetAddress())
hWithPort, _, err = staticRepo.UpdateHost(ctx, hcWithPort.GetScopeId(), hWithPort, hWithPort.GetVersion(), []string{"address"})
require.NoError(t, err)
phc := plugin.TestCatalog(t, conn, proj.GetPublicId(), plg.GetPublicId())
phs := plugin.TestSet(t, conn, kms, sche, phc, plgm, plugin.WithPreferredEndpoints([]string{"cidr:10.0.0.0/24"}))
@@ -2746,6 +2772,7 @@ func TestAuthorizeSession(t *testing.T) {
}})
require.NoError(t, err)
const defaultPort = 2
cases := []struct {
name string
hostSourceId string
@@ -2758,20 +2785,29 @@ func TestAuthorizeSession(t *testing.T) {
hostSourceId: shs.GetPublicId(),
credSourceId: clsResp.GetItem().GetId(),
wantedHostId: h.GetPublicId(),
wantedEndpoint: h.GetAddress(),
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: hWithPort.GetAddress(),
},
{
name: "plugin host",
hostSourceId: phs.GetPublicId(),
credSourceId: clsResp.GetItem().GetId(),
wantedHostId: "?",
wantedEndpoint: "10.0.0.1",
wantedEndpoint: fmt.Sprintf("10.0.0.1:%d", defaultPort),
},
}
s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn)
require.NoError(t, err)
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), tc.name)
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), tc.name, target.WithDefaultPort(defaultPort))
apiTar, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{
Id: tar.GetPublicId(),
Version: tar.GetVersion(),
@@ -3059,7 +3095,8 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), tc.name)
const defaultPort = 2
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), tc.name, target.WithDefaultPort(defaultPort))
apiTar, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{
Id: tar.GetPublicId(),
Version: tar.GetVersion(),
@@ -3102,7 +3139,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
HostSetId: tc.hostSourceId,
HostId: tc.wantedHostId,
Type: "tcp",
Endpoint: fmt.Sprintf("tcp://%s", tc.wantedEndpoint),
Endpoint: fmt.Sprintf("tcp://%s:%d", tc.wantedEndpoint, defaultPort),
Credentials: []*pb.SessionCredential{
{
CredentialLibrary: &pb.CredentialLibrary{
@@ -3231,6 +3268,25 @@ func TestAuthorizeSession_Errors(t *testing.T) {
}
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.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{
Id: tar.GetPublicId(),
Version: tar.GetVersion(),
HostSetIds: []string{hs.GetPublicId()},
})
require.NoError(t, err)
h.Address = fmt.Sprintf("%s:54321", h.GetAddress())
repo, err := staticHostRepoFn()
require.NoError(t, err)
h, _, err = repo.UpdateHost(ctx, hc.GetScopeId(), 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]
@@ -3307,6 +3363,11 @@ func TestAuthorizeSession_Errors(t *testing.T) {
name: "success",
setup: []func(tcpTarget target.Target) uint32{workerExists, hostExists, libraryExists},
},
{
name: "no port",
setup: []func(tcpTarget target.Target) uint32{workerExists, hostWithoutPort, libraryExists},
err: true,
},
{
name: "no hosts",
setup: []func(tcpTarget target.Target) uint32{workerExists, hostSetNoHostExists, libraryExists},
@@ -10,6 +10,8 @@ import (
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
)
const defaultPortField = "attributes.default_port"
type attribute struct {
*pb.TcpTargetAttributes
}
@@ -24,8 +26,23 @@ func (a *attribute) Options() []target.Option {
func (a *attribute) Vet() map[string]string {
badFields := map[string]string{}
if a.GetDefaultPort() != nil && a.GetDefaultPort().GetValue() == 0 {
badFields["attributes.default_port"] = "This optional field cannot be set to 0."
if a.GetDefaultPort() == nil {
badFields["attributes.default_port"] = "This field is required."
} else if a.GetDefaultPort().GetValue() == 0 {
badFields["attributes.default_port"] = "This field cannot be set to zero."
}
return badFields
}
func (a *attribute) VetForUpdate(p []string) map[string]string {
if !handlers.MaskContains(p, defaultPortField) {
return nil
}
badFields := map[string]string{}
if a.GetDefaultPort() == nil {
badFields["attributes.default_port"] = "This field is required."
} else if a.GetDefaultPort().GetValue() == 0 {
badFields["attributes.default_port"] = "This cannot be set to zero."
}
return badFields
}
+40 -15
View File
@@ -21,16 +21,36 @@ type AllocFunc func() Target
// be used by the Repository.
type VetFunc func(context.Context, Target) error
// VetForUpdateFunc is a function that checks the given Target and field mask
// paths are valid and be used to update a target in the Repository.
type VetForUpdateFunc func(context.Context, Target, []string) error
// VetCredentialLibrariesFunc is a function that checks the given CredentialLibraries
// to ensure that they are valid for a Target subtype.
type VetCredentialLibrariesFunc func(context.Context, []*CredentialLibrary) error
// targetHooks defines the interface containing all the hooks needed for
// managing target suptypes.
type targetHooks interface {
// NewTarget creates a new in memory target.
NewTarget(scopeId string, opt ...Option) (Target, error)
// AllocTarget will allocate an empty target.
AllocTarget() Target
// Vet validates that the given Target has the proper fields and values
// for creation in the database for this type of target.
Vet(ctx context.Context, t Target) error
// VetForUpdate validates that the given Target has the proper fields
// and values for updating the database for this type of target given the
// field mask paths.
VetForUpdate(ctx context.Context, t Target, paths []string) error
// VetCredentialLibraries checks that the provided credential
// libriaries are properly built for association with a target of this type.
VetCredentialLibraries(ctx context.Context, cls []*CredentialLibrary) error
}
type registryEntry struct {
newFunc NewFunc
alloc AllocFunc
vet VetFunc
vetCredentialLibraries VetCredentialLibrariesFunc
prefix string
targetHooks targetHooks
prefix string
}
type registry struct {
@@ -71,7 +91,7 @@ func (r *registry) newFunc(s subtypes.Subtype) (NewFunc, bool) {
if !ok {
return nil, ok
}
return entry.newFunc, ok
return entry.targetHooks.NewTarget, ok
}
func (r *registry) allocFunc(s subtypes.Subtype) (AllocFunc, bool) {
@@ -79,7 +99,7 @@ func (r *registry) allocFunc(s subtypes.Subtype) (AllocFunc, bool) {
if !ok {
return nil, ok
}
return entry.alloc, ok
return entry.targetHooks.AllocTarget, ok
}
func (r *registry) vetFunc(s subtypes.Subtype) (VetFunc, bool) {
@@ -87,7 +107,15 @@ func (r *registry) vetFunc(s subtypes.Subtype) (VetFunc, bool) {
if !ok {
return nil, ok
}
return entry.vet, ok
return entry.targetHooks.Vet, ok
}
func (r *registry) vetForUpdateFunc(s subtypes.Subtype) (VetForUpdateFunc, bool) {
entry, ok := r.get(s)
if !ok {
return nil, ok
}
return entry.targetHooks.VetForUpdate, ok
}
func (r *registry) vetCredentialLibrariesFunc(s subtypes.Subtype) (VetCredentialLibrariesFunc, bool) {
@@ -95,7 +123,7 @@ func (r *registry) vetCredentialLibrariesFunc(s subtypes.Subtype) (VetCredential
if !ok {
return nil, ok
}
return entry.vetCredentialLibraries, ok
return entry.targetHooks.VetCredentialLibraries, ok
}
func (r *registry) idPrefix(s subtypes.Subtype) (string, bool) {
@@ -140,12 +168,9 @@ func New(ctx context.Context, subtype subtypes.Subtype, scopeId string, opt ...O
// Register registers repository hooks and the prefixes for a provided Subtype. Register
// panics if the subtype has already been registered or if any of the
// prefixes are associated with another subtype.
func Register(s subtypes.Subtype, nf NewFunc, af AllocFunc, vf VetFunc, vclf VetCredentialLibrariesFunc, prefix string) {
func Register(s subtypes.Subtype, th targetHooks, prefix string) {
subtypeRegistry.set(s, &registryEntry{
newFunc: nf,
alloc: af,
vet: vf,
vetCredentialLibraries: vclf,
prefix: prefix,
targetHooks: th,
prefix: prefix,
})
}
+2 -2
View File
@@ -493,11 +493,11 @@ func (r *Repository) UpdateTarget(ctx context.Context, target Target, version ui
if target == nil {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target")
}
vet, ok := subtypeRegistry.vetFunc(target.GetType())
vet, ok := subtypeRegistry.vetForUpdateFunc(target.GetType())
if !ok {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported target type %s", target.GetType()))
}
if err := vet(ctx, target); err != nil {
if err := vet(ctx, target, fieldMaskPaths); err != nil {
return nil, nil, nil, db.NoRowsAffected, err
}
@@ -16,8 +16,30 @@ import (
"github.com/stretchr/testify/require"
)
type hooks struct{}
func (h hooks) NewTarget(scopeId string, opt ...target.Option) (target.Target, error) {
return targettest.New(scopeId, opt...)
}
func (h hooks) AllocTarget() target.Target {
return targettest.Alloc()
}
func (h hooks) Vet(ctx context.Context, t target.Target) error {
return targettest.Vet(ctx, t)
}
func (h hooks) VetForUpdate(ctx context.Context, t target.Target, paths []string) error {
return targettest.VetForUpdate(ctx, t, paths)
}
func (h hooks) VetCredentialLibraries(ctx context.Context, cls []*target.CredentialLibrary) error {
return targettest.VetCredentialLibraries(ctx, cls)
}
func TestRepository_SetTargetCredentialSources(t *testing.T) {
target.Register(targettest.Subtype, targettest.New, targettest.Alloc, targettest.Vet, targettest.VetCredentialLibraries, targettest.TargetPrefix)
target.Register(targettest.Subtype, hooks{}, targettest.TargetPrefix)
t.Parallel()
conn, _ := db.TestSetup(t, "postgres")
+27
View File
@@ -5,6 +5,7 @@ package targettest
import (
"context"
"strings"
"testing"
"github.com/hashicorp/boundary/internal/db"
@@ -203,6 +204,32 @@ func Vet(ctx context.Context, t target.Target) error {
return nil
}
// vet validates that the given Target is a targettest.Target and that it
// has a Target store.
func VetForUpdate(ctx context.Context, t target.Target, paths []string) error {
const op = "targettest.vetForUpdate"
tt, ok := t.(*Target)
if !ok {
return errors.New(ctx, errors.InvalidParameter, op, "target is not a tcp.Target")
}
switch {
case tt == nil:
return errors.New(ctx, errors.InvalidParameter, op, "missing target")
case tt.Target == nil:
return errors.New(ctx, errors.InvalidParameter, op, "missing target store")
}
for _, f := range paths {
if strings.EqualFold("defaultport", f) && tt.GetDefaultPort() == 0 {
return errors.New(ctx, errors.InvalidParameter, op, "clearing or setting default port to zero")
}
}
return nil
}
// VetCredentialLibraries allows for any CredentialLibraries.
func VetCredentialLibraries(_ context.Context, _ []*target.CredentialLibrary) error {
return nil
+1 -1
View File
@@ -13,7 +13,7 @@ var (
// performed by NewTarget, allowing tests to create Targets with
// nil scopeIds for more robust testing.
func NewTestTarget(scopeId string, opt ...target.Option) target.Target {
t, _ := newTarget("testScope", opt...)
t, _ := targetHooks{}.NewTarget("testScope", opt...)
t.SetScopeId(scopeId)
return t
}
+38 -5
View File
@@ -3,14 +3,17 @@ package tcp
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/target"
)
type targetHooks struct{}
func init() {
target.Register(Subtype, newTarget, allocTarget, vet, vetCredentialLibraries, TargetPrefix)
target.Register(Subtype, targetHooks{}, TargetPrefix)
}
const (
@@ -18,9 +21,9 @@ const (
TargetPrefix = "ttcp"
)
// vet validates that the given target.Target is a tcp.Target and that it
// Vet validates that the given target.Target is a tcp.Target and that it
// has a Target store.
func vet(ctx context.Context, t target.Target) error {
func (h targetHooks) Vet(ctx context.Context, t target.Target) error {
const op = "tcp.vet"
tt, ok := t.(*Target)
@@ -35,12 +38,42 @@ func vet(ctx context.Context, t target.Target) error {
if tt.Target == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing target store")
}
if tt.GetDefaultPort() == 0 {
return errors.New(ctx, errors.InvalidParameter, op, "missing target default port")
}
return nil
}
// vetCredentialLibraries checks that all of the provided credential libriaries have a CredentialPurpose
// VetForUpdate validates that the given target.Target is a tcp.Target,
// and that it has a Target store and that it isn't attempting to clear or
// set to zero the default port.
func (h targetHooks) VetForUpdate(ctx context.Context, t target.Target, paths []string) error {
const op = "tcp.vetForUpdate"
tt, ok := t.(*Target)
if !ok {
return errors.New(ctx, errors.InvalidParameter, op, "target is not a tcp.Target")
}
switch {
case tt == nil:
return errors.New(ctx, errors.InvalidParameter, op, "missing target")
case tt.Target == nil:
return errors.New(ctx, errors.InvalidParameter, op, "missing target store")
}
for _, f := range paths {
if strings.EqualFold("defaultport", f) && tt.GetDefaultPort() == 0 {
return errors.New(ctx, errors.InvalidParameter, op, "clearing or setting default port to zero")
}
}
return nil
}
// VetCredentialLibraries checks that all of the provided credential libriaries have a CredentialPurpose
// of ApplicationPurpose. Any other CredentialPurpose will result in an error.
func vetCredentialLibraries(ctx context.Context, cls []*target.CredentialLibrary) error {
func (h targetHooks) VetCredentialLibraries(ctx context.Context, cls []*target.CredentialLibrary) error {
const op = "tcp.vetCredentialLibraries"
for _, cl := range cls {
+4 -4
View File
@@ -37,9 +37,9 @@ var (
_ oplog.ReplayableMessage = (*Target)(nil)
)
// newTarget creates a new in memory tcp target. WithName, WithDescription and
// NewTarget creates a new in memory tcp target. WithName, WithDescription and
// WithDefaultPort options are supported
func newTarget(scopeId string, opt ...target.Option) (target.Target, error) {
func (h targetHooks) NewTarget(scopeId string, opt ...target.Option) (target.Target, error) {
const op = "tcp.NewTarget"
opts := target.GetOpts(opt...)
if scopeId == "" {
@@ -59,8 +59,8 @@ func newTarget(scopeId string, opt ...target.Option) (target.Target, error) {
return t, nil
}
// allocTarget will allocate a tcp target
func allocTarget() target.Target {
// AllocTarget will allocate a tcp target
func (h targetHooks) AllocTarget() target.Target {
return &Target{
Target: &store.Target{},
}
+5 -5
View File
@@ -45,7 +45,7 @@ func TestHostSetASD(t *testing.T) {
require.NotNil(hSet2)
tarClient := targets.NewClient(client)
tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"))
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.HostSetIds)
@@ -97,7 +97,7 @@ func TestCredentialLibraryASD(t *testing.T) {
require.NotNil(r1)
tarClient := targets.NewClient(client)
tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"))
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.ApplicationCredentialLibraryIds)
@@ -160,7 +160,7 @@ func TestList(t *testing.T) {
expected = append(expected, &targets.Target{Name: fmt.Sprint(i)})
}
tcr, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName(expected[0].Name))
tcr, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName(expected[0].Name), targets.WithTcpTargetDefaultPort(2))
require.NoError(err)
expected[0] = tcr.Item
@@ -237,7 +237,7 @@ func TestCrud(t *testing.T) {
tarClient := targets.NewClient(client)
tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"))
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)
@@ -268,7 +268,7 @@ func TestSet_Errors(t *testing.T) {
tarClient := targets.NewClient(client)
tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"))
tar, err := tarClient.Create(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo"), targets.WithTcpTargetDefaultPort(2))
require.NoError(err)
assert.NotNil(tar)