Add handler and API support for alias creation with target (#4508)
* Add handler and API support for alias creation with target
This commit is contained in:
15 files changed
+1116
-484
No files matched your search
@@ -5,6 +5,8 @@
|
||||
package targets
|
||||
|
||||
type Alias struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Id string `json:"id,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
ScopeId string `json:"scope_id,omitempty"`
|
||||
Attributes *TargetAliasAttributes `json:"attributes,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Code generated by "make api"; DO NOT EDIT.
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package targets
|
||||
|
||||
type AuthorizeSessionArguments struct {
|
||||
HostId string `json:"host_id,omitempty"`
|
||||
}
|
||||
@@ -115,6 +115,12 @@ func DefaultAddress() Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithAliases(inAliases []Alias) Option {
|
||||
return func(o *options) {
|
||||
o.postMap["with_aliases"] = inAliases
|
||||
}
|
||||
}
|
||||
|
||||
func WithAttributes(inAttributes map[string]interface{}) Option {
|
||||
return func(o *options) {
|
||||
o.postMap["attributes"] = inAttributes
|
||||
|
||||
@@ -42,6 +42,7 @@ type Target struct {
|
||||
AuthorizedActions []string `json:"authorized_actions,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
Aliases []*Alias `json:"aliases,omitempty"`
|
||||
WithAliases []*Alias `json:"with_aliases,omitempty"`
|
||||
|
||||
response *api.Response
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// Code generated by "make api"; DO NOT EDIT.
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package targets
|
||||
|
||||
type TargetAliasAttributes struct {
|
||||
AuthorizeSessionArguments *AuthorizeSessionArguments `json:"authorize_session_arguments,omitempty"`
|
||||
}
|
||||
@@ -122,4 +122,5 @@ const (
|
||||
DeleteAfterField = "delete_after"
|
||||
DestinationIdField = "destination_id"
|
||||
ValueField = "value"
|
||||
WithAliasesField = "with_aliases"
|
||||
)
|
||||
@@ -1010,6 +1010,14 @@ var inputStructs = []*structInfo{
|
||||
inProto: &targets.Alias{},
|
||||
outFile: "targets/alias.gen.go",
|
||||
},
|
||||
{
|
||||
inProto: &targets.TargetAliasAttributes{},
|
||||
outFile: "targets/target_alias_attributes.gen.go",
|
||||
},
|
||||
{
|
||||
inProto: &targets.AuthorizeSessionArguments{},
|
||||
outFile: "targets/authorize_session_arguments.gen.go",
|
||||
},
|
||||
{
|
||||
inProto: &targets.SessionSecret{},
|
||||
outFile: "targets/session_secret.gen.go",
|
||||
@@ -1130,6 +1138,13 @@ var inputStructs = []*structInfo{
|
||||
ProtoName: "injected_application_credential_source_ids",
|
||||
FieldType: "[]string",
|
||||
},
|
||||
// with_aliases is used when creating a target with alaises.
|
||||
{
|
||||
Name: "Aliases",
|
||||
ProtoName: "with_aliases",
|
||||
FieldType: "[]Alias",
|
||||
SkipDefault: true,
|
||||
},
|
||||
},
|
||||
versionEnabled: true,
|
||||
createResponseTypes: []string{CreateResponseType, ReadResponseType, UpdateResponseType, DeleteResponseType, ListResponseType},
|
||||
|
||||
@@ -403,6 +403,13 @@ func (s Service) CreateTarget(ctx context.Context, req *pbs.CreateTargetRequest)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
for _, a := range req.GetItem().WithAliases {
|
||||
authResults := s.aliasCreateAuthResult(ctx, a.GetScopeId())
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
}
|
||||
|
||||
t, ts, cl, err := s.createInRepo(ctx, req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1305,7 +1312,21 @@ func (s Service) createInRepo(ctx context.Context, item *pb.Target) (target.Targ
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
out, err := repo.CreateTarget(ctx, u)
|
||||
|
||||
createOptions := []target.Option{}
|
||||
if len(item.GetWithAliases()) > 0 {
|
||||
writeAliases := make([]*talias.Alias, 0, len(item.GetWithAliases()))
|
||||
for _, a := range item.GetWithAliases() {
|
||||
na, err := talias.NewAlias(ctx, a.GetScopeId(), a.GetValue(), talias.WithHostId(a.GetAttributes().GetAuthorizeSessionArguments().GetHostId()))
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("converting from proto to storage alias"))
|
||||
}
|
||||
writeAliases = append(writeAliases, na)
|
||||
}
|
||||
createOptions = append(createOptions, target.WithAliases(writeAliases))
|
||||
}
|
||||
|
||||
out, err := repo.CreateTarget(ctx, u, createOptions...)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target"))
|
||||
}
|
||||
@@ -1579,6 +1600,30 @@ func (s Service) removeCredentialSourcesInRepo(ctx context.Context, targetId str
|
||||
return out, hs, credSources, nil
|
||||
}
|
||||
|
||||
// aliasCreateAuthResult verifies authorization for creating an alias
|
||||
func (s Service) aliasCreateAuthResult(ctx context.Context, parentId string) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
a := action.Create
|
||||
opts := []auth.Option{auth.WithType(resource.Alias), auth.WithAction(a)}
|
||||
iamRepo, err := s.iamRepoFn()
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
}
|
||||
scp, err := iamRepo.LookupScope(ctx, parentId)
|
||||
if err != nil {
|
||||
res.Error = err
|
||||
return res
|
||||
}
|
||||
if scp == nil {
|
||||
res.Error = handlers.NotFoundError()
|
||||
return res
|
||||
}
|
||||
opts = append(opts, auth.WithScopeId(parentId))
|
||||
ret := auth.Verify(ctx, opts...)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, lookupOpt ...target.Option) auth.VerifyResults {
|
||||
res := auth.VerifyResults{}
|
||||
|
||||
@@ -1704,6 +1749,9 @@ func toProto(ctx context.Context, in target.Target, opt ...handlers.Option) (*pb
|
||||
}
|
||||
if outputFields.Has(globals.AliasesField) {
|
||||
for _, a := range aliases {
|
||||
// Even though pb.Alias has more than just these 2 fields, we only
|
||||
// want to return these 2 fields to the client. Any more information
|
||||
// may be sharing more than the client should know.
|
||||
out.Aliases = append(out.Aliases, &pb.Alias{
|
||||
Id: a.PublicId,
|
||||
Value: a.Value,
|
||||
@@ -1777,6 +1825,15 @@ func validateCreateRequest(req *pbs.CreateTargetRequest) error {
|
||||
if item.GetName() == nil || item.GetName().GetValue() == "" {
|
||||
badFields[globals.NameField] = "This field is required."
|
||||
}
|
||||
for _, a := range item.GetWithAliases() {
|
||||
pa := proto.Clone(a).(*pb.Alias)
|
||||
pa.Value = ""
|
||||
pa.ScopeId = ""
|
||||
pa.Attributes = nil
|
||||
if !proto.Equal(pa, &pb.Alias{}) {
|
||||
badFields[globals.WithAliasesField] = "Included aliases can only specify a value, a scope id, and attributes."
|
||||
}
|
||||
}
|
||||
if item.GetSessionConnectionLimit() != nil {
|
||||
val := item.GetSessionConnectionLimit().GetValue()
|
||||
switch {
|
||||
@@ -1860,6 +1917,9 @@ func validateUpdateRequest(req *pbs.UpdateTargetRequest) error {
|
||||
if item.GetSessionMaxSeconds() != nil && item.GetSessionMaxSeconds().GetValue() == 0 {
|
||||
badFields[globals.SessionMaxSecondsField] = "This must be greater than zero."
|
||||
}
|
||||
if len(item.GetWithAliases()) > 0 {
|
||||
badFields[globals.WithAliasesField] = "This field can only be set at target creation time."
|
||||
}
|
||||
// worker_filter is mutually exclusive from ingress and egress filter
|
||||
workerFilterFound := false
|
||||
if workerFilter := item.GetWorkerFilter(); workerFilter != nil {
|
||||
|
||||
@@ -156,7 +156,7 @@ func TestGet(t *testing.T) {
|
||||
|
||||
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()))
|
||||
al := talias.TestAlias(t, rw, "test.alias", talias.WithDestinationId(tar.GetPublicId()), talias.WithHostId("hsht_1234567890"))
|
||||
|
||||
pAlias := &pb.Alias{
|
||||
Id: al.GetPublicId(),
|
||||
@@ -983,9 +983,10 @@ func TestCreate(t *testing.T) {
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.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
|
||||
@@ -1035,6 +1036,140 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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{
|
||||
@@ -1168,11 +1303,13 @@ func TestCreate(t *testing.T) {
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
|
||||
got, gErr := s.CreateTarget(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
if tc.err != nil || tc.errStr != "" {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "CreateTarget(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
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 != "" {
|
||||
require.ErrorContains(gErr, tc.errStr)
|
||||
assert.ErrorContains(gErr, tc.errStr)
|
||||
}
|
||||
} else {
|
||||
assert.Nil(gErr, "Unexpected err: %v", gErr)
|
||||
@@ -1194,6 +1331,9 @@ func TestCreate(t *testing.T) {
|
||||
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
|
||||
}),
|
||||
@@ -1204,6 +1344,93 @@ func TestCreate(t *testing.T) {
|
||||
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()
|
||||
@@ -1529,6 +1756,19 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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{
|
||||
|
||||
@@ -6924,6 +6924,23 @@
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Ouput only. The value of the alias referencing this target."
|
||||
},
|
||||
"scope_id": {
|
||||
"type": "string",
|
||||
"title": ";"
|
||||
},
|
||||
"attributes": {
|
||||
"$ref": "#/definitions/controller.api.resources.targets.v1.TargetAliasAttributes"
|
||||
}
|
||||
},
|
||||
"description": "Alias is the alias information related to the aliases associated with the target."
|
||||
},
|
||||
"controller.api.resources.targets.v1.AuthorizeSessionArguments": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"host_id": {
|
||||
"type": "string",
|
||||
"description": "host_id is the id of the host that the session will be authorized for.\nWhen specified authorizing a session using this alias will have the same\neffect of authorizing a session to the alias' destination_id and passing\nin this value through the -host-id flag. If the host-id flag is also\nspecified when calling authorize-session an error will be returned unless\nthe provided host-id matches this value."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -7240,10 +7257,27 @@
|
||||
},
|
||||
"description": "Output only. The aliases that point to this Target.",
|
||||
"readOnly": true
|
||||
},
|
||||
"with_aliases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/controller.api.resources.targets.v1.Alias"
|
||||
},
|
||||
"description": "Input only. with_aliases specify the aliases that should be created when\nthe target is created. This field is only usable at target creation time."
|
||||
}
|
||||
},
|
||||
"title": "Target contains all fields related to a Target resource"
|
||||
},
|
||||
"controller.api.resources.targets.v1.TargetAliasAttributes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"authorize_session_arguments": {
|
||||
"$ref": "#/definitions/controller.api.resources.targets.v1.AuthorizeSessionArguments",
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"controller.api.resources.users.v1.Account": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -106,7 +106,7 @@ message AuthorizeSessionArguments {
|
||||
// specified when calling authorize-session an error will be returned unless
|
||||
// the provided host-id matches this value.
|
||||
string host_id = 100 [
|
||||
json_name = "login_name",
|
||||
json_name = "host_id",
|
||||
(custom_options.v1.generate_sdk_option) = true,
|
||||
(custom_options.v1.mask_mapping) = {
|
||||
this: "attributes.authorize_session_arguments.host_id"
|
||||
|
||||
@@ -14,12 +14,31 @@ import "google/protobuf/wrappers.proto";
|
||||
|
||||
option go_package = "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets;targets";
|
||||
|
||||
// Alias is the alias information related to the aliases associated with the target.
|
||||
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"`
|
||||
|
||||
string scope_id = 30 [json_name = "scope_id"]; // @gotags: `class:"public" eventstream:"observation"`;
|
||||
|
||||
TargetAliasAttributes attributes = 40;
|
||||
}
|
||||
|
||||
message TargetAliasAttributes {
|
||||
AuthorizeSessionArguments authorize_session_arguments = 1 [json_name = "authorize_session_arguments"]; // @gotags: `class:"public"`
|
||||
}
|
||||
|
||||
message AuthorizeSessionArguments {
|
||||
// host_id is the id of the host that the session will be authorized for.
|
||||
// When specified authorizing a session using this alias will have the same
|
||||
// effect of authorizing a session to the alias' destination_id and passing
|
||||
// in this value through the -host-id flag. If the host-id flag is also
|
||||
// specified when calling authorize-session an error will be returned unless
|
||||
// the provided host-id matches this value.
|
||||
string host_id = 1 [json_name = "host_id"]; // @gotags: `class:"public"`
|
||||
}
|
||||
|
||||
message HostSource {
|
||||
@@ -222,6 +241,10 @@ message Target {
|
||||
// Output only. The aliases that point to this Target.
|
||||
repeated Alias aliases = 550 [json_name = "aliases"]; // @gotags: `class:"public"`
|
||||
|
||||
// Input only. with_aliases specify the aliases that should be created when
|
||||
// the target is created. This field is only usable at target creation time.
|
||||
repeated Alias with_aliases = 560 [json_name = "with_aliases"]; // @gotags: `class:"public"`
|
||||
|
||||
// Deprecated fields
|
||||
reserved "application_credential_library_ids", "application_credential_libraries";
|
||||
reserved 150, 180;
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"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"
|
||||
@@ -574,6 +575,48 @@ func comparableSlice(in []*targets.Target) []targets.Target {
|
||||
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)
|
||||
@@ -666,6 +709,13 @@ func TestSet_Errors(t *testing.T) {
|
||||
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)
|
||||
|
||||
@@ -281,7 +281,7 @@ type AuthorizeSessionArguments struct {
|
||||
// in this value through the -host-id flag. If the host-id flag is also
|
||||
// specified when calling authorize-session an error will be returned unless
|
||||
// the provided host-id matches this value.
|
||||
HostId string `protobuf:"bytes,100,opt,name=host_id,json=login_name,proto3" json:"host_id,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
HostId string `protobuf:"bytes,100,opt,name=host_id,proto3" json:"host_id,omitempty" class:"public"` // @gotags: `class:"public"`
|
||||
}
|
||||
|
||||
func (x *AuthorizeSessionArguments) Reset() {
|
||||
@@ -411,20 +411,20 @@ var file_controller_api_resources_aliases_v1_alias_proto_rawDesc = []byte{
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x1b, 0x61,
|
||||
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7a, 0x0a, 0x19, 0x41, 0x75,
|
||||
0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x77, 0x0a, 0x19, 0x41, 0x75,
|
||||
0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x72,
|
||||
0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x5d, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f,
|
||||
0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x5a, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x42, 0x40, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd,
|
||||
0x29, 0x38, 0x0a, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61,
|
||||
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x5f,
|
||||
0x69, 0x64, 0x12, 0x06, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x52, 0x0a, 0x6c, 0x6f, 0x67, 0x69,
|
||||
0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73,
|
||||
0x3b, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x69, 0x64, 0x12, 0x06, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x52, 0x07, 0x68, 0x6f, 0x73, 0x74,
|
||||
0x5f, 0x69, 0x64, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e,
|
||||
0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e,
|
||||
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x3b, 0x61, 0x6c,
|
||||
0x69, 0x61, 0x73, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user