From 9663404f1a460eefe20411a31bf3f7402bd12fd6 Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Sat, 11 Jun 2022 13:23:28 -0700 Subject: [PATCH 01/15] feat(scheduler): Support unlimited job runs (#2173) --- internal/daemon/controller/controller.go | 5 ++--- internal/scheduler/job/options.go | 5 +++-- internal/scheduler/job/options_test.go | 2 +- internal/scheduler/job/query.go | 2 +- internal/scheduler/job/repository_run.go | 12 +++++++++++- internal/scheduler/job/repository_run_test.go | 14 ++++++++++++-- internal/scheduler/options.go | 5 +++-- internal/scheduler/scheduler.go | 2 +- internal/scheduler/scheduler_test.go | 17 ++++++++++++++++- 9 files changed, 50 insertions(+), 14 deletions(-) diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index 6c651a21f..9fa9162dc 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -247,9 +247,8 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { jobRepoFn := func() (*job.Repository, error) { return job.NewRepository(dbase, dbase, c.kms) } - // TODO: the RunJobsLimit is temporary until a better fix gets in. This - // currently caps the scheduler at running 10 jobs per interval. - schedulerOpts := []scheduler.Option{scheduler.WithRunJobsLimit(10)} + // TODO: Allow setting run jobs limit from config + schedulerOpts := []scheduler.Option{scheduler.WithRunJobsLimit(-1)} if c.conf.RawConfig.Controller.SchedulerRunJobInterval > 0 { schedulerOpts = append(schedulerOpts, scheduler.WithRunJobsInterval(c.conf.RawConfig.Controller.SchedulerRunJobInterval)) } diff --git a/internal/scheduler/job/options.go b/internal/scheduler/job/options.go index 5b0cc64cf..f1413d379 100644 --- a/internal/scheduler/job/options.go +++ b/internal/scheduler/job/options.go @@ -24,7 +24,7 @@ type Option func(*options) // options = how options are represented type options struct { withNextRunIn time.Duration - withRunJobsLimit uint + withRunJobsLimit int withLimit int withName string withServerId string @@ -47,7 +47,8 @@ func WithNextRunIn(d time.Duration) Option { // WithRunJobsLimit provides an option to provide the number of jobs to run. // If WithRunJobsLimit == 0, then default run jobs limit is used. -func WithRunJobsLimit(l uint) Option { +// If WithRunJobsLimit < 0, then no limit is used. +func WithRunJobsLimit(l int) Option { return func(o *options) { o.withRunJobsLimit = l if o.withRunJobsLimit == 0 { diff --git a/internal/scheduler/job/options_test.go b/internal/scheduler/job/options_test.go index 1b7e4e78d..cf7f768c9 100644 --- a/internal/scheduler/job/options_test.go +++ b/internal/scheduler/job/options_test.go @@ -31,7 +31,7 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithRunJobsLimit(0)) testOpts := getDefaultOptions() assert.Equal(opts, testOpts) - assert.Equal(uint(defaultRunJobsLimit), opts.withRunJobsLimit) + assert.Equal(defaultRunJobsLimit, opts.withRunJobsLimit) }) t.Run("WithLimit", func(t *testing.T) { assert := assert.New(t) diff --git a/internal/scheduler/job/query.go b/internal/scheduler/job/query.go index 48f5e185e..a4e93ee15 100644 --- a/internal/scheduler/job/query.go +++ b/internal/scheduler/job/query.go @@ -8,7 +8,7 @@ const runJobsQuery = ` job_plugin_id, job_name, ? from job_jobs_to_run order by next_scheduled_run asc - limit ? + %s on conflict (job_plugin_id, job_name) where status = 'running' diff --git a/internal/scheduler/job/repository_run.go b/internal/scheduler/job/repository_run.go index 0c630b332..0137fb29d 100644 --- a/internal/scheduler/job/repository_run.go +++ b/internal/scheduler/job/repository_run.go @@ -23,10 +23,20 @@ func (r *Repository) RunJobs(ctx context.Context, serverId string, opt ...Option } opts := getOpts(opt...) + var limit string + switch { + case opts.withRunJobsLimit == 0: + // zero signals the defaults should be used. + limit = fmt.Sprintf("limit %d", defaultRunJobsLimit) + case opts.withRunJobsLimit > 0: + limit = fmt.Sprintf("limit %d", opts.withRunJobsLimit) + } + + query := fmt.Sprintf(runJobsQuery, limit) var runs []*Run _, err := r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(r db.Reader, w db.Writer) error { - rows, err := w.Query(ctx, runJobsQuery, []interface{}{serverId, opts.withRunJobsLimit}) + rows, err := w.Query(ctx, query, []interface{}{serverId}) if err != nil { return errors.Wrap(ctx, err, op) } diff --git a/internal/scheduler/job/repository_run_test.go b/internal/scheduler/job/repository_run_test.go index 3647b3280..ed8b66160 100644 --- a/internal/scheduler/job/repository_run_test.go +++ b/internal/scheduler/job/repository_run_test.go @@ -112,7 +112,7 @@ func TestRepository_RunJobs_Limits(t *testing.T) { kms := kms.TestKms(t, conn, wrapper) iam.TestRepo(t, conn, wrapper) - numJobs := 10 + numJobs := 20 server := testController(t, conn, wrapper) tests := []struct { @@ -122,7 +122,7 @@ func TestRepository_RunJobs_Limits(t *testing.T) { }{ { name: "with-more-than-available", - opts: []Option{WithRunJobsLimit(uint(numJobs * 2))}, + opts: []Option{WithRunJobsLimit(numJobs * 2)}, wantLen: numJobs, }, { @@ -139,6 +139,11 @@ func TestRepository_RunJobs_Limits(t *testing.T) { opts: []Option{WithRunJobsLimit(0)}, wantLen: defaultRunJobsLimit, }, + { + name: "unlimited", + opts: []Option{WithRunJobsLimit(-1)}, + wantLen: numJobs, + }, } for _, tt := range tests { @@ -156,6 +161,11 @@ func TestRepository_RunJobs_Limits(t *testing.T) { got, err := repo.RunJobs(context.Background(), server.PrivateId, tt.opts...) require.NoError(err) assert.Len(got, tt.wantLen) + + // Clean up jobs for next run + rows, err := rw.Query(context.Background(), "delete from job", nil) + require.NoError(err) + _ = rows.Close() }) } } diff --git a/internal/scheduler/options.go b/internal/scheduler/options.go index d8e481659..6acccef33 100644 --- a/internal/scheduler/options.go +++ b/internal/scheduler/options.go @@ -24,7 +24,7 @@ type Option func(*options) // options = how options are represented type options struct { withNextRunIn time.Duration - withRunJobsLimit uint + withRunJobsLimit int withRunJobInterval time.Duration withMonitorInterval time.Duration withInterruptThreshold time.Duration @@ -43,7 +43,8 @@ func getDefaultOptions() options { // WithRunJobsLimit provides an option to provide the number of jobs that will be requested // by the scheduler when querying for jobs to run. // If WithRunJobsLimit == 0, then default run jobs limit is used. -func WithRunJobsLimit(l uint) Option { +// If WithRunJobsLimit < 0, then no limit is used. +func WithRunJobsLimit(l int) Option { return func(o *options) { o.withRunJobsLimit = l if o.withRunJobsLimit == 0 { diff --git a/internal/scheduler/scheduler.go b/internal/scheduler/scheduler.go index 313a2ce55..626b5d00c 100644 --- a/internal/scheduler/scheduler.go +++ b/internal/scheduler/scheduler.go @@ -29,7 +29,7 @@ type Scheduler struct { runningJobs *sync.Map started ua.Bool - runJobsLimit uint + runJobsLimit int runJobsInterval time.Duration monitorInterval time.Duration interruptThreshold time.Duration diff --git a/internal/scheduler/scheduler_test.go b/internal/scheduler/scheduler_test.go index 6f25524ff..5cbdefcd4 100644 --- a/internal/scheduler/scheduler_test.go +++ b/internal/scheduler/scheduler_test.go @@ -30,7 +30,7 @@ func TestScheduler_New(t *testing.T) { type args struct { serverId string jobRepo jobRepoFactory - runLimit uint + runLimit int runInterval time.Duration } tests := []struct { @@ -84,6 +84,21 @@ func TestScheduler_New(t *testing.T) { runInterval: time.Hour, }, }, + { + name: "valid-with-unlimited", + args: args{ + serverId: "test-server", + jobRepo: jobRepoFn, + }, + opts: []Option{ + WithRunJobsLimit(-1), + }, + want: args{ + serverId: "test-server", + runLimit: -1, + runInterval: time.Minute, + }, + }, { name: "valid-with-limit", args: args{ From 06a2f4ce9c55f9899715d1543b0cb8c35e72d631 Mon Sep 17 00:00:00 2001 From: Todd Date: Mon, 13 Jun 2022 08:26:24 -0700 Subject: [PATCH 02/15] Require default ports to be defined for newly created tcp targets. (#2148) --- .../controller/handlers/targets/registry.go | 6 + .../handlers/targets/target_service.go | 30 ++-- .../targets/tcp/target_service_test.go | 133 +++++++++++++----- .../controller/handlers/targets/tcp/tcp.go | 21 ++- internal/target/registry.go | 55 ++++++-- internal/target/repository.go | 4 +- .../repository_credential_source_test.go | 24 +++- internal/target/targettest/target.go | 27 ++++ internal/target/tcp/exports_test.go | 2 +- internal/target/tcp/register.go | 43 +++++- internal/target/tcp/target.go | 8 +- internal/tests/api/targets/target_test.go | 10 +- 12 files changed, 282 insertions(+), 81 deletions(-) diff --git a/internal/daemon/controller/handlers/targets/registry.go b/internal/daemon/controller/handlers/targets/registry.go index 98d6aa95e..e2494983f 100644 --- a/internal/daemon/controller/handlers/targets/registry.go +++ b/internal/daemon/controller/handlers/targets/registry.go @@ -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 diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index 9c312cbc3..f27c75a78 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -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 } } diff --git a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go index 46e256643..2aa37128d 100644 --- a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go @@ -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}, diff --git a/internal/daemon/controller/handlers/targets/tcp/tcp.go b/internal/daemon/controller/handlers/targets/tcp/tcp.go index 442c1bc84..5f937e9d0 100644 --- a/internal/daemon/controller/handlers/targets/tcp/tcp.go +++ b/internal/daemon/controller/handlers/targets/tcp/tcp.go @@ -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 } diff --git a/internal/target/registry.go b/internal/target/registry.go index 46a6854be..e54abb82f 100644 --- a/internal/target/registry.go +++ b/internal/target/registry.go @@ -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, ®istryEntry{ - newFunc: nf, - alloc: af, - vet: vf, - vetCredentialLibraries: vclf, - prefix: prefix, + targetHooks: th, + prefix: prefix, }) } diff --git a/internal/target/repository.go b/internal/target/repository.go index 9b3163ec4..e89e8b03a 100644 --- a/internal/target/repository.go +++ b/internal/target/repository.go @@ -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 } diff --git a/internal/target/repository_credential_source_test.go b/internal/target/repository_credential_source_test.go index f1afe7072..96f262ad9 100644 --- a/internal/target/repository_credential_source_test.go +++ b/internal/target/repository_credential_source_test.go @@ -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") diff --git a/internal/target/targettest/target.go b/internal/target/targettest/target.go index 101d2b60f..741add0a2 100644 --- a/internal/target/targettest/target.go +++ b/internal/target/targettest/target.go @@ -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 diff --git a/internal/target/tcp/exports_test.go b/internal/target/tcp/exports_test.go index d1cb5cdfe..a9258b18e 100644 --- a/internal/target/tcp/exports_test.go +++ b/internal/target/tcp/exports_test.go @@ -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 } diff --git a/internal/target/tcp/register.go b/internal/target/tcp/register.go index df0acb464..db8119c01 100644 --- a/internal/target/tcp/register.go +++ b/internal/target/tcp/register.go @@ -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 { diff --git a/internal/target/tcp/target.go b/internal/target/tcp/target.go index bcbe4d471..f68ccc664 100644 --- a/internal/target/tcp/target.go +++ b/internal/target/tcp/target.go @@ -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{}, } diff --git a/internal/tests/api/targets/target_test.go b/internal/tests/api/targets/target_test.go index 835b00f71..9943b702f 100644 --- a/internal/tests/api/targets/target_test.go +++ b/internal/tests/api/targets/target_test.go @@ -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) From b6ca61a7f62d01183f47f7fbff761679b7522b54 Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Mon, 13 Jun 2022 09:01:01 -0700 Subject: [PATCH 03/15] refact(cred-libs): Update user_password to username_password (#2154) * refact(cred-libs): Update user_password to username_password --- CHANGELOG.md | 6 +- internal/credential/credential.go | 4 +- .../vault/credential_library_test.go | 12 +- .../credential/vault/mapping_overriders.go | 2 +- .../vault/mapping_overriders_test.go | 6 +- internal/credential/vault/options_test.go | 4 +- internal/credential/vault/private_library.go | 4 +- .../credential/vault/private_library_test.go | 46 ++++---- .../repository_credential_library_test.go | 56 ++++----- .../vault/repository_credentials_test.go | 8 +- .../credentiallibrary_service.go | 6 +- .../credentiallibrary_service_test.go | 44 +++---- .../targets/tcp/target_service_test.go | 8 +- .../oss/postgres/22/02_credential_type.up.sql | 1 + .../oss/postgres/32/01_credential_type.up.sql | 23 ++++ .../migrations/oss/postgres_32_01_test.go | 111 ++++++++++++++++++ .../sqltest/initdb.d/03_widgets_persona.sql | 10 +- .../vault/credential_vault_library.sql | 10 +- ...library_user_password_mapping_override.sql | 10 +- 19 files changed, 254 insertions(+), 117 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/32/01_credential_type.up.sql create mode 100644 internal/db/schema/migrations/oss/postgres_32_01_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index bd660f6bc..b4181635f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. ### Deprecations/Changes +* Credential Libraries: The `user_password` credential type has been renamed to + `username_password` to remove any inconsistency over what the credential type is. + All existing `user_password` typed credential libraries will be migrated to + `username_password` ([PR](https://github.com/hashicorp/boundary/pull/2154)). * controller: Change the default behavior of the session list endpoint to no longer include sessions in a terminated state and introduces a new query parameter/cli flag to include the terminated sessions. @@ -28,8 +32,6 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. documentation](https://www.boundaryproject.io/docs/concepts/security/permissions/assignable-permissions) for more details. -### Bug Fixes - ## 0.8.1 (2022/05/13) ### Bug Fixes diff --git a/internal/credential/credential.go b/internal/credential/credential.go index fbb8d2afb..33e46cb0b 100644 --- a/internal/credential/credential.go +++ b/internal/credential/credential.go @@ -26,8 +26,8 @@ type Type string // Credential type values. const ( - UnspecifiedType Type = "unspecified" - UserPasswordType Type = "user_password" + UnspecifiedType Type = "unspecified" + UsernamePasswordType Type = "username_password" ) // A Library is a resource that provides credentials that are of the same diff --git a/internal/credential/vault/credential_library_test.go b/internal/credential/vault/credential_library_test.go index c6e165b7e..b4eb68c1f 100644 --- a/internal/credential/vault/credential_library_test.go +++ b/internal/credential/vault/credential_library_test.go @@ -214,7 +214,7 @@ func TestCredentialLibrary_New(t *testing.T) { vaultPath: "vault/path", opts: []Option{ WithMethod(MethodGet), - WithCredentialType(credential.UserPasswordType), + WithCredentialType(credential.UsernamePasswordType), }, }, want: &CredentialLibrary{ @@ -222,7 +222,7 @@ func TestCredentialLibrary_New(t *testing.T) { StoreId: cs.PublicId, VaultPath: "vault/path", HttpMethod: string(MethodGet), - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, }, @@ -233,7 +233,7 @@ func TestCredentialLibrary_New(t *testing.T) { vaultPath: "vault/path", opts: []Option{ WithMethod(MethodGet), - WithCredentialType(credential.UserPasswordType), + WithCredentialType(credential.UsernamePasswordType), WithMappingOverride(NewUserPasswordOverride(WithOverrideUsernameAttribute("test"))), }, }, @@ -243,7 +243,7 @@ func TestCredentialLibrary_New(t *testing.T) { StoreId: cs.PublicId, VaultPath: "vault/path", HttpMethod: string(MethodGet), - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, }, @@ -262,8 +262,8 @@ func TestCredentialLibrary_New(t *testing.T) { assert.Equal(tt.want, got) switch ct := tt.want.GetCredentialType(); ct { - case string(credential.UserPasswordType): - assert.Equal(credential.UserPasswordType, got.CredentialType()) + case string(credential.UsernamePasswordType): + assert.Equal(credential.UsernamePasswordType, got.CredentialType()) case string(credential.UnspecifiedType), "": assert.Equal(credential.UnspecifiedType, got.CredentialType()) default: diff --git a/internal/credential/vault/mapping_overriders.go b/internal/credential/vault/mapping_overriders.go index cf2b4d889..29d1d8e13 100644 --- a/internal/credential/vault/mapping_overriders.go +++ b/internal/credential/vault/mapping_overriders.go @@ -15,7 +15,7 @@ func validMappingOverride(m MappingOverride, ct credential.Type) bool { case nil: return true // it is always valid to not specify a mapping override case *UserPasswordOverride: - return ct == credential.UserPasswordType + return ct == credential.UsernamePasswordType default: return false // an unknown mapping override type is never valid } diff --git a/internal/credential/vault/mapping_overriders_test.go b/internal/credential/vault/mapping_overriders_test.go index 735645ed1..a7f35e980 100644 --- a/internal/credential/vault/mapping_overriders_test.go +++ b/internal/credential/vault/mapping_overriders_test.go @@ -34,7 +34,7 @@ func TestValidMappingOverrides(t *testing.T) { }, { m: nil, - ct: credential.UserPasswordType, + ct: credential.UsernamePasswordType, want: true, }, { @@ -44,7 +44,7 @@ func TestValidMappingOverrides(t *testing.T) { }, { m: unknownMapper(1), - ct: credential.UserPasswordType, + ct: credential.UsernamePasswordType, want: false, }, { @@ -54,7 +54,7 @@ func TestValidMappingOverrides(t *testing.T) { }, { m: allocUserPasswordOverride(), - ct: credential.UserPasswordType, + ct: credential.UsernamePasswordType, want: true, }, } diff --git a/internal/credential/vault/options_test.go b/internal/credential/vault/options_test.go index eabbb98bb..759abb793 100644 --- a/internal/credential/vault/options_test.go +++ b/internal/credential/vault/options_test.go @@ -85,9 +85,9 @@ func Test_GetOpts(t *testing.T) { assert.Equal(t, opts, testOpts) }) t.Run("WithCredentialType", func(t *testing.T) { - opts := getOpts(WithCredentialType(credential.UserPasswordType)) + opts := getOpts(WithCredentialType(credential.UsernamePasswordType)) testOpts := getDefaultOptions() - testOpts.withCredentialType = credential.UserPasswordType + testOpts.withCredentialType = credential.UsernamePasswordType assert.Equal(t, opts, testOpts) }) t.Run("WithOverrideUsernameAttribute", func(t *testing.T) { diff --git a/internal/credential/vault/private_library.go b/internal/credential/vault/private_library.go index e3947fcb5..76f750517 100644 --- a/internal/credential/vault/private_library.go +++ b/internal/credential/vault/private_library.go @@ -47,7 +47,7 @@ func (bc *baseCred) getExpiration() time.Duration { return bc.expiration } // UnspecifiedType. func convert(ctx context.Context, bc *baseCred) (dynamicCred, error) { switch bc.Library().CredentialType() { - case credential.UserPasswordType: + case credential.UsernamePasswordType: return baseToUsrPass(ctx, bc) } return bc, nil @@ -59,7 +59,7 @@ func baseToUsrPass(ctx context.Context, bc *baseCred) (*usrPassCred, error) { return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("nil baseCred")) case bc.lib == nil: return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("nil baseCred.lib")) - case bc.Library().CredentialType() != credential.UserPasswordType: + case bc.Library().CredentialType() != credential.UsernamePasswordType: return nil, errors.E(ctx, errors.WithCode(errors.InvalidParameter), errors.WithMsg("invalid credential type")) } diff --git a/internal/credential/vault/private_library_test.go b/internal/credential/vault/private_library_test.go index c2f71b62f..76bdb94f4 100644 --- a/internal/credential/vault/private_library_test.go +++ b/internal/credential/vault/private_library_test.go @@ -115,7 +115,7 @@ func TestRepository_getPrivateLibraries(t *testing.T) { } { opts := []Option{ - WithCredentialType(credential.UserPasswordType), + WithCredentialType(credential.UsernamePasswordType), } libIn, err := NewCredentialLibrary(origStore.GetPublicId(), "/vault/path", opts...) assert.NoError(err) @@ -129,7 +129,7 @@ func TestRepository_getPrivateLibraries(t *testing.T) { } { opts := []Option{ - WithCredentialType(credential.UserPasswordType), + WithCredentialType(credential.UsernamePasswordType), WithMappingOverride(NewUserPasswordOverride( WithOverrideUsernameAttribute("test-username"), )), @@ -146,7 +146,7 @@ func TestRepository_getPrivateLibraries(t *testing.T) { } { opts := []Option{ - WithCredentialType(credential.UserPasswordType), + WithCredentialType(credential.UsernamePasswordType), WithMappingOverride(NewUserPasswordOverride( WithOverridePasswordAttribute("test-password"), )), @@ -163,7 +163,7 @@ func TestRepository_getPrivateLibraries(t *testing.T) { } { opts := []Option{ - WithCredentialType(credential.UserPasswordType), + WithCredentialType(credential.UsernamePasswordType), WithMappingOverride(NewUserPasswordOverride( WithOverrideUsernameAttribute("test-username"), WithOverridePasswordAttribute("test-password"), @@ -336,7 +336,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-no-username-default-password-attribute", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "password": "my-password", @@ -348,7 +348,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-no-password-default-username-attribute", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "username": "my-username", @@ -360,7 +360,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "valid-default-attributes", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "username": "my-username", @@ -376,7 +376,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "valid-override-attributes", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), UsernameAttribute: "test-username", PasswordAttribute: "test-password", }, @@ -396,7 +396,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "valid-default-username-override-password", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), PasswordAttribute: "test-password", }, secretData: map[string]interface{}{ @@ -415,7 +415,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "valid-override-username-default-password", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), UsernameAttribute: "test-username", }, secretData: map[string]interface{}{ @@ -434,7 +434,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-username-override", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), UsernameAttribute: "missing-username", }, secretData: map[string]interface{}{ @@ -450,7 +450,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-password-override", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), UsernameAttribute: "missing-password", }, secretData: map[string]interface{}{ @@ -466,7 +466,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-kv2-no-metadata-field", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "data": map[string]interface{}{ @@ -481,7 +481,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-kv2-no-data-field", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "metadata": map[string]interface{}{}, @@ -493,7 +493,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-kv2-no-username-default-password-attribute", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "metadata": map[string]interface{}{}, @@ -508,7 +508,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-kv2-no-passsword-default-username-attribute", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "metadata": map[string]interface{}{}, @@ -523,7 +523,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-kv2-invalid-metadata-type", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "metadata": "hello", @@ -539,7 +539,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-kv2-invalid-metadata-type", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "metadata": map[string]interface{}{}, @@ -552,7 +552,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "invalid-kv2-additional-field", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "bad-field": "hello", @@ -569,7 +569,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "valid-kv2-default-attributes", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), }, secretData: map[string]interface{}{ "metadata": map[string]interface{}{}, @@ -588,7 +588,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "valid-kv2-override-attributes", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), UsernameAttribute: "test-username", PasswordAttribute: "test-password", }, @@ -611,7 +611,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "valid-kv2-default-username-override-password", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), PasswordAttribute: "test-password", }, secretData: map[string]interface{}{ @@ -633,7 +633,7 @@ func TestBaseToUsrPass(t *testing.T) { name: "valid-kv2-override-username-default-password", given: &baseCred{ lib: &privateLibrary{ - CredType: string(credential.UserPasswordType), + CredType: string(credential.UsernamePasswordType), UsernameAttribute: "test-username", }, secretData: map[string]interface{}{ diff --git a/internal/credential/vault/repository_credential_library_test.go b/internal/credential/vault/repository_credential_library_test.go index 968e1cb06..6676f973c 100644 --- a/internal/credential/vault/repository_credential_library_test.go +++ b/internal/credential/vault/repository_credential_library_test.go @@ -171,7 +171,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, want: &CredentialLibrary{ @@ -179,7 +179,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, }, @@ -191,7 +191,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, wantErr: errors.VaultInvalidMappingOverride, @@ -218,7 +218,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, want: &CredentialLibrary{ @@ -229,7 +229,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, }, @@ -243,7 +243,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, want: &CredentialLibrary{ @@ -254,7 +254,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, }, @@ -269,7 +269,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, want: &CredentialLibrary{ @@ -281,7 +281,7 @@ func TestRepository_CreateCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, }, @@ -761,7 +761,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { CredentialLibrary: &store.CredentialLibrary{ HttpMethod: "GET", VaultPath: "/old/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, chgFn: changeVaultPath("/new/path"), @@ -771,7 +771,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { CredentialLibrary: &store.CredentialLibrary{ HttpMethod: "GET", VaultPath: "/new/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, wantCount: 1, @@ -921,7 +921,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, chgFn: changeCredentialType(credential.UnspecifiedType), @@ -939,7 +939,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, chgFn: changeMappingOverride( @@ -956,7 +956,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, wantCount: 1, @@ -972,7 +972,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, chgFn: changeMappingOverride( @@ -989,7 +989,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, wantCount: 1, @@ -1005,7 +1005,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, chgFn: changeMappingOverride( @@ -1024,7 +1024,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, wantCount: 1, @@ -1036,7 +1036,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, chgFn: changeMappingOverride( @@ -1055,7 +1055,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, wantCount: 1, @@ -1071,7 +1071,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, chgFn: changeMappingOverride(nil), @@ -1081,7 +1081,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, wantCount: 1, @@ -1324,7 +1324,7 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, }, @@ -1338,7 +1338,7 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, }, @@ -1352,7 +1352,7 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, }, @@ -1367,7 +1367,7 @@ func TestRepository_LookupCredentialLibrary(t *testing.T) { StoreId: cs.GetPublicId(), HttpMethod: "GET", VaultPath: "/some/path", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, }, }, @@ -1528,7 +1528,7 @@ func TestRepository_DeleteCredentialLibrary(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, } @@ -1572,7 +1572,7 @@ func TestRepository_ListCredentialLibraries(t *testing.T) { HttpMethod: "GET", VaultPath: "/some/path", Name: "test-name-repo", - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }, } diff --git a/internal/credential/vault/repository_credentials_test.go b/internal/credential/vault/repository_credentials_test.go index 5dc45294d..69cc145de 100644 --- a/internal/credential/vault/repository_credentials_test.go +++ b/internal/credential/vault/repository_credentials_test.go @@ -128,7 +128,7 @@ func TestRepository_IssueCredentials(t *testing.T) { { libPath := path.Join("database", "creds", "opened") opts := []vault.Option{ - vault.WithCredentialType(credential.UserPasswordType), + vault.WithCredentialType(credential.UsernamePasswordType), } libIn, err := vault.NewCredentialLibrary(origStore.GetPublicId(), libPath, opts...) assert.NoError(t, err) @@ -141,7 +141,7 @@ func TestRepository_IssueCredentials(t *testing.T) { { libPath := path.Join("database", "creds", "opened") opts := []vault.Option{ - vault.WithCredentialType(credential.UserPasswordType), + vault.WithCredentialType(credential.UsernamePasswordType), vault.WithMappingOverride(vault.NewUserPasswordOverride( vault.WithOverrideUsernameAttribute("test-username"), vault.WithOverridePasswordAttribute("test-password"), @@ -158,7 +158,7 @@ func TestRepository_IssueCredentials(t *testing.T) { { libPath := path.Join("secret", "data", "my-secret") opts := []vault.Option{ - vault.WithCredentialType(credential.UserPasswordType), + vault.WithCredentialType(credential.UsernamePasswordType), } libIn, err := vault.NewCredentialLibrary(origStore.GetPublicId(), libPath, opts...) assert.NoError(t, err) @@ -326,7 +326,7 @@ func TestRepository_IssueCredentials(t *testing.T) { assert.NotZero(len(got)) for _, dc := range got { switch dc.Library().CredentialType() { - case credential.UserPasswordType: + case credential.UsernamePasswordType: if upc, ok := dc.(credential.UserPassword); ok { assert.NotEmpty(upc.Username()) assert.NotEmpty(upc.Password()) diff --git a/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service.go b/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service.go index 1df38bffd..c3f00856f 100644 --- a/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service.go +++ b/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service.go @@ -579,7 +579,7 @@ func toStorageVaultLibrary(storeId string, in *pb.CredentialLibrary) (out *vault credentialType := credential.Type(in.GetCredentialType()) switch credentialType { - case credential.UserPasswordType: + case credential.UsernamePasswordType: opts = append(opts, vault.WithCredentialType(credentialType)) overrides := in.CredentialMappingOverrides.AsMap() var mapOpts []vault.Option @@ -694,7 +694,7 @@ func validateMapping(badFields map[string]string, credentialType credential.Type badFields[globals.CredentialMappingOverridesField] = fmt.Sprintf("This field can only be set if %q is set", globals.CredentialTypeField) } return - case credential.UserPasswordType: + case credential.UsernamePasswordType: validFields[usernameAttribute] = true validFields[passwordAttribute] = true default: @@ -734,7 +734,7 @@ func getMappingUpdates(credentialType credential.Type, current vault.MappingOver } switch credentialType { - case credential.UserPasswordType: + case credential.UsernamePasswordType: var currentUser, currentPass interface{} if overrides, ok := current.(*vault.UserPasswordOverride); ok { currentUser = overrides.UsernameAttribute diff --git a/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service_test.go b/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service_test.go index 94d53b829..6209941ed 100644 --- a/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service_test.go +++ b/internal/daemon/controller/handlers/credentiallibraries/credentiallibrary_service_test.go @@ -318,7 +318,7 @@ func TestCreate(t *testing.T) { err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, { - name: "Invalid user_password mapping", + name: "Invalid username_password mapping", req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{ CredentialStoreId: store.GetPublicId(), Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{ @@ -326,7 +326,7 @@ func TestCreate(t *testing.T) { Path: wrapperspb.String("something"), }, }, - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), CredentialMappingOverrides: func() *structpb.Struct { v := map[string]interface{}{ usernameAttribute: "user-test", @@ -410,7 +410,7 @@ func TestCreate(t *testing.T) { }, }, { - name: "Create a valid vault CredentialLibrary user_password type", + name: "Create a valid vault CredentialLibrary username_password type", req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{ CredentialStoreId: store.GetPublicId(), Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{ @@ -418,7 +418,7 @@ func TestCreate(t *testing.T) { Path: wrapperspb.String("something"), }, }, - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }}, idPrefix: vault.CredentialLibraryPrefix + "_", res: &pbs.CreateCredentialLibraryResponse{ @@ -437,13 +437,13 @@ func TestCreate(t *testing.T) { HttpMethod: wrapperspb.String("GET"), }, }, - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), AuthorizedActions: testAuthorizedActions, }, }, }, { - name: "Create a valid vault CredentialLibrary user_password type with username mapping", + name: "Create a valid vault CredentialLibrary username_password type with username mapping", req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{ CredentialStoreId: store.GetPublicId(), Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{ @@ -459,7 +459,7 @@ func TestCreate(t *testing.T) { require.NoError(t, err) return ret }(), - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }}, idPrefix: vault.CredentialLibraryPrefix + "_", res: &pbs.CreateCredentialLibraryResponse{ @@ -478,7 +478,7 @@ func TestCreate(t *testing.T) { HttpMethod: wrapperspb.String("GET"), }, }, - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), CredentialMappingOverrides: func() *structpb.Struct { v := map[string]interface{}{ usernameAttribute: "user-test", @@ -492,7 +492,7 @@ func TestCreate(t *testing.T) { }, }, { - name: "Create a valid vault CredentialLibrary user_password type with username/password mapping", + name: "Create a valid vault CredentialLibrary username_password type with username/password mapping", req: &pbs.CreateCredentialLibraryRequest{Item: &pb.CredentialLibrary{ CredentialStoreId: store.GetPublicId(), Attrs: &pb.CredentialLibrary_VaultCredentialLibraryAttributes{ @@ -509,7 +509,7 @@ func TestCreate(t *testing.T) { require.NoError(t, err) return ret }(), - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), }}, idPrefix: vault.CredentialLibraryPrefix + "_", res: &pbs.CreateCredentialLibraryResponse{ @@ -528,7 +528,7 @@ func TestCreate(t *testing.T) { HttpMethod: wrapperspb.String("GET"), }, }, - CredentialType: string(credential.UserPasswordType), + CredentialType: string(credential.UsernamePasswordType), CredentialMappingOverrides: func() *structpb.Struct { v := map[string]interface{}{ usernameAttribute: "user-test", @@ -607,7 +607,7 @@ func TestGet(t *testing.T) { repo, err := repoFn() require.NoError(t, err) lib, err := vault.NewCredentialLibrary(store.GetPublicId(), "vault/path", - vault.WithCredentialType("user_password"), + vault.WithCredentialType("username_password"), vault.WithMappingOverride( vault.NewUserPasswordOverride( vault.WithOverrideUsernameAttribute("user"), @@ -664,7 +664,7 @@ func TestGet(t *testing.T) { HttpMethod: wrapperspb.String(userPassLib.GetHttpMethod()), }, }, - CredentialType: "user_password", + CredentialType: "username_password", CredentialMappingOverrides: func() *structpb.Struct { v := map[string]interface{}{ usernameAttribute: "user", @@ -903,7 +903,7 @@ func TestUpdate(t *testing.T) { { name: "user-password-attributes-change-username-attribute", opts: []vault.Option{ - vault.WithCredentialType("user_password"), + vault.WithCredentialType("username_password"), vault.WithMappingOverride( vault.NewUserPasswordOverride( vault.WithOverrideUsernameAttribute("orig-user"), @@ -932,7 +932,7 @@ func TestUpdate(t *testing.T) { { name: "user-password-attributes-change-password-attribute", opts: []vault.Option{ - vault.WithCredentialType("user_password"), + vault.WithCredentialType("username_password"), vault.WithMappingOverride( vault.NewUserPasswordOverride( vault.WithOverrideUsernameAttribute("orig-user"), @@ -961,7 +961,7 @@ func TestUpdate(t *testing.T) { { name: "user-password-attributes-change-username-and-password-attributes", opts: []vault.Option{ - vault.WithCredentialType("user_password"), + vault.WithCredentialType("username_password"), vault.WithMappingOverride( vault.NewUserPasswordOverride( vault.WithOverrideUsernameAttribute("orig-user"), @@ -992,7 +992,7 @@ func TestUpdate(t *testing.T) { { name: "no-mapping-override-change-username-and-password-attributes", opts: []vault.Option{ - vault.WithCredentialType("user_password"), + vault.WithCredentialType("username_password"), }, req: &pbs.UpdateCredentialLibraryRequest{ UpdateMask: fieldmask(passwordAttrField, usernameAttrField), @@ -1023,7 +1023,7 @@ func TestUpdate(t *testing.T) { { name: "user-password-attributes-delete-mapping-override", opts: []vault.Option{ - vault.WithCredentialType("user_password"), + vault.WithCredentialType("username_password"), vault.WithMappingOverride( vault.NewUserPasswordOverride( vault.WithOverrideUsernameAttribute("orig-user"), @@ -1045,7 +1045,7 @@ func TestUpdate(t *testing.T) { { name: "no-mapping-override-delete-mapping-override", opts: []vault.Option{ - vault.WithCredentialType("user_password"), + vault.WithCredentialType("username_password"), }, req: &pbs.UpdateCredentialLibraryRequest{ UpdateMask: fieldmask(credentialMappingPathField), @@ -1062,7 +1062,7 @@ func TestUpdate(t *testing.T) { { name: "user-password-attributes-delete-mapping-override-field-specific", opts: []vault.Option{ - vault.WithCredentialType("user_password"), + vault.WithCredentialType("username_password"), vault.WithMappingOverride( vault.NewUserPasswordOverride( vault.WithOverrideUsernameAttribute("orig-user"), @@ -1092,7 +1092,7 @@ func TestUpdate(t *testing.T) { { name: "no-mapping-override-delete-mapping-override-field-specific", opts: []vault.Option{ - vault.WithCredentialType("user_password"), + vault.WithCredentialType("username_password"), }, req: &pbs.UpdateCredentialLibraryRequest{ UpdateMask: fieldmask(passwordAttrField, usernameAttrField), @@ -1185,7 +1185,7 @@ func TestUpdate(t *testing.T) { { name: "read only credential type", path: "credential_type", - item: &pb.CredentialLibrary{CredentialType: string(credential.UserPasswordType)}, + item: &pb.CredentialLibrary{CredentialType: string(credential.UsernamePasswordType)}, }, } for _, tc := range errCases { diff --git a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go index 2aa37128d..36a061dab 100644 --- a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go @@ -3002,7 +3002,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { HttpMethod: wrapperspb.String("GET"), }, }, - CredentialType: "user_password", + CredentialType: "username_password", }}) require.NoError(t, err) @@ -3033,7 +3033,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { HttpMethod: wrapperspb.String("GET"), }, }, - CredentialType: "user_password", + CredentialType: "username_password", CredentialMappingOverrides: &structpb.Struct{Fields: map[string]*structpb.Value{ "username_attribute": structpb.NewStringValue("non-default-user"), "password_attribute": structpb.NewStringValue("non-default-pass"), @@ -3072,7 +3072,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { require.NoError(t, err) return st }(), - wantedCredType: string(credential.UserPasswordType), + wantedCredType: string(credential.UsernamePasswordType), }, { name: "userpassword-with-mapping", @@ -3089,7 +3089,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { require.NoError(t, err) return st }(), - wantedCredType: string(credential.UserPasswordType), + wantedCredType: string(credential.UsernamePasswordType), }, } diff --git a/internal/db/schema/migrations/oss/postgres/22/02_credential_type.up.sql b/internal/db/schema/migrations/oss/postgres/22/02_credential_type.up.sql index 3bd027cc3..3eb30caca 100644 --- a/internal/db/schema/migrations/oss/postgres/22/02_credential_type.up.sql +++ b/internal/db/schema/migrations/oss/postgres/22/02_credential_type.up.sql @@ -2,6 +2,7 @@ begin; create table credential_type_enm ( name text primary key + -- This constraint is replaced in 32/01_credential_type.up.sql constraint only_predefined_credential_types_allowed check ( name in ( diff --git a/internal/db/schema/migrations/oss/postgres/32/01_credential_type.up.sql b/internal/db/schema/migrations/oss/postgres/32/01_credential_type.up.sql new file mode 100644 index 000000000..56ab30bf8 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/32/01_credential_type.up.sql @@ -0,0 +1,23 @@ +begin; + + -- drop constraint so we can migrate user_password to username_password + alter table credential_type_enm + drop constraint only_predefined_credential_types_allowed; + + -- Next: we will update user_password to username_password + update credential_type_enm + set name = 'username_password' + where name = 'user_password'; + + -- Add new constraint that only allows unspecified and new username_password + -- This replaces the constraint defined in 2/02_credential_type.up.sql + alter table credential_type_enm + add constraint only_predefined_credential_types_allowed + check ( + name in ( + 'unspecified', + 'username_password' + ) + ); + +commit; diff --git a/internal/db/schema/migrations/oss/postgres_32_01_test.go b/internal/db/schema/migrations/oss/postgres_32_01_test.go new file mode 100644 index 000000000..45163b102 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres_32_01_test.go @@ -0,0 +1,111 @@ +package oss_test + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/credential/vault" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/common" + "github.com/hashicorp/boundary/internal/db/schema" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/testing/dbtest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMigrations_user_password_Migration(t *testing.T) { + const ( + priorMigration = 31002 + currentMigration = 32001 + ) + + t.Parallel() + ctx := context.Background() + dialect := dbtest.Postgres + + c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1)) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, c()) + }) + d, err := common.SqlOpen(dialect, u) + require.NoError(t, err) + + // migration to the prior migration (before the one we want to test) + m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions( + schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}), + )) + require.NoError(t, err) + + require.NoError(t, m.ApplyMigrations(ctx)) + state, err := m.CurrentState(ctx) + require.NoError(t, err) + want := &schema.State{ + Initialized: true, + Editions: []schema.EditionState{ + { + Name: "oss", + BinarySchemaVersion: priorMigration, + DatabaseSchemaVersion: priorMigration, + DatabaseSchemaState: schema.Equal, + }, + }, + } + require.Equal(t, want, state) + + // get a connection + dbType, err := db.StringToDbType(dialect) + require.NoError(t, err) + conn, err := db.Open(dbType, u) + require.NoError(t, err) + rw := db.New(conn) + + rootWrapper := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, rootWrapper) + _, prj := iam.TestScopes(t, iamRepo) + + cs, err := vault.NewCredentialStore(prj.PublicId, "https://vault", []byte("token")) + cs.PublicId = "csvlt_test1234" + require.NoError(t, rw.Create(context.Background(), cs)) + + upLib, err := vault.NewCredentialLibrary(cs.PublicId, "vault_path", vault.WithMethod("GET"), vault.WithCredentialType("user_password")) + upLib.PublicId = "clvlt_testuplib" + require.NoError(t, rw.Create(context.Background(), upLib)) + + lib, err := vault.NewCredentialLibrary(cs.PublicId, "vault_path", vault.WithMethod("GET")) + lib.PublicId = "clvlt_testlib" + require.NoError(t, rw.Create(context.Background(), lib)) + + // now we're ready for the migration we want to test. + m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions( + schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}), + )) + require.NoError(t, err) + + require.NoError(t, m.ApplyMigrations(ctx)) + state, err = m.CurrentState(ctx) + require.NoError(t, err) + want = &schema.State{ + Initialized: true, + Editions: []schema.EditionState{ + { + Name: "oss", + BinarySchemaVersion: currentMigration, + DatabaseSchemaVersion: currentMigration, + DatabaseSchemaState: schema.Equal, + }, + }, + } + require.Equal(t, want, state) + + // Validate uplib was migrated to username_password + err = rw.LookupByPublicId(context.Background(), upLib) + require.NoError(t, err) + assert.Equal(t, "username_password", upLib.GetCredentialType()) + + // Validate lib was left as unspecified + err = rw.LookupByPublicId(context.Background(), lib) + require.NoError(t, err) + assert.Equal(t, "unspecified", lib.GetCredentialType()) +} diff --git a/internal/db/sqltest/initdb.d/03_widgets_persona.sql b/internal/db/sqltest/initdb.d/03_widgets_persona.sql index c6aef2661..cbd970038 100644 --- a/internal/db/sqltest/initdb.d/03_widgets_persona.sql +++ b/internal/db/sqltest/initdb.d/03_widgets_persona.sql @@ -346,11 +346,11 @@ begin; values ('vs_______wvs', 'vl______wvl1', 'widget vault library', 'None', '/secrets', 'GET', 'unspecified'), ('vs_______wvs', 'vl______wvl2', 'widget vault ssh', 'None', '/secrets/ssh/admin', 'GET', 'unspecified'), - ('vs_______wvs', 'vl______wvl3', 'widget vault kv one', 'None', '/secrets/kv/one', 'GET', 'user_password'), - ('vs_______wvs', 'vl______wvl4', 'widget vault kv two', 'None', '/secrets/kv/two', 'GET', 'user_password'), - ('vs_______wvs', 'vl______wvl5', 'widget vault kv three', 'None', '/secrets/kv/three', 'GET', 'user_password'), - ('vs_______wvs', 'vl______wvl6', 'widget vault kv four', 'None', '/secrets/kv/four', 'GET', 'user_password'), - ('vs_______wvs', 'vl______wvl7', 'widget vault kv five', 'None', '/secrets/kv/five', 'GET', 'user_password'); + ('vs_______wvs', 'vl______wvl3', 'widget vault kv one', 'None', '/secrets/kv/one', 'GET', 'username_password'), + ('vs_______wvs', 'vl______wvl4', 'widget vault kv two', 'None', '/secrets/kv/two', 'GET', 'username_password'), + ('vs_______wvs', 'vl______wvl5', 'widget vault kv three', 'None', '/secrets/kv/three', 'GET', 'username_password'), + ('vs_______wvs', 'vl______wvl6', 'widget vault kv four', 'None', '/secrets/kv/four', 'GET', 'username_password'), + ('vs_______wvs', 'vl______wvl7', 'widget vault kv five', 'None', '/secrets/kv/five', 'GET', 'username_password'); insert into credential_vault_library_user_password_mapping_override (library_id) diff --git a/internal/db/sqltest/tests/credential/vault/credential_vault_library.sql b/internal/db/sqltest/tests/credential/vault/credential_vault_library.sql index 566dc0a4e..2e625e991 100644 --- a/internal/db/sqltest/tests/credential/vault/credential_vault_library.sql +++ b/internal/db/sqltest/tests/credential/vault/credential_vault_library.sql @@ -11,19 +11,19 @@ begin; select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl______wvl1' and credential_type = 'unspecified'; select is(count(*), 1::bigint) from credential_library where public_id = 'vl______wvl1' and credential_type = 'unspecified'; - select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl______wvl3' and credential_type = 'user_password'; - select is(count(*), 1::bigint) from credential_library where public_id = 'vl______wvl3' and credential_type = 'user_password'; + select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl______wvl3' and credential_type = 'username_password'; + select is(count(*), 1::bigint) from credential_library where public_id = 'vl______wvl3' and credential_type = 'username_password'; -- validate the insert triggers prepare insert_vault_library as insert into credential_vault_library (store_id, public_id, vault_path, http_method, credential_type) values - ('vs_______wvs', 'vl_______tt1', '/secrets/kv', 'GET', 'user_password'); + ('vs_______wvs', 'vl_______tt1', '/secrets/kv', 'GET', 'username_password'); select lives_ok('insert_vault_library'); - select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl_______tt1' and credential_type = 'user_password'; - select is(count(*), 1::bigint) from credential_library where public_id = 'vl_______tt1' and credential_type = 'user_password'; + select is(count(*), 1::bigint) from credential_vault_library where public_id = 'vl_______tt1' and credential_type = 'username_password'; + select is(count(*), 1::bigint) from credential_library where public_id = 'vl_______tt1' and credential_type = 'username_password'; -- validate the delete triggers prepare delete_vault_library as diff --git a/internal/db/sqltest/tests/credential/vault/credential_vault_library_user_password_mapping_override.sql b/internal/db/sqltest/tests/credential/vault/credential_vault_library_user_password_mapping_override.sql index 139992c87..febb37774 100644 --- a/internal/db/sqltest/tests/credential/vault/credential_vault_library_user_password_mapping_override.sql +++ b/internal/db/sqltest/tests/credential/vault/credential_vault_library_user_password_mapping_override.sql @@ -26,11 +26,11 @@ begin; 'select_private_libraries', $$VALUES ('vl______wvl2', 'unspecified', null, null), - ('vl______wvl3', 'user_password', null, null), - ('vl______wvl4', 'user_password', null, null), - ('vl______wvl5', 'user_password', 'my_username', null), - ('vl______wvl6', 'user_password', null, 'my_password'), - ('vl______wvl7', 'user_password', 'my_username', 'my_password')$$ + ('vl______wvl3', 'username_password', null, null), + ('vl______wvl4', 'username_password', null, null), + ('vl______wvl5', 'username_password', 'my_username', null), + ('vl______wvl6', 'username_password', null, 'my_password'), + ('vl______wvl7', 'username_password', 'my_username', 'my_password')$$ ); -- validate the insert triggers From 1da2cf06b03233ae0ecda332a3a8ad041163eec2 Mon Sep 17 00:00:00 2001 From: Cameron Perera Date: Mon, 13 Jun 2022 22:48:54 -0500 Subject: [PATCH 04/15] chore: create redirects for onboarding links (#2169) --- website/redirects.js | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/website/redirects.js b/website/redirects.js index 30170acd2..82c80f42a 100644 --- a/website/redirects.js +++ b/website/redirects.js @@ -1,6 +1,6 @@ module.exports = [ // define your custom redirects within this file. - // vercel's redirect documentation: https://vercel.com/docs/configuration#project/redirects + // vercel's redirect documentation: https://vercel.com/docs/project-configuration#project-configuration/redirects // Top level redirect { @@ -13,6 +13,11 @@ module.exports = [ destination: '/docs', permanent: false, }, + { + source: '/help/admin-ui/downloads', + destination: '/downloads', + permanent: true, + }, // `//index.html` to / { @@ -40,6 +45,28 @@ module.exports = [ 'https://learn.hashicorp.com/tutorials/boundary/getting-started-desktop-app', permanent: true, }, + { + source: '/help/admin-ui/getting-started/desktop', + destination: + 'https://learn.hashicorp.com/tutorials/boundary/getting-started-desktop-app', + permanent: true, + }, + + ///////////////////////////////// + // API CLIENT + ///////////////////////////////// + { + source: '/help/admin-ui/api-client/cli', + destination: + '/docs/api-clients/cli', + permanent: true, + }, + { + source: '/help/admin-ui/api-client/api', + destination: + '/docs/api-clients/api', + permanent: true, + }, ///////////////////////////////// // DOMAIN MODEL CONCEPTS From 3441fa04479f26d611870733c563044b450c4635 Mon Sep 17 00:00:00 2001 From: Damian Debkowski Date: Tue, 14 Jun 2022 08:52:53 -0700 Subject: [PATCH 05/15] (fix) added more context to error details for cli command (#2170) * (test) added more context to error details for cli commands * (fix) refactor solution to utilize custom error type casting * (test) added api unit test to validate InvalidArgumentError return message * Update internal/types/subtypes/error.go Co-authored-by: Johan Brandhorst-Satzkorn * (refactor) rename InvalidArgumentError into UnknownSubtypeIDError Co-authored-by: Johan Brandhorst-Satzkorn --- .../handlers/authmethods/authmethod_service.go | 4 +++- internal/tests/api/accounts/account_test.go | 11 ++++++++++- .../tests/api/authmethods/authmethod_test.go | 16 ++++++++++++++++ internal/types/subtypes/error.go | 15 +++++++++++++++ internal/types/subtypes/interceptor.go | 13 +++++++++++-- 5 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 internal/types/subtypes/error.go diff --git a/internal/daemon/controller/handlers/authmethods/authmethod_service.go b/internal/daemon/controller/handlers/authmethods/authmethod_service.go index dd95baeb9..a04c533d8 100644 --- a/internal/daemon/controller/handlers/authmethods/authmethod_service.go +++ b/internal/daemon/controller/handlers/authmethods/authmethod_service.go @@ -1265,7 +1265,9 @@ func transformAuthenticateRequestAttributes(msg proto.Message) error { return fmt.Errorf("%s: unknown command %q", op, authRequest.GetCommand()) } default: - return fmt.Errorf("%s: unknown auth method subtype in ID %q", op, authRequest.GetAuthMethodId()) + return &subtypes.UnknownSubtypeIDError{ + ID: authRequest.GetAuthMethodId(), + } } return nil } diff --git a/internal/tests/api/accounts/account_test.go b/internal/tests/api/accounts/account_test.go index bf3e15372..f066a33e3 100644 --- a/internal/tests/api/accounts/account_test.go +++ b/internal/tests/api/accounts/account_test.go @@ -398,5 +398,14 @@ func TestErrorsOidc(t *testing.T) { accounts.WithPasswordAccountLoginName("foo"), ) require.Error(err) - require.JSONEq(err.Error(), `{"kind":"InvalidArgument", "message":"Error in provided request.", "details":{"request_fields":[{"name":"attributes", "description":"Attribute fields do not match the expected format."}]}}`) + require.JSONEq(err.Error(), `{ + "details": { + "request_fields": [{ + "description": "Attribute fields do not match the expected format.", + "name": "attributes" + }] + }, + "kind": "InvalidArgument", + "message": "Error in provided request." + }`) } diff --git a/internal/tests/api/authmethods/authmethod_test.go b/internal/tests/api/authmethods/authmethod_test.go index 6ed4ad3db..ec48119b4 100644 --- a/internal/tests/api/authmethods/authmethod_test.go +++ b/internal/tests/api/authmethods/authmethod_test.go @@ -362,4 +362,20 @@ func TestErrors(t *testing.T) { apiErr = api.AsServerError(err) require.NotNil(apiErr) assert.EqualValues(http.StatusBadRequest, apiErr.Response().StatusCode()) + + // Passing in a invalid authentication method sub-type should return an error + _, err = amClient.Authenticate(tc.Context(), "ampwd_1234567890", "login", map[string]interface{}{ + "login_name": "admin", + "password": "password", + }) + require.Error(err) + apiErr = api.AsServerError(err) + require.NotNil(apiErr) + assert.Len(apiErr.Details.RequestFields, 1) + assert.EqualValues(apiErr.Details.RequestFields, []*api.FieldError{ + { + Name: "attributes", + Description: "unknown subtype in ID: ampwd_1234567890", + }, + }) } diff --git a/internal/types/subtypes/error.go b/internal/types/subtypes/error.go new file mode 100644 index 000000000..769bc7fac --- /dev/null +++ b/internal/types/subtypes/error.go @@ -0,0 +1,15 @@ +package subtypes + +// UnknownSubtypeIDError is an error type that describes an invalid +// resource sub-type identifer. For example, this authentication sub-type +// ID "ampwd_1234567890" is an error because the prefix "ampwd" is invalid. +type UnknownSubtypeIDError struct { + // ID is the resource identifier + ID string +} + +// Error returns a string describing an unknown subtype based on a given resource ID +// Example: "unknown subtype in ID: ampwd_1234567890" +func (e *UnknownSubtypeIDError) Error() string { + return "unknown subtype in ID: " + e.ID +} diff --git a/internal/types/subtypes/interceptor.go b/internal/types/subtypes/interceptor.go index 6ba56945a..182023fe2 100644 --- a/internal/types/subtypes/interceptor.go +++ b/internal/types/subtypes/interceptor.go @@ -2,6 +2,7 @@ package subtypes import ( "context" + "errors" "github.com/hashicorp/boundary/internal/daemon/controller/handlers" "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" @@ -297,8 +298,16 @@ func AttributeTransformerInterceptor(_ context.Context) grpc.UnaryServerIntercep return func(interceptorCtx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { if reqMsg, ok := req.(proto.Message); ok { if err := transformRequest(reqMsg); err != nil { - return nil, handlers.InvalidArgumentErrorf("Error in provided request.", - map[string]string{"attributes": "Attribute fields do not match the expected format."}) + fieldErrs := map[string]string{ + "attributes": "Attribute fields do not match the expected format.", + } + + var unknownSubTypeIDErr *UnknownSubtypeIDError + if errors.As(err, &unknownSubTypeIDErr) { + fieldErrs["attributes"] = unknownSubTypeIDErr.Error() + } + + return nil, handlers.InvalidArgumentErrorf("Error in provided request.", fieldErrs) } } From b6da1ccac7dda4685824470cf6954e365c2ac58d Mon Sep 17 00:00:00 2001 From: Michael Gaffney Date: Mon, 16 May 2022 12:33:45 -0400 Subject: [PATCH 06/15] feat(db): add static credential store schema --- .../postgres/50/01_static_credential.up.sql | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 internal/db/schema/migrations/oss/postgres/50/01_static_credential.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/50/01_static_credential.up.sql b/internal/db/schema/migrations/oss/postgres/50/01_static_credential.up.sql new file mode 100644 index 000000000..d397f0eaa --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/50/01_static_credential.up.sql @@ -0,0 +1,118 @@ +begin; + + create table credential_static_store ( + public_id wt_public_id primary key, + scope_id wt_scope_id not null + constraint iam_scope_fkey + references iam_scope (public_id) + on delete cascade + on update cascade, + name wt_name, + description wt_description, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + constraint credential_store_fkey + foreign key (scope_id, public_id) + references credential_store (scope_id, public_id) + on delete cascade + on update cascade, + constraint credential_static_store_scope_id_name_uq + unique(scope_id, name) + ); + comment on table credential_static_store is + 'credential_static_store is a table where each row is a resource that represents a static credential store. ' + 'It is a credential_store subtype and an aggregate root.'; + + create trigger update_version_column after update on credential_static_store + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on credential_static_store + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on credential_static_store + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on credential_static_store + for each row execute procedure immutable_columns('public_id', 'scope_id','create_time'); + + create trigger insert_credential_store_subtype before insert on credential_static_store + for each row execute procedure insert_credential_store_subtype(); + + create trigger delete_credential_store_subtype after delete on credential_static_store + for each row execute procedure delete_credential_store_subtype(); + + create table credential_static_username_password_credential ( + public_id wt_public_id primary key, + store_id wt_public_id not null + constraint credential_static_store_fkey + references credential_static_store (public_id) + on delete cascade + on update cascade, + name wt_name, + description wt_description, + create_time wt_timestamp, + update_time wt_timestamp, + version wt_version, + + username text not null + constraint username_must_not_be_empty + check(length(trim(username)) > 0), + password_encrypted bytea not null + constraint password_encrypted_must_not_be_empty + check(length(password_encrypted) > 0), + password_hmac bytea not null + constraint password_hmac_must_not_be_empty + check(length(password_hmac) > 0), + key_id text not null + constraint kms_data_key_version_fkey + references kms_data_key_version (private_id) + on delete restrict + on update cascade, + constraint credential_static_fkey + foreign key (store_id, public_id) + references credential_static (store_id, public_id) + on delete cascade + on update cascade, + + constraint credential_static_username_password_credential_store_id_name_uq + unique(store_id, name), + + -- The name of this constraint does not follow our naming conventions for + -- unique constraints because it would be to long. The max length for + -- identifiers in PostgreSQL is 63 characters. + -- credential_static_username_password_credential_store_id_public_id_uq + -- is 68 characters. + -- + -- https://www.postgresql.org/docs/current/limits.html + constraint credential_static_username_password_store_id_public_id_uq + unique(store_id, public_id) + ); + comment on table credential_static_username_password_credential is + 'credential_static_username_password_credential is a table where each row is a resource that represents a static username password credential. ' + 'It is a credential_static subtype and an aggregate root.'; + + create trigger update_version_column after update on credential_static_username_password_credential + for each row execute procedure update_version_column(); + + create trigger update_time_column before update on credential_static_username_password_credential + for each row execute procedure update_time_column(); + + create trigger default_create_time_column before insert on credential_static_username_password_credential + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on credential_static_username_password_credential + for each row execute procedure immutable_columns('public_id', 'store_id','create_time'); + + create trigger insert_credential_static_subtype before insert on credential_static_username_password_credential + for each row execute procedure insert_credential_static_subtype(); + + create trigger delete_credential_static_subtype after delete on credential_static_username_password_credential + for each row execute procedure delete_credential_static_subtype(); + + insert into oplog_ticket (name, version) + values + ('credential_static_store', 1), + ('credential_static_username_password_credential', 1); + +commit; From 58d546cdd4057bcf12b76f9816356a15262c18d4 Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Wed, 18 May 2022 20:20:45 -0700 Subject: [PATCH 07/15] feat(credential): Add static credential store and username_password credential --- Makefile | 3 + api/credentials/credential.gen.go | 325 +++++++ api/credentials/option.gen.go | 135 +++ .../username_password_attributes.gen.go | 8 + internal/api/genapi/input.go | 65 +- internal/cmd/commands.go | 61 ++ .../credentialscmd/credentials.gen.go | 273 ++++++ internal/cmd/commands/credentialscmd/funcs.go | 177 ++++ .../username_password_credentials.gen.go | 258 +++++ .../credentialscmd/username_password_funcs.go | 97 ++ .../cmd/commands/credentialstorescmd/funcs.go | 10 +- .../static_credentialstores.gen.go | 261 +++++ .../credentialstorescmd/static_funcs.go | 33 + internal/cmd/gencli/input.go | 42 + internal/credential/credential.go | 10 +- .../credential/static/credential_store.go | 65 ++ .../static/credential_store_test.go | 111 +++ internal/credential/static/doc.go | 2 + internal/credential/static/fields.go | 9 + internal/credential/static/options.go | 47 + internal/credential/static/options_test.go | 29 + internal/credential/static/public_ids.go | 44 + internal/credential/static/repository.go | 49 + .../static/repository_credential.go | 309 ++++++ .../static/repository_credential_store.go | 237 +++++ .../repository_credential_store_test.go | 746 +++++++++++++++ .../static/repository_credential_test.go | 896 ++++++++++++++++++ internal/credential/static/repository_test.go | 119 +++ internal/credential/static/store/static.pb.go | 467 +++++++++ internal/credential/static/testing.go | 131 +++ internal/credential/static/testing_test.go | 86 ++ .../static/usernamepassword_credential.go | 121 +++ .../usernamepassword_credential_test.go | 178 ++++ internal/credential/vault/private_library.go | 2 +- .../vault/repository_credentials_test.go | 6 +- internal/daemon/controller/common/common.go | 26 +- internal/daemon/controller/controller.go | 29 +- internal/daemon/controller/handler.go | 13 +- .../credentials/credential_service.go | 592 ++++++++++++ .../credentials/credential_service_test.go | 742 +++++++++++++++ .../credentialstore_service.go | 315 ++++-- .../credentialstore_service_test.go | 505 +++++++++- internal/gen/controller.swagger.json | 246 +++++ .../api/services/credential_service.pb.go | 864 +++++++++++++++++ .../api/services/credential_service.pb.gw.go | 632 ++++++++++++ .../services/credential_service_grpc.pb.go | 287 ++++++ internal/perms/grants_test.go | 2 +- .../resources/credentials/v1/credential.proto | 102 ++ .../api/services/v1/credential_service.proto | 127 +++ .../credential/static/store/v1/static.proto | 118 +++ .../credentials_classification_test.go | 201 ++++ .../tests/api/credentials/credentials_test.go | 208 ++++ .../credentialstores/credentialstore_test.go | 7 +- .../cli/boundary/_credential_stores.bash | 30 + internal/tests/cli/boundary/_credentials.bash | 34 + .../tests/cli/boundary/credential_stores.bats | 47 + internal/tests/cli/boundary/credentials.bats | 60 ++ internal/types/resource/resource.go | 3 + .../resources/credentials/credential.pb.go | 462 +++++++++ 59 files changed, 10859 insertions(+), 205 deletions(-) create mode 100644 api/credentials/credential.gen.go create mode 100644 api/credentials/option.gen.go create mode 100644 api/credentials/username_password_attributes.gen.go create mode 100644 internal/cmd/commands/credentialscmd/credentials.gen.go create mode 100644 internal/cmd/commands/credentialscmd/funcs.go create mode 100644 internal/cmd/commands/credentialscmd/username_password_credentials.gen.go create mode 100644 internal/cmd/commands/credentialscmd/username_password_funcs.go create mode 100644 internal/cmd/commands/credentialstorescmd/static_credentialstores.gen.go create mode 100644 internal/cmd/commands/credentialstorescmd/static_funcs.go create mode 100644 internal/credential/static/credential_store.go create mode 100644 internal/credential/static/credential_store_test.go create mode 100644 internal/credential/static/doc.go create mode 100644 internal/credential/static/fields.go create mode 100644 internal/credential/static/options.go create mode 100644 internal/credential/static/options_test.go create mode 100644 internal/credential/static/public_ids.go create mode 100644 internal/credential/static/repository.go create mode 100644 internal/credential/static/repository_credential.go create mode 100644 internal/credential/static/repository_credential_store.go create mode 100644 internal/credential/static/repository_credential_store_test.go create mode 100644 internal/credential/static/repository_credential_test.go create mode 100644 internal/credential/static/repository_test.go create mode 100644 internal/credential/static/store/static.pb.go create mode 100644 internal/credential/static/testing.go create mode 100644 internal/credential/static/testing_test.go create mode 100644 internal/credential/static/usernamepassword_credential.go create mode 100644 internal/credential/static/usernamepassword_credential_test.go create mode 100644 internal/daemon/controller/handlers/credentials/credential_service.go create mode 100644 internal/daemon/controller/handlers/credentials/credential_service_test.go create mode 100644 internal/gen/controller/api/services/credential_service.pb.go create mode 100644 internal/gen/controller/api/services/credential_service.pb.gw.go create mode 100644 internal/gen/controller/api/services/credential_service_grpc.pb.go create mode 100644 internal/proto/controller/api/resources/credentials/v1/credential.proto create mode 100644 internal/proto/controller/api/services/v1/credential_service.proto create mode 100644 internal/proto/controller/storage/credential/static/store/v1/static.proto create mode 100644 internal/tests/api/credentials/credentials_classification_test.go create mode 100644 internal/tests/api/credentials/credentials_test.go create mode 100644 internal/tests/cli/boundary/_credential_stores.bash create mode 100644 internal/tests/cli/boundary/_credentials.bash create mode 100644 internal/tests/cli/boundary/credential_stores.bats create mode 100644 internal/tests/cli/boundary/credentials.bats create mode 100644 sdk/pbs/controller/api/resources/credentials/credential.pb.go diff --git a/Makefile b/Makefile index 2b1a239aa..266328b8c 100644 --- a/Makefile +++ b/Makefile @@ -155,6 +155,7 @@ protobuild: @protoc-go-inject-tag -input=./internal/scheduler/job/store/job.pb.go @protoc-go-inject-tag -input=./internal/credential/store/credential.pb.go @protoc-go-inject-tag -input=./internal/credential/vault/store/vault.pb.go + @protoc-go-inject-tag -input=./internal/credential/static/store/static.pb.go @protoc-go-inject-tag -input=./internal/servers/servers.pb.go @protoc-go-inject-tag -input=./internal/kms/store/audit_key.pb.go @@ -184,6 +185,8 @@ protobuild: @protoc-go-inject-tag -input=./internal/gen/controller/api/services/credential_store_service.pb.go @protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/credentiallibraries/credential_library.pb.go @protoc-go-inject-tag -input=./internal/gen/controller/api/services/credential_library_service.pb.go + @protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/credentials/credential.pb.go + @protoc-go-inject-tag -input=./internal/gen/controller/api/services/credential_service.pb.go @protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/roles/role.pb.go @protoc-go-inject-tag -input=./internal/gen/controller/api/services/role_service.pb.go @protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/sessions/session.pb.go diff --git a/api/credentials/credential.gen.go b/api/credentials/credential.gen.go new file mode 100644 index 000000000..30bcd3657 --- /dev/null +++ b/api/credentials/credential.gen.go @@ -0,0 +1,325 @@ +// Code generated by "make api"; DO NOT EDIT. +package credentials + +import ( + "context" + "errors" + "fmt" + "net/url" + "time" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/scopes" +) + +type Credential struct { + Id string `json:"id,omitempty"` + CredentialStoreId string `json:"credential_store_id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + CreatedTime time.Time `json:"created_time,omitempty"` + UpdatedTime time.Time `json:"updated_time,omitempty"` + Version uint32 `json:"version,omitempty"` + Type string `json:"type,omitempty"` + Attributes map[string]interface{} `json:"attributes,omitempty"` + AuthorizedActions []string `json:"authorized_actions,omitempty"` + + response *api.Response +} + +type CredentialReadResult struct { + Item *Credential + response *api.Response +} + +func (n CredentialReadResult) GetItem() interface{} { + return n.Item +} + +func (n CredentialReadResult) GetResponse() *api.Response { + return n.response +} + +type CredentialCreateResult = CredentialReadResult +type CredentialUpdateResult = CredentialReadResult + +type CredentialDeleteResult struct { + response *api.Response +} + +// GetItem will always be nil for CredentialDeleteResult +func (n CredentialDeleteResult) GetItem() interface{} { + return nil +} + +func (n CredentialDeleteResult) GetResponse() *api.Response { + return n.response +} + +type CredentialListResult struct { + Items []*Credential + response *api.Response +} + +func (n CredentialListResult) GetItems() interface{} { + return n.Items +} + +func (n CredentialListResult) GetResponse() *api.Response { + return n.response +} + +// Client is a client for this collection +type Client struct { + client *api.Client +} + +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. +func NewClient(c *api.Client) *Client { + return &Client{client: c.Clone()} +} + +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client +} + +func (c *Client) Create(ctx context.Context, resourceType string, credentialStoreId string, opt ...Option) (*CredentialCreateResult, error) { + if credentialStoreId == "" { + return nil, fmt.Errorf("empty credentialStoreId value passed into Create request") + } + + opts, apiOpts := getOpts(opt...) + + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + if resourceType == "" { + return nil, fmt.Errorf("empty resourceType value passed into Create request") + } else { + opts.postMap["type"] = resourceType + } + + opts.postMap["credential_store_id"] = credentialStoreId + + req, err := c.client.NewRequest(ctx, "POST", "credentials", opts.postMap, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating Create request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during Create call: %w", err) + } + + target := new(CredentialCreateResult) + target.Item = new(Credential) + apiErr, err := resp.Decode(target.Item) + if err != nil { + return nil, fmt.Errorf("error decoding Create response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.response = resp + return target, nil +} + +func (c *Client) Read(ctx context.Context, id string, opt ...Option) (*CredentialReadResult, error) { + if id == "" { + return nil, fmt.Errorf("empty id value passed into Read request") + } + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + + req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("credentials/%s", url.PathEscape(id)), nil, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating Read request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error performing client request during Read call: %w", err) + } + + target := new(CredentialReadResult) + target.Item = new(Credential) + apiErr, err := resp.Decode(target.Item) + if err != nil { + return nil, fmt.Errorf("error decoding Read response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.response = resp + return target, nil +} + +func (c *Client) Update(ctx context.Context, id string, version uint32, opt ...Option) (*CredentialUpdateResult, error) { + if id == "" { + return nil, fmt.Errorf("empty id value passed into Update request") + } + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, errors.New("zero version number passed into Update request and automatic versioning not specified") + } + existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...) + if existingErr != nil { + if api.AsServerError(existingErr) != nil { + return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) + } + return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingTarget == nil { + return nil, errors.New("nil resource response found when performing initial check-and-set read") + } + if existingTarget.Item == nil { + return nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Item.Version + } + + opts.postMap["version"] = version + + req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("credentials/%s", url.PathEscape(id)), opts.postMap, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating Update request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during Update call: %w", err) + } + + target := new(CredentialUpdateResult) + target.Item = new(Credential) + apiErr, err := resp.Decode(target.Item) + if err != nil { + return nil, fmt.Errorf("error decoding Update response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.response = resp + return target, nil +} + +func (c *Client) Delete(ctx context.Context, id string, opt ...Option) (*CredentialDeleteResult, error) { + if id == "" { + return nil, fmt.Errorf("empty id value passed into Delete request") + } + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + + req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("credentials/%s", url.PathEscape(id)), nil, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating Delete request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during Delete call: %w", err) + } + + apiErr, err := resp.Decode(nil) + if err != nil { + return nil, fmt.Errorf("error decoding Delete response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + + target := &CredentialDeleteResult{ + response: resp, + } + return target, nil +} + +func (c *Client) List(ctx context.Context, credentialStoreId string, opt ...Option) (*CredentialListResult, error) { + if credentialStoreId == "" { + return nil, fmt.Errorf("empty credentialStoreId value passed into List request") + } + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + opts.queryMap["credential_store_id"] = credentialStoreId + + req, err := c.client.NewRequest(ctx, "GET", "credentials", nil, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating List request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during List call: %w", err) + } + + target := new(CredentialListResult) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, fmt.Errorf("error decoding List response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.response = resp + return target, nil +} diff --git a/api/credentials/option.gen.go b/api/credentials/option.gen.go new file mode 100644 index 000000000..ac95b9e24 --- /dev/null +++ b/api/credentials/option.gen.go @@ -0,0 +1,135 @@ +package credentials + +import ( + "strings" + + "github.com/hashicorp/boundary/api" +) + +// Option is a func that sets optional attributes for a call. This does not need +// to be used directly, but instead option arguments are built from the +// functions in this package. WithX options set a value to that given in the +// argument; DefaultX options indicate that the value should be set to its +// default. When an API call is made options are processed in ther order they +// appear in the function call, so for a given argument X, a succession of WithX +// or DefaultX calls will result in the last call taking effect. +type Option func(*options) + +type options struct { + postMap map[string]interface{} + queryMap map[string]string + withAutomaticVersioning bool + withSkipCurlOutput bool + withFilter string +} + +func getDefaultOptions() options { + return options{ + postMap: make(map[string]interface{}), + queryMap: make(map[string]string), + } +} + +func getOpts(opt ...Option) (options, []api.Option) { + opts := getDefaultOptions() + for _, o := range opt { + if o != nil { + o(&opts) + } + } + var apiOpts []api.Option + if opts.withSkipCurlOutput { + apiOpts = append(apiOpts, api.WithSkipCurlOutput(true)) + } + if opts.withFilter != "" { + opts.queryMap["filter"] = opts.withFilter + } + return opts, apiOpts +} + +// If set, and if the version is zero during an update, the API will perform a +// fetch to get the current version of the resource and populate it during the +// update call. This is convenient but opens up the possibility for subtle +// order-of-modification issues, so use carefully. +func WithAutomaticVersioning(enable bool) Option { + return func(o *options) { + o.withAutomaticVersioning = enable + } +} + +// WithSkipCurlOutput tells the API to not use the current call for cURL output. +// Useful for when we need to look up versions. +func WithSkipCurlOutput(skip bool) Option { + return func(o *options) { + o.withSkipCurlOutput = true + } +} + +// WithFilter tells the API to filter the items returned using the provided +// filter term. The filter should be in a format supported by +// hashicorp/go-bexpr. +func WithFilter(filter string) Option { + return func(o *options) { + o.withFilter = strings.TrimSpace(filter) + } +} + +func WithAttributes(inAttributes map[string]interface{}) Option { + return func(o *options) { + o.postMap["attributes"] = inAttributes + } +} + +func DefaultAttributes() Option { + return func(o *options) { + o.postMap["attributes"] = nil + } +} + +func WithDescription(inDescription string) Option { + return func(o *options) { + o.postMap["description"] = inDescription + } +} + +func DefaultDescription() Option { + return func(o *options) { + o.postMap["description"] = nil + } +} + +func WithName(inName string) Option { + return func(o *options) { + o.postMap["name"] = inName + } +} + +func DefaultName() Option { + return func(o *options) { + o.postMap["name"] = nil + } +} + +func WithUsernamePasswordCredentialPassword(inPassword string) Option { + return func(o *options) { + raw, ok := o.postMap["attributes"] + if !ok { + raw = interface{}(map[string]interface{}{}) + } + val := raw.(map[string]interface{}) + val["password"] = inPassword + o.postMap["attributes"] = val + } +} + +func WithUsernamePasswordCredentialUsername(inUsername string) Option { + return func(o *options) { + raw, ok := o.postMap["attributes"] + if !ok { + raw = interface{}(map[string]interface{}{}) + } + val := raw.(map[string]interface{}) + val["username"] = inUsername + o.postMap["attributes"] = val + } +} diff --git a/api/credentials/username_password_attributes.gen.go b/api/credentials/username_password_attributes.gen.go new file mode 100644 index 000000000..e0923d0fc --- /dev/null +++ b/api/credentials/username_password_attributes.gen.go @@ -0,0 +1,8 @@ +// Code generated by "make api"; DO NOT EDIT. +package credentials + +type UsernamePasswordAttributes struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + PasswordHmac string `json:"password_hmac,omitempty"` +} diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index 7db6a6d5d..0ae65b2e7 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authmethods" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authtokens" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentialstores" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/groups" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" @@ -435,15 +436,24 @@ var inputStructs = []*structInfo{ versionEnabled: true, createResponseTypes: true, }, - // Credentials { - inProto: &credentialstores.VaultCredentialStoreAttributes{}, - outFile: "credentialstores/vault_credential_store_attributes.gen.go", - subtypeName: "VaultCredentialStore", + inProto: &credentials.UsernamePasswordAttributes{}, + outFile: "credentials/username_password_attributes.gen.go", + subtypeName: "UsernamePasswordCredential", + fieldOverrides: []fieldInfo{ + { + Name: "Username", + SkipDefault: true, + }, + { + Name: "Password", + SkipDefault: true, + }, + }, }, { - inProto: &credentialstores.CredentialStore{}, - outFile: "credentialstores/credential_store.gen.go", + inProto: &credentials.Credential{}, + outFile: "credentials/credential.gen.go", templates: []*template.Template{ clientTemplate, createTemplate, @@ -452,50 +462,13 @@ var inputStructs = []*structInfo{ deleteTemplate, listTemplate, }, - pluralResourceName: "credential-stores", - parentTypeName: "scope", - typeOnCreate: true, - versionEnabled: true, - createResponseTypes: true, - recursiveListing: true, - fieldOverrides: []fieldInfo{ - { - Name: "Address", - SkipDefault: true, - }, - { - Name: "Token", - SkipDefault: true, - }, - }, - }, - { - inProto: &credentiallibraries.VaultCredentialLibraryAttributes{}, - outFile: "credentiallibraries/vault_credential_library_attributes.gen.go", - subtypeName: "VaultCredentialLibrary", - fieldOverrides: []fieldInfo{ - { - Name: "Path", - SkipDefault: true, - }, - }, - }, - { - inProto: &credentiallibraries.CredentialLibrary{}, - outFile: "credentiallibraries/credential_library.gen.go", - templates: []*template.Template{ - clientTemplate, - createTemplate, - readTemplate, - updateTemplate, - deleteTemplate, - listTemplate, - }, - pluralResourceName: "credential-libraries", + pluralResourceName: "credentials", parentTypeName: "credential-store", versionEnabled: true, createResponseTypes: true, + typeOnCreate: true, }, + // Host related resources { inProto: &hostcatalogs.HostCatalog{}, diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index eb54312c5..0a78ba796 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/boundary/internal/cmd/commands/config" "github.com/hashicorp/boundary/internal/cmd/commands/connect" "github.com/hashicorp/boundary/internal/cmd/commands/credentiallibrariescmd" + "github.com/hashicorp/boundary/internal/cmd/commands/credentialscmd" "github.com/hashicorp/boundary/internal/cmd/commands/credentialstorescmd" "github.com/hashicorp/boundary/internal/cmd/commands/database" "github.com/hashicorp/boundary/internal/cmd/commands/dev" @@ -410,6 +411,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "create", }, nil }, + "credential-stores create static": func() (cli.Command, error) { + return &credentialstorescmd.StaticCommand{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, "credential-stores update": func() (cli.Command, error) { return &credentialstorescmd.Command{ Command: base.NewCommand(ui), @@ -422,6 +429,60 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "update", }, nil }, + "credential-stores update static": func() (cli.Command, error) { + return &credentialstorescmd.StaticCommand{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + + "credentials": func() (cli.Command, error) { + return &credentialscmd.Command{ + Command: base.NewCommand(ui), + }, nil + }, + "credentials read": func() (cli.Command, error) { + return &credentialscmd.Command{ + Command: base.NewCommand(ui), + Func: "read", + }, nil + }, + "credentials delete": func() (cli.Command, error) { + return &credentialscmd.Command{ + Command: base.NewCommand(ui), + Func: "delete", + }, nil + }, + "credentials list": func() (cli.Command, error) { + return &credentialscmd.Command{ + Command: base.NewCommand(ui), + Func: "list", + }, nil + }, + "credentials create": func() (cli.Command, error) { + return &credentialscmd.Command{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "credentials create username-password": func() (cli.Command, error) { + return &credentialscmd.UsernamePasswordCommand{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "credentials update": func() (cli.Command, error) { + return &credentialscmd.Command{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + "credentials update username-password": func() (cli.Command, error) { + return &credentialscmd.UsernamePasswordCommand{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, "groups": func() (cli.Command, error) { return &groupscmd.Command{ diff --git a/internal/cmd/commands/credentialscmd/credentials.gen.go b/internal/cmd/commands/credentialscmd/credentials.gen.go new file mode 100644 index 000000000..7726a2e80 --- /dev/null +++ b/internal/cmd/commands/credentialscmd/credentials.gen.go @@ -0,0 +1,273 @@ +// Code generated by "make cli"; DO NOT EDIT. +package credentialscmd + +import ( + "errors" + "fmt" + "sync" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/credentials" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +func initFlags() { + flagsOnce.Do(func() { + extraFlags := extraActionsFlagsMapFunc() + for k, v := range extraFlags { + flagsMap[k] = append(flagsMap[k], v...) + } + }) +} + +var ( + _ cli.Command = (*Command)(nil) + _ cli.CommandAutocomplete = (*Command)(nil) +) + +type Command struct { + *base.Command + + Func string + + plural string +} + +func (c *Command) AutocompleteArgs() complete.Predictor { + initFlags() + return complete.PredictAnything +} + +func (c *Command) AutocompleteFlags() complete.Flags { + initFlags() + return c.Flags().Completions() +} + +func (c *Command) Synopsis() string { + if extra := extraSynopsisFunc(c); extra != "" { + return extra + } + + synopsisStr := "credential" + + return common.SynopsisFunc(c.Func, synopsisStr) +} + +func (c *Command) Help() string { + initFlags() + + var helpStr string + helpMap := common.HelpMap("credential") + + switch c.Func { + + case "read": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + case "delete": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + case "list": + helpStr = helpMap[c.Func]() + c.Flags().Help() + + default: + + helpStr = c.extraHelpFunc(helpMap) + + } + + // Keep linter from complaining if we don't actually generate code using it + _ = helpMap + return helpStr +} + +var flagsMap = map[string][]string{ + + "read": {"id"}, + + "delete": {"id"}, + + "list": {"credential-store-id", "filter"}, +} + +func (c *Command) Flags() *base.FlagSets { + if len(flagsMap[c.Func]) == 0 { + return c.FlagSet(base.FlagSetNone) + } + + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + common.PopulateCommonFlags(c.Command, f, "credential", flagsMap, c.Func) + + extraFlagsFunc(c, set, f) + + return set +} + +func (c *Command) Run(args []string) int { + initFlags() + + switch c.Func { + case "": + return cli.RunResultHelp + + case "create", "update": + return cli.RunResultHelp + + } + + c.plural = "credential" + switch c.Func { + case "list": + c.plural = "credentials" + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + + if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" { + c.PrintCliError(errors.New("ID is required but not passed in via -id")) + return base.CommandUserError + } + + var opts []credentials.Option + + if strutil.StrListContains(flagsMap[c.Func], "credential-store-id") { + switch c.Func { + + case "list": + if c.FlagCredentialStoreId == "" { + c.PrintCliError(errors.New("CredentialStore ID must be passed in via -credential-store-id or BOUNDARY_CREDENTIAL_STORE_ID")) + return base.CommandUserError + } + + } + } + + client, err := c.Client() + if c.WrapperCleanupFunc != nil { + defer func() { + if err := c.WrapperCleanupFunc(); err != nil { + c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err)) + } + }() + } + if err != nil { + c.PrintCliError(fmt.Errorf("Error creating API client: %w", err)) + return base.CommandCliError + } + credentialsClient := credentials.NewClient(client) + + if c.FlagFilter != "" { + opts = append(opts, credentials.WithFilter(c.FlagFilter)) + } + + var version uint32 + + if ok := extraFlagsHandlingFunc(c, f, &opts); !ok { + return base.CommandUserError + } + + var result api.GenericResult + + var listResult api.GenericListResult + + switch c.Func { + + case "read": + result, err = credentialsClient.Read(c.Context, c.FlagId, opts...) + + case "delete": + result, err = credentialsClient.Delete(c.Context, c.FlagId, opts...) + + case "list": + listResult, err = credentialsClient.List(c.Context, c.FlagCredentialStoreId, opts...) + + } + + result, err = executeExtraActions(c, result, err, credentialsClient, version, opts) + + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil { + var opts []base.Option + + c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...) + return base.CommandApiError + } + c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error())) + return base.CommandCliError + } + + output, err := printCustomActionOutput(c) + if err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + if output { + return base.CommandSuccess + } + + switch c.Func { + + case "delete": + switch base.Format(c.UI) { + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + + case "table": + c.UI.Output("The delete operation completed successfully.") + } + + return base.CommandSuccess + + case "list": + switch base.Format(c.UI) { + case "json": + if ok := c.PrintJsonItems(listResult); !ok { + return base.CommandCliError + } + + case "table": + listedItems := listResult.GetItems().([]*credentials.Credential) + c.UI.Output(c.printListTable(listedItems)) + } + + return base.CommandSuccess + + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printItemTable(result)) + + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + } + + return base.CommandSuccess +} + +var ( + flagsOnce = new(sync.Once) + + extraActionsFlagsMapFunc = func() map[string][]string { return nil } + extraSynopsisFunc = func(*Command) string { return "" } + extraFlagsFunc = func(*Command, *base.FlagSets, *base.FlagSet) {} + extraFlagsHandlingFunc = func(*Command, *base.FlagSets, *[]credentials.Option) bool { return true } + executeExtraActions = func(_ *Command, inResult api.GenericResult, inErr error, _ *credentials.Client, _ uint32, _ []credentials.Option) (api.GenericResult, error) { + return inResult, inErr + } + printCustomActionOutput = func(*Command) (bool, error) { return false, nil } +) diff --git a/internal/cmd/commands/credentialscmd/funcs.go b/internal/cmd/commands/credentialscmd/funcs.go new file mode 100644 index 000000000..ca3bd25f4 --- /dev/null +++ b/internal/cmd/commands/credentialscmd/funcs.go @@ -0,0 +1,177 @@ +package credentialscmd + +import ( + "fmt" + "time" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/credentials" + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func (c *Command) extraHelpFunc(helpMap map[string]func() string) string { + var helpStr string + switch c.Func { + case "": + return base.WrapForHelpText([]string{ + "Usage: boundary credentials [sub command] [options] [args]", + "", + " This command allows operations on Boundary credential resources. Example:", + "", + " Read a credential:", + "", + ` $ boundary credentials read -id cred_1234567890`, + "", + " Please see the credentials subcommand help for detailed usage information.", + }) + case "create": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary credentials create [type] [sub command] [options] [args]", + "", + " This command allows create operations on Boundary credential resources. Example:", + "", + " Create a credential:", + "", + ` $ boundary credentials create username-password -credential-store-id cs_1234567890 -username user -password pass`, + "", + " Please see the typed subcommand help for detailed usage information.", + }) + case "update": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary credentials update [type] [sub command] [options] [args]", + "", + " This command allows update operations on Boundary credential resources. Example:", + "", + " Update a user password credential:", + "", + ` $ boundary credentials update username-password -id cred_1234567890 -name devops -description "For DevOps usage"`, + "", + " Please see the typed subcommand help for detailed usage information.", + }) + default: + helpStr = helpMap["base"]() + } + return helpStr + c.Flags().Help() +} + +func (c *Command) printListTable(items []*credentials.Credential) string { + if len(items) == 0 { + return "No credentials found" + } + + var output []string + output = []string{ + "", + "Credential information:", + } + for i, m := range items { + if i > 0 { + output = append(output, "") + } + if m.Id != "" { + output = append(output, + fmt.Sprintf(" ID: %s", m.Id), + ) + } else { + output = append(output, + fmt.Sprintf(" ID: %s", "(not available)"), + ) + } + if m.Version > 0 { + output = append(output, + fmt.Sprintf(" Version: %d", m.Version), + ) + } + if m.Type != "" { + output = append(output, + fmt.Sprintf(" Type: %s", m.Type), + ) + } + if m.Name != "" { + output = append(output, + fmt.Sprintf(" Name: %s", m.Name), + ) + } + if m.Description != "" { + output = append(output, + fmt.Sprintf(" Description: %s", m.Description), + ) + } + if len(m.AuthorizedActions) > 0 { + output = append(output, + " Authorized Actions:", + base.WrapSlice(6, m.AuthorizedActions), + ) + } + } + + return base.WrapForHelpText(output) +} + +func printItemTable(result api.GenericResult) string { + item := result.GetItem().(*credentials.Credential) + nonAttributeMap := map[string]interface{}{} + if item.Id != "" { + nonAttributeMap["ID"] = item.Id + } + if item.Version != 0 { + nonAttributeMap["Version"] = item.Version + } + if !item.CreatedTime.IsZero() { + nonAttributeMap["Created Time"] = item.CreatedTime.Local().Format(time.RFC1123) + } + if !item.UpdatedTime.IsZero() { + nonAttributeMap["Updated Time"] = item.UpdatedTime.Local().Format(time.RFC1123) + } + if item.CredentialStoreId != "" { + nonAttributeMap["Credential Store ID"] = item.CredentialStoreId + } + if item.Name != "" { + nonAttributeMap["Name"] = item.Name + } + if item.Description != "" { + nonAttributeMap["Description"] = item.Description + } + if item.Type != "" { + nonAttributeMap["Type"] = item.Type + } + + maxLength := base.MaxAttributesLength(nonAttributeMap, item.Attributes, keySubstMap) + + ret := []string{ + "", + "Credential information:", + base.WrapMap(2, maxLength+2, nonAttributeMap), + } + + if item.Scope != nil { + ret = append(ret, + "", + " Scope:", + base.ScopeInfoForOutput(item.Scope, maxLength), + ) + } + + if len(item.AuthorizedActions) > 0 { + ret = append(ret, + "", + " Authorized Actions:", + base.WrapSlice(4, item.AuthorizedActions), + ) + } + + if len(item.Attributes) > 0 { + ret = append(ret, + "", + " Attributes:", + base.WrapMap(4, maxLength, item.Attributes), + ) + } + + return base.WrapForHelpText(ret) +} + +var keySubstMap = map[string]string{ + "username": "Username", + "password_hmac": "Password HMAC", +} diff --git a/internal/cmd/commands/credentialscmd/username_password_credentials.gen.go b/internal/cmd/commands/credentialscmd/username_password_credentials.gen.go new file mode 100644 index 000000000..a9c7833d7 --- /dev/null +++ b/internal/cmd/commands/credentialscmd/username_password_credentials.gen.go @@ -0,0 +1,258 @@ +// Code generated by "make cli"; DO NOT EDIT. +package credentialscmd + +import ( + "errors" + "fmt" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/credentials" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +func initUsernamePasswordFlags() { + flagsOnce.Do(func() { + extraFlags := extraUsernamePasswordActionsFlagsMapFunc() + for k, v := range extraFlags { + flagsUsernamePasswordMap[k] = append(flagsUsernamePasswordMap[k], v...) + } + }) +} + +var ( + _ cli.Command = (*UsernamePasswordCommand)(nil) + _ cli.CommandAutocomplete = (*UsernamePasswordCommand)(nil) +) + +type UsernamePasswordCommand struct { + *base.Command + + Func string + + plural string + + extraUsernamePasswordCmdVars +} + +func (c *UsernamePasswordCommand) AutocompleteArgs() complete.Predictor { + initUsernamePasswordFlags() + return complete.PredictAnything +} + +func (c *UsernamePasswordCommand) AutocompleteFlags() complete.Flags { + initUsernamePasswordFlags() + return c.Flags().Completions() +} + +func (c *UsernamePasswordCommand) Synopsis() string { + if extra := extraUsernamePasswordSynopsisFunc(c); extra != "" { + return extra + } + + synopsisStr := "credential" + + synopsisStr = fmt.Sprintf("%s %s", "username_password-type", synopsisStr) + + return common.SynopsisFunc(c.Func, synopsisStr) +} + +func (c *UsernamePasswordCommand) Help() string { + initUsernamePasswordFlags() + + var helpStr string + helpMap := common.HelpMap("credential") + + switch c.Func { + + default: + + helpStr = c.extraUsernamePasswordHelpFunc(helpMap) + + } + + // Keep linter from complaining if we don't actually generate code using it + _ = helpMap + return helpStr +} + +var flagsUsernamePasswordMap = map[string][]string{ + + "create": {"credential-store-id", "name", "description"}, + + "update": {"id", "name", "description", "version"}, +} + +func (c *UsernamePasswordCommand) Flags() *base.FlagSets { + if len(flagsUsernamePasswordMap[c.Func]) == 0 { + return c.FlagSet(base.FlagSetNone) + } + + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + common.PopulateCommonFlags(c.Command, f, "username_password-type credential", flagsUsernamePasswordMap, c.Func) + + extraUsernamePasswordFlagsFunc(c, set, f) + + return set +} + +func (c *UsernamePasswordCommand) Run(args []string) int { + initUsernamePasswordFlags() + + switch c.Func { + case "": + return cli.RunResultHelp + + } + + c.plural = "username_password-type credential" + switch c.Func { + case "list": + c.plural = "username_password-type credentials" + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + + if strutil.StrListContains(flagsUsernamePasswordMap[c.Func], "id") && c.FlagId == "" { + c.PrintCliError(errors.New("ID is required but not passed in via -id")) + return base.CommandUserError + } + + var opts []credentials.Option + + if strutil.StrListContains(flagsUsernamePasswordMap[c.Func], "credential-store-id") { + switch c.Func { + + case "create": + if c.FlagCredentialStoreId == "" { + c.PrintCliError(errors.New("CredentialStore ID must be passed in via -credential-store-id or BOUNDARY_CREDENTIAL_STORE_ID")) + return base.CommandUserError + } + + } + } + + client, err := c.Client() + if c.WrapperCleanupFunc != nil { + defer func() { + if err := c.WrapperCleanupFunc(); err != nil { + c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err)) + } + }() + } + if err != nil { + c.PrintCliError(fmt.Errorf("Error creating API client: %w", err)) + return base.CommandCliError + } + credentialsClient := credentials.NewClient(client) + + switch c.FlagName { + case "": + case "null": + opts = append(opts, credentials.DefaultName()) + default: + opts = append(opts, credentials.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, credentials.DefaultDescription()) + default: + opts = append(opts, credentials.WithDescription(c.FlagDescription)) + } + + if c.FlagFilter != "" { + opts = append(opts, credentials.WithFilter(c.FlagFilter)) + } + + var version uint32 + + switch c.Func { + + case "update": + switch c.FlagVersion { + case 0: + opts = append(opts, credentials.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + + } + + if ok := extraUsernamePasswordFlagsHandlingFunc(c, f, &opts); !ok { + return base.CommandUserError + } + + var result api.GenericResult + + switch c.Func { + + case "create": + result, err = credentialsClient.Create(c.Context, "username_password", c.FlagCredentialStoreId, opts...) + + case "update": + result, err = credentialsClient.Update(c.Context, c.FlagId, version, opts...) + + } + + result, err = executeExtraUsernamePasswordActions(c, result, err, credentialsClient, version, opts) + + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil { + var opts []base.Option + + opts = append(opts, base.WithAttributeFieldPrefix("username_password")) + + c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...) + return base.CommandApiError + } + c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error())) + return base.CommandCliError + } + + output, err := printCustomUsernamePasswordActionOutput(c) + if err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + if output { + return base.CommandSuccess + } + + switch c.Func { + + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printItemTable(result)) + + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + } + + return base.CommandSuccess +} + +var ( + extraUsernamePasswordActionsFlagsMapFunc = func() map[string][]string { return nil } + extraUsernamePasswordSynopsisFunc = func(*UsernamePasswordCommand) string { return "" } + extraUsernamePasswordFlagsFunc = func(*UsernamePasswordCommand, *base.FlagSets, *base.FlagSet) {} + extraUsernamePasswordFlagsHandlingFunc = func(*UsernamePasswordCommand, *base.FlagSets, *[]credentials.Option) bool { return true } + executeExtraUsernamePasswordActions = func(_ *UsernamePasswordCommand, inResult api.GenericResult, inErr error, _ *credentials.Client, _ uint32, _ []credentials.Option) (api.GenericResult, error) { + return inResult, inErr + } + printCustomUsernamePasswordActionOutput = func(*UsernamePasswordCommand) (bool, error) { return false, nil } +) diff --git a/internal/cmd/commands/credentialscmd/username_password_funcs.go b/internal/cmd/commands/credentialscmd/username_password_funcs.go new file mode 100644 index 000000000..a950e4a9d --- /dev/null +++ b/internal/cmd/commands/credentialscmd/username_password_funcs.go @@ -0,0 +1,97 @@ +package credentialscmd + +import ( + "github.com/hashicorp/boundary/api/credentials" + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func init() { + extraUsernamePasswordFlagsFunc = extraUsernamePasswordFlagsFuncImpl + extraUsernamePasswordActionsFlagsMapFunc = extraUsernamePasswordActionsFlagsMapFuncImpl + extraUsernamePasswordFlagsHandlingFunc = extraUsernamePasswordFlagHandlingFuncImpl +} + +const ( + usernameFlagName = "username" + passwordFlagName = "password" +) + +type extraUsernamePasswordCmdVars struct { + flagUsername string + flagPassword string +} + +func extraUsernamePasswordActionsFlagsMapFuncImpl() map[string][]string { + flags := map[string][]string{ + "create": { + usernameFlagName, + passwordFlagName, + }, + } + flags["update"] = flags["create"] + return flags +} + +func extraUsernamePasswordFlagsFuncImpl(c *UsernamePasswordCommand, set *base.FlagSets, _ *base.FlagSet) { + f := set.NewFlagSet("User Password Credential Options") + + for _, name := range flagsUsernamePasswordMap[c.Func] { + switch name { + case usernameFlagName: + f.StringVar(&base.StringVar{ + Name: usernameFlagName, + Target: &c.flagUsername, + Usage: "The username associated with the credential.", + }) + case passwordFlagName: + f.StringVar(&base.StringVar{ + Name: passwordFlagName, + Target: &c.flagPassword, + Usage: "The password associated with the credential.", + }) + } + } +} + +func extraUsernamePasswordFlagHandlingFuncImpl(c *UsernamePasswordCommand, _ *base.FlagSets, opts *[]credentials.Option) bool { + switch c.flagUsername { + case "": + default: + *opts = append(*opts, credentials.WithUsernamePasswordCredentialUsername(c.flagUsername)) + } + switch c.flagPassword { + case "": + default: + *opts = append(*opts, credentials.WithUsernamePasswordCredentialPassword(c.flagPassword)) + } + + return true +} + +func (c *UsernamePasswordCommand) extraUsernamePasswordHelpFunc(_ map[string]func() string) string { + var helpStr string + switch c.Func { + case "create": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary credentials create username-password -credential-store-id [options] [args]", + "", + " Create a user password credential. Example:", + "", + ` $ boundary credentials create username-password -credential-store-id csvlt_1234567890 -username user -password pass`, + "", + "", + }) + + case "update": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary credentials update user password [options] [args]", + "", + " Update a user password credential given its ID. Example:", + "", + ` $ boundary credentials update username-password -id clvlt_1234567890 -name devops -description "For DevOps usage"`, + "", + "", + }) + } + return helpStr + c.Flags().Help() +} diff --git a/internal/cmd/commands/credentialstorescmd/funcs.go b/internal/cmd/commands/credentialstorescmd/funcs.go index 94772e536..a0ed898f4 100644 --- a/internal/cmd/commands/credentialstorescmd/funcs.go +++ b/internal/cmd/commands/credentialstorescmd/funcs.go @@ -33,7 +33,11 @@ func (c *Command) extraHelpFunc(helpMap map[string]func() string) string { "", " Create a vault-type credential store:", "", - ` $ boundary credential-stores create vault -vault-address "http://localhost:8200" -vault-token "s.s0m3t0k3n"`, + ` $ boundary credential-stores create vault -scope-id p_1234567890 -vault-address "http://localhost:8200" -vault-token "s.s0m3t0k3n"`, + "", + " Create a static-type credential store:", + "", + ` $ boundary credential-stores create static -scope-id p_1234567890`, "", " Please see the typed subcommand help for detailed usage information.", }) @@ -47,6 +51,10 @@ func (c *Command) extraHelpFunc(helpMap map[string]func() string) string { "", ` $ boundary credential-stores update vault -id csvlt_1234567890 -name devops -description "For DevOps usage"`, "", + " Update a static-type credential store:", + "", + ` $ boundary credential-stores update static -id cs_1234567890 -name devops -description "For DevOps usage"`, + "", " Please see the typed subcommand help for detailed usage information.", }) default: diff --git a/internal/cmd/commands/credentialstorescmd/static_credentialstores.gen.go b/internal/cmd/commands/credentialstorescmd/static_credentialstores.gen.go new file mode 100644 index 000000000..cb65d9545 --- /dev/null +++ b/internal/cmd/commands/credentialstorescmd/static_credentialstores.gen.go @@ -0,0 +1,261 @@ +// Code generated by "make cli"; DO NOT EDIT. +package credentialstorescmd + +import ( + "errors" + "fmt" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/credentialstores" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +func initStaticFlags() { + flagsOnce.Do(func() { + extraFlags := extraStaticActionsFlagsMapFunc() + for k, v := range extraFlags { + flagsStaticMap[k] = append(flagsStaticMap[k], v...) + } + }) +} + +var ( + _ cli.Command = (*StaticCommand)(nil) + _ cli.CommandAutocomplete = (*StaticCommand)(nil) +) + +type StaticCommand struct { + *base.Command + + Func string + + plural string +} + +func (c *StaticCommand) AutocompleteArgs() complete.Predictor { + initStaticFlags() + return complete.PredictAnything +} + +func (c *StaticCommand) AutocompleteFlags() complete.Flags { + initStaticFlags() + return c.Flags().Completions() +} + +func (c *StaticCommand) Synopsis() string { + if extra := extraStaticSynopsisFunc(c); extra != "" { + return extra + } + + synopsisStr := "credential store" + + synopsisStr = fmt.Sprintf("%s %s", "static-type", synopsisStr) + + return common.SynopsisFunc(c.Func, synopsisStr) +} + +func (c *StaticCommand) Help() string { + initStaticFlags() + + var helpStr string + helpMap := common.HelpMap("credential store") + + switch c.Func { + + default: + + helpStr = c.extraStaticHelpFunc(helpMap) + + } + + // Keep linter from complaining if we don't actually generate code using it + _ = helpMap + return helpStr +} + +var flagsStaticMap = map[string][]string{ + + "create": {"scope-id", "name", "description"}, + + "update": {"id", "name", "description", "version"}, +} + +func (c *StaticCommand) Flags() *base.FlagSets { + if len(flagsStaticMap[c.Func]) == 0 { + return c.FlagSet(base.FlagSetNone) + } + + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + common.PopulateCommonFlags(c.Command, f, "static-type credential store", flagsStaticMap, c.Func) + + extraStaticFlagsFunc(c, set, f) + + return set +} + +func (c *StaticCommand) Run(args []string) int { + initStaticFlags() + + switch c.Func { + case "": + return cli.RunResultHelp + + } + + c.plural = "static-type credential store" + switch c.Func { + case "list": + c.plural = "static-type credential stores" + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + + if strutil.StrListContains(flagsStaticMap[c.Func], "id") && c.FlagId == "" { + c.PrintCliError(errors.New("ID is required but not passed in via -id")) + return base.CommandUserError + } + + var opts []credentialstores.Option + + if strutil.StrListContains(flagsStaticMap[c.Func], "scope-id") { + switch c.Func { + + case "create": + if c.FlagScopeId == "" { + c.PrintCliError(errors.New("Scope ID must be passed in via -scope-id or BOUNDARY_SCOPE_ID")) + return base.CommandUserError + } + + } + } + + client, err := c.Client() + if c.WrapperCleanupFunc != nil { + defer func() { + if err := c.WrapperCleanupFunc(); err != nil { + c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err)) + } + }() + } + if err != nil { + c.PrintCliError(fmt.Errorf("Error creating API client: %w", err)) + return base.CommandCliError + } + credentialstoresClient := credentialstores.NewClient(client) + + switch c.FlagName { + case "": + case "null": + opts = append(opts, credentialstores.DefaultName()) + default: + opts = append(opts, credentialstores.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, credentialstores.DefaultDescription()) + default: + opts = append(opts, credentialstores.WithDescription(c.FlagDescription)) + } + + switch c.FlagRecursive { + case true: + opts = append(opts, credentialstores.WithRecursive(true)) + } + + if c.FlagFilter != "" { + opts = append(opts, credentialstores.WithFilter(c.FlagFilter)) + } + + var version uint32 + + switch c.Func { + + case "update": + switch c.FlagVersion { + case 0: + opts = append(opts, credentialstores.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + + } + + if ok := extraStaticFlagsHandlingFunc(c, f, &opts); !ok { + return base.CommandUserError + } + + var result api.GenericResult + + switch c.Func { + + case "create": + result, err = credentialstoresClient.Create(c.Context, "static", c.FlagScopeId, opts...) + + case "update": + result, err = credentialstoresClient.Update(c.Context, c.FlagId, version, opts...) + + } + + result, err = executeExtraStaticActions(c, result, err, credentialstoresClient, version, opts) + + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil { + var opts []base.Option + + opts = append(opts, base.WithAttributeFieldPrefix("static")) + + c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural), opts...) + return base.CommandApiError + } + c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error())) + return base.CommandCliError + } + + output, err := printCustomStaticActionOutput(c) + if err != nil { + c.PrintCliError(err) + return base.CommandUserError + } + if output { + return base.CommandSuccess + } + + switch c.Func { + + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printItemTable(result)) + + case "json": + if ok := c.PrintJsonItem(result); !ok { + return base.CommandCliError + } + } + + return base.CommandSuccess +} + +var ( + extraStaticActionsFlagsMapFunc = func() map[string][]string { return nil } + extraStaticSynopsisFunc = func(*StaticCommand) string { return "" } + extraStaticFlagsFunc = func(*StaticCommand, *base.FlagSets, *base.FlagSet) {} + extraStaticFlagsHandlingFunc = func(*StaticCommand, *base.FlagSets, *[]credentialstores.Option) bool { return true } + executeExtraStaticActions = func(_ *StaticCommand, inResult api.GenericResult, inErr error, _ *credentialstores.Client, _ uint32, _ []credentialstores.Option) (api.GenericResult, error) { + return inResult, inErr + } + printCustomStaticActionOutput = func(*StaticCommand) (bool, error) { return false, nil } +) diff --git a/internal/cmd/commands/credentialstorescmd/static_funcs.go b/internal/cmd/commands/credentialstorescmd/static_funcs.go new file mode 100644 index 000000000..4632de401 --- /dev/null +++ b/internal/cmd/commands/credentialstorescmd/static_funcs.go @@ -0,0 +1,33 @@ +package credentialstorescmd + +import ( + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func (c *StaticCommand) extraStaticHelpFunc(helpMap map[string]func() string) string { + var helpStr string + switch c.Func { + case "create": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary credential-stores create static [options] [args]", + "", + " Create a static-type credential store. Example:", + "", + ` $ boundary credential-stores create static -scope-id p_1234567890`, + "", + "", + }) + + case "update": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary credential-stores update static [options] [args]", + "", + " Update a static-type credential store given its ID. Example:", + "", + ` $ boundary credential-stores update static -name devops -description "For DevOps usage"`, + "", + "", + }) + } + return helpStr + c.Flags().Help() +} diff --git a/internal/cmd/gencli/input.go b/internal/cmd/gencli/input.go index 6772eea7d..519613496 100644 --- a/internal/cmd/gencli/input.go +++ b/internal/cmd/gencli/input.go @@ -200,6 +200,21 @@ var inputStructs = map[string][]*cmdInfo{ NeedsSubtypeInCreate: true, PrefixAttributeFieldErrorsWithSubactionPrefix: true, }, + { + ResourceType: resource.CredentialStore.String(), + Pkg: "credentialstores", + StdActions: []string{"create", "update"}, + SubActionPrefix: "static", + SkipNormalHelp: true, + HasExtraHelpFunc: true, + HasId: true, + HasName: true, + HasDescription: true, + Container: "Scope", + VersionedActions: []string{"update"}, + NeedsSubtypeInCreate: true, + PrefixAttributeFieldErrorsWithSubactionPrefix: true, + }, }, "credentiallibraries": { { @@ -227,6 +242,33 @@ var inputStructs = map[string][]*cmdInfo{ PrefixAttributeFieldErrorsWithSubactionPrefix: true, }, }, + "credentials": { + { + ResourceType: resource.Credential.String(), + Pkg: "credentials", + StdActions: []string{"read", "delete", "list"}, + IsAbstractType: true, + HasExtraHelpFunc: true, + Container: "CredentialStore", + HasId: true, + }, + { + ResourceType: resource.Credential.String(), + Pkg: "credentials", + StdActions: []string{"create", "update"}, + SubActionPrefix: "username_password", + HasExtraCommandVars: true, + SkipNormalHelp: true, + HasExtraHelpFunc: true, + HasId: true, + HasName: true, + HasDescription: true, + Container: "CredentialStore", + VersionedActions: []string{"update"}, + NeedsSubtypeInCreate: true, + PrefixAttributeFieldErrorsWithSubactionPrefix: true, + }, + }, "groups": { { ResourceType: resource.Group.String(), diff --git a/internal/credential/credential.go b/internal/credential/credential.go index 33e46cb0b..e085d6c47 100644 --- a/internal/credential/credential.go +++ b/internal/credential/credential.go @@ -86,6 +86,12 @@ type Dynamic interface { Purpose() Purpose } +// Static is a static credential that is stored directly in a credential store. +type Static interface { + boundary.Resource + GetStoreId() string +} + // A Request represents a request for a credential from the SourceId for // the given purpose. For dynamic credentials, the SourceId is the PublicId // of a credential library. @@ -118,8 +124,8 @@ type Password string // PrivateKey represents a secret private key. type PrivateKey []byte -// UserPassword is a credential containing a username and a password. -type UserPassword interface { +// UsernamePassword is a credential containing a username and a password. +type UsernamePassword interface { Credential Username() string Password() Password diff --git a/internal/credential/static/credential_store.go b/internal/credential/static/credential_store.go new file mode 100644 index 000000000..e1875cea6 --- /dev/null +++ b/internal/credential/static/credential_store.go @@ -0,0 +1,65 @@ +package static + +import ( + "github.com/hashicorp/boundary/internal/credential/static/store" + "github.com/hashicorp/boundary/internal/oplog" + "google.golang.org/protobuf/proto" +) + +// A CredentialStore contains credentials. It is owned by a scope. +type CredentialStore struct { + *store.CredentialStore + tableName string `gorm:"-"` +} + +// NewCredentialStore creates a new in memory static CredentialStore assigned to scopeId. +// Name and description are the only valid options. All other options are ignored. +func NewCredentialStore(scopeId string, opt ...Option) (*CredentialStore, error) { + opts := getOpts(opt...) + cs := &CredentialStore{ + CredentialStore: &store.CredentialStore{ + ScopeId: scopeId, + Name: opts.withName, + Description: opts.withDescription, + }, + } + return cs, nil +} + +func allocCredentialStore() *CredentialStore { + return &CredentialStore{ + CredentialStore: &store.CredentialStore{}, + } +} + +func (cs *CredentialStore) clone() *CredentialStore { + cp := proto.Clone(cs.CredentialStore) + return &CredentialStore{ + CredentialStore: cp.(*store.CredentialStore), + } +} + +// TableName returns the table name. +func (cs *CredentialStore) TableName() string { + if cs.tableName != "" { + return cs.tableName + } + return "credential_static_store" +} + +// SetTableName sets the table name. +func (cs *CredentialStore) SetTableName(n string) { + cs.tableName = n +} + +func (cs *CredentialStore) oplog(op oplog.OpType) oplog.Metadata { + metadata := oplog.Metadata{ + "resource-public-id": []string{cs.PublicId}, + "resource-type": []string{"credential-static-store"}, + "op-type": []string{op.String()}, + } + if cs.ScopeId != "" { + metadata["scope-id"] = []string{cs.ScopeId} + } + return metadata +} diff --git a/internal/credential/static/credential_store_test.go b/internal/credential/static/credential_store_test.go new file mode 100644 index 000000000..ce68252c1 --- /dev/null +++ b/internal/credential/static/credential_store_test.go @@ -0,0 +1,111 @@ +package static + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/boundary/internal/credential/static/store" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/iam" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" +) + +func TestCredentialStore_New(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + rw := db.New(conn) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + scope := prj + + type args struct { + scopeId string + opts []Option + } + + tests := []struct { + name string + args args + want *CredentialStore + wantCreateErr bool + }{ + { + name: "missing-scope-id", + want: allocCredentialStore(), + wantCreateErr: true, + }, + { + name: "valid-no-options", + args: args{ + scopeId: scope.PublicId, + }, + want: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + ScopeId: scope.PublicId, + }, + }, + }, + { + name: "valid-with-name", + args: args{ + scopeId: scope.PublicId, + opts: []Option{ + WithName("test-name"), + }, + }, + want: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + ScopeId: scope.PublicId, + Name: "test-name", + }, + }, + }, + { + name: "valid-with-description", + args: args{ + scopeId: scope.PublicId, + opts: []Option{ + WithDescription("test-description"), + }, + }, + want: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + ScopeId: scope.PublicId, + Description: "test-description", + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, err := NewCredentialStore(tt.args.scopeId, tt.args.opts...) + require.NoError(err) + require.NotNil(got) + + assert.Emptyf(got.PublicId, "PublicId set") + assert.Equal(tt.want, got) + assert.Empty(cmp.Diff(tt.want, got.clone(), protocmp.Transform())) + + id, err := newCredentialStoreId(context.Background()) + assert.NoError(err) + + tt.want.PublicId = id + got.PublicId = id + + err = rw.Create(context.Background(), got) + if tt.wantCreateErr { + require.Error(err) + return + } + + assert.NoError(err) + }) + } +} diff --git a/internal/credential/static/doc.go b/internal/credential/static/doc.go new file mode 100644 index 000000000..d673f57b0 --- /dev/null +++ b/internal/credential/static/doc.go @@ -0,0 +1,2 @@ +// Package static implements a credential store for static credentials. +package static diff --git a/internal/credential/static/fields.go b/internal/credential/static/fields.go new file mode 100644 index 000000000..445ce4ca3 --- /dev/null +++ b/internal/credential/static/fields.go @@ -0,0 +1,9 @@ +package static + +// These constants are the field names used in the static related field masks. +const ( + nameField = "Name" + descriptionField = "Description" + usernameField = "Username" + passwordField = "Password" +) diff --git a/internal/credential/static/options.go b/internal/credential/static/options.go new file mode 100644 index 000000000..04c7a87e4 --- /dev/null +++ b/internal/credential/static/options.go @@ -0,0 +1,47 @@ +package static + +// getOpts - iterate the inbound Options and return a struct +func getOpts(opt ...Option) options { + opts := getDefaultOptions() + for _, o := range opt { + o(&opts) + } + return opts +} + +// Option - how Options are passed as arguments. +type Option func(*options) + +// options = how options are represented +type options struct { + withName string + withDescription string + withLimit int +} + +func getDefaultOptions() options { + return options{} +} + +// WithDescription provides an optional description. +func WithDescription(desc string) Option { + return func(o *options) { + o.withDescription = desc + } +} + +// WithName provides an optional name. +func WithName(name string) Option { + return func(o *options) { + o.withName = name + } +} + +// WithLimit provides an option to provide a limit. Intentionally allowing +// negative integers. If WithLimit < 0, then unlimited results are +// returned. If WithLimit == 0, then default limits are used for results. +func WithLimit(l int) Option { + return func(o *options) { + o.withLimit = l + } +} diff --git a/internal/credential/static/options_test.go b/internal/credential/static/options_test.go new file mode 100644 index 000000000..1c3d2e0f6 --- /dev/null +++ b/internal/credential/static/options_test.go @@ -0,0 +1,29 @@ +package static + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_GetOpts(t *testing.T) { + t.Parallel() + t.Run("WithName", func(t *testing.T) { + opts := getOpts(WithName("test")) + testOpts := getDefaultOptions() + testOpts.withName = "test" + assert.Equal(t, opts, testOpts) + }) + t.Run("WithDescription", func(t *testing.T) { + opts := getOpts(WithDescription("test desc")) + testOpts := getDefaultOptions() + testOpts.withDescription = "test desc" + assert.Equal(t, opts, testOpts) + }) + t.Run("WithLimit", func(t *testing.T) { + opts := getOpts(WithLimit(5)) + testOpts := getDefaultOptions() + testOpts.withLimit = 5 + assert.Equal(t, opts, testOpts) + }) +} diff --git a/internal/credential/static/public_ids.go b/internal/credential/static/public_ids.go new file mode 100644 index 000000000..fb3730d2e --- /dev/null +++ b/internal/credential/static/public_ids.go @@ -0,0 +1,44 @@ +package static + +import ( + "context" + + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/types/subtypes" +) + +func init() { + if err := subtypes.Register(credential.Domain, Subtype, CredentialStorePrefix); err != nil { + panic(err) + } + if err := subtypes.Register(credential.Domain, UsernamePasswordSubtype, CredentialPrefix); err != nil { + panic(err) + } +} + +// PublicId prefixes for the resources in the static package. +const ( + CredentialStorePrefix = "cs" + CredentialPrefix = "cred" + + Subtype = subtypes.Subtype("static") + UsernamePasswordSubtype = subtypes.Subtype("username_password") +) + +func newCredentialStoreId(ctx context.Context) (string, error) { + id, err := db.NewPublicId(CredentialStorePrefix) + if err != nil { + return "", errors.Wrap(ctx, err, "static.newCredentialStoreId") + } + return id, nil +} + +func newCredentialId(ctx context.Context) (string, error) { + id, err := db.NewPublicId(CredentialPrefix) + if err != nil { + return "", errors.Wrap(ctx, err, "static.newCredentialId") + } + return id, nil +} diff --git a/internal/credential/static/repository.go b/internal/credential/static/repository.go new file mode 100644 index 000000000..df03971ec --- /dev/null +++ b/internal/credential/static/repository.go @@ -0,0 +1,49 @@ +package static + +import ( + "context" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" +) + +// A Repository stores and retrieves the persistent types in the static +// package. It is not safe to use a repository concurrently. +type Repository struct { + reader db.Reader + writer db.Writer + kms *kms.Kms + // defaultLimit provides a default for limiting the number of results + // returned from the repo + defaultLimit int +} + +// NewRepository creates a new Repository. The returned repository should +// only be used for one transaction and it is not safe for concurrent go +// routines to access it. WithLimit option is used as a repo wide default +// limit applied to all ListX methods. +func NewRepository(ctx context.Context, r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) { + const op = "static.NewRepository" + switch { + case r == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "db.Reader") + case w == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "db.Writer") + case kms == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "kms") + } + + opts := getOpts(opt...) + if opts.withLimit == 0 { + // zero signals the boundary defaults should be used. + opts.withLimit = db.DefaultLimit + } + + return &Repository{ + reader: r, + writer: w, + kms: kms, + defaultLimit: opts.withLimit, + }, nil +} diff --git a/internal/credential/static/repository_credential.go b/internal/credential/static/repository_credential.go new file mode 100644 index 000000000..3829397c2 --- /dev/null +++ b/internal/credential/static/repository_credential.go @@ -0,0 +1,309 @@ +package static + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/boundary/internal/db" + dbcommon "github.com/hashicorp/boundary/internal/db/common" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" +) + +// CreateUsernamePasswordCredential inserts c into the repository and returns a new +// UsernamePasswordCredential containing the credential's PublicId. c is not +// changed. c must not contain a PublicId. The PublicId is generated and +// assigned by this method. c must contain a valid StoreId. +// +// The password is encrypted and a HmacSha256 of the password is calculated. Only the +// PasswordHmac is returned, the plain-text and encrypted password is not returned. +// +// Both c.Name and c.Description are optional. If c.Name is set, it must +// be unique within c.ScopeId. Both c.CreateTime and c.UpdateTime are +// ignored. +func (r *Repository) CreateUsernamePasswordCredential( + ctx context.Context, + scopeId string, + c *UsernamePasswordCredential, + _ ...Option, +) (*UsernamePasswordCredential, error) { + const op = "static.(Repository).CreateUsernamePasswordCredential" + if c == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing credential") + } + if c.UsernamePasswordCredential == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing embedded credential") + } + if scopeId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") + } + if c.Username == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing username") + } + if c.Password == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing password") + } + if c.StoreId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing store id") + } + if c.PublicId != "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") + } + + c = c.clone() + id, err := newCredentialId(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + c.PublicId = id + oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + // encrypt + databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper")) + } + if err := c.encrypt(ctx, databaseWrapper); err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + var newCred *UsernamePasswordCredential + _, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + newCred = c.clone() + if err := w.Create(ctx, newCred, + db.WithOplog(oplogWrapper, newCred.oplog(oplog.OpType_OP_TYPE_CREATE))); err != nil { + return errors.Wrap(ctx, err, op) + } + + return nil + }, + ) + if err != nil { + if errors.IsUniqueError(err) { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in store: %s: name %s already exists", c.StoreId, c.Name))) + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in store: %s", c.StoreId))) + } + + // Clear password fields, only passwordHmac should be returned + newCred.CtPassword = nil + newCred.Password = nil + + return newCred, nil +} + +// LookupCredential returns the Credential for the publicId. Returns +// nil, nil if no Credential is found for the publicId. +// TODO: This should hit a view and return the interface type... +func (r *Repository) LookupCredential(ctx context.Context, publicId string, _ ...Option) (*UsernamePasswordCredential, error) { + const op = "static.(Repository).LookupCredential" + if publicId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + cred := allocUsernamePasswordCredential() + cred.PublicId = publicId + if err := r.reader.LookupByPublicId(ctx, cred); err != nil { + if errors.IsNotFoundError(err) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", publicId))) + } + + // Clear password fields, only passwordHmac should be returned + cred.CtPassword = nil + cred.Password = nil + + return cred, nil +} + +// UpdateUsernamePasswordCredential updates the repository entry for c.PublicId with +// the values in c for the fields listed in fieldMaskPaths. It returns a +// new UsernamePasswordCredential containing the updated values and a count of the +// number of records updated. c is not changed. +// +// c must contain a valid PublicId. Only Name, Description, Username and Password can be +// changed. If c.Name is set to a non-empty string, it must be unique within c.ScopeId. +// +// An attribute of c will be set to NULL in the database if the attribute +// in c is the zero value and it is included in fieldMaskPaths. +func (r *Repository) UpdateUsernamePasswordCredential(ctx context.Context, + scopeId string, + c *UsernamePasswordCredential, + version uint32, + fieldMaskPaths []string, + _ ...Option, +) (*UsernamePasswordCredential, int, error) { + const op = "static.(Repository).UpdateUsernamePasswordCredential" + if c == nil { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing Credential") + } + if c.UsernamePasswordCredential == nil { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing embedded Credential") + } + if c.PublicId == "" { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id") + } + if version == 0 { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version") + } + if scopeId == "" { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") + } + if c.StoreId == "" { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing store id") + } + c = c.clone() + + for _, f := range fieldMaskPaths { + switch { + case strings.EqualFold(nameField, f): + case strings.EqualFold(descriptionField, f): + case strings.EqualFold(usernameField, f): + case strings.EqualFold(passwordField, f): + default: + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, f) + } + } + dbMask, nullFields := dbcommon.BuildUpdatePaths( + map[string]interface{}{ + nameField: c.Name, + descriptionField: c.Description, + usernameField: c.Username, + passwordField: c.Password, + }, + fieldMaskPaths, + nil, + ) + if len(dbMask) == 0 && len(nullFields) == 0 { + return nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "missing field mask") + } + + for _, f := range fieldMaskPaths { + if strings.EqualFold(passwordField, f) { + // Password has been updated, re-encrypt and recalculate hmac + databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase) + if err != nil { + return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper")) + } + if err := c.encrypt(ctx, databaseWrapper); err != nil { + return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + + // Set PasswordHmac and CtPassword masks for update. + dbMask = append(dbMask, "PasswordHmac", "CtPassword") + } + } + + oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, db.NoRowsAffected, + errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + var rowsUpdated int + var returnedCredential *UsernamePasswordCredential + _, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + returnedCredential = c.clone() + var err error + rowsUpdated, err = w.Update(ctx, returnedCredential, + dbMask, nullFields, + db.WithOplog(oplogWrapper, returnedCredential.oplog(oplog.OpType_OP_TYPE_UPDATE)), + db.WithVersion(&version)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rowsUpdated > 1 { + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated") + } + return nil + }, + ) + if err != nil { + return nil, db.NoRowsAffected, err + } + + // Clear password fields, only passwordHmac should be returned + returnedCredential.CtPassword = nil + returnedCredential.Password = nil + + return returnedCredential, rowsUpdated, nil +} + +// ListCredentials returns a slice of UsernamePasswordCredentials for the +// scopeIds. WithLimit is the only option supported. +// TODO: This should hit a view and return the interface type... +func (r *Repository) ListCredentials(ctx context.Context, storeId string, opt ...Option) ([]*UsernamePasswordCredential, error) { + const op = "static.(Repository).ListCredentials" + if storeId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no storeId") + } + opts := getOpts(opt...) + limit := r.defaultLimit + if opts.withLimit != 0 { + // non-zero signals an override of the default limit for the repo. + limit = opts.withLimit + } + var creds []*UsernamePasswordCredential + err := r.reader.SearchWhere(ctx, &creds, "store_id = ?", []interface{}{storeId}, db.WithLimit(limit)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + for _, c := range creds { + // Clear password fields, only passwordHmac should be returned + c.CtPassword = nil + c.Password = nil + } + + return creds, nil +} + +// DeleteCredential deletes publicId from the repository and returns +// the number of records deleted. All options are ignored. +// TODO: This should hit a view... +func (r *Repository) DeleteCredential(ctx context.Context, scopeId, id string, _ ...Option) (int, error) { + const op = "static.(Repository).DeleteCredential" + if id == "" { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id") + } + if scopeId == "" { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + + c := allocUsernamePasswordCredential() + c.PublicId = id + + oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get oplog wrapper")) + } + + var rowsDeleted int + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) (err error) { + rowsDeleted, err = w.Delete(ctx, c, db.WithOplog(oplogWrapper, c.oplog(oplog.OpType_OP_TYPE_DELETE))) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rowsDeleted > 1 { + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been deleted") + } + return nil + }, + ) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(id)) + } + + return rowsDeleted, nil +} diff --git a/internal/credential/static/repository_credential_store.go b/internal/credential/static/repository_credential_store.go new file mode 100644 index 000000000..61848e7b0 --- /dev/null +++ b/internal/credential/static/repository_credential_store.go @@ -0,0 +1,237 @@ +package static + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/boundary/internal/db" + dbcommon "github.com/hashicorp/boundary/internal/db/common" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" +) + +// CreateCredentialStore inserts cs into the repository and returns a new +// CredentialStore containing the credential store's PublicId. cs is not +// changed. cs must not contain a PublicId. The PublicId is generated and +// assigned by this method. cs must contain a valid ScopeId. +// +// Both cs.Name and cs.Description are optional. If cs.Name is set, it must +// be unique within cs.ScopeId. Both cs.CreateTime and cs.UpdateTime are +// ignored. +func (r *Repository) CreateCredentialStore(ctx context.Context, cs *CredentialStore, _ ...Option) (*CredentialStore, error) { + const op = "static.(Repository).CreateCredentialStore" + if cs == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing CredentialStore") + } + if cs.CredentialStore == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing embedded CredentialStore") + } + if cs.ScopeId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") + } + if cs.PublicId != "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") + } + + cs = cs.clone() + id, err := newCredentialStoreId(ctx) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + cs.PublicId = id + + oplogWrapper, err := r.kms.GetWrapper(ctx, cs.ScopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + var newCredentialStore *CredentialStore + _, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + newCredentialStore = cs.clone() + if err := w.Create(ctx, newCredentialStore, + db.WithOplog(oplogWrapper, newCredentialStore.oplog(oplog.OpType_OP_TYPE_CREATE))); err != nil { + return errors.Wrap(ctx, err, op) + } + + return nil + }, + ) + if err != nil { + if errors.IsUniqueError(err) { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s: name %s already exists", cs.ScopeId, cs.Name))) + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in scope: %s", cs.ScopeId))) + } + + return newCredentialStore, nil +} + +// LookupCredentialStore returns the CredentialStore for publicId. Returns +// nil, nil if no CredentialStore is found for publicId. +func (r *Repository) LookupCredentialStore(ctx context.Context, publicId string, _ ...Option) (*CredentialStore, error) { + const op = "static.(Repository).LookupCredentialStore" + if publicId == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id") + } + cs := allocCredentialStore() + cs.PublicId = publicId + if err := r.reader.LookupByPublicId(ctx, cs); err != nil { + if errors.IsNotFoundError(err) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", publicId))) + } + return cs, nil +} + +// UpdateCredentialStore updates the repository entry for cs.PublicId with +// the values in cs for the fields listed in fieldMaskPaths. It returns a +// new CredentialStore containing the updated values and a count of the +// number of records updated. cs is not changed. +// +// cs must contain a valid PublicId. Only Name and Description can be changed. If cs.Name +// is set to a non-empty string, it must be unique within cs.ScopeId. +// +// An attribute of cs will be set to NULL in the database if the attribute +// in cs is the zero value and it is included in fieldMaskPaths. +func (r *Repository) UpdateCredentialStore(ctx context.Context, cs *CredentialStore, version uint32, fieldMaskPaths []string, _ ...Option) (*CredentialStore, int, error) { + const op = "static.(Repository).UpdateCredentialStore" + if cs == nil { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing CredentialStore") + } + if cs.CredentialStore == nil { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing embedded CredentialStore") + } + if cs.PublicId == "" { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id") + } + if version == 0 { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version") + } + if cs.ScopeId == "" { + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") + } + cs = cs.clone() + + for _, f := range fieldMaskPaths { + switch { + case strings.EqualFold(nameField, f): + case strings.EqualFold(descriptionField, f): + default: + return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, f) + } + } + dbMask, nullFields := dbcommon.BuildUpdatePaths( + map[string]interface{}{ + nameField: cs.Name, + descriptionField: cs.Description, + }, + fieldMaskPaths, + nil, + ) + if len(dbMask) == 0 && len(nullFields) == 0 { + return nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "missing field mask") + } + + oplogWrapper, err := r.kms.GetWrapper(ctx, cs.ScopeId, kms.KeyPurposeOplog) + if err != nil { + return nil, db.NoRowsAffected, + errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + var rowsUpdated int + var returnedCredentialStore *CredentialStore + _, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + returnedCredentialStore = cs.clone() + var err error + rowsUpdated, err = w.Update(ctx, returnedCredentialStore, + dbMask, nullFields, + db.WithOplog(oplogWrapper, returnedCredentialStore.oplog(oplog.OpType_OP_TYPE_UPDATE)), + db.WithVersion(&version)) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rowsUpdated > 1 { + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated") + } + return nil + }, + ) + if err != nil { + return nil, db.NoRowsAffected, err + } + + return returnedCredentialStore, rowsUpdated, nil +} + +// ListCredentialStores returns a slice of CredentialStores for the +// scopeIds. WithLimit is the only option supported. +func (r *Repository) ListCredentialStores(ctx context.Context, scopeIds []string, opt ...Option) ([]*CredentialStore, error) { + const op = "static.(Repository).ListCredentialStores" + if len(scopeIds) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no scopeIds") + } + opts := getOpts(opt...) + limit := r.defaultLimit + if opts.withLimit != 0 { + // non-zero signals an override of the default limit for the repo. + limit = opts.withLimit + } + var credentialStores []*CredentialStore + err := r.reader.SearchWhere(ctx, &credentialStores, "scope_id in (?)", []interface{}{scopeIds}, db.WithLimit(limit)) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return credentialStores, nil +} + +// DeleteCredentialStore deletes publicId from the repository and returns +// the number of records deleted. All options are ignored. +func (r *Repository) DeleteCredentialStore(ctx context.Context, publicId string, _ ...Option) (int, error) { + const op = "static.(Repository).DeleteCredentialStore" + if publicId == "" { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id") + } + cs := allocCredentialStore() + cs.PublicId = publicId + if err := r.reader.LookupByPublicId(ctx, cs); err != nil { + if errors.IsNotFoundError(err) { + return db.NoRowsAffected, nil + } + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId))) + } + if cs.ScopeId == "" { + return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no scope id") + } + + oplogWrapper, err := r.kms.GetWrapper(ctx, cs.ScopeId, kms.KeyPurposeOplog) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get oplog wrapper")) + } + + var rowsDeleted int + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) (err error) { + rowsDeleted, err = w.Delete(ctx, cs, db.WithOplog(oplogWrapper, cs.oplog(oplog.OpType_OP_TYPE_DELETE))) + if err != nil { + return errors.Wrap(ctx, err, op) + } + if rowsDeleted > 1 { + return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been deleted") + } + return nil + }, + ) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(publicId)) + } + + return rowsDeleted, nil +} diff --git a/internal/credential/static/repository_credential_store_test.go b/internal/credential/static/repository_credential_store_test.go new file mode 100644 index 000000000..72c193ceb --- /dev/null +++ b/internal/credential/static/repository_credential_store_test.go @@ -0,0 +1,746 @@ +package static + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/credential/static/store" + "github.com/hashicorp/boundary/internal/db" + dbassert "github.com/hashicorp/boundary/internal/db/assert" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func assertPublicId(t *testing.T, prefix, actual string) { + t.Helper() + assert.NotEmpty(t, actual) + parts := strings.Split(actual, "_") + assert.Equalf(t, 2, len(parts), "want one '_' in PublicId, got multiple in %q", actual) + assert.Equalf(t, prefix, parts[0], "PublicId want prefix: %q, got: %q in %q", prefix, parts[0], actual) +} + +func TestRepository_CreateCredentialStore(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + tests := []struct { + name string + store *CredentialStore + wantErr bool + wantErrCode errors.Code + }{ + { + name: "missing-store", + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "missing-embedded-store", + store: &CredentialStore{}, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "missing-scope-id", + store: &CredentialStore{ + CredentialStore: &store.CredentialStore{}, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "public-id-set", + store: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + ScopeId: prj.PublicId, + PublicId: "bad-dont-set-this", + }, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "valid", + store: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + ScopeId: prj.PublicId, + }, + }, + }, + { + name: "valid-with-name", + store: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + ScopeId: prj.PublicId, + Name: "test-store", + }, + }, + }, + { + name: "valid-with-description", + store: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + ScopeId: prj.PublicId, + Description: "test-store-description", + }, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kms) + require.NoError(err) + require.NotNil(repo) + + got, err := repo.CreateCredentialStore(ctx, tt.store) + if tt.wantErr { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + assert.Nil(got) + return + } + require.NoError(err) + assert.Empty(tt.store.PublicId) + require.NotNil(got) + assertPublicId(t, CredentialStorePrefix, got.PublicId) + assert.NotSame(tt.store, got) + assert.Equal(got.CreateTime, got.UpdateTime) + assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) + }) + } + + t.Run("duplicate-names", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kms) + require.NoError(err) + require.NotNil(repo) + org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NoError(err) + + in, err := NewCredentialStore(prj.GetPublicId(), WithName("my-name"), WithDescription("desc")) + assert.NoError(err) + + got, err := repo.CreateCredentialStore(ctx, in) + require.NoError(err) + assert.Equal(in.Name, got.Name) + assert.Equal(in.Description, got.Description) + + in2, err := NewCredentialStore(prj.GetPublicId(), WithName("my-name"), WithDescription("desc")) + got2, err := repo.CreateCredentialStore(ctx, in2) + assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) + assert.Nil(got2) + + // Creating credential in different scope should not conflict + in3, err := NewCredentialStore(org.GetPublicId(), WithName("my-name"), WithDescription("desc")) + got3, err := repo.CreateCredentialStore(ctx, in3) + require.NoError(err) + assert.Equal(in.Name, got3.Name) + assert.Equal(in3.Name, got3.Name) + assert.Equal(in3.Description, got3.Description) + + assert.NotEqual(got.PublicId, got3.PublicId) + }) +} + +func TestRepository_LookupCredentialStore(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + store := TestCredentialStore(t, conn, wrapper, prj.PublicId) + + tests := []struct { + name string + id string + want *CredentialStore + wantErr errors.Code + }{ + { + name: "valid-with-client-cert", + id: store.GetPublicId(), + want: store, + }, + { + name: "empty-public-id", + id: "", + wantErr: errors.InvalidParameter, + }, + { + name: "not-found", + id: "cs_fake", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + + got, err := repo.LookupCredentialStore(ctx, tt.id) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + assert.Nil(got) + return + } + require.NoError(err) + + if tt.want == nil { + assert.Nil(got) + return + } + assert.NotNil(got) + assert.Equal(got, tt.want) + }) + } +} + +func TestRepository_UpdateCredentialStore(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + + changeName := func(n string) func(*CredentialStore) *CredentialStore { + return func(cs *CredentialStore) *CredentialStore { + cs.Name = n + return cs + } + } + + changeDescription := func(d string) func(*CredentialStore) *CredentialStore { + return func(cs *CredentialStore) *CredentialStore { + cs.Description = d + return cs + } + } + + makeNil := func() func(*CredentialStore) *CredentialStore { + return func(cs *CredentialStore) *CredentialStore { + return nil + } + } + + makeEmbeddedNil := func() func(*CredentialStore) *CredentialStore { + return func(cs *CredentialStore) *CredentialStore { + return &CredentialStore{} + } + } + + deletePublicId := func() func(*CredentialStore) *CredentialStore { + return func(cs *CredentialStore) *CredentialStore { + cs.PublicId = "" + return cs + } + } + + nonExistentPublicId := func() func(*CredentialStore) *CredentialStore { + return func(cs *CredentialStore) *CredentialStore { + cs.PublicId = "abcd_OOOOOOOOOO" + return cs + } + } + + combine := func(fns ...func(cs *CredentialStore) *CredentialStore) func(*CredentialStore) *CredentialStore { + return func(cs *CredentialStore) *CredentialStore { + for _, fn := range fns { + cs = fn(cs) + } + return cs + } + } + + tests := []struct { + name string + orig *CredentialStore + chgFn func(*CredentialStore) *CredentialStore + masks []string + want *CredentialStore + wantCount int + wantErr errors.Code + }{ + { + name: "nil-credential-store", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{}, + }, + chgFn: makeNil(), + masks: []string{"Name", "Description"}, + wantErr: errors.InvalidParameter, + }, + { + name: "nil-embedded-credential-store", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{}, + }, + chgFn: makeEmbeddedNil(), + masks: []string{"Name", "Description"}, + wantErr: errors.InvalidParameter, + }, + { + name: "no-public-id", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{}, + }, + chgFn: deletePublicId(), + masks: []string{"Name", "Description"}, + wantErr: errors.InvalidPublicId, + }, + { + name: "updating-non-existent-credential-store", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + }, + }, + chgFn: combine(nonExistentPublicId(), changeName("test-update-name-repo")), + masks: []string{"Name"}, + wantErr: errors.RecordNotFound, + }, + { + name: "empty-field-mask", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + }, + }, + chgFn: changeName("test-update-name-repo"), + wantErr: errors.EmptyFieldMask, + }, + { + name: "read-only-fields-in-field-mask", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + }, + }, + chgFn: changeName("test-update-name-repo"), + masks: []string{"PublicId", "CreateTime", "UpdateTime", "ScopeId"}, + wantErr: errors.InvalidFieldMask, + }, + { + name: "unknown-field-in-field-mask", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + }, + }, + chgFn: changeName("test-update-name-repo"), + masks: []string{"Bilbo"}, + wantErr: errors.InvalidFieldMask, + }, + { + name: "change-name", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + }, + }, + chgFn: changeName("test-update-name-repo"), + masks: []string{"Name"}, + want: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-update-name-repo", + }, + }, + wantCount: 1, + }, + { + name: "change-description", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Description: "test-description-repo", + }, + }, + chgFn: changeDescription("test-update-description-repo"), + masks: []string{"Description"}, + want: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Description: "test-update-description-repo", + }, + }, + wantCount: 1, + }, + { + name: "change-name-and-description", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + Description: "test-description-repo", + }, + }, + chgFn: combine(changeDescription("test-update-description-repo"), changeName("test-update-name-repo")), + masks: []string{"Name", "Description"}, + want: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-update-name-repo", + Description: "test-update-description-repo", + }, + }, + wantCount: 1, + }, + { + name: "delete-name", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + Description: "test-description-repo", + }, + }, + masks: []string{"Name"}, + chgFn: combine(changeDescription("test-update-description-repo"), changeName("")), + want: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Description: "test-description-repo", + }, + }, + wantCount: 1, + }, + { + name: "delete-description", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + Description: "test-description-repo", + }, + }, + masks: []string{"Description"}, + chgFn: combine(changeDescription(""), changeName("test-update-name-repo")), + want: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + }, + }, + wantCount: 1, + }, + { + name: "do-not-delete-name", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + Description: "test-description-repo", + }, + }, + masks: []string{"Description"}, + chgFn: combine(changeDescription("test-update-description-repo"), changeName("")), + want: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + Description: "test-update-description-repo", + }, + }, + wantCount: 1, + }, + { + name: "do-not-delete-description", + orig: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + Description: "test-description-repo", + }, + }, + masks: []string{"Name"}, + chgFn: combine(changeDescription(""), changeName("test-update-name-repo")), + want: &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-update-name-repo", + Description: "test-description-repo", + }, + }, + wantCount: 1, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + tt.orig.ScopeId = prj.GetPublicId() + + orig, err := repo.CreateCredentialStore(ctx, tt.orig) + assert.NoError(err) + require.NotNil(orig) + + if tt.chgFn != nil { + orig = tt.chgFn(orig) + } + got, gotCount, err := repo.UpdateCredentialStore(ctx, orig, 1, tt.masks) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + assert.Equal(tt.wantCount, gotCount, "row count") + assert.Nil(got) + return + } + assert.NoError(err) + assert.Empty(tt.orig.PublicId) + require.NotNil(got) + assertPublicId(t, CredentialStorePrefix, got.PublicId) + assert.Equal(tt.wantCount, gotCount, "row count") + assert.NotSame(tt.orig, got) + assert.Equal(tt.orig.ScopeId, got.ScopeId) + underlyingDB, err := conn.SqlDB(ctx) + require.NoError(err) + dbassert := dbassert.New(t, underlyingDB) + if tt.want.Name == "" { + dbassert.IsNull(got, "name") + } else { + assert.Equal(tt.want.Name, got.Name) + } + + if tt.want.Description == "" { + dbassert.IsNull(got, "description") + } else { + assert.Equal(tt.want.Description, got.Description) + } + + if tt.wantCount > 0 { + assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))) + } + }) + } + + t.Run("invalid-duplicate-names", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + require.NoError(err) + + name := "test-dup-name" + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + css := TestCredentialStores(t, conn, wrapper, prj.PublicId, 2) + + csA, csB := css[0], css[1] + + csA.Name = name + got1, gotCount1, err := repo.UpdateCredentialStore(ctx, csA, 1, []string{"Name"}) + assert.NoError(err) + require.NotNil(got1) + assert.Equal(name, got1.Name) + assert.Equal(1, gotCount1, "row count") + assert.NoError(db.TestVerifyOplog(t, rw, csA.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))) + + csB.Name = name + got2, gotCount2, err := repo.UpdateCredentialStore(ctx, csB, 1, []string{"Name"}) + assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) + assert.Nil(got2) + assert.Equal(db.NoRowsAffected, gotCount2, "row count") + }) + + t.Run("valid-duplicate-names-diff-scopes", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + + org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + in := &CredentialStore{ + CredentialStore: &store.CredentialStore{ + Name: "test-name-repo", + }, + } + in2 := in.clone() + + in.ScopeId = prj.GetPublicId() + got, err := repo.CreateCredentialStore(ctx, in) + assert.NoError(err) + require.NotNil(got) + assertPublicId(t, CredentialStorePrefix, got.PublicId) + assert.NotSame(in, got) + assert.Equal(in.Name, got.Name) + assert.Equal(in.Description, got.Description) + + in2.ScopeId = org.GetPublicId() + in2.Name = "first-name" + got2, err := repo.CreateCredentialStore(ctx, in2) + assert.NoError(err) + require.NotNil(got2) + got2.Name = got.Name + got3, gotCount3, err := repo.UpdateCredentialStore(ctx, got2, 1, []string{"Name"}) + assert.NoError(err) + require.NotNil(got3) + assert.NotSame(got2, got3) + assert.Equal(got.Name, got3.Name) + assert.Equal(got2.Description, got3.Description) + assert.Equal(1, gotCount3, "row count") + }) + + t.Run("change-scope-id", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + + iamRepo := iam.TestRepo(t, conn, wrapper) + _, prj1 := iam.TestScopes(t, iamRepo) + _, prj2 := iam.TestScopes(t, iamRepo) + csA, csB := TestCredentialStores(t, conn, wrapper, prj1.PublicId, 1)[0], TestCredentialStores(t, conn, wrapper, prj2.PublicId, 1)[0] + assert.NotEqual(csA.ScopeId, csB.ScopeId) + orig := csA.clone() + + csA.ScopeId = csB.ScopeId + assert.Equal(csA.ScopeId, csB.ScopeId) + + got1, gotCount1, err := repo.UpdateCredentialStore(ctx, csA, 1, []string{"Name"}) + + assert.NoError(err) + require.NotNil(got1) + assert.Equal(orig.ScopeId, got1.ScopeId) + assert.Equal(1, gotCount1, "row count") + }) +} + +func TestRepository_ListCredentialStores(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(context.Background(), rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + + const num = 10 + var prjs []string + var total int + for i := 0; i < num; i++ { + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + prjs = append(prjs, prj.GetPublicId()) + TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), num) + total += num + } + + type args struct { + scopeIds []string + opt []Option + } + tests := []struct { + name string + args args + wantCnt int + }{ + { + name: "no-limit", + args: args{ + scopeIds: prjs, + opt: []Option{WithLimit(-1)}, + }, + wantCnt: total, + }, + { + name: "default-limit", + args: args{ + scopeIds: prjs, + }, + wantCnt: total, + }, + { + name: "custom-limit", + args: args{ + scopeIds: prjs, + opt: []Option{WithLimit(3)}, + }, + wantCnt: 3, + }, + { + name: "bad-scope", + args: args{ + scopeIds: []string{"bad-id"}, + }, + wantCnt: 0, + }, + } + for _, tt := range tests { + got, err := repo.ListCredentialStores(context.Background(), tt.args.scopeIds, tt.args.opt...) + require.NoError(err) + assert.Equal(tt.wantCnt, len(got)) + } +} + +func TestRepository_DeleteCredentialStore(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + + kms := kms.TestKms(t, conn, wrapper) + iam.TestRepo(t, conn, wrapper) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + store := TestCredentialStore(t, conn, wrapper, prj.PublicId) + + tests := []struct { + name string + in string + want int + wantErr bool + wantErrCode errors.Code + }{ + { + name: "With no public id", + wantErr: true, + wantErrCode: errors.InvalidPublicId, + }, + { + name: "With non existing account id", + in: "cs_fakeid", + want: 0, + }, + { + name: "With existing account id", + in: store.GetPublicId(), + want: 1, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(context.Background(), rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + got, err := repo.DeleteCredentialStore(context.Background(), tt.in) + if tt.wantErr { + assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "Unexpected error %s", err) + return + } + require.NoError(err) + assert.EqualValues(tt.want, got) + }) + } +} diff --git a/internal/credential/static/repository_credential_test.go b/internal/credential/static/repository_credential_test.go new file mode 100644 index 000000000..fe1227a1b --- /dev/null +++ b/internal/credential/static/repository_credential_test.go @@ -0,0 +1,896 @@ +package static + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/credential/static/store" + "github.com/hashicorp/boundary/internal/db" + dbassert "github.com/hashicorp/boundary/internal/db/assert" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/crypto" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepository_CreateUsernamePasswordCredential(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + cs := TestCredentialStore(t, conn, wrapper, prj.PublicId) + + tests := []struct { + name string + scopeId string + cred *UsernamePasswordCredential + wantErr bool + wantErrCode errors.Code + }{ + { + name: "missing-store", + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "missing-embedded-cred", + cred: &UsernamePasswordCredential{}, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "missing-scope-id", + cred: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "my-user", + Password: []byte("secret"), + StoreId: cs.PublicId, + }, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "missing-username", + scopeId: prj.PublicId, + cred: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Password: []byte("secret"), + StoreId: cs.PublicId, + }, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "missing-password", + scopeId: prj.PublicId, + cred: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "my-user", + StoreId: cs.PublicId, + }, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "missing-store-id", + scopeId: prj.PublicId, + cred: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "my-user", + Password: []byte("secret"), + }, + }, + wantErr: true, + wantErrCode: errors.InvalidParameter, + }, + { + name: "valid", + scopeId: prj.PublicId, + cred: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "my-user", + Password: []byte("secret"), + StoreId: cs.PublicId, + }, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kkms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kkms) + require.NoError(err) + require.NotNil(repo) + + got, err := repo.CreateUsernamePasswordCredential(ctx, tt.scopeId, tt.cred) + if tt.wantErr { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + assert.Nil(got) + return + } + require.NoError(err) + assertPublicId(t, CredentialPrefix, got.PublicId) + assert.Equal(tt.cred.Username, got.Username) + assert.Nil(got.Password) + assert.Nil(got.CtPassword) + + // Validate password + lookupCred := allocUsernamePasswordCredential() + lookupCred.PublicId = got.PublicId + require.NoError(rw.LookupById(ctx, lookupCred)) + + databaseWrapper, err := kkms.GetWrapper(context.Background(), tt.scopeId, kms.KeyPurposeDatabase) + require.NoError(err) + require.NoError(lookupCred.decrypt(ctx, databaseWrapper)) + assert.Equal(tt.cred.Password, lookupCred.Password) + + assert.Empty(got.Password) + assert.Empty(got.CtPassword) + assert.NotEmpty(got.PasswordHmac) + + // Validate hmac + hm, err := crypto.HmacSha256(ctx, tt.cred.Password, databaseWrapper, []byte(tt.cred.StoreId), nil, crypto.WithEd25519()) + require.NoError(err) + assert.Equal([]byte(hm), got.PasswordHmac) + + // Validate oplog + assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) + }) + } + + t.Run("duplicate-names", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kms) + require.NoError(err) + require.NotNil(repo) + org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NoError(err) + + prjCs := TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + orgCs := TestCredentialStore(t, conn, wrapper, org.GetPublicId()) + + in, err := NewUsernamePasswordCredential(prjCs.GetPublicId(), "user", "pass", WithName("my-name"), WithDescription("original")) + assert.NoError(err) + + got, err := repo.CreateUsernamePasswordCredential(ctx, prj.PublicId, in) + require.NoError(err) + assert.Equal(in.Name, got.Name) + assert.Equal(in.Description, got.Description) + + in2, err := NewUsernamePasswordCredential(prjCs.GetPublicId(), "user", "pass", WithName("my-name"), WithDescription("different")) + got2, err := repo.CreateUsernamePasswordCredential(ctx, prj.GetPublicId(), in2) + assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "want err code: %v got err: %v", errors.NotUnique, err) + assert.Nil(got2) + + // Creating credential in different scope should not conflict + in3, err := NewUsernamePasswordCredential(orgCs.GetPublicId(), "user", "pass", WithName("my-name"), WithDescription("different")) + got3, err := repo.CreateUsernamePasswordCredential(ctx, org.GetPublicId(), in3) + require.NoError(err) + assert.Equal(in3.Name, got3.Name) + assert.Equal(in3.Description, got3.Description) + + assert.NotEqual(got.PublicId, got3.PublicId) + }) +} + +func TestRepository_LookupCredential(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + store := TestCredentialStore(t, conn, wrapper, prj.PublicId) + cred := TestUsernamePasswordCredential(t, conn, wrapper, "username", "password", store.PublicId, prj.PublicId) + + tests := []struct { + name string + id string + want *UsernamePasswordCredential + wantErr errors.Code + }{ + { + name: "valid-with-client-cert", + id: cred.GetPublicId(), + want: cred, + }, + { + name: "empty-public-id", + id: "", + wantErr: errors.InvalidParameter, + }, + { + name: "not-found", + id: "cred_fake", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + + got, err := repo.LookupCredential(ctx, tt.id) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + assert.Nil(got) + return + } + require.NoError(err) + + if tt.want == nil { + assert.Nil(got) + return + } + + assert.NotNil(got) + assert.Empty(got.Password) + assert.Empty(got.CtPassword) + assert.NotEmpty(got.PasswordHmac) + }) + } +} + +func TestRepository_ListCredentials(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(context.Background(), rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + + total := 10 + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + store := TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId(), total) + + type args struct { + storeId string + opt []Option + } + tests := []struct { + name string + args args + wantCnt int + }{ + { + name: "no-limit", + args: args{ + storeId: store.PublicId, + opt: []Option{WithLimit(-1)}, + }, + wantCnt: total, + }, + { + name: "default-limit", + args: args{ + storeId: store.PublicId, + }, + wantCnt: total, + }, + { + name: "custom-limit", + args: args{ + storeId: store.PublicId, + opt: []Option{WithLimit(3)}, + }, + wantCnt: 3, + }, + { + name: "bad-store", + args: args{ + storeId: "bad-id", + }, + wantCnt: 0, + }, + } + for _, tt := range tests { + got, err := repo.ListCredentials(context.Background(), tt.args.storeId, tt.args.opt...) + require.NoError(err) + assert.Equal(tt.wantCnt, len(got)) + + // Validate only passwordHmac is returned + for _, c := range got { + assert.Empty(c.Password) + assert.Empty(c.CtPassword) + assert.NotEmpty(c.PasswordHmac) + } + } +} + +func TestRepository_DeleteCredential(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + + kms := kms.TestKms(t, conn, wrapper) + iam.TestRepo(t, conn, wrapper) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + store := TestCredentialStore(t, conn, wrapper, prj.PublicId) + cred := TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId()) + + tests := []struct { + name string + in string + want int + wantErr bool + wantErrCode errors.Code + }{ + { + name: "With no public id", + wantErr: true, + wantErrCode: errors.InvalidPublicId, + }, + { + name: "With non existing account id", + in: "cred_fakeid", + want: 0, + }, + { + name: "With existing id", + in: cred.GetPublicId(), + want: 1, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(context.Background(), rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + got, err := repo.DeleteCredential(context.Background(), prj.GetPublicId(), tt.in) + if tt.wantErr { + assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "Unexpected error %s", err) + return + } + require.NoError(err) + assert.EqualValues(tt.want, got) + }) + } +} + +func TestRepository_UpdateUsernamePasswordCredential(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + + changeName := func(n string) func(credential *UsernamePasswordCredential) *UsernamePasswordCredential { + return func(c *UsernamePasswordCredential) *UsernamePasswordCredential { + c.Name = n + return c + } + } + + changeDescription := func(d string) func(*UsernamePasswordCredential) *UsernamePasswordCredential { + return func(c *UsernamePasswordCredential) *UsernamePasswordCredential { + c.Description = d + return c + } + } + + makeNil := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential { + return func(_ *UsernamePasswordCredential) *UsernamePasswordCredential { + return nil + } + } + + makeEmbeddedNil := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential { + return func(_ *UsernamePasswordCredential) *UsernamePasswordCredential { + return &UsernamePasswordCredential{} + } + } + + deletePublicId := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential { + return func(c *UsernamePasswordCredential) *UsernamePasswordCredential { + c.PublicId = "" + return c + } + } + + deleteStoreId := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential { + return func(c *UsernamePasswordCredential) *UsernamePasswordCredential { + c.StoreId = "" + return c + } + } + + deleteVersion := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential { + return func(c *UsernamePasswordCredential) *UsernamePasswordCredential { + c.Version = 0 + return c + } + } + + nonExistentPublicId := func() func(*UsernamePasswordCredential) *UsernamePasswordCredential { + return func(c *UsernamePasswordCredential) *UsernamePasswordCredential { + c.PublicId = "abcd_OOOOOOOOOO" + return c + } + } + + changeUser := func(n string) func(credential *UsernamePasswordCredential) *UsernamePasswordCredential { + return func(c *UsernamePasswordCredential) *UsernamePasswordCredential { + c.Username = n + return c + } + } + + changePassword := func(d string) func(*UsernamePasswordCredential) *UsernamePasswordCredential { + return func(c *UsernamePasswordCredential) *UsernamePasswordCredential { + c.Password = []byte(d) + return c + } + } + + combine := func(fns ...func(cs *UsernamePasswordCredential) *UsernamePasswordCredential) func(*UsernamePasswordCredential) *UsernamePasswordCredential { + return func(c *UsernamePasswordCredential) *UsernamePasswordCredential { + for _, fn := range fns { + c = fn(c) + } + return c + } + } + + tests := []struct { + name string + orig *UsernamePasswordCredential + chgFn func(*UsernamePasswordCredential) *UsernamePasswordCredential + masks []string + want *UsernamePasswordCredential + wantCount int + wantErr errors.Code + }{ + { + name: "nil-credential", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: makeNil(), + masks: []string{"Name", "Description"}, + wantErr: errors.InvalidParameter, + }, + { + name: "nil-embedded-credential", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: makeEmbeddedNil(), + masks: []string{"Name", "Description"}, + wantErr: errors.InvalidParameter, + }, + { + name: "no-public-id", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: deletePublicId(), + masks: []string{"Name", "Description"}, + wantErr: errors.InvalidPublicId, + }, + { + name: "no-store-id", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: deleteStoreId(), + masks: []string{"Name", "Description"}, + wantErr: errors.InvalidParameter, + }, + { + name: "no-version", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: deleteVersion(), + masks: []string{"Name", "Description"}, + wantErr: errors.InvalidParameter, + }, + { + name: "updating-non-existent-credential", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: combine(nonExistentPublicId(), changeName("test-update-name-repo")), + masks: []string{"Name"}, + wantErr: errors.RecordNotFound, + }, + { + name: "empty-field-mask", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: changeName("test-update-name-repo"), + wantErr: errors.EmptyFieldMask, + }, + { + name: "read-only-fields-in-field-mask", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: changeName("test-update-name-repo"), + masks: []string{"PublicId", "CreateTime", "UpdateTime", "ScopeId"}, + wantErr: errors.InvalidFieldMask, + }, + { + name: "unknown-field-in-field-mask", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: changeName("test-update-name-repo"), + masks: []string{"Bilbo"}, + wantErr: errors.InvalidFieldMask, + }, + { + name: "change-name", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: changeName("test-update-name-repo"), + masks: []string{"Name"}, + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-update-name-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + wantCount: 1, + }, + { + name: "change-description", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Description: "test-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: changeDescription("test-update-description-repo"), + masks: []string{"Description"}, + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Description: "test-update-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + wantCount: 1, + }, + { + name: "change-name-and-description", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Description: "test-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: combine(changeDescription("test-update-description-repo"), changeName("test-update-name-repo")), + masks: []string{"Name", "Description"}, + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-update-name-repo", + Description: "test-update-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + wantCount: 1, + }, + { + name: "change-username", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: changeUser("test-update-user"), + masks: []string{"Username"}, + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "test-update-user", + Password: []byte("pass"), + }, + }, + wantCount: 1, + }, + { + name: "change-password", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: changePassword("test-update-pass"), + masks: []string{"Password"}, + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("test-update-pass"), + }, + }, + wantCount: 1, + }, + { + name: "change-username-and-password", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("pass"), + }, + }, + chgFn: combine(changeUser("test-update-user"), changePassword("test-update-pass")), + masks: []string{"Username", "Password"}, + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "test-update-user", + Password: []byte("test-update-pass"), + }, + }, + wantCount: 1, + }, + { + name: "do-not-delete-password", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("pass"), + }, + }, + masks: []string{"Username"}, + chgFn: combine(changeUser("test-new-user"), changePassword("")), + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "test-new-user", + Password: []byte("pass"), + }, + }, + wantCount: 1, + }, + { + name: "do-not-delete-username", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("pass"), + }, + }, + masks: []string{"Password"}, + chgFn: combine(changeUser(""), changePassword("test-new-password")), + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "user", + Password: []byte("test-new-password"), + }, + }, + wantCount: 1, + }, + { + name: "delete-name", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Description: "test-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + masks: []string{"Name"}, + chgFn: combine(changeDescription("test-update-description-repo"), changeName("")), + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Description: "test-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + wantCount: 1, + }, + { + name: "delete-description", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Description: "test-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + masks: []string{"Description"}, + chgFn: combine(changeDescription(""), changeName("test-update-name-repo")), + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + wantCount: 1, + }, + { + name: "do-not-delete-name", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Description: "test-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + masks: []string{"Description"}, + chgFn: combine(changeDescription("test-update-description-repo"), changeName("")), + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Description: "test-update-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + wantCount: 1, + }, + { + name: "do-not-delete-description", + orig: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-name-repo", + Description: "test-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + masks: []string{"Name"}, + chgFn: combine(changeDescription(""), changeName("test-update-name-repo")), + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Name: "test-update-name-repo", + Description: "test-description-repo", + Username: "user", + Password: []byte("pass"), + }, + }, + wantCount: 1, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + kkms := kms.TestKms(t, conn, wrapper) + repo, err := NewRepository(ctx, rw, rw, kkms) + assert.NoError(err) + require.NotNil(repo) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + store := TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + tt.orig.StoreId = store.PublicId + + orig, err := repo.CreateUsernamePasswordCredential(ctx, prj.GetPublicId(), tt.orig) + assert.NoError(err) + require.NotNil(orig) + + if tt.chgFn != nil { + orig = tt.chgFn(orig) + } + var version uint32 + if orig != nil { + version = orig.GetVersion() + } + got, gotCount, err := repo.UpdateUsernamePasswordCredential(ctx, prj.GetPublicId(), orig, version, tt.masks) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + assert.Equal(tt.wantCount, gotCount, "row count") + assert.Nil(got) + return + } + assert.NoError(err) + assert.Empty(tt.orig.PublicId) + require.NotNil(got) + assertPublicId(t, CredentialPrefix, got.PublicId) + assert.Equal(tt.wantCount, gotCount, "row count") + assert.NotSame(tt.orig, got) + assert.Equal(tt.orig.StoreId, got.StoreId) + underlyingDB, err := conn.SqlDB(ctx) + require.NoError(err) + dbassert := dbassert.New(t, underlyingDB) + if tt.want.Name == "" { + got := got.clone() + dbassert.IsNull(got, "name") + } else { + assert.Equal(tt.want.Name, got.Name) + } + + if tt.want.Description == "" { + got := got.clone() + dbassert.IsNull(got, "description") + } else { + assert.Equal(tt.want.Description, got.Description) + } + + assert.Equal(tt.want.Username, got.Username) + + // Validate only passwordHmac is returned + assert.Empty(got.Password) + assert.Empty(got.CtPassword) + assert.NotEmpty(got.PasswordHmac) + + // Validate hmac + databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase) + require.NoError(err) + hm, err := crypto.HmacSha256(ctx, tt.want.Password, databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519()) + require.NoError(err) + assert.Equal([]byte(hm), got.PasswordHmac) + + if tt.wantCount > 0 { + assert.NoError(db.TestVerifyOplog(t, rw, got.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))) + } + }) + } +} diff --git a/internal/credential/static/repository_test.go b/internal/credential/static/repository_test.go new file mode 100644 index 000000000..390c829e5 --- /dev/null +++ b/internal/credential/static/repository_test.go @@ -0,0 +1,119 @@ +package static + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepository_New(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + + type args struct { + r db.Reader + w db.Writer + kms *kms.Kms + opts []Option + } + + tests := []struct { + name string + args args + want *Repository + wantIsErr errors.Code + }{ + { + name: "valid", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + }, + want: &Repository{ + reader: rw, + writer: rw, + kms: kmsCache, + defaultLimit: db.DefaultLimit, + }, + }, + { + name: "valid-with-limit", + args: args{ + r: rw, + w: rw, + kms: kmsCache, + opts: []Option{WithLimit(5)}, + }, + want: &Repository{ + reader: rw, + writer: rw, + kms: kmsCache, + defaultLimit: 5, + }, + }, + { + name: "nil-reader", + args: args{ + r: nil, + w: rw, + kms: kmsCache, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-writer", + args: args{ + r: rw, + w: nil, + kms: kmsCache, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "nil-kms", + args: args{ + r: rw, + w: rw, + kms: nil, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + { + name: "all-nils", + args: args{ + r: nil, + w: nil, + kms: nil, + }, + want: nil, + wantIsErr: errors.InvalidParameter, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, err := NewRepository(context.Background(), tt.args.r, tt.args.w, tt.args.kms, tt.args.opts...) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err) + assert.Nil(got) + return + } + assert.NoError(err) + require.NotNil(got) + assert.Equal(tt.want, got) + }) + } +} diff --git a/internal/credential/static/store/static.pb.go b/internal/credential/static/store/static.pb.go new file mode 100644 index 000000000..3ba2f1032 --- /dev/null +++ b/internal/credential/static/store/static.pb.go @@ -0,0 +1,467 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/storage/credential/static/store/v1/static.proto + +// Package store provides protobufs for storing types in the static +// credential package. + +package store + +import ( + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type CredentialStore struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // name is optional. If set, it must be unique within scope_id. + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // description is optional. + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // The scope_id of the owning scope. + // It must be set. + // @inject_tag: `gorm:"not_null"` + ScopeId string `protobuf:"bytes,6,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"not_null"` + // version allows optimistic locking of the resource. + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` +} + +func (x *CredentialStore) Reset() { + *x = CredentialStore{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CredentialStore) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CredentialStore) ProtoMessage() {} + +func (x *CredentialStore) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CredentialStore.ProtoReflect.Descriptor instead. +func (*CredentialStore) Descriptor() ([]byte, []int) { + return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{0} +} + +func (x *CredentialStore) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *CredentialStore) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *CredentialStore) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *CredentialStore) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CredentialStore) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *CredentialStore) GetScopeId() string { + if x != nil { + return x.ScopeId + } + return "" +} + +func (x *CredentialStore) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +type UsernamePasswordCredential struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // name is optional. If set, it must be unique within scope_id. + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // description is optional. + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // store_id of the owning static credential store. + // It must be set. + // @inject_tag: `gorm:"not_null"` + StoreId string `protobuf:"bytes,6,opt,name=store_id,json=storeId,proto3" json:"store_id,omitempty" gorm:"not_null"` + // version allows optimistic locking of the resource. + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` + // username is the username associated with the credential. + // It must be set. + // @inject_tag: `gorm:"not_null"` + Username string `protobuf:"bytes,8,opt,name=username,proto3" json:"username,omitempty" gorm:"not_null"` + // password is the plain-text of the password associated with the credential. We are + // not storing this plain-text password in the database. + // @inject_tag: `gorm:"-" wrapping:"pt,password_data"` + Password []byte `protobuf:"bytes,9,opt,name=password,proto3" json:"password,omitempty" gorm:"-" wrapping:"pt,password_data"` + // ct_password is the ciphertext of the password. It + // is stored in the database. + // @inject_tag: `gorm:"column:password_encrypted;not_null" wrapping:"ct,password_data"` + CtPassword []byte `protobuf:"bytes,10,opt,name=ct_password,json=ctPassword,proto3" json:"ct_password,omitempty" gorm:"column:password_encrypted;not_null" wrapping:"ct,password_data"` + // password_hmac is a sha256-hmac of the unencrypted password. It is recalculated + // everytime the password is updated. + // @inject_tag: `gorm:"not_null"` + PasswordHmac []byte `protobuf:"bytes,11,opt,name=password_hmac,json=passwordHmac,proto3" json:"password_hmac,omitempty" gorm:"not_null"` + // The key_id of the kms database key used for encrypting this entry. + // It must be set. + // @inject_tag: `gorm:"not_null"` + KeyId string `protobuf:"bytes,12,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty" gorm:"not_null"` +} + +func (x *UsernamePasswordCredential) Reset() { + *x = UsernamePasswordCredential{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UsernamePasswordCredential) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UsernamePasswordCredential) ProtoMessage() {} + +func (x *UsernamePasswordCredential) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_credential_static_store_v1_static_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UsernamePasswordCredential.ProtoReflect.Descriptor instead. +func (*UsernamePasswordCredential) Descriptor() ([]byte, []int) { + return file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP(), []int{1} +} + +func (x *UsernamePasswordCredential) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *UsernamePasswordCredential) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *UsernamePasswordCredential) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *UsernamePasswordCredential) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UsernamePasswordCredential) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *UsernamePasswordCredential) GetStoreId() string { + if x != nil { + return x.StoreId + } + return "" +} + +func (x *UsernamePasswordCredential) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *UsernamePasswordCredential) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *UsernamePasswordCredential) GetPassword() []byte { + if x != nil { + return x.Password + } + return nil +} + +func (x *UsernamePasswordCredential) GetCtPassword() []byte { + if x != nil { + return x.CtPassword + } + return nil +} + +func (x *UsernamePasswordCredential) GetPasswordHmac() []byte { + if x != nil { + return x.PasswordHmac + } + return nil +} + +func (x *UsernamePasswordCredential) GetKeyId() string { + if x != nil { + return x.KeyId + } + return "" +} + +var File_controller_storage_credential_static_store_v1_static_proto protoreflect.FileDescriptor + +var file_controller_storage_credential_static_store_v1_static_proto_rawDesc = []byte{ + 0x0a, 0x3a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x2d, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe5, 0x02, 0x0a, 0x0f, 0x43, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x1b, 0x0a, 0x09, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, + 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x73, + 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, + 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x22, 0xfd, 0x04, 0x0a, 0x1a, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, + 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, + 0x0a, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x23, 0xc2, 0xdd, 0x29, 0x1f, 0x0a, 0x08, 0x55, 0x73, 0x65, + 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x23, 0xc2, 0xdd, 0x29, 0x1f, 0x0a, 0x08, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x74, 0x5f, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x74, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x51, 0x0a, 0x0d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x2c, 0xc2, + 0xdd, 0x29, 0x28, 0x0a, 0x0c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x6d, 0x61, + 0x63, 0x12, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x52, 0x0c, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x6d, 0x61, 0x63, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, + 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, + 0x42, 0x46, 0x5a, 0x44, 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, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_storage_credential_static_store_v1_static_proto_rawDescOnce sync.Once + file_controller_storage_credential_static_store_v1_static_proto_rawDescData = file_controller_storage_credential_static_store_v1_static_proto_rawDesc +) + +func file_controller_storage_credential_static_store_v1_static_proto_rawDescGZIP() []byte { + file_controller_storage_credential_static_store_v1_static_proto_rawDescOnce.Do(func() { + file_controller_storage_credential_static_store_v1_static_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_credential_static_store_v1_static_proto_rawDescData) + }) + return file_controller_storage_credential_static_store_v1_static_proto_rawDescData +} + +var file_controller_storage_credential_static_store_v1_static_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_controller_storage_credential_static_store_v1_static_proto_goTypes = []interface{}{ + (*CredentialStore)(nil), // 0: controller.storage.credential.static.store.v1.CredentialStore + (*UsernamePasswordCredential)(nil), // 1: controller.storage.credential.static.store.v1.UsernamePasswordCredential + (*timestamp.Timestamp)(nil), // 2: controller.storage.timestamp.v1.Timestamp +} +var file_controller_storage_credential_static_store_v1_static_proto_depIdxs = []int32{ + 2, // 0: controller.storage.credential.static.store.v1.CredentialStore.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // 1: controller.storage.credential.static.store.v1.CredentialStore.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // 2: controller.storage.credential.static.store.v1.UsernamePasswordCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // 3: controller.storage.credential.static.store.v1.UsernamePasswordCredential.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_controller_storage_credential_static_store_v1_static_proto_init() } +func file_controller_storage_credential_static_store_v1_static_proto_init() { + if File_controller_storage_credential_static_store_v1_static_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_credential_static_store_v1_static_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CredentialStore); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_credential_static_store_v1_static_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UsernamePasswordCredential); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_storage_credential_static_store_v1_static_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_credential_static_store_v1_static_proto_goTypes, + DependencyIndexes: file_controller_storage_credential_static_store_v1_static_proto_depIdxs, + MessageInfos: file_controller_storage_credential_static_store_v1_static_proto_msgTypes, + }.Build() + File_controller_storage_credential_static_store_v1_static_proto = out.File + file_controller_storage_credential_static_store_v1_static_proto_rawDesc = nil + file_controller_storage_credential_static_store_v1_static_proto_goTypes = nil + file_controller_storage_credential_static_store_v1_static_proto_depIdxs = nil +} diff --git a/internal/credential/static/testing.go b/internal/credential/static/testing.go new file mode 100644 index 000000000..9db96217d --- /dev/null +++ b/internal/credential/static/testing.go @@ -0,0 +1,131 @@ +package static + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/kms" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestCredentialStore creates a static credential store in the provided DB with +// the provided scope and any values passed in through the Options vars. +// If any errors are encountered during the creation of the store, the test will fail. +func TestCredentialStore(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, scopeId string, opts ...Option) *CredentialStore { + t.Helper() + ctx := context.Background() + kmsCache := kms.TestKms(t, conn, wrapper) + w := db.New(conn) + + databaseWrapper, err := kmsCache.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase) + assert.NoError(t, err) + require.NotNil(t, databaseWrapper) + + cs, err := NewCredentialStore(scopeId, opts...) + assert.NoError(t, err) + require.NotNil(t, cs) + id, err := newCredentialStoreId(ctx) + assert.NoError(t, err) + require.NotEmpty(t, id) + cs.PublicId = id + + _, err2 := w.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, iw db.Writer) error { + require.NoError(t, iw.Create(ctx, cs)) + return nil + }, + ) + require.NoError(t, err2) + + return cs +} + +// TestCredentialStores creates count number of static credential stores in +// the provided DB with the provided scope id. If any errors are +// encountered during the creation of the credential stores, the test will +// fail. +func TestCredentialStores(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, scopeId string, count int) []*CredentialStore { + t.Helper() + ctx := context.Background() + kmsCache := kms.TestKms(t, conn, wrapper) + + databaseWrapper, err := kmsCache.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase) + assert.NoError(t, err) + require.NotNil(t, databaseWrapper) + + css := make([]*CredentialStore, 0, count) + for i := 0; i < count; i++ { + css = append(css, TestCredentialStore(t, conn, wrapper, scopeId)) + } + return css +} + +// TestUsernamePasswordCredential creates a user password credential in the provided DB with +// the provided scope and any values passed in through. +// If any errors are encountered during the creation of the store, the test will fail. +func TestUsernamePasswordCredential( + t testing.TB, + conn *db.DB, + wrapper wrapping.Wrapper, + username, password, storeId, scopeId string, + opts ...Option, +) *UsernamePasswordCredential { + t.Helper() + ctx := context.Background() + kmsCache := kms.TestKms(t, conn, wrapper) + w := db.New(conn) + + databaseWrapper, err := kmsCache.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase) + assert.NoError(t, err) + require.NotNil(t, databaseWrapper) + + cred, err := NewUsernamePasswordCredential(storeId, username, credential.Password(password), opts...) + require.NoError(t, err) + require.NotNil(t, cred) + + id, err := newCredentialId(ctx) + require.NoError(t, err) + cred.PublicId = id + + err = cred.encrypt(ctx, databaseWrapper) + require.NoError(t, err) + + _, err2 := w.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, + func(_ db.Reader, iw db.Writer) error { + require.NoError(t, iw.Create(ctx, cred)) + return nil + }, + ) + require.NoError(t, err2) + + return cred +} + +// TestUsernamePasswordCredentials creates count number of user password credentials in +// the provided DB with the provided scope id. If any errors are +// encountered during the creation of the credentials, the test will fail. +func TestUsernamePasswordCredentials( + t testing.TB, + conn *db.DB, + wrapper wrapping.Wrapper, + username, password, storeId, scopeId string, + count int, +) []*UsernamePasswordCredential { + t.Helper() + ctx := context.Background() + kmsCache := kms.TestKms(t, conn, wrapper) + + databaseWrapper, err := kmsCache.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase) + assert.NoError(t, err) + require.NotNil(t, databaseWrapper) + + creds := make([]*UsernamePasswordCredential, 0, count) + for i := 0; i < count; i++ { + creds = append(creds, TestUsernamePasswordCredential(t, conn, wrapper, username, password, storeId, scopeId)) + } + return creds +} diff --git a/internal/credential/static/testing_test.go b/internal/credential/static/testing_test.go new file mode 100644 index 000000000..47e1f1ab5 --- /dev/null +++ b/internal/credential/static/testing_test.go @@ -0,0 +1,86 @@ +package static + +import ( + "context" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/crypto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_TestCredentialStore(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NotNil(prj) + assert.NotEmpty(prj.GetPublicId()) + + cs := TestCredentialStore(t, conn, wrapper, prj.GetPublicId(), WithName("my-name"), WithDescription("my-description")) + require.NotNil(cs) + assert.NotEmpty(cs.GetPublicId()) + assert.Equal(cs.Name, "my-name") + assert.Equal(cs.Description, "my-description") +} + +func Test_TestCredentialStores(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NotNil(prj) + assert.NotEmpty(prj.GetPublicId()) + + count := 4 + css := TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), count) + assert.Len(css, count) +} + +func Test_TestUsernamePasswordCredential(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kkms := kms.TestKms(t, conn, wrapper) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NotNil(prj) + assert.NotEmpty(prj.GetPublicId()) + + store := TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + + cred := TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId(), WithName("my-name"), WithDescription("my-description")) + require.NotNil(cred) + assert.NotEmpty(cred.GetPublicId()) + assert.Equal(cred.Name, "my-name") + assert.Equal(cred.Description, "my-description") + assert.Equal(cred.Username, "user") + assert.Equal(cred.Password, []byte("pass")) + + // Validate hmac + databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.PublicId, kms.KeyPurposeDatabase) + hm, err := crypto.HmacSha256(context.Background(), cred.Password, databaseWrapper, []byte(cred.StoreId), nil, crypto.WithEd25519()) + require.NoError(err) + assert.Equal([]byte(hm), cred.PasswordHmac) +} + +func Test_TestUsernamePasswordCredentials(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + require.NotNil(prj) + assert.NotEmpty(prj.GetPublicId()) + + store := TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + + count := 4 + creds := TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId(), count) + assert.Len(creds, count) +} diff --git a/internal/credential/static/usernamepassword_credential.go b/internal/credential/static/usernamepassword_credential.go new file mode 100644 index 000000000..b5ad2fd73 --- /dev/null +++ b/internal/credential/static/usernamepassword_credential.go @@ -0,0 +1,121 @@ +package static + +import ( + "context" + + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/credential/static/store" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/libs/crypto" + "github.com/hashicorp/boundary/internal/oplog" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" + "github.com/hashicorp/go-kms-wrapping/v2/extras/structwrapping" + "google.golang.org/protobuf/proto" +) + +// A UsernamePasswordCredential contains the credential with a username and password. +// It is owned by a credential store. +type UsernamePasswordCredential struct { + *store.UsernamePasswordCredential + tableName string `gorm:"-"` +} + +// NewUsernamePasswordCredential creates a new in memory static Credential containing a +// username and password that is assigned to storeId. Name and description are the only +// valid options. All other options are ignored. +func NewUsernamePasswordCredential( + storeId string, + username string, + password credential.Password, + opt ...Option, +) (*UsernamePasswordCredential, error) { + opts := getOpts(opt...) + l := &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + StoreId: storeId, + Name: opts.withName, + Description: opts.withDescription, + Username: username, + Password: []byte(password), + }, + } + return l, nil +} + +func allocUsernamePasswordCredential() *UsernamePasswordCredential { + return &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{}, + } +} + +func (c *UsernamePasswordCredential) clone() *UsernamePasswordCredential { + cp := proto.Clone(c.UsernamePasswordCredential) + return &UsernamePasswordCredential{ + UsernamePasswordCredential: cp.(*store.UsernamePasswordCredential), + } +} + +// TableName returns the table name. +func (c *UsernamePasswordCredential) TableName() string { + if c.tableName != "" { + return c.tableName + } + return "credential_static_username_password_credential" +} + +// SetTableName sets the table name. +func (c *UsernamePasswordCredential) SetTableName(n string) { + c.tableName = n +} + +func (c *UsernamePasswordCredential) oplog(op oplog.OpType) oplog.Metadata { + metadata := oplog.Metadata{ + "resource-public-id": []string{c.PublicId}, + "resource-type": []string{"credential-static-username-password"}, + "op-type": []string{op.String()}, + } + if c.StoreId != "" { + metadata["store-id"] = []string{c.StoreId} + } + return metadata +} + +func (c *UsernamePasswordCredential) encrypt(ctx context.Context, cipher wrapping.Wrapper) error { + const op = "static.(UsernamePasswordCredential).encrypt" + if len(c.Password) == 0 { + return errors.New(ctx, errors.InvalidParameter, op, "no password defined") + } + if err := structwrapping.WrapStruct(ctx, cipher, c.UsernamePasswordCredential, nil); err != nil { + return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt)) + } + keyId, err := cipher.KeyId(ctx) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("error reading cipher key id")) + } + c.KeyId = keyId + if err := c.hmacPassword(ctx, cipher); err != nil { + return errors.Wrap(ctx, err, op) + } + return nil +} + +func (c *UsernamePasswordCredential) decrypt(ctx context.Context, cipher wrapping.Wrapper) error { + const op = "static.(UsernamePasswordCredential).decrypt" + if err := structwrapping.UnwrapStruct(ctx, cipher, c.UsernamePasswordCredential, nil); err != nil { + return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt)) + } + return nil +} + +func (c *UsernamePasswordCredential) hmacPassword(ctx context.Context, cipher wrapping.Wrapper) error { + const op = "static.(UsernamePasswordCredential).hmacPassword" + if cipher == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing cipher") + } + hm, err := crypto.HmacSha256(ctx, c.Password, cipher, []byte(c.StoreId), nil, crypto.WithEd25519()) + if err != nil { + return errors.Wrap(ctx, err, op) + } + c.PasswordHmac = []byte(hm) + return nil +} diff --git a/internal/credential/static/usernamepassword_credential_test.go b/internal/credential/static/usernamepassword_credential_test.go new file mode 100644 index 000000000..c7596fde6 --- /dev/null +++ b/internal/credential/static/usernamepassword_credential_test.go @@ -0,0 +1,178 @@ +package static + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/credential/static/store" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/crypto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" +) + +func TestCredential_New(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kkms := kms.TestKms(t, conn, wrapper) + rw := db.New(conn) + + _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + cs := TestCredentialStore(t, conn, wrapper, prj.PublicId) + + type args struct { + username string + password credential.Password + storeId string + options []Option + } + + tests := []struct { + name string + args args + want *UsernamePasswordCredential + wantCreateErr bool + wantEncryptErr bool + }{ + { + name: "missing-password", + args: args{ + username: "test-user", + storeId: cs.PublicId, + }, + want: allocUsernamePasswordCredential(), + wantEncryptErr: true, + }, + { + name: "missing-username", + args: args{ + password: "test-pass", + storeId: cs.PublicId, + }, + want: allocUsernamePasswordCredential(), + wantCreateErr: true, + }, + { + name: "missing-store-id", + args: args{ + username: "test-user", + password: "test-pass", + }, + want: allocUsernamePasswordCredential(), + wantCreateErr: true, + }, + { + name: "valid-no-options", + args: args{ + username: "test-user", + password: "test-pass", + storeId: cs.PublicId, + }, + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "test-user", + Password: []byte("test-pass"), + StoreId: cs.PublicId, + }, + }, + }, + { + name: "valid-with-name", + args: args{ + username: "test-user", + password: "test-pass", + storeId: cs.PublicId, + options: []Option{WithName("my-credential")}, + }, + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "test-user", + Password: []byte("test-pass"), + StoreId: cs.PublicId, + Name: "my-credential", + }, + }, + }, + { + name: "valid-with-description", + args: args{ + username: "test-user", + password: "test-pass", + storeId: cs.PublicId, + options: []Option{WithDescription("my-credential-description")}, + }, + want: &UsernamePasswordCredential{ + UsernamePasswordCredential: &store.UsernamePasswordCredential{ + Username: "test-user", + Password: []byte("test-pass"), + StoreId: cs.PublicId, + Description: "my-credential-description", + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + ctx := context.Background() + got, err := NewUsernamePasswordCredential(tt.args.storeId, tt.args.username, tt.args.password, tt.args.options...) + require.NoError(err) + require.NotNil(got) + assert.Emptyf(got.PublicId, "PublicId set") + + id, err := newCredentialId(ctx) + require.NoError(err) + + tt.want.PublicId = id + got.PublicId = id + + databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.PublicId, kms.KeyPurposeDatabase) + + err = got.encrypt(ctx, databaseWrapper) + if tt.wantEncryptErr { + require.Error(err) + return + } + assert.NoError(err) + + err = rw.Create(context.Background(), got) + if tt.wantCreateErr { + require.Error(err) + return + } + assert.NoError(err) + + got2 := allocUsernamePasswordCredential() + got2.PublicId = id + assert.Equal(id, got2.GetPublicId()) + require.NoError(rw.LookupById(ctx, got2)) + + err = got2.decrypt(ctx, databaseWrapper) + require.NoError(err) + + // Timestamps and version are automatically set + tt.want.CreateTime = got2.CreateTime + tt.want.UpdateTime = got2.UpdateTime + tt.want.Version = got2.Version + + // KeyId is allocated via kms no need to validate in this test + tt.want.KeyId = got2.KeyId + got2.CtPassword = nil + + // encrypt also calculates the hmac, validate it is correct + hm, err := crypto.HmacSha256(ctx, got.Password, databaseWrapper, []byte(got.StoreId), nil, crypto.WithEd25519()) + require.NoError(err) + tt.want.PasswordHmac = []byte(hm) + + assert.Empty(cmp.Diff(tt.want, got2.clone(), protocmp.Transform())) + }) + } +} diff --git a/internal/credential/vault/private_library.go b/internal/credential/vault/private_library.go index 76f750517..ba05a45c3 100644 --- a/internal/credential/vault/private_library.go +++ b/internal/credential/vault/private_library.go @@ -18,7 +18,7 @@ import ( "google.golang.org/protobuf/proto" ) -var _ credential.UserPassword = (*usrPassCred)(nil) +var _ credential.UsernamePassword = (*usrPassCred)(nil) type usrPassCred struct { *baseCred diff --git a/internal/credential/vault/repository_credentials_test.go b/internal/credential/vault/repository_credentials_test.go index 69cc145de..2fa094eb6 100644 --- a/internal/credential/vault/repository_credentials_test.go +++ b/internal/credential/vault/repository_credentials_test.go @@ -327,14 +327,14 @@ func TestRepository_IssueCredentials(t *testing.T) { for _, dc := range got { switch dc.Library().CredentialType() { case credential.UsernamePasswordType: - if upc, ok := dc.(credential.UserPassword); ok { + if upc, ok := dc.(credential.UsernamePassword); ok { assert.NotEmpty(upc.Username()) assert.NotEmpty(upc.Password()) break } - assert.Fail("want UserPassword credential from library with credential type UserPassword") + assert.Fail("want UserPassword credential from library with credential type UsernamePassword") case credential.UnspecifiedType: - if _, ok := dc.(credential.UserPassword); ok { + if _, ok := dc.(credential.UsernamePassword); ok { assert.Fail("do not want UserPassword credential from library with credential type Unspecified") } } diff --git a/internal/daemon/controller/common/common.go b/internal/daemon/controller/common/common.go index 4945b44d9..4499ec908 100644 --- a/internal/daemon/controller/common/common.go +++ b/internal/daemon/controller/common/common.go @@ -3,6 +3,7 @@ package common import ( "github.com/hashicorp/boundary/internal/auth/oidc" "github.com/hashicorp/boundary/internal/auth/password" + credstatic "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/credential/vault" pluginhost "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" @@ -14,16 +15,17 @@ import ( ) type ( - AuthTokenRepoFactory = oidc.AuthTokenRepoFactory - VaultCredentialRepoFactory = func() (*vault.Repository, error) - IamRepoFactory func() (*iam.Repository, error) - OidcAuthRepoFactory = oidc.OidcRepoFactory - PasswordAuthRepoFactory func() (*password.Repository, error) - ServersRepoFactory func() (*servers.Repository, error) - StaticRepoFactory func() (*static.Repository, error) - PluginHostRepoFactory func() (*pluginhost.Repository, error) - HostPluginRepoFactory func() (*hostplugin.Repository, error) - SessionRepoFactory func() (*session.Repository, error) - ConnectionRepoFactory func() (*session.ConnectionRepository, error) - TargetRepoFactory func() (*target.Repository, error) + AuthTokenRepoFactory = oidc.AuthTokenRepoFactory + VaultCredentialRepoFactory = func() (*vault.Repository, error) + StaticCredentialRepoFactory = func() (*credstatic.Repository, error) + IamRepoFactory func() (*iam.Repository, error) + OidcAuthRepoFactory = oidc.OidcRepoFactory + PasswordAuthRepoFactory func() (*password.Repository, error) + ServersRepoFactory func() (*servers.Repository, error) + StaticRepoFactory func() (*static.Repository, error) + PluginHostRepoFactory func() (*pluginhost.Repository, error) + HostPluginRepoFactory func() (*hostplugin.Repository, error) + SessionRepoFactory func() (*session.Repository, error) + ConnectionRepoFactory func() (*session.ConnectionRepository, error) + TargetRepoFactory func() (*target.Repository, error) ) diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index 9fa9162dc..1284c11f1 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/config" + credstatic "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/daemon/controller/common" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/health" @@ -64,18 +65,19 @@ type Controller struct { apiGrpcGatewayTicket string // Repo factory methods - AuthTokenRepoFn common.AuthTokenRepoFactory - VaultCredentialRepoFn common.VaultCredentialRepoFactory - IamRepoFn common.IamRepoFactory - OidcRepoFn common.OidcAuthRepoFactory - PasswordAuthRepoFn common.PasswordAuthRepoFactory - ServersRepoFn common.ServersRepoFactory - SessionRepoFn common.SessionRepoFactory - ConnectionRepoFn common.ConnectionRepoFactory - StaticHostRepoFn common.StaticRepoFactory - PluginHostRepoFn common.PluginHostRepoFactory - HostPluginRepoFn common.HostPluginRepoFactory - TargetRepoFn common.TargetRepoFactory + AuthTokenRepoFn common.AuthTokenRepoFactory + VaultCredentialRepoFn common.VaultCredentialRepoFactory + StaticCredentialRepoFn common.StaticCredentialRepoFactory + IamRepoFn common.IamRepoFactory + OidcRepoFn common.OidcAuthRepoFactory + PasswordAuthRepoFn common.PasswordAuthRepoFactory + ServersRepoFn common.ServersRepoFactory + SessionRepoFn common.SessionRepoFactory + ConnectionRepoFn common.ConnectionRepoFactory + StaticHostRepoFn common.StaticRepoFactory + PluginHostRepoFn common.PluginHostRepoFactory + HostPluginRepoFn common.HostPluginRepoFactory + TargetRepoFn common.TargetRepoFactory scheduler *scheduler.Scheduler @@ -276,6 +278,9 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { c.VaultCredentialRepoFn = func() (*vault.Repository, error) { return vault.NewRepository(dbase, dbase, c.kms, c.scheduler) } + c.StaticCredentialRepoFn = func() (*credstatic.Repository, error) { + return credstatic.NewRepository(ctx, dbase, dbase, c.kms) + } c.ServersRepoFn = func() (*servers.Repository, error) { return servers.NewRepository(dbase, dbase, c.kms) } diff --git a/internal/daemon/controller/handler.go b/internal/daemon/controller/handler.go index e5218278c..0c9c0f973 100644 --- a/internal/daemon/controller/handler.go +++ b/internal/daemon/controller/handler.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/boundary/internal/daemon/controller/handlers/authmethods" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/authtokens" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentials" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentialstores" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/groups" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/health" @@ -209,7 +210,7 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error { services.RegisterManagedGroupServiceServer(s, mgs) } if _, ok := currentServices[services.CredentialStoreService_ServiceDesc.ServiceName]; !ok { - cs, err := credentialstores.NewService(c.VaultCredentialRepoFn, c.IamRepoFn) + cs, err := credentialstores.NewService(c.VaultCredentialRepoFn, c.StaticCredentialRepoFn, c.IamRepoFn) if err != nil { return fmt.Errorf("failed to create credential store handler service: %w", err) } @@ -222,6 +223,13 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error { } services.RegisterCredentialLibraryServiceServer(s, cl) } + if _, ok := currentServices[services.CredentialService_ServiceDesc.ServiceName]; !ok { + c, err := credentials.NewService(c.StaticCredentialRepoFn, c.IamRepoFn) + if err != nil { + return fmt.Errorf("failed to create credential handler service: %w", err) + } + services.RegisterCredentialServiceServer(s, c) + } if _, ok := s.GetServiceInfo()[opsservices.HealthService_ServiceDesc.ServiceName]; !ok { hs := health.NewService() opsservices.RegisterHealthServiceServer(s, hs) @@ -279,6 +287,9 @@ func registerGrpcGatewayEndpoints(ctx context.Context, gwMux *runtime.ServeMux, if err := services.RegisterCredentialLibraryServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil { return fmt.Errorf("failed to register credential library service handler: %w", err) } + if err := services.RegisterCredentialServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil { + return fmt.Errorf("failed to register credential service handler: %w", err) + } return nil } diff --git a/internal/daemon/controller/handlers/credentials/credential_service.go b/internal/daemon/controller/handlers/credentials/credential_service.go new file mode 100644 index 000000000..a45f9cd7d --- /dev/null +++ b/internal/daemon/controller/handlers/credentials/credential_service.go @@ -0,0 +1,592 @@ +package credentials + +import ( + "context" + "encoding/base64" + "fmt" + + "github.com/hashicorp/boundary/globals" + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/credential/static" + "github.com/hashicorp/boundary/internal/credential/static/store" + "github.com/hashicorp/boundary/internal/daemon/controller/auth" + "github.com/hashicorp/boundary/internal/daemon/controller/common" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers" + "github.com/hashicorp/boundary/internal/errors" + pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/perms" + "github.com/hashicorp/boundary/internal/requests" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" + "github.com/hashicorp/boundary/internal/types/subtypes" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials" + "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +const ( + usernameField = "attributes.username" + passwordField = "attributes.password" + domain = "credential" +) + +var ( + maskManager handlers.MaskManager + + // IdActions contains the set of actions that can be performed on + // individual resources + IdActions = action.ActionSet{ + action.NoOp, + action.Read, + action.Update, + action.Delete, + } + + // CollectionActions contains the set of actions that can be performed on + // this collection + CollectionActions = action.ActionSet{ + action.Create, + action.List, + } +) + +func init() { + var err error + if maskManager, err = handlers.NewMaskManager(handlers.MaskDestination{&store.UsernamePasswordCredential{}}, + handlers.MaskSource{&pb.Credential{}, &pb.UsernamePasswordAttributes{}}); err != nil { + panic(err) + } +} + +// Service handles request as described by the pbs.CredentialServiceServer interface. +type Service struct { + pbs.UnimplementedCredentialServiceServer + + iamRepoFn common.IamRepoFactory + repoFn common.StaticCredentialRepoFactory +} + +// NewService returns a credential service which handles credential related requests to boundary. +func NewService(repo common.StaticCredentialRepoFactory, iamRepo common.IamRepoFactory) (Service, error) { + const op = "credentials.NewService" + if iamRepo == nil { + return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing iam repository") + } + if repo == nil { + return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static credential repository") + } + return Service{iamRepoFn: iamRepo, repoFn: repo}, nil +} + +var _ pbs.CredentialServiceServer = Service{} + +// ListCredentials implements the interface pbs.CredentialServiceServer +func (s Service) ListCredentials(ctx context.Context, req *pbs.ListCredentialsRequest) (*pbs.ListCredentialsResponse, error) { + if err := validateListRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetCredentialStoreId(), action.List) + if authResults.Error != nil { + return nil, authResults.Error + } + + creds, err := s.listFromRepo(ctx, req.GetCredentialStoreId()) + if err != nil { + return nil, err + } + if len(creds) == 0 { + return &pbs.ListCredentialsResponse{}, nil + } + + filter, err := handlers.NewFilter(req.GetFilter()) + if err != nil { + return nil, err + } + finalItems := make([]*pb.Credential, 0, len(creds)) + res := perms.Resource{ + ScopeId: authResults.Scope.Id, + Type: resource.Credential, + Pin: req.GetCredentialStoreId(), + } + for _, item := range creds { + res.Id = item.GetPublicId() + authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings() + if len(authorizedActions) == 0 { + continue + } + + outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId) + outputOpts := make([]handlers.Option, 0, 3) + outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if outputFields.Has(globals.ScopeField) { + outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) + } + if outputFields.Has(globals.AuthorizedActionsField) { + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions)) + } + + item, err := toProto(item, outputOpts...) + if err != nil { + return nil, err + } + + filterable, err := subtypes.Filterable(item) + if err != nil { + return nil, err + } + if filter.Match(filterable) { + finalItems = append(finalItems, item) + } + } + return &pbs.ListCredentialsResponse{Items: finalItems}, nil +} + +// GetCredential implements the interface pbs.CredentialServiceServer. +func (s Service) GetCredential(ctx context.Context, req *pbs.GetCredentialRequest) (*pbs.GetCredentialResponse, error) { + const op = "credentials.(Service).GetCredential" + + if err := validateGetRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } + c, err := s.getFromRepo(ctx, req.GetId()) + if err != nil { + return nil, err + } + + outputFields, ok := requests.OutputFields(ctx) + if !ok { + return nil, errors.New(ctx, errors.Internal, op, "no request context found") + } + + outputOpts := make([]handlers.Option, 0, 3) + outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if outputFields.Has(globals.ScopeField) { + outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) + } + if outputFields.Has(globals.AuthorizedActionsField) { + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, c.GetPublicId(), IdActions).Strings())) + } + + item, err := toProto(c, outputOpts...) + if err != nil { + return nil, err + } + + return &pbs.GetCredentialResponse{Item: item}, nil +} + +// CreateCredential implements the interface pbs.CredentialServiceServer. +func (s Service) CreateCredential(ctx context.Context, req *pbs.CreateCredentialRequest) (*pbs.CreateCredentialResponse, error) { + const op = "credentials.(Service).CreateCredential" + + if err := validateCreateRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetItem().GetCredentialStoreId(), action.Create) + if authResults.Error != nil { + return nil, authResults.Error + } + cl, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem()) + if err != nil { + return nil, err + } + + outputFields, ok := requests.OutputFields(ctx) + if !ok { + return nil, errors.New(ctx, errors.Internal, op, "no request context found") + } + + outputOpts := make([]handlers.Option, 0, 3) + outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if outputFields.Has(globals.ScopeField) { + outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) + } + if outputFields.Has(globals.AuthorizedActionsField) { + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cl.GetPublicId(), IdActions).Strings())) + } + + item, err := toProto(cl, outputOpts...) + if err != nil { + return nil, err + } + + return &pbs.CreateCredentialResponse{ + Item: item, + Uri: fmt.Sprintf("credentials/%s", item.GetId()), + }, nil +} + +// UpdateCredential implements the interface pbs.CredentialServiceServer. +func (s Service) UpdateCredential(ctx context.Context, req *pbs.UpdateCredentialRequest) (*pbs.UpdateCredentialResponse, error) { + const op = "credentials.(Service).UpdateCredential" + + cur, err := s.getFromRepo(ctx, req.GetId()) + if err != nil { + return nil, err + } + storeId := cur.GetStoreId() + + if err := validateUpdateRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetId(), action.Update) + if authResults.Error != nil { + return nil, authResults.Error + } + c, err := s.updateInRepo(ctx, authResults.Scope.GetId(), storeId, req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) + if err != nil { + return nil, err + } + + outputFields, ok := requests.OutputFields(ctx) + if !ok { + return nil, errors.New(ctx, errors.Internal, op, "no request context found") + } + + outputOpts := make([]handlers.Option, 0, 3) + outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) + if outputFields.Has(globals.ScopeField) { + outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) + } + if outputFields.Has(globals.AuthorizedActionsField) { + outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, c.GetPublicId(), IdActions).Strings())) + } + + item, err := toProto(c, outputOpts...) + if err != nil { + return nil, err + } + + return &pbs.UpdateCredentialResponse{Item: item}, nil +} + +// DeleteCredential implements the interface pbs.CredentialServiceServer. +func (s Service) DeleteCredential(ctx context.Context, req *pbs.DeleteCredentialRequest) (*pbs.DeleteCredentialResponse, error) { + if err := validateDeleteRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } + _, err := s.deleteFromRepo(ctx, authResults.Scope.GetId(), req.GetId()) + if err != nil { + return nil, err + } + return nil, nil +} + +func (s Service) listFromRepo(ctx context.Context, storeId string) ([]credential.Static, error) { + const op = "credentials.(Service).listFromRepo" + repo, err := s.repoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + up, err := repo.ListCredentials(ctx, storeId) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + creds := make([]credential.Static, 0, len(up)) + for _, s := range up { + creds = append(creds, s) + } + + return creds, nil +} + +func (s Service) getFromRepo(ctx context.Context, id string) (credential.Static, error) { + const op = "credentials.(Service).getFromRepo" + repo, err := s.repoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + cred, err := repo.LookupCredential(ctx, id) + if err != nil && !errors.IsNotFoundError(err) { + return nil, errors.Wrap(ctx, err, op) + } + if cred == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("credential %q not found", id)) + } + return cred, err +} + +func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Credential) (credential.Static, error) { + const op = "credentials.(Service).createInRepo" + switch item.GetType() { + case static.UsernamePasswordSubtype.String(): + cred, err := toUsernamePasswordStorageCredential(item.GetCredentialStoreId(), item) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + repo, err := s.repoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + out, err := repo.CreateUsernamePasswordCredential(ctx, scopeId, cred) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential")) + } + if out == nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential but no error returned from repository.") + } + return out, nil + default: + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, fmt.Sprintf("Unsupported credential type %q", item.GetType())) + } +} + +func (s Service) updateInRepo( + ctx context.Context, + scopeId, storeId, id string, + masks []string, + in *pb.Credential, +) (credential.Static, error) { + const op = "credentials.(Service).updateInRepo" + + var dbMasks []string + item := proto.Clone(in).(*pb.Credential) + + dbMasks = append(dbMasks, maskManager.Translate(masks)...) + if len(dbMasks) == 0 { + return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."}) + } + + switch subtypes.SubtypeFromId(domain, id) { + case static.UsernamePasswordSubtype: + cred, err := toUsernamePasswordStorageCredential(storeId, in) + cred.PublicId = id + repo, err := s.repoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + out, rowsUpdated, err := repo.UpdateUsernamePasswordCredential(ctx, scopeId, cred, item.GetVersion(), dbMasks) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential")) + } + if rowsUpdated == 0 { + return nil, handlers.NotFoundErrorf("Credential %q doesn't exist or incorrect version provided.", id) + } + return out, nil + default: + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, fmt.Sprintf("Unsupported credential type %q", item.GetType())) + + } +} + +func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) { + const op = "credentials.(Service).deleteFromRepo" + repo, err := s.repoFn() + if err != nil { + return false, err + } + + rows, err := repo.DeleteCredential(ctx, scopeId, id) + if err != nil { + if errors.IsNotFoundError(err) { + return false, nil + } + return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential")) + } + return rows > 0, nil +} + +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + const op = "credentials.(Service).authResult" + res := auth.VerifyResults{} + repo, err := s.repoFn() + if err != nil { + res.Error = err + return res + } + + var parentId string + opts := []auth.Option{auth.WithType(resource.Credential), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + default: + opts = append(opts, auth.WithId(id)) + cred, err := repo.LookupCredential(ctx, id) + if err != nil { + res.Error = err + return res + } + if cred == nil { + res.Error = handlers.NotFoundError() + return res + } + parentId = cred.GetStoreId() + + } + + if parentId == "" { + res.Error = errors.New(ctx, errors.RecordNotFound, op, "unable to find credential store for provided credential") + return res + } + opts = append(opts, auth.WithPin(parentId)) + + cs, err := repo.LookupCredentialStore(ctx, parentId) + if err != nil { + res.Error = err + return res + } + if cs == nil { + res.Error = handlers.NotFoundError() + return res + } + opts = append(opts, auth.WithScopeId(cs.GetScopeId())) + + return auth.Verify(ctx, opts...) +} + +func toProto(in credential.Static, opt ...handlers.Option) (*pb.Credential, error) { + opts := handlers.GetOpts(opt...) + if opts.WithOutputFields == nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building credential proto") + } + outputFields := *opts.WithOutputFields + + out := pb.Credential{} + if outputFields.Has(globals.IdField) { + out.Id = in.GetPublicId() + } + if outputFields.Has(globals.CredentialStoreIdField) { + out.CredentialStoreId = in.GetStoreId() + } + if outputFields.Has(globals.TypeField) { + switch in.(type) { + case *static.UsernamePasswordCredential: + out.Type = static.UsernamePasswordSubtype.String() + } + } + if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" { + out.Description = wrapperspb.String(in.GetDescription()) + } + if outputFields.Has(globals.NameField) && in.GetName() != "" { + out.Name = wrapperspb.String(in.GetName()) + } + if outputFields.Has(globals.CreatedTimeField) { + out.CreatedTime = in.GetCreateTime().GetTimestamp() + } + if outputFields.Has(globals.UpdatedTimeField) { + out.UpdatedTime = in.GetUpdateTime().GetTimestamp() + } + if outputFields.Has(globals.VersionField) { + out.Version = in.GetVersion() + } + if outputFields.Has(globals.ScopeField) { + out.Scope = opts.WithScope + } + if outputFields.Has(globals.AuthorizedActionsField) { + out.AuthorizedActions = opts.WithAuthorizedActions + } + + switch cred := in.(type) { + case *static.UsernamePasswordCredential: + if outputFields.Has(globals.AttributesField) { + out.Attrs = &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String(cred.GetUsername()), + PasswordHmac: base64.RawURLEncoding.EncodeToString(cred.GetPasswordHmac()), + }, + } + } + } + return &out, nil +} + +func toUsernamePasswordStorageCredential(storeId string, in *pb.Credential) (out *static.UsernamePasswordCredential, err error) { + const op = "credentials.toStorageCredential" + var opts []static.Option + if in.GetName() != nil { + opts = append(opts, static.WithName(in.GetName().GetValue())) + } + if in.GetDescription() != nil { + opts = append(opts, static.WithDescription(in.GetDescription().GetValue())) + } + + attrs := in.GetUsernamePasswordAttributes() + cs, err := static.NewUsernamePasswordCredential( + storeId, + attrs.GetUsername().GetValue(), + credential.Password(attrs.GetPassword().GetValue()), + opts...) + if err != nil { + return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to build credential")) + } + + return cs, err +} + +// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce +// requirements on the structure of the request. They verify that: +// * The path passed in is correctly formatted +// * All required parameters are set +// * There are no conflicting parameters provided +func validateGetRequest(req *pbs.GetCredentialRequest) error { + return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, static.CredentialPrefix) +} + +func validateCreateRequest(req *pbs.CreateCredentialRequest) error { + return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string { + badFields := map[string]string{} + if req.Item.GetType() != static.UsernamePasswordSubtype.String() { + badFields[globals.TypeField] = fmt.Sprintf("Unsupported credential type %q", req.Item.GetType()) + } + if req.Item.GetCredentialStoreId() == "" { + badFields[globals.CredentialStoreIdField] = "This field must be a valid credential store id." + } + + if req.Item.GetUsernamePasswordAttributes().GetUsername().GetValue() == "" { + badFields[usernameField] = "Field required for creating a username-password credential." + } + if req.Item.GetUsernamePasswordAttributes().GetPassword().GetValue() == "" { + badFields[passwordField] = "Field required for creating a username-password credential." + } + + return badFields + }) +} + +func validateUpdateRequest(req *pbs.UpdateCredentialRequest) error { + return handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string { + badFields := map[string]string{} + if req.GetItem().GetType() != "" && req.GetItem().GetType() != static.UsernamePasswordSubtype.String() { + badFields[globals.TypeField] = "Cannot modify resource type." + } + + attrs := req.GetItem().GetUsernamePasswordAttributes() + if handlers.MaskContains(req.GetUpdateMask().GetPaths(), usernameField) && attrs.GetUsername().GetValue() == "" { + badFields[usernameField] = "This is a required field and cannot be set to empty." + } + if handlers.MaskContains(req.GetUpdateMask().GetPaths(), passwordField) && attrs.GetPassword().GetValue() == "" { + badFields[passwordField] = "This is a required field and cannot be set to empty." + } + + return badFields + }, static.CredentialPrefix) +} + +func validateDeleteRequest(req *pbs.DeleteCredentialRequest) error { + return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, static.CredentialPrefix) +} + +func validateListRequest(req *pbs.ListCredentialsRequest) error { + badFields := map[string]string{} + if !handlers.ValidId(handlers.Id(req.GetCredentialStoreId()), static.CredentialStorePrefix) { + badFields[globals.CredentialStoreIdField] = "This field must be a valid credential store id." + } + if _, err := handlers.NewFilter(req.GetFilter()); err != nil { + badFields["filter"] = fmt.Sprintf("This field could not be parsed. %v", err) + } + if len(badFields) > 0 { + return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields) + } + return nil +} diff --git a/internal/daemon/controller/handlers/credentials/credential_service_test.go b/internal/daemon/controller/handlers/credentials/credential_service_test.go new file mode 100644 index 000000000..96cc51cc0 --- /dev/null +++ b/internal/daemon/controller/handlers/credentials/credential_service_test.go @@ -0,0 +1,742 @@ +package credentials + +import ( + "context" + "encoding/base64" + "fmt" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/boundary/internal/credential/static" + "github.com/hashicorp/boundary/internal/credential/vault" + "github.com/hashicorp/boundary/internal/daemon/controller/auth" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/libs/crypto" + "github.com/hashicorp/boundary/internal/scheduler" + "github.com/hashicorp/boundary/internal/types/scope" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials" + scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/fieldmaskpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +var testAuthorizedActions = []string{"no-op", "read", "update", "delete"} + +func TestList(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kkms := kms.TestKms(t, conn, wrapper) + rw := db.New(conn) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + staticRepoFn := func() (*static.Repository, error) { + return static.NewRepository(context.Background(), rw, rw, kkms) + } + + _, prj := iam.TestScopes(t, iamRepo) + store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + storeNoCreds := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + + var wantCreds []*pb.Credential + for i := 0; i < 10; i++ { + user := fmt.Sprintf("user-%d", i) + pass := fmt.Sprintf("pass-%d", i) + c := static.TestUsernamePasswordCredential(t, conn, wrapper, user, pass, store.GetPublicId(), prj.GetPublicId()) + + databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase) + require.NoError(t, err) + hm, err := crypto.HmacSha256(context.Background(), []byte(pass), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519()) + require.NoError(t, err) + + wantCreds = append(wantCreds, &pb.Credential{ + Id: c.GetPublicId(), + CredentialStoreId: store.GetPublicId(), + Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()}, + CreatedTime: c.GetCreateTime().GetTimestamp(), + UpdatedTime: c.GetUpdateTime().GetTimestamp(), + Version: c.GetVersion(), + Type: static.UsernamePasswordSubtype.String(), + AuthorizedActions: testAuthorizedActions, + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String(c.GetUsername()), + PasswordHmac: base64.RawURLEncoding.EncodeToString([]byte(hm)), + }, + }, + }) + } + + cases := []struct { + name string + req *pbs.ListCredentialsRequest + res *pbs.ListCredentialsResponse + anonRes *pbs.ListCredentialsResponse + err error + }{ + { + name: "List many credentials", + req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId()}, + res: &pbs.ListCredentialsResponse{Items: wantCreds}, + anonRes: &pbs.ListCredentialsResponse{Items: wantCreds}, + }, + { + name: "List no credentials", + req: &pbs.ListCredentialsRequest{CredentialStoreId: storeNoCreds.GetPublicId()}, + res: &pbs.ListCredentialsResponse{}, + anonRes: &pbs.ListCredentialsResponse{}, + }, + { + name: "Filter to one credential", + req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantCreds[1].GetId())}, + res: &pbs.ListCredentialsResponse{Items: wantCreds[1:2]}, + anonRes: &pbs.ListCredentialsResponse{Items: wantCreds[1:2]}, + }, + { + name: "Filter on Attribute", + req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/username"==%q`, wantCreds[2].GetUsernamePasswordAttributes().GetUsername().Value)}, + res: &pbs.ListCredentialsResponse{Items: wantCreds[2:3]}, + anonRes: &pbs.ListCredentialsResponse{}, // anonymous user does not have access to attributes + }, + { + name: "Filter to no credential", + req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: `"/item/id"=="doesnt match"`}, + res: &pbs.ListCredentialsResponse{}, + anonRes: &pbs.ListCredentialsResponse{}, + }, + { + name: "Filter Bad Format", + req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: `"//id/"=="bad"`}, + err: handlers.InvalidArgumentErrorf("bad format", nil), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + s, err := NewService(staticRepoFn, iamRepoFn) + require.NoError(t, err, "Couldn't create new host set service.") + + // Test non-anonymous listing + got, gErr := s.ListCredentials(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req) + if tc.err != nil { + require.Error(t, gErr) + assert.True(t, errors.Is(gErr, tc.err), "ListCredentialStore(%q) got error %v, wanted %v", tc.req, gErr, tc.err) + return + } + require.NoError(t, gErr) + assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform(), protocmp.SortRepeated(func(x, y *pb.Credential) bool { + return x.Id < y.Id + }))) + + // Test anonymous listing + got, gErr = s.ListCredentials(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), tc.req) + require.NoError(t, gErr) + assert.Len(t, got.Items, len(tc.anonRes.Items)) + for _, item := range got.GetItems() { + require.Nil(t, item.CreatedTime) + require.Nil(t, item.UpdatedTime) + require.Zero(t, item.Version) + } + }) + } +} + +func TestGet(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kkms := kms.TestKms(t, conn, wrapper) + rw := db.New(conn) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + staticRepoFn := func() (*static.Repository, error) { + return static.NewRepository(context.Background(), rw, rw, kkms) + } + + _, prj := iam.TestScopes(t, iamRepo) + + store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + cred := static.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId()) + s, err := NewService(staticRepoFn, iamRepoFn) + require.NoError(t, err) + + // Calculate hmac + databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase) + require.NoError(t, err) + hm, err := crypto.HmacSha256(context.Background(), []byte("pass"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519()) + require.NoError(t, err) + + cases := []struct { + name string + id string + res *pbs.GetCredentialResponse + err error + }{ + { + name: "success", + id: cred.GetPublicId(), + res: &pbs.GetCredentialResponse{ + Item: &pb.Credential{ + Id: cred.GetPublicId(), + CredentialStoreId: cred.GetStoreId(), + Scope: &scopepb.ScopeInfo{Id: store.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()}, + Type: static.UsernamePasswordSubtype.String(), + AuthorizedActions: testAuthorizedActions, + CreatedTime: cred.CreateTime.GetTimestamp(), + UpdatedTime: cred.UpdateTime.GetTimestamp(), + Version: 1, + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String("user"), + PasswordHmac: base64.RawURLEncoding.EncodeToString([]byte(hm)), + }, + }, + }, + }, + }, + { + name: "not found error", + id: fmt.Sprintf("%s_1234567890", static.CredentialPrefix), + err: handlers.NotFoundError(), + }, + { + name: "bad prefix", + id: fmt.Sprintf("%s_1234567890", static.CredentialStorePrefix), + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + req := &pbs.GetCredentialRequest{Id: tc.id} + // Test non-anonymous get + got, gErr := s.GetCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), req) + if tc.err != nil { + require.Error(t, gErr) + assert.True(t, errors.Is(gErr, tc.err)) + return + } + require.NoError(t, gErr) + assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform())) + + // Test anonymous get + got, gErr = s.GetCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), req) + require.NoError(t, gErr) + require.Nil(t, got.Item.CreatedTime) + require.Nil(t, got.Item.UpdatedTime) + require.Zero(t, got.Item.Version) + }) + } +} + +func TestDelete(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + rw := db.New(conn) + err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms) + require.NoError(t, err) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + staticRepoFn := func() (*static.Repository, error) { + return static.NewRepository(context.Background(), rw, rw, kms) + } + + _, prj := iam.TestScopes(t, iamRepo) + + store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + cred := static.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId()) + s, err := NewService(staticRepoFn, iamRepoFn) + require.NoError(t, err) + + cases := []struct { + name string + id string + err error + res *pbs.DeleteCredentialResponse + }{ + { + name: "success", + id: cred.GetPublicId(), + }, + { + name: "not found error", + id: fmt.Sprintf("%s_1234567890", static.CredentialPrefix), + err: handlers.NotFoundError(), + }, + { + name: "bad prefix", + id: fmt.Sprintf("%s_1234567890", static.CredentialStorePrefix), + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got, gErr := s.DeleteCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), &pbs.DeleteCredentialRequest{Id: tc.id}) + assert.EqualValuesf(t, tc.res, got, "DeleteCredentialStore got response %q, wanted %q", got, tc.res) + if tc.err != nil { + require.Error(t, gErr) + assert.True(t, errors.Is(gErr, tc.err)) + return + } + require.NoError(t, gErr) + g, err := s.GetCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), &pbs.GetCredentialRequest{Id: tc.id}) + assert.Nil(t, g) + assert.True(t, errors.Is(err, handlers.NotFoundError())) + }) + } +} + +func TestCreate(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kkms := kms.TestKms(t, conn, wrapper) + rw := db.New(conn) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + repoFn := func() (*static.Repository, error) { + return static.NewRepository(context.Background(), rw, rw, kkms) + } + + _, prj := iam.TestScopes(t, iamRepo) + store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + + cases := []struct { + name string + req *pbs.CreateCredentialRequest + res *pbs.CreateCredentialResponse + idPrefix string + err error + wantErr bool + }{ + { + name: "Can't specify Id", + req: &pbs.CreateCredentialRequest{Item: &pb.Credential{ + CredentialStoreId: store.GetPublicId(), + Id: static.CredentialPrefix + "_notallowed", + Type: static.UsernamePasswordSubtype.String(), + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String("username"), + Password: wrapperspb.String("password"), + }, + }, + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Can't specify Created Time", + req: &pbs.CreateCredentialRequest{Item: &pb.Credential{ + CredentialStoreId: store.GetPublicId(), + CreatedTime: timestamppb.Now(), + Type: static.UsernamePasswordSubtype.String(), + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String("username"), + }, + }, + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Can't specify Updated Time", + req: &pbs.CreateCredentialRequest{Item: &pb.Credential{ + CredentialStoreId: store.GetPublicId(), + UpdatedTime: timestamppb.Now(), + Type: static.UsernamePasswordSubtype.String(), + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String("username"), + Password: wrapperspb.String("password"), + }, + }, + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Must provide type", + req: &pbs.CreateCredentialRequest{Item: &pb.Credential{ + CredentialStoreId: store.GetPublicId(), + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String("username"), + Password: wrapperspb.String("password"), + }, + }, + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Must provide username", + req: &pbs.CreateCredentialRequest{Item: &pb.Credential{ + CredentialStoreId: store.GetPublicId(), + Type: static.UsernamePasswordSubtype.String(), + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Password: wrapperspb.String("password"), + }, + }, + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Must provide password]", + req: &pbs.CreateCredentialRequest{Item: &pb.Credential{ + CredentialStoreId: store.GetPublicId(), + Type: static.UsernamePasswordSubtype.String(), + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String("username"), + }, + }, + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "valid", + req: &pbs.CreateCredentialRequest{Item: &pb.Credential{ + CredentialStoreId: store.GetPublicId(), + Type: static.UsernamePasswordSubtype.String(), + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String("username"), + Password: wrapperspb.String("password"), + }, + }, + }}, + idPrefix: static.CredentialPrefix + "_", + res: &pbs.CreateCredentialResponse{ + Uri: fmt.Sprintf("credentials/%s_", static.CredentialPrefix), + Item: &pb.Credential{ + Id: store.GetPublicId(), + CredentialStoreId: store.GetPublicId(), + CreatedTime: store.GetCreateTime().GetTimestamp(), + UpdatedTime: store.GetUpdateTime().GetTimestamp(), + Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()}, + Version: 1, + Type: static.UsernamePasswordSubtype.String(), + AuthorizedActions: testAuthorizedActions, + }, + }, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + s, err := NewService(repoFn, iamRepoFn) + require.NoError(err, "Error when getting new credential store service.") + + got, gErr := s.CreateCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req) + if tc.wantErr || tc.err != nil { + require.Error(gErr) + if tc.err != nil { + assert.True(errors.Is(gErr, tc.err), "CreateCredential(...) got error %v, wanted %v", gErr, tc.err) + } + return + } + require.NoError(gErr) + if tc.res == nil { + require.Nil(got) + } + if got != nil { + assert.Contains(got.GetUri(), tc.res.Uri) + assert.True(strings.HasPrefix(got.GetItem().GetId(), tc.idPrefix)) + gotCreateTime := got.GetItem().GetCreatedTime() + gotUpdateTime := got.GetItem().GetUpdatedTime() + + // Verify it is a credential store created after the test setup's default credential store + assert.True(gotCreateTime.AsTime().After(store.CreateTime.AsTime()), "New credential should have been created after default credential store. Was created %v, which is after %v", gotCreateTime, store.CreateTime.AsTime()) + assert.True(gotUpdateTime.AsTime().After(store.CreateTime.AsTime()), "New credential should have been updated after default credential store. Was updated %v, which is after %v", gotUpdateTime, store.CreateTime.AsTime()) + + // Calculate hmac + databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.PublicId, kms.KeyPurposeDatabase) + password := tc.req.GetItem().GetUsernamePasswordAttributes().GetPassword().GetValue() + hm, err := crypto.HmacSha256(context.Background(), []byte(password), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519()) + require.NoError(err) + + // Validate attributes equal + assert.Equal(tc.req.GetItem().GetUsernamePasswordAttributes().GetUsername().GetValue(), + got.GetItem().GetUsernamePasswordAttributes().GetUsername().GetValue()) + assert.Equal(base64.RawURLEncoding.EncodeToString([]byte(hm)), got.GetItem().GetUsernamePasswordAttributes().GetPasswordHmac()) + assert.Empty(got.GetItem().GetUsernamePasswordAttributes().GetPassword()) + + // Clear attributes for compare below + got.Item.Attrs = nil + + // Clear all values which are hard to compare against. + got.Uri, tc.res.Uri = "", "" + got.Item.Id, tc.res.Item.Id = "", "" + got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil + } + assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform(), protocmp.SortRepeatedFields(got)), "CreateCredential(%q) got response %q, wanted %q", tc.req, got, tc.res) + }) + } +} + +func TestUpdate(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kkms := kms.TestKms(t, conn, wrapper) + rw := db.New(conn) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + staticRepoFn := func() (*static.Repository, error) { + return static.NewRepository(context.Background(), rw, rw, kkms) + } + + _, prj := iam.TestScopes(t, iamRepo) + ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()) + + s, err := NewService(staticRepoFn, iamRepoFn) + require.NoError(t, err) + + fieldmask := func(paths ...string) *fieldmaskpb.FieldMask { + return &fieldmaskpb.FieldMask{Paths: paths} + } + + store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + + freshCred := func(user, pass string) (*static.UsernamePasswordCredential, func()) { + t.Helper() + cred := static.TestUsernamePasswordCredential(t, conn, wrapper, user, pass, store.GetPublicId(), prj.GetPublicId()) + clean := func() { + _, err := s.DeleteCredential(ctx, &pbs.DeleteCredentialRequest{Id: cred.GetPublicId()}) + require.NoError(t, err) + } + return cred, clean + } + + databaseWrapper, err := kkms.GetWrapper(context.Background(), prj.GetPublicId(), kms.KeyPurposeDatabase) + require.NoError(t, err) + + successCases := []struct { + name string + req *pbs.UpdateCredentialRequest + res func(cred *pb.Credential) *pb.Credential + }{ + { + name: "name", + req: &pbs.UpdateCredentialRequest{ + UpdateMask: fieldmask("name"), + Item: &pb.Credential{ + Name: wrapperspb.String("new-name"), + }, + }, + res: func(in *pb.Credential) *pb.Credential { + out := proto.Clone(in).(*pb.Credential) + out.Name = wrapperspb.String("new-name") + return out + }, + }, + { + name: "description", + req: &pbs.UpdateCredentialRequest{ + UpdateMask: fieldmask("description"), + Item: &pb.Credential{ + Description: wrapperspb.String("new-description"), + }, + }, + res: func(in *pb.Credential) *pb.Credential { + out := proto.Clone(in).(*pb.Credential) + out.Description = wrapperspb.String("new-description") + return out + }, + }, + { + name: "name-and-description", + req: &pbs.UpdateCredentialRequest{ + UpdateMask: fieldmask("name", "description"), + Item: &pb.Credential{ + Name: wrapperspb.String("new-name"), + Description: wrapperspb.String("new-description"), + }, + }, + res: func(in *pb.Credential) *pb.Credential { + out := proto.Clone(in).(*pb.Credential) + out.Name = wrapperspb.String("new-name") + out.Description = wrapperspb.String("new-description") + return out + }, + }, + { + name: "update-username", + req: &pbs.UpdateCredentialRequest{ + UpdateMask: fieldmask("attributes.username"), + Item: &pb.Credential{ + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String("new-user-name"), + }, + }, + }, + }, + res: func(in *pb.Credential) *pb.Credential { + out := proto.Clone(in).(*pb.Credential) + out.GetUsernamePasswordAttributes().Username = wrapperspb.String("new-user-name") + return out + }, + }, + { + name: "update-password", + req: &pbs.UpdateCredentialRequest{ + UpdateMask: fieldmask("attributes.password"), + Item: &pb.Credential{ + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Password: wrapperspb.String("new-password"), + }, + }, + }, + }, + res: func(in *pb.Credential) *pb.Credential { + out := proto.Clone(in).(*pb.Credential) + hm, err := crypto.HmacSha256(context.Background(), []byte("new-password"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519()) + require.NoError(t, err) + out.GetUsernamePasswordAttributes().PasswordHmac = base64.RawURLEncoding.EncodeToString([]byte(hm)) + return out + }, + }, + { + name: "update-username-and-password", + req: &pbs.UpdateCredentialRequest{ + UpdateMask: fieldmask("attributes.username", "attributes.password"), + Item: &pb.Credential{ + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: wrapperspb.String("new-username"), + Password: wrapperspb.String("new-password"), + }, + }, + }, + }, + res: func(in *pb.Credential) *pb.Credential { + out := proto.Clone(in).(*pb.Credential) + + out.GetUsernamePasswordAttributes().Username = wrapperspb.String("new-username") + + hm, err := crypto.HmacSha256(context.Background(), []byte("new-password"), databaseWrapper, []byte(store.GetPublicId()), nil, crypto.WithEd25519()) + require.NoError(t, err) + out.GetUsernamePasswordAttributes().PasswordHmac = base64.RawURLEncoding.EncodeToString([]byte(hm)) + return out + }, + }, + } + + for _, tc := range successCases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + cred, cleanup := freshCred("user", "pass") + defer cleanup() + + if tc.req.Item.GetVersion() == 0 { + tc.req.Item.Version = 1 + } + if tc.req.GetId() == "" { + tc.req.Id = cred.GetPublicId() + } + resToChange, err := s.GetCredential(ctx, &pbs.GetCredentialRequest{Id: cred.GetPublicId()}) + require.NoError(err) + want := &pbs.UpdateCredentialResponse{Item: tc.res(resToChange.GetItem())} + + got, gErr := s.UpdateCredential(ctx, tc.req) + require.NoError(gErr) + require.NotNil(got) + + gotUpdateTime := got.GetItem().GetUpdatedTime() + created := cred.GetCreateTime().GetTimestamp() + assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created) + + want.Item.UpdatedTime = got.Item.UpdatedTime + + assert.EqualValues(2, got.Item.Version) + want.Item.Version = 2 + + assert.Empty(cmp.Diff(got, want, protocmp.Transform())) + }) + } + + // cant update read only fields + cred, cleanup := freshCred("user", "pass") + defer cleanup() + + newStore := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + + roCases := []struct { + path string + item *pb.Credential + matcher func(t *testing.T, e error) // When not set defaults to checking against InvalidArgument Error + }{ + { + path: "type", + item: &pb.Credential{Type: "something"}, + }, + { + path: "store_id", + item: &pb.Credential{CredentialStoreId: newStore.GetPublicId()}, + }, + { + path: "updated_time", + item: &pb.Credential{UpdatedTime: timestamppb.Now()}, + }, + { + path: "created_time", + item: &pb.Credential{UpdatedTime: timestamppb.Now()}, + }, + { + path: "authorized_actions", + item: &pb.Credential{AuthorizedActions: append(testAuthorizedActions, "another")}, + }, + } + for _, tc := range roCases { + t.Run(fmt.Sprintf("ReadOnlyField/%s", tc.path), func(t *testing.T) { + req := &pbs.UpdateCredentialRequest{ + Id: cred.GetPublicId(), + Item: tc.item, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{tc.path}}, + } + req.Item.Version = cred.Version + + got, gErr := s.UpdateCredential(ctx, req) + assert.Error(t, gErr) + matcher := tc.matcher + if matcher == nil { + matcher = func(t *testing.T, e error) { + assert.Truef(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)), "got error %v, wanted invalid argument", gErr) + } + } + matcher(t, gErr) + assert.Nil(t, got) + }) + } +} diff --git a/internal/daemon/controller/handlers/credentialstores/credentialstore_service.go b/internal/daemon/controller/handlers/credentialstores/credentialstore_service.go index 54ff1419f..c5769a004 100644 --- a/internal/daemon/controller/handlers/credentialstores/credentialstore_service.go +++ b/internal/daemon/controller/handlers/credentialstores/credentialstore_service.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/credential/vault/store" "github.com/hashicorp/boundary/internal/daemon/controller/auth" @@ -15,6 +16,7 @@ import ( "github.com/hashicorp/boundary/internal/daemon/controller/common/scopeids" "github.com/hashicorp/boundary/internal/daemon/controller/handlers" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentials" "github.com/hashicorp/boundary/internal/errors" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/perms" @@ -25,6 +27,7 @@ import ( "github.com/hashicorp/boundary/internal/types/subtypes" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentialstores" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -57,9 +60,12 @@ var ( action.List, } - collectionTypeMap = map[resource.Type]action.ActionSet{ + vaultCollectionTypeMap = map[resource.Type]action.ActionSet{ resource.CredentialLibrary: credentiallibraries.CollectionActions, } + staticCollectionTypeMap = map[resource.Type]action.ActionSet{ + resource.Credential: credentials.CollectionActions, + } ) func init() { @@ -74,20 +80,28 @@ func init() { type Service struct { pbs.UnimplementedCredentialStoreServiceServer - iamRepoFn common.IamRepoFactory - repoFn common.VaultCredentialRepoFactory + iamRepoFn common.IamRepoFactory + vaultRepoFn common.VaultCredentialRepoFactory + staticRepoFn common.StaticCredentialRepoFactory } // NewService returns a credential store service which handles credential store related requests to boundary. -func NewService(repo common.VaultCredentialRepoFactory, iamRepo common.IamRepoFactory) (Service, error) { +func NewService( + vaultRepo common.VaultCredentialRepoFactory, + staticRepo common.StaticCredentialRepoFactory, + iamRepo common.IamRepoFactory, +) (Service, error) { const op = "credentialstores.NewService" if iamRepo == nil { return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing iam repository") } - if repo == nil { + if vaultRepo == nil { return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing vault credential repository") } - return Service{iamRepoFn: iamRepo, repoFn: repo}, nil + if staticRepo == nil { + return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing static credential repository") + } + return Service{iamRepoFn: iamRepo, vaultRepoFn: vaultRepo, staticRepoFn: staticRepo}, nil } var _ pbs.CredentialStoreServiceServer = Service{} @@ -155,7 +169,7 @@ func (s Service) ListCredentialStores(ctx context.Context, req *pbs.ListCredenti outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions)) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, item.GetPublicId()) + collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, item.GetPublicId()) if err != nil { return nil, err } @@ -208,7 +222,7 @@ func (s Service) GetCredentialStore(ctx context.Context, req *pbs.GetCredentialS outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings())) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, cs.GetPublicId()) + collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId()) if err != nil { return nil, err } @@ -253,7 +267,7 @@ func (s Service) CreateCredentialStore(ctx context.Context, req *pbs.CreateCrede outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings())) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, cs.GetPublicId()) + collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId()) if err != nil { return nil, err } @@ -301,7 +315,7 @@ func (s Service) UpdateCredentialStore(ctx context.Context, req *pbs.UpdateCrede outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, cs.GetPublicId(), IdActions).Strings())) } if outputFields.Has(globals.AuthorizedCollectionActionsField) { - collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, cs.GetPublicId()) + collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, cs.GetPublicId()) if err != nil { return nil, err } @@ -332,74 +346,154 @@ func (s Service) DeleteCredentialStore(ctx context.Context, req *pbs.DeleteCrede return nil, nil } -func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*vault.CredentialStore, error) { +func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]credential.Store, error) { const op = "credentialstores.(Service).listFromRepo" - repo, err := s.repoFn() + + vaultRepo, err := s.vaultRepoFn() if err != nil { return nil, errors.Wrap(ctx, err, op) } - csl, err := repo.ListCredentialStores(ctx, scopeIds) + vaultCsl, err := vaultRepo.ListCredentialStores(ctx, scopeIds) if err != nil { return nil, errors.Wrap(ctx, err, op) } + + staticRepo, err := s.staticRepoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + staticCsl, err := staticRepo.ListCredentialStores(ctx, scopeIds) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + csl := make([]credential.Store, 0, len(staticCsl)+len(vaultCsl)) + for _, s := range vaultCsl { + csl = append(csl, s) + } + for _, s := range staticCsl { + csl = append(csl, s) + } + return csl, nil } func (s Service) getFromRepo(ctx context.Context, id string) (credential.Store, error) { const op = "credentialstores.(Service).getFromRepo" - repo, err := s.repoFn() - if err != nil { - return nil, errors.Wrap(ctx, err, op) + + switch subtypes.SubtypeFromId(domain, id) { + case vault.Subtype: + repo, err := s.vaultRepoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + cs, err := repo.LookupCredentialStore(ctx, id) + if err != nil && !errors.IsNotFoundError(err) { + return nil, errors.Wrap(ctx, err, op) + } + if cs != nil { + return cs, nil + } + + case static.Subtype: + repo, err := s.staticRepoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + cs, err := repo.LookupCredentialStore(ctx, id) + if err != nil && !errors.IsNotFoundError(err) { + return nil, errors.Wrap(ctx, err, op) + } + if cs != nil { + return cs, nil + } } - cs, err := repo.LookupCredentialStore(ctx, id) - if err != nil && !errors.IsNotFoundError(err) { - return nil, errors.Wrap(ctx, err, op) - } - if cs == nil { - return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("credential store %q not found", id)) - } - return cs, err + + return nil, handlers.NotFoundErrorf("credential store %q not found", id) } func (s Service) createInRepo(ctx context.Context, projId string, item *pb.CredentialStore) (credential.Store, error) { const op = "credentialstores.(Service).createInRepo" - cs, err := toStorageVaultStore(projId, item) - if err != nil { - return nil, errors.Wrap(ctx, err, op) + + switch item.Type { + case vault.Subtype.String(): + cs, err := toStorageVaultStore(projId, item) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + repo, err := s.vaultRepoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + out, err := repo.CreateCredentialStore(ctx, cs) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential store")) + } + return out, nil + + case static.Subtype.String(): + cs, err := toStorageStaticStore(projId, item) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + repo, err := s.staticRepoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + out, err := repo.CreateCredentialStore(ctx, cs) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential store")) + } + return out, nil + + default: + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential store, unknown type.") } - repo, err := s.repoFn() - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - out, err := repo.CreateCredentialStore(ctx, cs) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create credential store")) - } - if out == nil { - return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create credential store but no error returned from repository.") - } - return out, nil } func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []string, item *pb.CredentialStore) (credential.Store, error) { const op = "credentialstores.(Service).updateInRepo" - cs, err := toStorageVaultStore(projId, item) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - cs.PublicId = id + + var out credential.Store + var rowsUpdated int dbMask := maskManager.Translate(mask) if len(dbMask) == 0 { return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."}) } - repo, err := s.repoFn() - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - out, rowsUpdated, err := repo.UpdateCredentialStore(ctx, cs, item.GetVersion(), dbMask) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential store")) + + switch subtypes.SubtypeFromId(domain, id) { + case vault.Subtype: + cs, err := toStorageVaultStore(projId, item) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + cs.PublicId = id + + repo, err := s.vaultRepoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + out, rowsUpdated, err = repo.UpdateCredentialStore(ctx, cs, item.GetVersion(), dbMask) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential store")) + } + + case static.Subtype: + cs, err := toStorageStaticStore(projId, item) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + cs.PublicId = id + + repo, err := s.staticRepoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + out, rowsUpdated, err = repo.UpdateCredentialStore(ctx, cs, item.GetVersion(), dbMask) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update credential store")) + } } if rowsUpdated == 0 { return nil, handlers.NotFoundErrorf("Credential Store %q doesn't exist or incorrect version provided.", id) @@ -409,16 +503,34 @@ func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []str func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) { const op = "credentialstores.(Service).deleteFromRepo" - repo, err := s.repoFn() - if err != nil { - return false, err - } - rows, err := repo.DeleteCredentialStore(ctx, id) - if err != nil { - if errors.IsNotFoundError(err) { - return false, nil + var rows int + + switch subtypes.SubtypeFromId(domain, id) { + case vault.Subtype: + repo, err := s.vaultRepoFn() + if err != nil { + return false, err + } + rows, err = repo.DeleteCredentialStore(ctx, id) + if err != nil { + if errors.IsNotFoundError(err) { + return false, nil + } + return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential store")) + } + + case static.Subtype: + repo, err := s.staticRepoFn() + if err != nil { + return false, err + } + rows, err = repo.DeleteCredentialStore(ctx, id) + if err != nil { + if errors.IsNotFoundError(err) { + return false, nil + } + return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential store")) } - return false, errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete credential store")) } return rows > 0, nil } @@ -430,7 +542,12 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth. res.Error = err return res } - repo, err := s.repoFn() + vaultRepo, err := s.vaultRepoFn() + if err != nil { + res.Error = err + return res + } + staticRepo, err := s.staticRepoFn() if err != nil { res.Error = err return res @@ -451,16 +568,32 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth. return res } default: - cs, err := repo.LookupCredentialStore(ctx, id) - if err != nil { - res.Error = err - return res + switch subtypes.SubtypeFromId(domain, id) { + case vault.Subtype: + cs, err := vaultRepo.LookupCredentialStore(ctx, id) + if err != nil { + res.Error = err + return res + } + if cs == nil { + res.Error = handlers.NotFoundError() + return res + } + parentId = cs.GetScopeId() + + case static.Subtype: + var err error + cs, err := staticRepo.LookupCredentialStore(ctx, id) + if err != nil { + res.Error = err + return res + } + if cs == nil { + res.Error = handlers.NotFoundError() + return res + } + parentId = cs.GetScopeId() } - if cs == nil { - res.Error = handlers.NotFoundError() - return res - } - parentId = cs.GetScopeId() opts = append(opts, auth.WithId(id)) } opts = append(opts, auth.WithScopeId(parentId)) @@ -550,6 +683,23 @@ func toProto(in credential.Store, opt ...handlers.Option) (*pb.CredentialStore, return &out, nil } +func toStorageStaticStore(scopeId string, in *pb.CredentialStore) (out *static.CredentialStore, err error) { + const op = "credentialstores.toStorageStaticStore" + var opts []static.Option + if in.GetName() != nil { + opts = append(opts, static.WithName(in.GetName().GetValue())) + } + if in.GetDescription() != nil { + opts = append(opts, static.WithDescription(in.GetDescription().GetValue())) + } + + cs, err := static.NewCredentialStore(scopeId, opts...) + if err != nil { + return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to build credential store for creation")) + } + return cs, err +} + func toStorageVaultStore(scopeId string, in *pb.CredentialStore) (out *vault.CredentialStore, err error) { const op = "credentialstores.toStorageVaultStore" var opts []vault.Option @@ -609,7 +759,7 @@ func toStorageVaultStore(scopeId string, in *pb.CredentialStore) (out *vault.Cre // * All required parameters are set // * There are no conflicting parameters provided func validateGetRequest(req *pbs.GetCredentialStoreRequest) error { - return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix) + return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix, static.CredentialStorePrefix) } func validateCreateRequest(req *pbs.CreateCredentialStoreRequest) error { @@ -651,6 +801,8 @@ func validateCreateRequest(req *pbs.CreateCredentialStoreRequest) error { if len(cs) > 0 && pk == nil { badFields[clientCertField] = "Cannot set a client certificate without a private key." } + case static.Subtype: + // No additional validation required for static credential store default: badFields[globals.TypeField] = "This is a required field and must be a known credential store type." } @@ -693,11 +845,11 @@ func validateUpdateRequest(req *pbs.UpdateCredentialStoreRequest) error { } } return badFields - }, vault.CredentialStorePrefix) + }, vault.CredentialStorePrefix, static.CredentialStorePrefix) } func validateDeleteRequest(req *pbs.DeleteCredentialStoreRequest) error { - return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix) + return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, vault.CredentialStorePrefix, static.CredentialStorePrefix) } func validateListRequest(req *pbs.ListCredentialStoresRequest) error { @@ -714,3 +866,20 @@ func validateListRequest(req *pbs.ListCredentialStoresRequest) error { } return nil } + +func calculateAuthorizedCollectionActions(ctx context.Context, authResults auth.VerifyResults, id string) (map[string]*structpb.ListValue, error) { + var collectionActions map[string]*structpb.ListValue + var err error + switch subtypes.SubtypeFromId(domain, id) { + case vault.Subtype: + collectionActions, err = auth.CalculateAuthorizedCollectionActions(ctx, authResults, vaultCollectionTypeMap, authResults.Scope.Id, id) + + case static.Subtype: + collectionActions, err = auth.CalculateAuthorizedCollectionActions(ctx, authResults, staticCollectionTypeMap, authResults.Scope.Id, id) + } + if err != nil { + return nil, err + } + + return collectionActions, nil +} diff --git a/internal/daemon/controller/handlers/credentialstores/credentialstore_service_test.go b/internal/daemon/controller/handlers/credentialstores/credentialstore_service_test.go index 50ba67293..3e5371c48 100644 --- a/internal/daemon/controller/handlers/credentialstores/credentialstore_service_test.go +++ b/internal/daemon/controller/handlers/credentialstores/credentialstore_service_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + credstatic "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/daemon/controller/auth" "github.com/hashicorp/boundary/internal/daemon/controller/handlers" @@ -36,8 +37,8 @@ import ( ) var ( - testAuthorizedActions = []string{"no-op", "read", "update", "delete"} - testAuthorizedCollectionActions = map[string]*structpb.ListValue{ + testAuthorizedActions = []string{"no-op", "read", "update", "delete"} + testAuthorizedVaultCollectionActions = map[string]*structpb.ListValue{ "credential-libraries": { Values: []*structpb.Value{ structpb.NewStringValue("create"), @@ -45,6 +46,14 @@ var ( }, }, } + testAuthorizedStaticCollectionActions = map[string]*structpb.ListValue{ + "credentials": { + Values: []*structpb.Value{ + structpb.NewStringValue("create"), + structpb.NewStringValue("list"), + }, + }, + } ) func TestList(t *testing.T) { @@ -60,9 +69,12 @@ func TestList(t *testing.T) { iamRepoFn := func() (*iam.Repository, error) { return iamRepo, nil } - repoFn := func() (*vault.Repository, error) { + vaultRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } + staticRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(context.Background(), rw, rw, kms) + } _, prjNoStores := iam.TestScopes(t, iamRepo) _, prj := iam.TestScopes(t, iamRepo) @@ -78,7 +90,7 @@ func TestList(t *testing.T) { Version: s.GetVersion(), Type: vault.Subtype.String(), AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: testAuthorizedCollectionActions, + AuthorizedCollectionActions: testAuthorizedVaultCollectionActions, Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{ VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{ Address: wrapperspb.String(s.GetVaultAddress()), @@ -91,6 +103,20 @@ func TestList(t *testing.T) { }) } + for _, s := range credstatic.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 10) { + wantStores = append(wantStores, &pb.CredentialStore{ + Id: s.GetPublicId(), + ScopeId: prj.GetPublicId(), + Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()}, + CreatedTime: s.GetCreateTime().GetTimestamp(), + UpdatedTime: s.GetUpdateTime().GetTimestamp(), + Version: s.GetVersion(), + Type: credstatic.Subtype.String(), + AuthorizedActions: testAuthorizedActions, + AuthorizedCollectionActions: testAuthorizedStaticCollectionActions, + }) + } + cases := []struct { name string req *pbs.ListCredentialStoresRequest @@ -111,11 +137,17 @@ func TestList(t *testing.T) { anonRes: &pbs.ListCredentialStoresResponse{}, }, { - name: "Filter to One Store", + name: "Filter to One Vault Store", req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantStores[1].GetId())}, res: &pbs.ListCredentialStoresResponse{Items: wantStores[1:2]}, anonRes: &pbs.ListCredentialStoresResponse{Items: wantStores[1:2]}, }, + { + name: "Filter to One static Store", + req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantStores[11].GetId())}, + res: &pbs.ListCredentialStoresResponse{Items: wantStores[11:12]}, + anonRes: &pbs.ListCredentialStoresResponse{Items: wantStores[11:12]}, + }, { name: "Filter on Attribute", req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/address"==%q`, wantStores[2].GetVaultCredentialStoreAttributes().GetAddress().Value)}, @@ -136,7 +168,7 @@ func TestList(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - s, err := NewService(repoFn, iamRepoFn) + s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn) require.NoError(t, err, "Couldn't create new host set service.") // Test non-anonymous listing @@ -164,7 +196,7 @@ func TestList(t *testing.T) { } } -func TestCreate(t *testing.T) { +func TestCreateVault(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) @@ -177,9 +209,12 @@ func TestCreate(t *testing.T) { iamRepoFn := func() (*iam.Repository, error) { return iamRepo, nil } - repoFn := func() (*vault.Repository, error) { + vaultRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } + staticRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(context.Background(), rw, rw, kms) + } _, prj := iam.TestScopes(t, iamRepo) defaultCreated := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0].GetCreateTime().GetTimestamp() @@ -343,22 +378,6 @@ func TestCreate(t *testing.T) { res: nil, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, - { - name: "Can't specify Update Time", - req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{ - ScopeId: prj.GetPublicId(), - UpdatedTime: timestamppb.Now(), - Type: vault.Subtype.String(), - Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{ - VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{ - Address: wrapperspb.String(v.Addr), - Token: wrapperspb.String(newToken()), - }, - }, - }}, - res: nil, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, { name: "Must specify type", req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{ @@ -433,7 +452,7 @@ func TestCreate(t *testing.T) { }, }, AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: testAuthorizedCollectionActions, + AuthorizedCollectionActions: testAuthorizedVaultCollectionActions, }, }, }, @@ -474,7 +493,7 @@ func TestCreate(t *testing.T) { }, }, AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: testAuthorizedCollectionActions, + AuthorizedCollectionActions: testAuthorizedVaultCollectionActions, }, }, }, @@ -483,7 +502,7 @@ func TestCreate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := NewService(repoFn, iamRepoFn) + s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn) require.NoError(err, "Error when getting new credential store service.") defer cleanup(s) @@ -533,6 +552,171 @@ func TestCreate(t *testing.T) { } } +func TestCreateStatic(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + rw := db.New(conn) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + vaultRepoFn := func() (*vault.Repository, error) { + return vault.NewRepository(rw, rw, kms, sche) + } + staticRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(context.Background(), rw, rw, kms) + } + + _, prj := iam.TestScopes(t, iamRepo) + defaultCreated := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + + cleanup := func(s Service) { + ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()) + r, err := s.ListCredentialStores(ctx, &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId()}) + require.NoError(t, err) + for _, i := range r.GetItems() { + _, err := s.DeleteCredentialStore(ctx, &pbs.DeleteCredentialStoreRequest{Id: i.GetId()}) + require.NoError(t, err) + } + } + + cases := []struct { + name string + req *pbs.CreateCredentialStoreRequest + res *pbs.CreateCredentialStoreResponse + idPrefix string + err error + wantErr bool + }{ + { + name: "Can't specify Id", + req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{ + ScopeId: prj.GetPublicId(), + Id: credstatic.CredentialStorePrefix + "_notallowed", + Type: credstatic.Subtype.String(), + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Can't specify Created Time", + req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{ + ScopeId: prj.GetPublicId(), + CreatedTime: timestamppb.Now(), + Type: credstatic.Subtype.String(), + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Can't specify Update Time", + req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{ + ScopeId: prj.GetPublicId(), + UpdatedTime: timestamppb.Now(), + Type: credstatic.Subtype.String(), + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Must specify type", + req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{ + ScopeId: prj.GetPublicId(), + }}, + res: nil, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + { + name: "Create a valid static CredentialStore", + req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{ + ScopeId: prj.GetPublicId(), + Type: credstatic.Subtype.String(), + }}, + idPrefix: credstatic.CredentialStorePrefix + "_", + res: &pbs.CreateCredentialStoreResponse{ + Uri: fmt.Sprintf("credential-stores/%s_", credstatic.CredentialStorePrefix), + Item: &pb.CredentialStore{ + ScopeId: prj.GetPublicId(), + Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()}, + Version: 1, + Type: credstatic.Subtype.String(), + AuthorizedActions: testAuthorizedActions, + AuthorizedCollectionActions: testAuthorizedStaticCollectionActions, + }, + }, + }, + { + name: "Create a valid static CredentialStore with name and description", + req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{ + ScopeId: prj.GetPublicId(), + Name: &wrapperspb.StringValue{Value: "name"}, + Description: &wrapperspb.StringValue{Value: "desc"}, + Type: credstatic.Subtype.String(), + }}, + idPrefix: credstatic.CredentialStorePrefix + "_", + res: &pbs.CreateCredentialStoreResponse{ + Uri: fmt.Sprintf("credential-stores/%s_", credstatic.CredentialStorePrefix), + Item: &pb.CredentialStore{ + ScopeId: prj.GetPublicId(), + Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()}, + Version: 1, + Type: credstatic.Subtype.String(), + AuthorizedActions: testAuthorizedActions, + Name: &wrapperspb.StringValue{Value: "name"}, + Description: &wrapperspb.StringValue{Value: "desc"}, + AuthorizedCollectionActions: testAuthorizedStaticCollectionActions, + }, + }, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn) + require.NoError(err, "Error when getting new credential store service.") + defer cleanup(s) + + got, gErr := s.CreateCredentialStore(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetItem().GetScopeId()), tc.req) + if tc.wantErr || tc.err != nil { + require.Error(gErr) + if tc.err != nil { + assert.True(errors.Is(gErr, tc.err), "CreateCredentialStore(...) got error %v, wanted %v", gErr, tc.err) + } + return + } + require.NoError(gErr) + if tc.res == nil { + require.Nil(got) + } + cmpOptions := []cmp.Option{ + protocmp.Transform(), + protocmp.SortRepeatedFields(got), + } + if got != nil { + assert.Contains(got.GetUri(), tc.res.Uri) + cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pbs.CreateCredentialStoreResponse{}, "uri")) + + assert.True(strings.HasPrefix(got.GetItem().GetId(), tc.idPrefix)) + cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pb.CredentialStore{}, "id")) + + gotCreateTime := got.GetItem().GetCreatedTime() + gotUpdateTime := got.GetItem().GetUpdatedTime() + + // Verify it is a credential store created after the test setup's default credential store + assert.True(gotCreateTime.AsTime().After(defaultCreated.CreateTime.AsTime()), "New credential store should have been created after default credential store. Was created %v, which is after %v", gotCreateTime, defaultCreated) + assert.True(gotUpdateTime.AsTime().After(defaultCreated.CreateTime.AsTime()), "New credential store should have been updated after default credential store. Was updated %v, which is after %v", gotUpdateTime, defaultCreated) + cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pb.CredentialStore{}, "created_time")) + cmpOptions = append(cmpOptions, protocmp.IgnoreFields(&pb.CredentialStore{}, "updated_time")) + } + assert.Empty(cmp.Diff(got, tc.res, cmpOptions...), "CreateCredentialStore(%q) got response %q, wanted %q", tc.req, got, tc.res) + }) + } +} + func TestGet(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) @@ -544,14 +728,18 @@ func TestGet(t *testing.T) { iamRepoFn := func() (*iam.Repository, error) { return iamRepo, nil } - repoFn := func() (*vault.Repository, error) { + vaultRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } + staticRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(context.Background(), rw, rw, kms) + } _, prj := iam.TestScopes(t, iamRepo) - store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0] - s, err := NewService(repoFn, iamRepoFn) + vaultStore := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0] + staticStore := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn) require.NoError(t, err) cases := []struct { @@ -561,35 +749,57 @@ func TestGet(t *testing.T) { err error }{ { - name: "success", - id: store.GetPublicId(), + name: "vault success", + id: vaultStore.GetPublicId(), res: &pbs.GetCredentialStoreResponse{ Item: &pb.CredentialStore{ - Id: store.GetPublicId(), - ScopeId: store.GetScopeId(), - Scope: &scopepb.ScopeInfo{Id: store.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()}, + Id: vaultStore.GetPublicId(), + ScopeId: vaultStore.GetScopeId(), + Scope: &scopepb.ScopeInfo{Id: vaultStore.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()}, Type: vault.Subtype.String(), AuthorizedActions: testAuthorizedActions, - AuthorizedCollectionActions: testAuthorizedCollectionActions, - CreatedTime: store.CreateTime.GetTimestamp(), - UpdatedTime: store.UpdateTime.GetTimestamp(), + AuthorizedCollectionActions: testAuthorizedVaultCollectionActions, + CreatedTime: vaultStore.CreateTime.GetTimestamp(), + UpdatedTime: vaultStore.UpdateTime.GetTimestamp(), Version: 1, Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{ VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{ - Address: wrapperspb.String(store.GetVaultAddress()), - TokenHmac: base64.RawURLEncoding.EncodeToString(store.Token().GetTokenHmac()), - ClientCertificate: wrapperspb.String(string(store.ClientCertificate().GetCertificate())), - ClientCertificateKeyHmac: base64.RawURLEncoding.EncodeToString(store.ClientCertificate().GetCertificateKeyHmac()), + Address: wrapperspb.String(vaultStore.GetVaultAddress()), + TokenHmac: base64.RawURLEncoding.EncodeToString(vaultStore.Token().GetTokenHmac()), + ClientCertificate: wrapperspb.String(string(vaultStore.ClientCertificate().GetCertificate())), + ClientCertificateKeyHmac: base64.RawURLEncoding.EncodeToString(vaultStore.ClientCertificate().GetCertificateKeyHmac()), }, }, }, }, }, { - name: "not found error", + name: "static success", + id: staticStore.GetPublicId(), + res: &pbs.GetCredentialStoreResponse{ + Item: &pb.CredentialStore{ + Id: staticStore.GetPublicId(), + ScopeId: staticStore.GetScopeId(), + Scope: &scopepb.ScopeInfo{Id: staticStore.GetScopeId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()}, + Type: credstatic.Subtype.String(), + AuthorizedActions: testAuthorizedActions, + AuthorizedCollectionActions: testAuthorizedStaticCollectionActions, + CreatedTime: staticStore.CreateTime.GetTimestamp(), + UpdatedTime: staticStore.UpdateTime.GetTimestamp(), + Version: 1, + }, + }, + }, + { + name: "vault not found error", id: fmt.Sprintf("%s_1234567890", vault.CredentialStorePrefix), err: handlers.NotFoundError(), }, + { + name: "static not found error", + id: fmt.Sprintf("%s_1234567890", credstatic.CredentialStorePrefix), + err: handlers.NotFoundError(), + }, { name: "bad prefix", id: fmt.Sprintf("%s_1234567890", static.HostPrefix), @@ -632,14 +842,18 @@ func TestDelete(t *testing.T) { iamRepoFn := func() (*iam.Repository, error) { return iamRepo, nil } - repoFn := func() (*vault.Repository, error) { + vaultRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } + staticRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(context.Background(), rw, rw, kms) + } _, prj := iam.TestScopes(t, iamRepo) - store := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)[0] - s, err := NewService(repoFn, iamRepoFn) + vaultStore := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)[0] + staticStore := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn) require.NoError(t, err) cases := []struct { @@ -649,14 +863,23 @@ func TestDelete(t *testing.T) { res *pbs.DeleteCredentialStoreResponse }{ { - name: "success", - id: store.GetPublicId(), + name: "vault success", + id: vaultStore.GetPublicId(), }, { - name: "not found error", + name: "static success", + id: staticStore.GetPublicId(), + }, + { + name: "vault not found error", id: fmt.Sprintf("%s_1234567890", vault.CredentialStorePrefix), err: handlers.NotFoundError(), }, + { + name: "static not found error", + id: fmt.Sprintf("%s_1234567890", credstatic.CredentialStorePrefix), + err: handlers.NotFoundError(), + }, { name: "bad prefix", id: fmt.Sprintf("%s_1234567890", static.HostPrefix), @@ -680,7 +903,7 @@ func TestDelete(t *testing.T) { } } -func TestUpdate(t *testing.T) { +func TestUpdateVault(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) @@ -693,14 +916,17 @@ func TestUpdate(t *testing.T) { iamRepoFn := func() (*iam.Repository, error) { return iamRepo, nil } - repoFn := func() (*vault.Repository, error) { + vaultRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } + staticRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(context.Background(), rw, rw, kms) + } _, prj := iam.TestScopes(t, iamRepo) ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()) - s, err := NewService(repoFn, iamRepoFn) + s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn) require.NoError(t, err) fieldmask := func(paths ...string) *fieldmaskpb.FieldMask { @@ -1010,3 +1236,178 @@ func TestUpdate(t *testing.T) { }) } } + +func TestUpdateStatic(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + sche := scheduler.TestScheduler(t, conn, wrapper) + rw := db.New(conn) + + iamRepo := iam.TestRepo(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + vaultRepoFn := func() (*vault.Repository, error) { + return vault.NewRepository(rw, rw, kms, sche) + } + staticRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(context.Background(), rw, rw, kms) + } + + _, prj := iam.TestScopes(t, iamRepo) + ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()) + + s, err := NewService(vaultRepoFn, staticRepoFn, iamRepoFn) + require.NoError(t, err) + + fieldmask := func(paths ...string) *fieldmaskpb.FieldMask { + return &fieldmaskpb.FieldMask{Paths: paths} + } + + freshStore := func() (*credstatic.CredentialStore, func()) { + t.Helper() + st := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + clean := func() { + _, err := s.DeleteCredentialStore(ctx, &pbs.DeleteCredentialStoreRequest{Id: st.GetPublicId()}) + require.NoError(t, err) + } + return st, clean + } + + successCases := []struct { + name string + req *pbs.UpdateCredentialStoreRequest + res func(*pb.CredentialStore) *pb.CredentialStore + }{ + { + name: "name", + req: &pbs.UpdateCredentialStoreRequest{ + UpdateMask: fieldmask("name"), + Item: &pb.CredentialStore{ + Name: wrapperspb.String("new-name"), + }, + }, + res: func(in *pb.CredentialStore) *pb.CredentialStore { + out := proto.Clone(in).(*pb.CredentialStore) + out.Name = wrapperspb.String("new-name") + return out + }, + }, + { + name: "description", + req: &pbs.UpdateCredentialStoreRequest{ + UpdateMask: fieldmask("description"), + Item: &pb.CredentialStore{ + Description: wrapperspb.String("new-description"), + }, + }, + res: func(in *pb.CredentialStore) *pb.CredentialStore { + out := proto.Clone(in).(*pb.CredentialStore) + out.Description = wrapperspb.String("new-description") + return out + }, + }, + { + name: "name-and-description", + req: &pbs.UpdateCredentialStoreRequest{ + UpdateMask: fieldmask("name", "description"), + Item: &pb.CredentialStore{ + Name: wrapperspb.String("new-name"), + Description: wrapperspb.String("new-description"), + }, + }, + res: func(in *pb.CredentialStore) *pb.CredentialStore { + out := proto.Clone(in).(*pb.CredentialStore) + out.Name = wrapperspb.String("new-name") + out.Description = wrapperspb.String("new-description") + return out + }, + }, + } + + for _, tc := range successCases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + st, cleanup := freshStore() + defer cleanup() + + if tc.req.Item.GetVersion() == 0 { + tc.req.Item.Version = 1 + } + if tc.req.GetId() == "" { + tc.req.Id = st.GetPublicId() + } + resToChange, err := s.GetCredentialStore(ctx, &pbs.GetCredentialStoreRequest{Id: st.GetPublicId()}) + require.NoError(err) + want := &pbs.UpdateCredentialStoreResponse{Item: tc.res(resToChange.GetItem())} + + got, gErr := s.UpdateCredentialStore(ctx, tc.req) + require.NoError(gErr) + require.NotNil(got) + + gotUpdateTime := got.GetItem().GetUpdatedTime() + created := st.GetCreateTime().GetTimestamp() + assert.True(gotUpdateTime.AsTime().After(created.AsTime()), "Should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created) + + want.Item.UpdatedTime = got.Item.UpdatedTime + + assert.EqualValues(2, got.Item.Version) + want.Item.Version = 2 + + assert.Empty(cmp.Diff(got, want, protocmp.Transform())) + }) + } + + // cant update read only fields + st, cleanup := freshStore() + defer cleanup() + + roCases := []struct { + path string + item *pb.CredentialStore + matcher func(t *testing.T, e error) // When not set defaults to checking against InvalidArgument Error + }{ + { + path: "type", + item: &pb.CredentialStore{Type: "something"}, + }, + { + path: "scope_id", + item: &pb.CredentialStore{ScopeId: "global"}, + }, + { + path: "updated_time", + item: &pb.CredentialStore{UpdatedTime: timestamppb.Now()}, + }, + { + path: "created_time", + item: &pb.CredentialStore{UpdatedTime: timestamppb.Now()}, + }, + { + path: "authorized_actions", + item: &pb.CredentialStore{AuthorizedActions: append(testAuthorizedActions, "another")}, + }, + } + for _, tc := range roCases { + t.Run(fmt.Sprintf("ReadOnlyField/%s", tc.path), func(t *testing.T) { + req := &pbs.UpdateCredentialStoreRequest{ + Id: st.GetPublicId(), + Item: tc.item, + UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{tc.path}}, + } + req.Item.Version = st.Version + + got, gErr := s.UpdateCredentialStore(ctx, req) + assert.Error(t, gErr) + matcher := tc.matcher + if matcher == nil { + matcher = func(t *testing.T, e error) { + assert.Truef(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)), "got error %v, wanted invalid argument", gErr) + } + } + matcher(t, gErr) + assert.Nil(t, got) + }) + } +} diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 5c1718c85..1da680fc7 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -20,6 +20,9 @@ { "name": "controller.api.services.v1.CredentialLibraryService" }, + { + "name": "controller.api.services.v1.CredentialService" + }, { "name": "controller.api.services.v1.CredentialStoreService" }, @@ -908,6 +911,147 @@ ] } }, + "/v1/credentials": { + "get": { + "summary": "Lists all Credentials.", + "operationId": "CredentialService_ListCredentials", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/controller.api.services.v1.ListCredentialsResponse" + } + } + }, + "parameters": [ + { + "name": "credential_store_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "filter", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.CredentialService" + ] + }, + "post": { + "summary": "Creates a single Credential.", + "operationId": "CredentialService_CreateCredential", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.credentials.v1.Credential" + } + } + }, + "parameters": [ + { + "name": "item", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.resources.credentials.v1.Credential" + } + } + ], + "tags": [ + "controller.api.services.v1.CredentialService" + ] + } + }, + "/v1/credentials/{id}": { + "get": { + "summary": "Gets a single Credential.", + "operationId": "CredentialService_GetCredential", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.credentials.v1.Credential" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.CredentialService" + ] + }, + "delete": { + "summary": "Deletes a Credential", + "operationId": "CredentialService_DeleteCredential", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/controller.api.services.v1.DeleteCredentialResponse" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.CredentialService" + ] + }, + "patch": { + "summary": "Updates a Credential.", + "operationId": "CredentialService_UpdateCredential", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.credentials.v1.Credential" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "item", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.resources.credentials.v1.Credential" + } + }, + { + "name": "update_mask", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.CredentialService" + ] + } + }, "/v1/groups": { "get": { "summary": "Lists all Groups.", @@ -3946,6 +4090,67 @@ }, "title": "CredentialLibrary contains all fields related to an Credential Library resource" }, + "controller.api.resources.credentials.v1.Credential": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Output only. The ID of the Credential.", + "readOnly": true + }, + "credential_store_id": { + "type": "string", + "description": "The ID of the Credential Store of which this Credential is a part." + }, + "scope": { + "$ref": "#/definitions/controller.api.resources.scopes.v1.ScopeInfo", + "description": "Output only. Scope information for this Credential.", + "readOnly": true + }, + "name": { + "type": "string", + "description": "Optional name for identification purposes." + }, + "description": { + "type": "string", + "description": "Optional user-set description for identification purposes." + }, + "created_time": { + "type": "string", + "format": "date-time", + "description": "Output only. The time this resource was created.", + "readOnly": true + }, + "updated_time": { + "type": "string", + "format": "date-time", + "description": "Output only. The time this resource was last updated.", + "readOnly": true + }, + "version": { + "type": "integer", + "format": "int64", + "description": "Version is used in mutation requests, after the initial creation, to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version." + }, + "type": { + "type": "string", + "description": "The Credential type." + }, + "attributes": { + "type": "object", + "description": "The attributes that are applicable for the specific Credential type." + }, + "authorized_actions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Output only. The available actions on this resource for this user.", + "readOnly": true + } + }, + "title": "Credential contains all fields related to an Credential resource" + }, "controller.api.resources.credentialstores.v1.CredentialStore": { "type": "object", "properties": { @@ -5524,6 +5729,17 @@ } } }, + "controller.api.services.v1.CreateCredentialResponse": { + "type": "object", + "properties": { + "uri": { + "type": "string" + }, + "item": { + "$ref": "#/definitions/controller.api.resources.credentials.v1.Credential" + } + } + }, "controller.api.services.v1.CreateCredentialStoreResponse": { "type": "object", "properties": { @@ -5646,6 +5862,9 @@ "controller.api.services.v1.DeleteCredentialLibraryResponse": { "type": "object" }, + "controller.api.services.v1.DeleteCredentialResponse": { + "type": "object" + }, "controller.api.services.v1.DeleteCredentialStoreResponse": { "type": "object" }, @@ -5708,6 +5927,14 @@ } } }, + "controller.api.services.v1.GetCredentialResponse": { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/controller.api.resources.credentials.v1.Credential" + } + } + }, "controller.api.services.v1.GetCredentialStoreResponse": { "type": "object", "properties": { @@ -5851,6 +6078,17 @@ } } }, + "controller.api.services.v1.ListCredentialsResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/controller.api.resources.credentials.v1.Credential" + } + } + } + }, "controller.api.services.v1.ListGroupsResponse": { "type": "object", "properties": { @@ -6137,6 +6375,14 @@ } } }, + "controller.api.services.v1.UpdateCredentialResponse": { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/controller.api.resources.credentials.v1.Credential" + } + } + }, "controller.api.services.v1.UpdateCredentialStoreResponse": { "type": "object", "properties": { diff --git a/internal/gen/controller/api/services/credential_service.pb.go b/internal/gen/controller/api/services/credential_service.pb.go new file mode 100644 index 000000000..2428ad687 --- /dev/null +++ b/internal/gen/controller/api/services/credential_service.pb.go @@ -0,0 +1,864 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/api/services/v1/credential_service.proto + +package services + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + credentials "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials" + _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetCredentialRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` +} + +func (x *GetCredentialRequest) Reset() { + *x = GetCredentialRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetCredentialRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCredentialRequest) ProtoMessage() {} + +func (x *GetCredentialRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCredentialRequest.ProtoReflect.Descriptor instead. +func (*GetCredentialRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{0} +} + +func (x *GetCredentialRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetCredentialResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *credentials.Credential `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *GetCredentialResponse) Reset() { + *x = GetCredentialResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetCredentialResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCredentialResponse) ProtoMessage() {} + +func (x *GetCredentialResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCredentialResponse.ProtoReflect.Descriptor instead. +func (*GetCredentialResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{1} +} + +func (x *GetCredentialResponse) GetItem() *credentials.Credential { + if x != nil { + return x.Item + } + return nil +} + +type ListCredentialsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CredentialStoreId string `protobuf:"bytes,1,opt,name=credential_store_id,json=scope_id,proto3" json:"credential_store_id,omitempty" class:"public"` // @gotags: `class:"public"` + Filter string `protobuf:"bytes,30,opt,name=filter,proto3" json:"filter,omitempty" class:"public"` // @gotags: `class:"public"` +} + +func (x *ListCredentialsRequest) Reset() { + *x = ListCredentialsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListCredentialsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListCredentialsRequest) ProtoMessage() {} + +func (x *ListCredentialsRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListCredentialsRequest.ProtoReflect.Descriptor instead. +func (*ListCredentialsRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{2} +} + +func (x *ListCredentialsRequest) GetCredentialStoreId() string { + if x != nil { + return x.CredentialStoreId + } + return "" +} + +func (x *ListCredentialsRequest) GetFilter() string { + if x != nil { + return x.Filter + } + return "" +} + +type ListCredentialsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Items []*credentials.Credential `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` +} + +func (x *ListCredentialsResponse) Reset() { + *x = ListCredentialsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListCredentialsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListCredentialsResponse) ProtoMessage() {} + +func (x *ListCredentialsResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListCredentialsResponse.ProtoReflect.Descriptor instead. +func (*ListCredentialsResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{3} +} + +func (x *ListCredentialsResponse) GetItems() []*credentials.Credential { + if x != nil { + return x.Items + } + return nil +} + +type CreateCredentialRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *credentials.Credential `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *CreateCredentialRequest) Reset() { + *x = CreateCredentialRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateCredentialRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateCredentialRequest) ProtoMessage() {} + +func (x *CreateCredentialRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateCredentialRequest.ProtoReflect.Descriptor instead. +func (*CreateCredentialRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateCredentialRequest) GetItem() *credentials.Credential { + if x != nil { + return x.Item + } + return nil +} + +type CreateCredentialResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty" class:"public"` // @gotags: `class:"public"` + Item *credentials.Credential `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *CreateCredentialResponse) Reset() { + *x = CreateCredentialResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateCredentialResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateCredentialResponse) ProtoMessage() {} + +func (x *CreateCredentialResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateCredentialResponse.ProtoReflect.Descriptor instead. +func (*CreateCredentialResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateCredentialResponse) GetUri() string { + if x != nil { + return x.Uri + } + return "" +} + +func (x *CreateCredentialResponse) GetItem() *credentials.Credential { + if x != nil { + return x.Item + } + return nil +} + +type UpdateCredentialRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` + Item *credentials.Credential `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` + UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=update_mask,proto3" json:"update_mask,omitempty"` +} + +func (x *UpdateCredentialRequest) Reset() { + *x = UpdateCredentialRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateCredentialRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateCredentialRequest) ProtoMessage() {} + +func (x *UpdateCredentialRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateCredentialRequest.ProtoReflect.Descriptor instead. +func (*UpdateCredentialRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{6} +} + +func (x *UpdateCredentialRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateCredentialRequest) GetItem() *credentials.Credential { + if x != nil { + return x.Item + } + return nil +} + +func (x *UpdateCredentialRequest) GetUpdateMask() *fieldmaskpb.FieldMask { + if x != nil { + return x.UpdateMask + } + return nil +} + +type UpdateCredentialResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *credentials.Credential `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *UpdateCredentialResponse) Reset() { + *x = UpdateCredentialResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateCredentialResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateCredentialResponse) ProtoMessage() {} + +func (x *UpdateCredentialResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateCredentialResponse.ProtoReflect.Descriptor instead. +func (*UpdateCredentialResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{7} +} + +func (x *UpdateCredentialResponse) GetItem() *credentials.Credential { + if x != nil { + return x.Item + } + return nil +} + +type DeleteCredentialRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` +} + +func (x *DeleteCredentialRequest) Reset() { + *x = DeleteCredentialRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteCredentialRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteCredentialRequest) ProtoMessage() {} + +func (x *DeleteCredentialRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteCredentialRequest.ProtoReflect.Descriptor instead. +func (*DeleteCredentialRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{8} +} + +func (x *DeleteCredentialRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DeleteCredentialResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteCredentialResponse) Reset() { + *x = DeleteCredentialResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteCredentialResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteCredentialResponse) ProtoMessage() {} + +func (x *DeleteCredentialResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_credential_service_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteCredentialResponse.ProtoReflect.Descriptor instead. +func (*DeleteCredentialResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_credential_service_proto_rawDescGZIP(), []int{9} +} + +var File_controller_api_services_v1_credential_service_proto protoreflect.FileDescriptor + +var file_controller_api_services_v1_credential_service_proto_rawDesc = []byte{ + 0x0a, 0x33, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x1a, 0x38, 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, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, + 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, + 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x26, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, + 0x60, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x22, 0x57, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x13, 0x63, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, + 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x64, 0x0a, 0x17, 0x4c, 0x69, + 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, + 0x22, 0x62, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x22, 0x75, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x72, 0x69, 0x12, 0x47, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb0, 0x01, 0x0a, 0x17, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x47, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x12, 0x3c, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, + 0x6b, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x63, + 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x22, 0x29, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1a, + 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xbe, 0x07, 0x0a, 0x11, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0xb6, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x92, 0x41, 0x1b, 0x12, 0x19, 0x47, 0x65, + 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x43, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x14, 0x2f, + 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xae, 0x01, 0x0a, 0x0f, 0x4c, 0x69, + 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x32, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x92, 0x41, 0x18, 0x12, 0x16, 0x4c, 0x69, 0x73, + 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0xc3, 0x01, 0x0a, 0x10, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, + 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x92, 0x41, 0x1e, 0x12, + 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, + 0x65, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1d, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x12, 0xc1, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x42, 0x92, 0x41, 0x17, 0x12, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, + 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x22, 0x32, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x12, 0xb4, 0x01, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x92, 0x41, 0x16, 0x12, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x73, 0x20, 0x61, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x2a, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, 0x5b, 0x5a, 0x4b, 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, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0xa2, 0xe3, 0x29, 0x0a, 0x63, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_api_services_v1_credential_service_proto_rawDescOnce sync.Once + file_controller_api_services_v1_credential_service_proto_rawDescData = file_controller_api_services_v1_credential_service_proto_rawDesc +) + +func file_controller_api_services_v1_credential_service_proto_rawDescGZIP() []byte { + file_controller_api_services_v1_credential_service_proto_rawDescOnce.Do(func() { + file_controller_api_services_v1_credential_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_services_v1_credential_service_proto_rawDescData) + }) + return file_controller_api_services_v1_credential_service_proto_rawDescData +} + +var file_controller_api_services_v1_credential_service_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_controller_api_services_v1_credential_service_proto_goTypes = []interface{}{ + (*GetCredentialRequest)(nil), // 0: controller.api.services.v1.GetCredentialRequest + (*GetCredentialResponse)(nil), // 1: controller.api.services.v1.GetCredentialResponse + (*ListCredentialsRequest)(nil), // 2: controller.api.services.v1.ListCredentialsRequest + (*ListCredentialsResponse)(nil), // 3: controller.api.services.v1.ListCredentialsResponse + (*CreateCredentialRequest)(nil), // 4: controller.api.services.v1.CreateCredentialRequest + (*CreateCredentialResponse)(nil), // 5: controller.api.services.v1.CreateCredentialResponse + (*UpdateCredentialRequest)(nil), // 6: controller.api.services.v1.UpdateCredentialRequest + (*UpdateCredentialResponse)(nil), // 7: controller.api.services.v1.UpdateCredentialResponse + (*DeleteCredentialRequest)(nil), // 8: controller.api.services.v1.DeleteCredentialRequest + (*DeleteCredentialResponse)(nil), // 9: controller.api.services.v1.DeleteCredentialResponse + (*credentials.Credential)(nil), // 10: controller.api.resources.credentials.v1.Credential + (*fieldmaskpb.FieldMask)(nil), // 11: google.protobuf.FieldMask +} +var file_controller_api_services_v1_credential_service_proto_depIdxs = []int32{ + 10, // 0: controller.api.services.v1.GetCredentialResponse.item:type_name -> controller.api.resources.credentials.v1.Credential + 10, // 1: controller.api.services.v1.ListCredentialsResponse.items:type_name -> controller.api.resources.credentials.v1.Credential + 10, // 2: controller.api.services.v1.CreateCredentialRequest.item:type_name -> controller.api.resources.credentials.v1.Credential + 10, // 3: controller.api.services.v1.CreateCredentialResponse.item:type_name -> controller.api.resources.credentials.v1.Credential + 10, // 4: controller.api.services.v1.UpdateCredentialRequest.item:type_name -> controller.api.resources.credentials.v1.Credential + 11, // 5: controller.api.services.v1.UpdateCredentialRequest.update_mask:type_name -> google.protobuf.FieldMask + 10, // 6: controller.api.services.v1.UpdateCredentialResponse.item:type_name -> controller.api.resources.credentials.v1.Credential + 0, // 7: controller.api.services.v1.CredentialService.GetCredential:input_type -> controller.api.services.v1.GetCredentialRequest + 2, // 8: controller.api.services.v1.CredentialService.ListCredentials:input_type -> controller.api.services.v1.ListCredentialsRequest + 4, // 9: controller.api.services.v1.CredentialService.CreateCredential:input_type -> controller.api.services.v1.CreateCredentialRequest + 6, // 10: controller.api.services.v1.CredentialService.UpdateCredential:input_type -> controller.api.services.v1.UpdateCredentialRequest + 8, // 11: controller.api.services.v1.CredentialService.DeleteCredential:input_type -> controller.api.services.v1.DeleteCredentialRequest + 1, // 12: controller.api.services.v1.CredentialService.GetCredential:output_type -> controller.api.services.v1.GetCredentialResponse + 3, // 13: controller.api.services.v1.CredentialService.ListCredentials:output_type -> controller.api.services.v1.ListCredentialsResponse + 5, // 14: controller.api.services.v1.CredentialService.CreateCredential:output_type -> controller.api.services.v1.CreateCredentialResponse + 7, // 15: controller.api.services.v1.CredentialService.UpdateCredential:output_type -> controller.api.services.v1.UpdateCredentialResponse + 9, // 16: controller.api.services.v1.CredentialService.DeleteCredential:output_type -> controller.api.services.v1.DeleteCredentialResponse + 12, // [12:17] is the sub-list for method output_type + 7, // [7:12] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_controller_api_services_v1_credential_service_proto_init() } +func file_controller_api_services_v1_credential_service_proto_init() { + if File_controller_api_services_v1_credential_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_api_services_v1_credential_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetCredentialRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_credential_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetCredentialResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_credential_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListCredentialsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_credential_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListCredentialsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_credential_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateCredentialRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_credential_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateCredentialResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_credential_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateCredentialRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_credential_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateCredentialResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_credential_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteCredentialRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_credential_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteCredentialResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_api_services_v1_credential_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_controller_api_services_v1_credential_service_proto_goTypes, + DependencyIndexes: file_controller_api_services_v1_credential_service_proto_depIdxs, + MessageInfos: file_controller_api_services_v1_credential_service_proto_msgTypes, + }.Build() + File_controller_api_services_v1_credential_service_proto = out.File + file_controller_api_services_v1_credential_service_proto_rawDesc = nil + file_controller_api_services_v1_credential_service_proto_goTypes = nil + file_controller_api_services_v1_credential_service_proto_depIdxs = nil +} diff --git a/internal/gen/controller/api/services/credential_service.pb.gw.go b/internal/gen/controller/api/services/credential_service.pb.gw.go new file mode 100644 index 000000000..ae502c708 --- /dev/null +++ b/internal/gen/controller/api/services/credential_service.pb.gw.go @@ -0,0 +1,632 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: controller/api/services/v1/credential_service.proto + +/* +Package services is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package services + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_CredentialService_GetCredential_0(ctx context.Context, marshaler runtime.Marshaler, client CredentialServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetCredentialRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := client.GetCredential(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_CredentialService_GetCredential_0(ctx context.Context, marshaler runtime.Marshaler, server CredentialServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetCredentialRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := server.GetCredential(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_CredentialService_ListCredentials_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_CredentialService_ListCredentials_0(ctx context.Context, marshaler runtime.Marshaler, client CredentialServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListCredentialsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_CredentialService_ListCredentials_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListCredentials(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_CredentialService_ListCredentials_0(ctx context.Context, marshaler runtime.Marshaler, server CredentialServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListCredentialsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_CredentialService_ListCredentials_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListCredentials(ctx, &protoReq) + return msg, metadata, err + +} + +func request_CredentialService_CreateCredential_0(ctx context.Context, marshaler runtime.Marshaler, client CredentialServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateCredentialRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.CreateCredential(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_CredentialService_CreateCredential_0(ctx context.Context, marshaler runtime.Marshaler, server CredentialServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateCredentialRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.CreateCredential(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_CredentialService_UpdateCredential_0 = &utilities.DoubleArray{Encoding: map[string]int{"item": 0, "id": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + +func request_CredentialService_UpdateCredential_0(ctx context.Context, marshaler runtime.Marshaler, client CredentialServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateCredentialRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 { + if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Item); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } else { + protoReq.UpdateMask = fieldMask + } + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_CredentialService_UpdateCredential_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.UpdateCredential(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_CredentialService_UpdateCredential_0(ctx context.Context, marshaler runtime.Marshaler, server CredentialServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateCredentialRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 { + if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Item); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } else { + protoReq.UpdateMask = fieldMask + } + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_CredentialService_UpdateCredential_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.UpdateCredential(ctx, &protoReq) + return msg, metadata, err + +} + +func request_CredentialService_DeleteCredential_0(ctx context.Context, marshaler runtime.Marshaler, client CredentialServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteCredentialRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := client.DeleteCredential(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_CredentialService_DeleteCredential_0(ctx context.Context, marshaler runtime.Marshaler, server CredentialServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteCredentialRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := server.DeleteCredential(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterCredentialServiceHandlerServer registers the http handlers for service CredentialService to "mux". +// UnaryRPC :call CredentialServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterCredentialServiceHandlerFromEndpoint instead. +func RegisterCredentialServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server CredentialServiceServer) error { + + mux.Handle("GET", pattern_CredentialService_GetCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/GetCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_CredentialService_GetCredential_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_CredentialService_GetCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_GetCredential_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_CredentialService_ListCredentials_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/ListCredentials", runtime.WithHTTPPathPattern("/v1/credentials")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_CredentialService_ListCredentials_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_CredentialService_ListCredentials_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_CredentialService_CreateCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/CreateCredential", runtime.WithHTTPPathPattern("/v1/credentials")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_CredentialService_CreateCredential_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_CredentialService_CreateCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_CreateCredential_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PATCH", pattern_CredentialService_UpdateCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/UpdateCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_CredentialService_UpdateCredential_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_CredentialService_UpdateCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_UpdateCredential_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_CredentialService_DeleteCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/DeleteCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_CredentialService_DeleteCredential_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_CredentialService_DeleteCredential_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterCredentialServiceHandlerFromEndpoint is same as RegisterCredentialServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterCredentialServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterCredentialServiceHandler(ctx, mux, conn) +} + +// RegisterCredentialServiceHandler registers the http handlers for service CredentialService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterCredentialServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterCredentialServiceHandlerClient(ctx, mux, NewCredentialServiceClient(conn)) +} + +// RegisterCredentialServiceHandlerClient registers the http handlers for service CredentialService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "CredentialServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "CredentialServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "CredentialServiceClient" to call the correct interceptors. +func RegisterCredentialServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client CredentialServiceClient) error { + + mux.Handle("GET", pattern_CredentialService_GetCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/GetCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_CredentialService_GetCredential_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_CredentialService_GetCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_GetCredential_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_CredentialService_ListCredentials_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/ListCredentials", runtime.WithHTTPPathPattern("/v1/credentials")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_CredentialService_ListCredentials_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_CredentialService_ListCredentials_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_CredentialService_CreateCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/CreateCredential", runtime.WithHTTPPathPattern("/v1/credentials")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_CredentialService_CreateCredential_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_CredentialService_CreateCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_CreateCredential_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PATCH", pattern_CredentialService_UpdateCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/UpdateCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_CredentialService_UpdateCredential_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_CredentialService_UpdateCredential_0(ctx, mux, outboundMarshaler, w, req, response_CredentialService_UpdateCredential_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_CredentialService_DeleteCredential_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.CredentialService/DeleteCredential", runtime.WithHTTPPathPattern("/v1/credentials/{id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_CredentialService_DeleteCredential_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_CredentialService_DeleteCredential_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +type response_CredentialService_GetCredential_0 struct { + proto.Message +} + +func (m response_CredentialService_GetCredential_0) XXX_ResponseBody() interface{} { + response := m.Message.(*GetCredentialResponse) + return response.Item +} + +type response_CredentialService_CreateCredential_0 struct { + proto.Message +} + +func (m response_CredentialService_CreateCredential_0) XXX_ResponseBody() interface{} { + response := m.Message.(*CreateCredentialResponse) + return response.Item +} + +type response_CredentialService_UpdateCredential_0 struct { + proto.Message +} + +func (m response_CredentialService_UpdateCredential_0) XXX_ResponseBody() interface{} { + response := m.Message.(*UpdateCredentialResponse) + return response.Item +} + +var ( + pattern_CredentialService_GetCredential_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "credentials", "id"}, "")) + + pattern_CredentialService_ListCredentials_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "credentials"}, "")) + + pattern_CredentialService_CreateCredential_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "credentials"}, "")) + + pattern_CredentialService_UpdateCredential_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "credentials", "id"}, "")) + + pattern_CredentialService_DeleteCredential_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "credentials", "id"}, "")) +) + +var ( + forward_CredentialService_GetCredential_0 = runtime.ForwardResponseMessage + + forward_CredentialService_ListCredentials_0 = runtime.ForwardResponseMessage + + forward_CredentialService_CreateCredential_0 = runtime.ForwardResponseMessage + + forward_CredentialService_UpdateCredential_0 = runtime.ForwardResponseMessage + + forward_CredentialService_DeleteCredential_0 = runtime.ForwardResponseMessage +) diff --git a/internal/gen/controller/api/services/credential_service_grpc.pb.go b/internal/gen/controller/api/services/credential_service_grpc.pb.go new file mode 100644 index 000000000..0fd4dfd59 --- /dev/null +++ b/internal/gen/controller/api/services/credential_service_grpc.pb.go @@ -0,0 +1,287 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package services + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// CredentialServiceClient is the client API for CredentialService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type CredentialServiceClient interface { + // GetCredential returns a stored Credential if present. The provided request + // must include the Credential Store id. If missing, malformed or referencing a + // non existing resource an error is returned. + GetCredential(ctx context.Context, in *GetCredentialRequest, opts ...grpc.CallOption) (*GetCredentialResponse, error) + // ListCredentials returns a list of stored Credentials which are in the + // provided store. The request must include the Credential Store id and if missing, + // malformed, or referencing a non existing scope, an error is returned. + ListCredentials(ctx context.Context, in *ListCredentialsRequest, opts ...grpc.CallOption) (*ListCredentialsResponse, error) + // CreateCredential creates and stores an Credential in boundary. The + // provided request must include the credential_store_id in which the Credential + // will be created. If the store id is missing, malformed or referencing a + // non existing resource an error is returned. If a name is provided that is + // in use in another Credential in the same store, an error is returned. + CreateCredential(ctx context.Context, in *CreateCredentialRequest, opts ...grpc.CallOption) (*CreateCredentialResponse, error) + // UpdateCredential updates an existing Credential in boundary. The provided + // Credential must not have any read only fields set. The update mask must be + // included in the request and contain at least 1 mutable field. To unset + // a field's value, include the field in the update mask and don't set it + // in the provided user. An error is returned if the Credential id is missing + // or reference a non existing resource. An error is also returned if the + // request attempts to update the name to one that is already in use by + // another Credential in the parent scope. + UpdateCredential(ctx context.Context, in *UpdateCredentialRequest, opts ...grpc.CallOption) (*UpdateCredentialResponse, error) + // DeleteCredential removes an Credential from Boundary. If the Credential id + // is malformed or not provided an error is returned. + DeleteCredential(ctx context.Context, in *DeleteCredentialRequest, opts ...grpc.CallOption) (*DeleteCredentialResponse, error) +} + +type credentialServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewCredentialServiceClient(cc grpc.ClientConnInterface) CredentialServiceClient { + return &credentialServiceClient{cc} +} + +func (c *credentialServiceClient) GetCredential(ctx context.Context, in *GetCredentialRequest, opts ...grpc.CallOption) (*GetCredentialResponse, error) { + out := new(GetCredentialResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.CredentialService/GetCredential", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *credentialServiceClient) ListCredentials(ctx context.Context, in *ListCredentialsRequest, opts ...grpc.CallOption) (*ListCredentialsResponse, error) { + out := new(ListCredentialsResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.CredentialService/ListCredentials", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *credentialServiceClient) CreateCredential(ctx context.Context, in *CreateCredentialRequest, opts ...grpc.CallOption) (*CreateCredentialResponse, error) { + out := new(CreateCredentialResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.CredentialService/CreateCredential", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *credentialServiceClient) UpdateCredential(ctx context.Context, in *UpdateCredentialRequest, opts ...grpc.CallOption) (*UpdateCredentialResponse, error) { + out := new(UpdateCredentialResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.CredentialService/UpdateCredential", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *credentialServiceClient) DeleteCredential(ctx context.Context, in *DeleteCredentialRequest, opts ...grpc.CallOption) (*DeleteCredentialResponse, error) { + out := new(DeleteCredentialResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.CredentialService/DeleteCredential", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CredentialServiceServer is the server API for CredentialService service. +// All implementations must embed UnimplementedCredentialServiceServer +// for forward compatibility +type CredentialServiceServer interface { + // GetCredential returns a stored Credential if present. The provided request + // must include the Credential Store id. If missing, malformed or referencing a + // non existing resource an error is returned. + GetCredential(context.Context, *GetCredentialRequest) (*GetCredentialResponse, error) + // ListCredentials returns a list of stored Credentials which are in the + // provided store. The request must include the Credential Store id and if missing, + // malformed, or referencing a non existing scope, an error is returned. + ListCredentials(context.Context, *ListCredentialsRequest) (*ListCredentialsResponse, error) + // CreateCredential creates and stores an Credential in boundary. The + // provided request must include the credential_store_id in which the Credential + // will be created. If the store id is missing, malformed or referencing a + // non existing resource an error is returned. If a name is provided that is + // in use in another Credential in the same store, an error is returned. + CreateCredential(context.Context, *CreateCredentialRequest) (*CreateCredentialResponse, error) + // UpdateCredential updates an existing Credential in boundary. The provided + // Credential must not have any read only fields set. The update mask must be + // included in the request and contain at least 1 mutable field. To unset + // a field's value, include the field in the update mask and don't set it + // in the provided user. An error is returned if the Credential id is missing + // or reference a non existing resource. An error is also returned if the + // request attempts to update the name to one that is already in use by + // another Credential in the parent scope. + UpdateCredential(context.Context, *UpdateCredentialRequest) (*UpdateCredentialResponse, error) + // DeleteCredential removes an Credential from Boundary. If the Credential id + // is malformed or not provided an error is returned. + DeleteCredential(context.Context, *DeleteCredentialRequest) (*DeleteCredentialResponse, error) + mustEmbedUnimplementedCredentialServiceServer() +} + +// UnimplementedCredentialServiceServer must be embedded to have forward compatible implementations. +type UnimplementedCredentialServiceServer struct { +} + +func (UnimplementedCredentialServiceServer) GetCredential(context.Context, *GetCredentialRequest) (*GetCredentialResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCredential not implemented") +} +func (UnimplementedCredentialServiceServer) ListCredentials(context.Context, *ListCredentialsRequest) (*ListCredentialsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListCredentials not implemented") +} +func (UnimplementedCredentialServiceServer) CreateCredential(context.Context, *CreateCredentialRequest) (*CreateCredentialResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateCredential not implemented") +} +func (UnimplementedCredentialServiceServer) UpdateCredential(context.Context, *UpdateCredentialRequest) (*UpdateCredentialResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateCredential not implemented") +} +func (UnimplementedCredentialServiceServer) DeleteCredential(context.Context, *DeleteCredentialRequest) (*DeleteCredentialResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteCredential not implemented") +} +func (UnimplementedCredentialServiceServer) mustEmbedUnimplementedCredentialServiceServer() {} + +// UnsafeCredentialServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CredentialServiceServer will +// result in compilation errors. +type UnsafeCredentialServiceServer interface { + mustEmbedUnimplementedCredentialServiceServer() +} + +func RegisterCredentialServiceServer(s grpc.ServiceRegistrar, srv CredentialServiceServer) { + s.RegisterService(&CredentialService_ServiceDesc, srv) +} + +func _CredentialService_GetCredential_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetCredentialRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CredentialServiceServer).GetCredential(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.CredentialService/GetCredential", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CredentialServiceServer).GetCredential(ctx, req.(*GetCredentialRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CredentialService_ListCredentials_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListCredentialsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CredentialServiceServer).ListCredentials(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.CredentialService/ListCredentials", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CredentialServiceServer).ListCredentials(ctx, req.(*ListCredentialsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CredentialService_CreateCredential_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateCredentialRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CredentialServiceServer).CreateCredential(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.CredentialService/CreateCredential", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CredentialServiceServer).CreateCredential(ctx, req.(*CreateCredentialRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CredentialService_UpdateCredential_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateCredentialRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CredentialServiceServer).UpdateCredential(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.CredentialService/UpdateCredential", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CredentialServiceServer).UpdateCredential(ctx, req.(*UpdateCredentialRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CredentialService_DeleteCredential_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteCredentialRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CredentialServiceServer).DeleteCredential(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.CredentialService/DeleteCredential", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CredentialServiceServer).DeleteCredential(ctx, req.(*DeleteCredentialRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// CredentialService_ServiceDesc is the grpc.ServiceDesc for CredentialService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var CredentialService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "controller.api.services.v1.CredentialService", + HandlerType: (*CredentialServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetCredential", + Handler: _CredentialService_GetCredential_Handler, + }, + { + MethodName: "ListCredentials", + Handler: _CredentialService_ListCredentials_Handler, + }, + { + MethodName: "CreateCredential", + Handler: _CredentialService_CreateCredential_Handler, + }, + { + MethodName: "UpdateCredential", + Handler: _CredentialService_UpdateCredential_Handler, + }, + { + MethodName: "DeleteCredential", + Handler: _CredentialService_DeleteCredential_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "controller/api/services/v1/credential_service.proto", +} diff --git a/internal/perms/grants_test.go b/internal/perms/grants_test.go index 19f807280..60fff56a3 100644 --- a/internal/perms/grants_test.go +++ b/internal/perms/grants_test.go @@ -108,7 +108,7 @@ func Test_ActionParsingValidation(t *testing.T) { func Test_ValidateType(t *testing.T) { t.Parallel() var g Grant - for i := resource.Unknown; i <= resource.CredentialLibrary; i++ { + for i := resource.Unknown; i <= resource.Credential; i++ { g.typ = i if i == resource.Controller || i == resource.Worker { assert.Error(t, g.validateType()) diff --git a/internal/proto/controller/api/resources/credentials/v1/credential.proto b/internal/proto/controller/api/resources/credentials/v1/credential.proto new file mode 100644 index 000000000..7b4bd58ec --- /dev/null +++ b/internal/proto/controller/api/resources/credentials/v1/credential.proto @@ -0,0 +1,102 @@ +syntax = "proto3"; + +package controller.api.resources.credentials.v1; + +import "controller/api/resources/scopes/v1/scope.proto"; +import "controller/custom_options/v1/options.proto"; +import "google/api/visibility.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; + +option go_package = "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials;credentials"; + +// Credential contains all fields related to an Credential resource +message Credential { + // Output only. The ID of the Credential. + string id = 10; // @gotags: `class:"public"` + + // The ID of the Credential Store of which this Credential is a part. + string credential_store_id = 20 [json_name = "credential_store_id"]; // @gotags: `class:"public"` + + // Output only. Scope information for this Credential. + resources.scopes.v1.ScopeInfo scope = 30; + + // Optional name for identification purposes. + google.protobuf.StringValue name = 40 [ + (custom_options.v1.generate_sdk_option) = true, + (custom_options.v1.mask_mapping) = { + this: "name" + that: "Name" + } + ]; // @gotags: `class:"public"` + + // Optional user-set description for identification purposes. + google.protobuf.StringValue description = 50 [ + (custom_options.v1.generate_sdk_option) = true, + (custom_options.v1.mask_mapping) = { + this: "description" + that: "Description" + } + ]; // @gotags: `class:"public"` + + // Output only. The time this resource was created. + google.protobuf.Timestamp created_time = 60 [json_name = "created_time"]; // @gotags: `class:"public"` + + // Output only. The time this resource was last updated. + google.protobuf.Timestamp updated_time = 70 [json_name = "updated_time"]; // @gotags: `class:"public"` + + // Version is used in mutation requests, after the initial creation, to ensure this resource has not changed. + // The mutation will fail if the version does not match the latest known good version. + uint32 version = 80; // @gotags: `class:"public"` + + // The Credential type. + string type = 90; // @gotags: `class:"public"` + + oneof attrs { + // The attributes that are applicable for the specific Credential type. + google.protobuf.Struct attributes = 100 [ + (custom_options.v1.generate_sdk_option) = true, + (custom_options.v1.subtype) = "default" + ]; + UsernamePasswordAttributes username_password_attributes = 101 [ + (google.api.field_visibility).restriction = "INTERNAL", + (custom_options.v1.generate_sdk_option) = true, + (custom_options.v1.subtype) = "username_password" + ]; + } + + // Output only. The available actions on this resource for this user. + repeated string authorized_actions = 300 [json_name = "authorized_actions"]; // @gotags: `class:"public"` +} + +// The attributes of a UsernamePassword Credential. +message UsernamePasswordAttributes { + // The username associated with the credential. + google.protobuf.StringValue username = 10 [ + (custom_options.v1.generate_sdk_option) = true, + (custom_options.v1.mask_mapping) = { + this: "attributes.username" + that: "Username" + } + ]; // @gotags: `class:"public"` + + // Input only. The password associated with the credential. + google.protobuf.StringValue password = 20 [ + json_name = "password", + (custom_options.v1.generate_sdk_option) = true, + (custom_options.v1.mask_mapping) = { + this: "attributes.password" + that: "Password" + } + ]; // @gotags: `class:"secret"` + + // Output only. The hmac value of the password. + string password_hmac = 30 [ + json_name = "password_hmac", + (custom_options.v1.mask_mapping) = { + this: "attributes.password_hmac" + that: "PasswordHmac" + } + ]; // @gotags: `class:"public"` +} diff --git a/internal/proto/controller/api/services/v1/credential_service.proto b/internal/proto/controller/api/services/v1/credential_service.proto new file mode 100644 index 000000000..f3cc24611 --- /dev/null +++ b/internal/proto/controller/api/services/v1/credential_service.proto @@ -0,0 +1,127 @@ +syntax = "proto3"; + +package controller.api.services.v1; + +import "controller/api/resources/credentials/v1/credential.proto"; +import "controller/custom_options/v1/options.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/field_mask.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; + +option go_package = "github.com/hashicorp/boundary/internal/gen/controller/api/services;services"; +option (custom_options.v1.domain) = "credential"; + +service CredentialService { + // GetCredential returns a stored Credential if present. The provided request + // must include the Credential Store id. If missing, malformed or referencing a + // non existing resource an error is returned. + rpc GetCredential(GetCredentialRequest) returns (GetCredentialResponse) { + option (google.api.http) = { + get: "/v1/credentials/{id}" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Gets a single Credential." + }; + } + + // ListCredentials returns a list of stored Credentials which are in the + // provided store. The request must include the Credential Store id and if missing, + // malformed, or referencing a non existing scope, an error is returned. + rpc ListCredentials(ListCredentialsRequest) returns (ListCredentialsResponse) { + option (google.api.http) = { + get: "/v1/credentials" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Lists all Credentials." + }; + } + + // CreateCredential creates and stores an Credential in boundary. The + // provided request must include the credential_store_id in which the Credential + // will be created. If the store id is missing, malformed or referencing a + // non existing resource an error is returned. If a name is provided that is + // in use in another Credential in the same store, an error is returned. + rpc CreateCredential(CreateCredentialRequest) returns (CreateCredentialResponse) { + option (google.api.http) = { + post: "/v1/credentials" + body: "item" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Creates a single Credential." + }; + } + + // UpdateCredential updates an existing Credential in boundary. The provided + // Credential must not have any read only fields set. The update mask must be + // included in the request and contain at least 1 mutable field. To unset + // a field's value, include the field in the update mask and don't set it + // in the provided user. An error is returned if the Credential id is missing + // or reference a non existing resource. An error is also returned if the + // request attempts to update the name to one that is already in use by + // another Credential in the parent scope. + rpc UpdateCredential(UpdateCredentialRequest) returns (UpdateCredentialResponse) { + option (google.api.http) = { + patch: "/v1/credentials/{id}" + body: "item" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Updates a Credential." + }; + } + + // DeleteCredential removes an Credential from Boundary. If the Credential id + // is malformed or not provided an error is returned. + rpc DeleteCredential(DeleteCredentialRequest) returns (DeleteCredentialResponse) { + option (google.api.http) = { + delete: "/v1/credentials/{id}" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Deletes a Credential" + }; + } +} + +message GetCredentialRequest { + string id = 1; // @gotags: `class:"public"` +} + +message GetCredentialResponse { + resources.credentials.v1.Credential item = 1; +} + +message ListCredentialsRequest { + string credential_store_id = 1 [json_name = "scope_id"]; // @gotags: `class:"public"` + string filter = 30 [json_name = "filter"]; // @gotags: `class:"public"` +} + +message ListCredentialsResponse { + repeated resources.credentials.v1.Credential items = 1; +} + +message CreateCredentialRequest { + resources.credentials.v1.Credential item = 1; +} + +message CreateCredentialResponse { + string uri = 1; // @gotags: `class:"public"` + resources.credentials.v1.Credential item = 2; +} + +message UpdateCredentialRequest { + string id = 1; // @gotags: `class:"public"` + resources.credentials.v1.Credential item = 2; + google.protobuf.FieldMask update_mask = 3 [json_name = "update_mask"]; +} + +message UpdateCredentialResponse { + resources.credentials.v1.Credential item = 1; +} + +message DeleteCredentialRequest { + string id = 1; // @gotags: `class:"public"` +} + +message DeleteCredentialResponse {} diff --git a/internal/proto/controller/storage/credential/static/store/v1/static.proto b/internal/proto/controller/storage/credential/static/store/v1/static.proto new file mode 100644 index 000000000..4a1116320 --- /dev/null +++ b/internal/proto/controller/storage/credential/static/store/v1/static.proto @@ -0,0 +1,118 @@ +syntax = "proto3"; + +// Package store provides protobufs for storing types in the static +// credential package. +package controller.storage.credential.static.store.v1; + +import "controller/custom_options/v1/options.proto"; +import "controller/storage/timestamp/v1/timestamp.proto"; + +option go_package = "github.com/hashicorp/boundary/internal/credential/static/store;store"; + +message CredentialStore { + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + string public_id = 1; + + // The create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 2; + + // The update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 3; + + // name is optional. If set, it must be unique within scope_id. + // @inject_tag: `gorm:"default:null"` + string name = 4 [(custom_options.v1.mask_mapping) = { + this: "Name" + that: "name" + }]; + + // description is optional. + // @inject_tag: `gorm:"default:null"` + string description = 5 [(custom_options.v1.mask_mapping) = { + this: "Description" + that: "description" + }]; + + // The scope_id of the owning scope. + // It must be set. + // @inject_tag: `gorm:"not_null"` + string scope_id = 6; + + // version allows optimistic locking of the resource. + // @inject_tag: `gorm:"default:null"` + uint32 version = 7; +} + +message UsernamePasswordCredential { + // public_id is a surrogate key suitable for use in a public API. + // @inject_tag: `gorm:"primary_key"` + string public_id = 1; + + // create_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 2; + + // update_time is set by the database. + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 3; + + // name is optional. If set, it must be unique within scope_id. + // @inject_tag: `gorm:"default:null"` + string name = 4 [(custom_options.v1.mask_mapping) = { + this: "Name" + that: "name" + }]; + + // description is optional. + // @inject_tag: `gorm:"default:null"` + string description = 5 [(custom_options.v1.mask_mapping) = { + this: "Description" + that: "description" + }]; + + // store_id of the owning static credential store. + // It must be set. + // @inject_tag: `gorm:"not_null"` + string store_id = 6; + + // version allows optimistic locking of the resource. + // @inject_tag: `gorm:"default:null"` + uint32 version = 7; + + // username is the username associated with the credential. + // It must be set. + // @inject_tag: `gorm:"not_null"` + string username = 8 [(custom_options.v1.mask_mapping) = { + this: "Username" + that: "attributes.username" + }]; + + // password is the plain-text of the password associated with the credential. We are + // not storing this plain-text password in the database. + // @inject_tag: `gorm:"-" wrapping:"pt,password_data"` + bytes password = 9 [(custom_options.v1.mask_mapping) = { + this: "Password" + that: "attributes.password" + }]; + + // ct_password is the ciphertext of the password. It + // is stored in the database. + // @inject_tag: `gorm:"column:password_encrypted;not_null" wrapping:"ct,password_data"` + bytes ct_password = 10; + + // password_hmac is a sha256-hmac of the unencrypted password. It is recalculated + // everytime the password is updated. + // @inject_tag: `gorm:"not_null"` + bytes password_hmac = 11 [(custom_options.v1.mask_mapping) = { + this: "PasswordHmac" + that: "attributes.password_hmac" + }]; + + // The key_id of the kms database key used for encrypting this entry. + // It must be set. + // @inject_tag: `gorm:"not_null"` + string key_id = 12; +} diff --git a/internal/tests/api/credentials/credentials_classification_test.go b/internal/tests/api/credentials/credentials_classification_test.go new file mode 100644 index 000000000..ea981df7a --- /dev/null +++ b/internal/tests/api/credentials/credentials_classification_test.go @@ -0,0 +1,201 @@ +package credentials_test + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/hashicorp/boundary/sdk/pbs/controller/api" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" + "github.com/hashicorp/boundary/sdk/wrapper" + "github.com/hashicorp/eventlogger" + "github.com/hashicorp/eventlogger/filters/encrypt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +func TestCredentialClassification(t *testing.T) { + ctx := context.Background() + now := time.Now() + pbNow := timestamppb.Now() + wrapper := wrapper.TestWrapper(t) + testEncryptingFilter := api.NewEncryptFilter(t, wrapper) + + cases := []struct { + name string + in *eventlogger.Event + want *eventlogger.Event + }{ + { + "Usernamepassword", + &eventlogger.Event{ + Type: "test", + CreatedAt: now, + Payload: &pb.Credential{ + Id: "id", + CredentialStoreId: "cred-store-id", + Scope: &scopes.ScopeInfo{ + Id: "scope-id", + Type: "scope-type", + Name: "scope-name", + Description: "scope-descriptione", + ParentScopeId: "scope-parent-scope-id", + }, + Name: &wrapperspb.StringValue{ + Value: "name", + }, + Description: &wrapperspb.StringValue{ + Value: "description", + }, + CreatedTime: pbNow, + UpdatedTime: pbNow, + Version: 1, + Type: "username_password", + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: &wrapperspb.StringValue{Value: "username"}, + Password: &wrapperspb.StringValue{Value: "password"}, + PasswordHmac: "password-hmac", + }, + }, + AuthorizedActions: []string{ + "action-1", + "action-2", + }, + }, + }, + &eventlogger.Event{ + Type: "test", + CreatedAt: now, + Payload: &pb.Credential{ + Id: "id", + CredentialStoreId: "cred-store-id", + Scope: &scopes.ScopeInfo{ + Id: "scope-id", + Type: "scope-type", + Name: "scope-name", + Description: "scope-descriptione", + ParentScopeId: "scope-parent-scope-id", + }, + Name: &wrapperspb.StringValue{ + Value: "name", + }, + Description: &wrapperspb.StringValue{ + Value: "description", + }, + CreatedTime: pbNow, + UpdatedTime: pbNow, + Version: 1, + Type: "username_password", + Attrs: &pb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{ + Username: &wrapperspb.StringValue{Value: "username"}, + Password: &wrapperspb.StringValue{Value: "[REDACTED]"}, + PasswordHmac: "password-hmac", + }, + }, + AuthorizedActions: []string{ + "action-1", + "action-2", + }, + }, + }, + }, + { + "Default", + &eventlogger.Event{ + Type: "test", + CreatedAt: now, + Payload: &pb.Credential{ + Id: "id", + CredentialStoreId: "cred-store-id", + Scope: &scopes.ScopeInfo{ + Id: "scope-id", + Type: "scope-type", + Name: "scope-name", + Description: "scope-descriptione", + ParentScopeId: "scope-parent-scope-id", + }, + Name: &wrapperspb.StringValue{ + Value: "name", + }, + Description: &wrapperspb.StringValue{ + Value: "description", + }, + CreatedTime: pbNow, + UpdatedTime: pbNow, + Version: 1, + Type: "username_password", + Attrs: &pb.Credential_Attributes{ + Attributes: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "field1": structpb.NewStringValue("value-1"), + "field2": structpb.NewStringValue("value-2"), + }, + }, + }, + AuthorizedActions: []string{ + "action-1", + "action-2", + }, + }, + }, + &eventlogger.Event{ + Type: "test", + CreatedAt: now, + Payload: &pb.Credential{ + Id: "id", + CredentialStoreId: "cred-store-id", + Scope: &scopes.ScopeInfo{ + Id: "scope-id", + Type: "scope-type", + Name: "scope-name", + Description: "scope-descriptione", + ParentScopeId: "scope-parent-scope-id", + }, + Name: &wrapperspb.StringValue{ + Value: "name", + }, + Description: &wrapperspb.StringValue{ + Value: "description", + }, + CreatedTime: pbNow, + UpdatedTime: pbNow, + Version: 1, + Type: "username_password", + Attrs: &pb.Credential_Attributes{ + Attributes: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "field1": structpb.NewStringValue(encrypt.RedactedData), + "field2": structpb.NewStringValue(encrypt.RedactedData), + }, + }, + }, + AuthorizedActions: []string{ + "action-1", + "action-2", + }, + }, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + wantJSON, err := json.Marshal(tc.want) + require.NoError(t, err) + + got, err := testEncryptingFilter.Process(ctx, tc.in) + require.NoError(t, err) + require.NotNil(t, got) + gotJSON, err := json.Marshal(got) + require.NoError(t, err) + assert.JSONEq(t, string(wantJSON), string(gotJSON)) + }) + } +} diff --git a/internal/tests/api/credentials/credentials_test.go b/internal/tests/api/credentials/credentials_test.go new file mode 100644 index 000000000..4b41ad42c --- /dev/null +++ b/internal/tests/api/credentials/credentials_test.go @@ -0,0 +1,208 @@ +package credentials_test + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/credentials" + "github.com/hashicorp/boundary/api/credentialstores" + "github.com/hashicorp/boundary/internal/credential/static" + "github.com/hashicorp/boundary/internal/daemon/controller" + "github.com/hashicorp/boundary/internal/iam" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +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)) + + cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) + require.NoError(err) + require.NotNil(cs) + + credClient := credentials.NewClient(client) + + ul, err := credClient.List(tc.Context(), cs.Item.Id) + require.NoError(err) + assert.Empty(ul.Items) + + cred, err := credClient.Create(tc.Context(), static.UsernamePasswordSubtype.String(), cs.Item.Id, + credentials.WithName("0"), + credentials.WithDescription("description"), + credentials.WithUsernamePasswordCredentialUsername("user"), + credentials.WithUsernamePasswordCredentialPassword("pass")) + require.NoError(err) + + expected := make([]*credentials.Credential, 10) + expected[0] = cred.Item + + ul, err = credClient.List(tc.Context(), cs.Item.Id) + require.NoError(err) + assert.ElementsMatch(comparableCatalogSlice(expected[:1]), comparableCatalogSlice(ul.Items)) + + for i := 1; i < 10; i++ { + cred, err := credClient.Create(tc.Context(), static.UsernamePasswordSubtype.String(), cs.Item.Id, + credentials.WithName(fmt.Sprintf("%d", i)), + credentials.WithDescription("description"), + credentials.WithUsernamePasswordCredentialUsername("user"), + credentials.WithUsernamePasswordCredentialPassword("pass")) + require.NoError(err) + expected[i] = cred.Item + } + ul, err = credClient.List(tc.Context(), cs.Item.Id) + require.NoError(err) + assert.ElementsMatch(comparableCatalogSlice(expected), comparableCatalogSlice(ul.Items)) + + filterItem := ul.Items[3] + ul, err = credClient.List(tc.Context(), cs.Item.Id, + credentials.WithFilter(fmt.Sprintf(`"/item/id"==%q`, filterItem.Id))) + require.NoError(err) + assert.Len(ul.Items, 1) + assert.Equal(filterItem.Id, ul.Items[0].Id) +} + +func comparableCatalogSlice(in []*credentials.Credential) []credentials.Credential { + var filtered []credentials.Credential + for _, i := range in { + p := credentials.Credential{ + Id: i.Id, + Name: i.Name, + Description: i.Description, + CreatedTime: i.CreatedTime, + UpdatedTime: i.UpdatedTime, + Attributes: i.Attributes, + } + filtered = append(filtered, p) + } + return filtered +} + +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)) + + cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) + require.NoError(err) + require.NotNil(cs) + + checkResource := func(step string, c *credentials.Credential, wantedName, wantedUser string, wantVersion uint32) { + assert.NotNil(c, "returned no resource", step) + assert.Equal(wantedName, c.Name, step) + gotUser, ok := c.Attributes["username"] + require.True(ok) + assert.Equal(wantedUser, gotUser, step) + assert.Equal(wantVersion, c.Version) + } + credClient := credentials.NewClient(client) + + cred, err := credClient.Create(tc.Context(), static.UsernamePasswordSubtype.String(), cs.Item.Id, credentials.WithName("foo"), + credentials.WithUsernamePasswordCredentialUsername("user"), credentials.WithUsernamePasswordCredentialPassword("pass")) + require.NoError(err) + require.NotNil(cs) + checkResource("create", cred.Item, "foo", "user", 1) + + cred, err = credClient.Read(tc.Context(), cred.Item.Id) + require.NoError(err) + require.NotNil(cs) + checkResource("read", cred.Item, "foo", "user", 1) + + cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithName("bar")) + require.NoError(err) + require.NotNil(cs) + checkResource("update", cred.Item, "bar", "user", 2) + + cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithUsernamePasswordCredentialUsername("newuser")) + require.NoError(err) + require.NotNil(cs) + checkResource("update", cred.Item, "bar", "newuser", 3) + + cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.DefaultName()) + require.NoError(err) + require.NotNil(cs) + checkResource("update", cred.Item, "", "newuser", 4) + + cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, + credentials.WithName("newuser"), credentials.WithUsernamePasswordCredentialUsername("neweruser")) + require.NoError(err) + require.NotNil(cs) + checkResource("update", cred.Item, "newuser", "neweruser", 5) + + _, err = credClient.Delete(tc.Context(), cred.Item.Id) + assert.NoError(err) + + _, err = credClient.Delete(tc.Context(), cred.Item.Id) + require.Error(err) + apiErr := api.AsServerError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) +} + +func TestErrors(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)) + cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) + require.NoError(err) + require.NotNil(cs) + + c := credentials.NewClient(client) + + cred, err := c.Create(tc.Context(), static.UsernamePasswordSubtype.String(), cs.Item.Id, credentials.WithName("foo"), + credentials.WithUsernamePasswordCredentialUsername("user"), credentials.WithUsernamePasswordCredentialPassword("pass")) + require.NoError(err) + require.NotNil(cred) + + // A malformed id is processed as the id and not a different path to the api. + _, err = c.Read(tc.Context(), fmt.Sprintf("%s/../", cred.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 the wrong version should fail. + _, err = c.Update(tc.Context(), cred.Item.Id, 73, credentials.WithName("anything")) + require.Error(err) + apiErr = api.AsServerError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) + + // Same name + _, err = c.Create(tc.Context(), static.UsernamePasswordSubtype.String(), proj.GetPublicId(), credentials.WithName("foo")) + require.Error(err) + apiErr = api.AsServerError(err) + assert.NotNil(apiErr) + + _, err = c.Read(tc.Context(), static.CredentialPrefix+"_doesntexis") + require.Error(err) + apiErr = api.AsServerError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode()) + + _, err = c.Read(tc.Context(), "invalid id") + require.Error(err) + apiErr = api.AsServerError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusBadRequest, apiErr.Response().StatusCode()) +} diff --git a/internal/tests/api/credentialstores/credentialstore_test.go b/internal/tests/api/credentialstores/credentialstore_test.go index 158a69ace..dc5e077bf 100644 --- a/internal/tests/api/credentialstores/credentialstore_test.go +++ b/internal/tests/api/credentialstores/credentialstore_test.go @@ -49,12 +49,17 @@ func TestList(t *testing.T) { require.NoError(err) assert.ElementsMatch(comparableCatalogSlice(expected[:1]), comparableCatalogSlice(ul.Items)) - for i := 1; i < 10; i++ { + for i := 1; i < 5; i++ { cr, err = storeClient.Create(tc.Context(), "vault", proj.GetPublicId(), credentialstores.WithName(expected[i].Name), credentialstores.WithVaultCredentialStoreAddress(vaultServ.Addr), credentialstores.WithVaultCredentialStoreToken(expected[i].Attributes["token"].(string))) require.NoError(err) expected[i] = cr.Item } + for i := 5; i < 10; i++ { + cr, err = storeClient.Create(tc.Context(), "static", proj.GetPublicId(), credentialstores.WithName(expected[i].Name)) + require.NoError(err) + expected[i] = cr.Item + } ul, err = storeClient.List(tc.Context(), proj.GetPublicId()) require.NoError(err) assert.ElementsMatch(comparableCatalogSlice(expected), comparableCatalogSlice(ul.Items)) diff --git a/internal/tests/cli/boundary/_credential_stores.bash b/internal/tests/cli/boundary/_credential_stores.bash new file mode 100644 index 000000000..54db7cc93 --- /dev/null +++ b/internal/tests/cli/boundary/_credential_stores.bash @@ -0,0 +1,30 @@ +load _authorized_actions + +function create_static_credential_store() { + local name=$1 + local sid=$2 + + boundary credential-stores create static \ + -name $name \ + -description 'test static credential store' \ + -scope-id $sid +} + +function read_credential_store() { + boundary credential-stores read -id $1 -format json +} + +function delete_credential_store() { + boundary credential-stores delete -id $1 +} + +function list_credential_stores() { + boundary credential-stores list -scope-id $1 -format json +} + +function credential_store_id() { + local name=$1 + local sid=$2 + + strip $(list_credential_stores $sid | jq -c ".items[] | select(.name | contains(\"$name\")) | .[\"id\"]") +} diff --git a/internal/tests/cli/boundary/_credentials.bash b/internal/tests/cli/boundary/_credentials.bash new file mode 100644 index 000000000..016ab48b7 --- /dev/null +++ b/internal/tests/cli/boundary/_credentials.bash @@ -0,0 +1,34 @@ +load _authorized_actions + +function create_username_password_credential() { + local name=$1 + local sid=$2 + local user=$3 + local pass=$4 + + boundary credentials create username-password \ + -name $name \ + -description 'test username password credential' \ + -credential-store-id $sid \ + -username $user \ + -password $pass +} + +function read_credential() { + boundary credentials read -id $1 -format json +} + +function delete_credential() { + boundary credentials delete -id $1 +} + +function list_credentials() { + boundary credentials list -credential-store-id $1 -format json +} + +function credential_id() { + local name=$1 + local sid=$2 + + strip $(list_credentials $sid | jq -c ".items[] | select(.name | contains(\"$name\")) | .[\"id\"]") +} diff --git a/internal/tests/cli/boundary/credential_stores.bats b/internal/tests/cli/boundary/credential_stores.bats new file mode 100644 index 000000000..a2dbde63a --- /dev/null +++ b/internal/tests/cli/boundary/credential_stores.bats @@ -0,0 +1,47 @@ +#!/usr/bin/env bats + +load _auth +load _helpers +load _credential_stores + +export NEW_STORE='test' + +@test "boundary/login: can login as default user" { + run login $DEFAULT_LOGIN + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/credential-stores: can create $NEW_STORE static store in default project" { + run create_static_credential_store $NEW_STORE $DEFAULT_P_ID + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/credential-stores: can not create already created $NEW_STORE static store" { + run create_static_credential_store $NEW_STORE $DEFAULT_P_ID + echo "$output" + [ "$status" -eq 1 ] +} + +@test "boundary/credential-stores: can read $NEW_STORE static store" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + run read_credential_store $csid + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/credential-stores: can delete $NEW_STORE static store" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + run delete_credential_store $csid + echo "$output" + run has_status_code "$output" "204" + [ "$status" -eq 0 ] +} + +@test "boundary/credential-stores: can not read deleted $NEW_STORE static store" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + run read_credential_store $csid + echo "$output" + [ "$status" -eq 1 ] +} diff --git a/internal/tests/cli/boundary/credentials.bats b/internal/tests/cli/boundary/credentials.bats new file mode 100644 index 000000000..8878f0437 --- /dev/null +++ b/internal/tests/cli/boundary/credentials.bats @@ -0,0 +1,60 @@ +#!/usr/bin/env bats + +load _auth +load _helpers +load _credential_stores +load _credentials + +export NEW_STORE='credentials-test-store' +export NEW_CREDENTIAL='test' + +@test "boundary/login: can login as default user" { + run login $DEFAULT_LOGIN + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/credential-stores: can create $NEW_STORE static store in default project" { + run create_static_credential_store $NEW_STORE $DEFAULT_P_ID + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/credentials: can create $NEW_CREDENTIAL credential in $NEW_STORE store" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + run create_username_password_credential $NEW_CREDENTIAL $csid 'username' 'password' + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/credentials: can not create already created $NEW_CREDENTIAL credential" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + run create_username_password_credential $NEW_CREDENTIAL $csid 'username' 'password' + echo "$output" + [ "$status" -eq 1 ] +} + +@test "boundary/credentials: can read $NEW_CREDENTIAL credential" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + run read_credential $cid + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/credentials: can delete $NEW_CREDENTIAL credential" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + run delete_credential $cid + echo "$output" + run has_status_code "$output" "204" + [ "$status" -eq 0 ] +} + +@test "boundary/credential-stores: can not read deleted $NEW_CREDENTIAL credential" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + run read_credential $cid + echo "$output" + [ "$status" -eq 1 ] +} diff --git a/internal/types/resource/resource.go b/internal/types/resource/resource.go index b53785030..4d30ab4a2 100644 --- a/internal/types/resource/resource.go +++ b/internal/types/resource/resource.go @@ -25,6 +25,7 @@ const ( ManagedGroup CredentialStore CredentialLibrary + Credential // NOTE: When adding a new type, be sure to update: // // * The Grant.validateType function and test @@ -58,6 +59,7 @@ func (r Type) String() string { "managed-group", "credential-store", "credential-library", + "credential", }[r] } @@ -90,4 +92,5 @@ var Map = map[string]Type{ ManagedGroup.String(): ManagedGroup, CredentialStore.String(): CredentialStore, CredentialLibrary.String(): CredentialLibrary, + Credential.String(): Credential, } diff --git a/sdk/pbs/controller/api/resources/credentials/credential.pb.go b/sdk/pbs/controller/api/resources/credentials/credential.pb.go new file mode 100644 index 000000000..cc8c3168f --- /dev/null +++ b/sdk/pbs/controller/api/resources/credentials/credential.pb.go @@ -0,0 +1,462 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: controller/api/resources/credentials/v1/credential.proto + +package credentials + +import ( + scopes "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" + _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" + _ "google.golang.org/genproto/googleapis/api/visibility" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Credential contains all fields related to an Credential resource +type Credential struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Output only. The ID of the Credential. + Id string `protobuf:"bytes,10,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` + // The ID of the Credential Store of which this Credential is a part. + CredentialStoreId string `protobuf:"bytes,20,opt,name=credential_store_id,proto3" json:"credential_store_id,omitempty" class:"public"` // @gotags: `class:"public"` + // Output only. Scope information for this Credential. + Scope *scopes.ScopeInfo `protobuf:"bytes,30,opt,name=scope,proto3" json:"scope,omitempty"` + // Optional name for identification purposes. + Name *wrapperspb.StringValue `protobuf:"bytes,40,opt,name=name,proto3" json:"name,omitempty" class:"public"` // @gotags: `class:"public"` + // Optional user-set description for identification purposes. + Description *wrapperspb.StringValue `protobuf:"bytes,50,opt,name=description,proto3" json:"description,omitempty" class:"public"` // @gotags: `class:"public"` + // Output only. The time this resource was created. + CreatedTime *timestamppb.Timestamp `protobuf:"bytes,60,opt,name=created_time,proto3" json:"created_time,omitempty" class:"public"` // @gotags: `class:"public"` + // Output only. The time this resource was last updated. + UpdatedTime *timestamppb.Timestamp `protobuf:"bytes,70,opt,name=updated_time,proto3" json:"updated_time,omitempty" class:"public"` // @gotags: `class:"public"` + // Version is used in mutation requests, after the initial creation, to ensure this resource has not changed. + // The mutation will fail if the version does not match the latest known good version. + Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"` + // The Credential type. + Type string `protobuf:"bytes,90,opt,name=type,proto3" json:"type,omitempty" class:"public"` // @gotags: `class:"public"` + // Types that are assignable to Attrs: + // *Credential_Attributes + // *Credential_UsernamePasswordAttributes + Attrs isCredential_Attrs `protobuf_oneof:"attrs"` + // Output only. The available actions on this resource for this user. + AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty" class:"public"` // @gotags: `class:"public"` +} + +func (x *Credential) Reset() { + *x = Credential{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Credential) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Credential) ProtoMessage() {} + +func (x *Credential) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Credential.ProtoReflect.Descriptor instead. +func (*Credential) Descriptor() ([]byte, []int) { + return file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP(), []int{0} +} + +func (x *Credential) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Credential) GetCredentialStoreId() string { + if x != nil { + return x.CredentialStoreId + } + return "" +} + +func (x *Credential) GetScope() *scopes.ScopeInfo { + if x != nil { + return x.Scope + } + return nil +} + +func (x *Credential) GetName() *wrapperspb.StringValue { + if x != nil { + return x.Name + } + return nil +} + +func (x *Credential) GetDescription() *wrapperspb.StringValue { + if x != nil { + return x.Description + } + return nil +} + +func (x *Credential) GetCreatedTime() *timestamppb.Timestamp { + if x != nil { + return x.CreatedTime + } + return nil +} + +func (x *Credential) GetUpdatedTime() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedTime + } + return nil +} + +func (x *Credential) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *Credential) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (m *Credential) GetAttrs() isCredential_Attrs { + if m != nil { + return m.Attrs + } + return nil +} + +func (x *Credential) GetAttributes() *structpb.Struct { + if x, ok := x.GetAttrs().(*Credential_Attributes); ok { + return x.Attributes + } + return nil +} + +func (x *Credential) GetUsernamePasswordAttributes() *UsernamePasswordAttributes { + if x, ok := x.GetAttrs().(*Credential_UsernamePasswordAttributes); ok { + return x.UsernamePasswordAttributes + } + return nil +} + +func (x *Credential) GetAuthorizedActions() []string { + if x != nil { + return x.AuthorizedActions + } + return nil +} + +type isCredential_Attrs interface { + isCredential_Attrs() +} + +type Credential_Attributes struct { + // The attributes that are applicable for the specific Credential type. + Attributes *structpb.Struct `protobuf:"bytes,100,opt,name=attributes,proto3,oneof"` +} + +type Credential_UsernamePasswordAttributes struct { + UsernamePasswordAttributes *UsernamePasswordAttributes `protobuf:"bytes,101,opt,name=username_password_attributes,json=usernamePasswordAttributes,proto3,oneof"` +} + +func (*Credential_Attributes) isCredential_Attrs() {} + +func (*Credential_UsernamePasswordAttributes) isCredential_Attrs() {} + +// The attributes of a UsernamePassword Credential. +type UsernamePasswordAttributes struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The username associated with the credential. + Username *wrapperspb.StringValue `protobuf:"bytes,10,opt,name=username,proto3" json:"username,omitempty" class:"public"` // @gotags: `class:"public"` + // Input only. The password associated with the credential. + Password *wrapperspb.StringValue `protobuf:"bytes,20,opt,name=password,proto3" json:"password,omitempty" class:"secret"` // @gotags: `class:"secret"` + // Output only. The hmac value of the password. + PasswordHmac string `protobuf:"bytes,30,opt,name=password_hmac,proto3" json:"password_hmac,omitempty" class:"public"` // @gotags: `class:"public"` +} + +func (x *UsernamePasswordAttributes) Reset() { + *x = UsernamePasswordAttributes{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UsernamePasswordAttributes) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UsernamePasswordAttributes) ProtoMessage() {} + +func (x *UsernamePasswordAttributes) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_resources_credentials_v1_credential_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UsernamePasswordAttributes.ProtoReflect.Descriptor instead. +func (*UsernamePasswordAttributes) Descriptor() ([]byte, []int) { + return file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP(), []int{1} +} + +func (x *UsernamePasswordAttributes) GetUsername() *wrapperspb.StringValue { + if x != nil { + return x.Username + } + return nil +} + +func (x *UsernamePasswordAttributes) GetPassword() *wrapperspb.StringValue { + if x != nil { + return x.Password + } + return nil +} + +func (x *UsernamePasswordAttributes) GetPasswordHmac() string { + if x != nil { + return x.PasswordHmac + } + return "" +} + +var File_controller_api_resources_credentials_v1_credential_proto protoreflect.FileDescriptor + +var file_controller_api_resources_credentials_v1_credential_proto_rawDesc = []byte{ + 0x0a, 0x38, 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, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, + 0x2e, 0x76, 0x31, 0x1a, 0x2e, 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, 0x73, 0x63, + 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, + 0x31, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x69, 0x73, 0x69, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, + 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa8, 0x06, 0x0a, 0x0a, + 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x63, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, + 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x4e, + 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, + 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, + 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, + 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x4a, 0x0a, 0x0a, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x0f, 0xa0, 0xda, 0x29, 0x01, 0x9a, 0xe3, + 0x29, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0xb2, 0x01, 0x0a, 0x1c, 0x75, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x43, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x42, 0x29, 0xa0, 0xda, 0x29, 0x01, 0x9a, 0xe3, 0x29, 0x11, 0x75, 0x73, 0x65, + 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0xfa, 0xd2, + 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x48, 0x00, + 0x52, 0x1a, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x07, 0x0a, + 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x22, 0xb6, 0x02, 0x0a, 0x1a, 0x55, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x61, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x27, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1f, 0x0a, + 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x08, + 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x61, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x27, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, + 0x29, 0x1f, 0x0a, 0x13, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x08, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x52, 0x0a, 0x0d, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x2c, 0xc2, 0xdd, 0x29, 0x28, 0x0a, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, + 0x61, 0x63, 0x12, 0x0c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x6d, 0x61, 0x63, + 0x52, 0x0d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x42, + 0x58, 0x5a, 0x56, 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, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x3b, 0x63, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_controller_api_resources_credentials_v1_credential_proto_rawDescOnce sync.Once + file_controller_api_resources_credentials_v1_credential_proto_rawDescData = file_controller_api_resources_credentials_v1_credential_proto_rawDesc +) + +func file_controller_api_resources_credentials_v1_credential_proto_rawDescGZIP() []byte { + file_controller_api_resources_credentials_v1_credential_proto_rawDescOnce.Do(func() { + file_controller_api_resources_credentials_v1_credential_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_resources_credentials_v1_credential_proto_rawDescData) + }) + return file_controller_api_resources_credentials_v1_credential_proto_rawDescData +} + +var file_controller_api_resources_credentials_v1_credential_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_controller_api_resources_credentials_v1_credential_proto_goTypes = []interface{}{ + (*Credential)(nil), // 0: controller.api.resources.credentials.v1.Credential + (*UsernamePasswordAttributes)(nil), // 1: controller.api.resources.credentials.v1.UsernamePasswordAttributes + (*scopes.ScopeInfo)(nil), // 2: controller.api.resources.scopes.v1.ScopeInfo + (*wrapperspb.StringValue)(nil), // 3: google.protobuf.StringValue + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 5: google.protobuf.Struct +} +var file_controller_api_resources_credentials_v1_credential_proto_depIdxs = []int32{ + 2, // 0: controller.api.resources.credentials.v1.Credential.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 3, // 1: controller.api.resources.credentials.v1.Credential.name:type_name -> google.protobuf.StringValue + 3, // 2: controller.api.resources.credentials.v1.Credential.description:type_name -> google.protobuf.StringValue + 4, // 3: controller.api.resources.credentials.v1.Credential.created_time:type_name -> google.protobuf.Timestamp + 4, // 4: controller.api.resources.credentials.v1.Credential.updated_time:type_name -> google.protobuf.Timestamp + 5, // 5: controller.api.resources.credentials.v1.Credential.attributes:type_name -> google.protobuf.Struct + 1, // 6: controller.api.resources.credentials.v1.Credential.username_password_attributes:type_name -> controller.api.resources.credentials.v1.UsernamePasswordAttributes + 3, // 7: controller.api.resources.credentials.v1.UsernamePasswordAttributes.username:type_name -> google.protobuf.StringValue + 3, // 8: controller.api.resources.credentials.v1.UsernamePasswordAttributes.password:type_name -> google.protobuf.StringValue + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_controller_api_resources_credentials_v1_credential_proto_init() } +func file_controller_api_resources_credentials_v1_credential_proto_init() { + if File_controller_api_resources_credentials_v1_credential_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_api_resources_credentials_v1_credential_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Credential); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_resources_credentials_v1_credential_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UsernamePasswordAttributes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_controller_api_resources_credentials_v1_credential_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Credential_Attributes)(nil), + (*Credential_UsernamePasswordAttributes)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_api_resources_credentials_v1_credential_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_api_resources_credentials_v1_credential_proto_goTypes, + DependencyIndexes: file_controller_api_resources_credentials_v1_credential_proto_depIdxs, + MessageInfos: file_controller_api_resources_credentials_v1_credential_proto_msgTypes, + }.Build() + File_controller_api_resources_credentials_v1_credential_proto = out.File + file_controller_api_resources_credentials_v1_credential_proto_rawDesc = nil + file_controller_api_resources_credentials_v1_credential_proto_goTypes = nil + file_controller_api_resources_credentials_v1_credential_proto_depIdxs = nil +} From 00dfea1244edab4110a9b6e8ec9dbd4744ad730d Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Wed, 8 Jun 2022 13:51:56 -0700 Subject: [PATCH 08/15] feat(target): Add support for static credential as target credential sources --- .../handlers/targets/target_service.go | 83 ++- .../targets/tcp/target_service_test.go | 238 +++++++-- .../oss/postgres/10/05_target.up.sql | 1 + .../oss/postgres/50/02_target.up.sql | 80 +++ .../storage/target/store/v1/target.proto | 46 ++ internal/session/repository_session_test.go | 9 +- internal/target/credential.go | 68 +++ internal/target/credential_library.go | 37 -- internal/target/credential_source.go | 61 ++- internal/target/credential_test.go | 69 +++ internal/target/options.go | 9 + internal/target/options_test.go | 33 ++ internal/target/query.go | 51 +- internal/target/registry.go | 17 +- .../target/repository_credential_source.go | 385 +++++++++----- .../repository_credential_source_test.go | 193 ++++--- internal/target/store/target.pb.go | 331 +++++++++++- internal/target/targettest/target.go | 4 +- internal/target/tcp/register.go | 15 +- .../tcp/repository_credential_source_test.go | 473 ++++++++++++------ .../target/tcp/repository_tcp_target_test.go | 2 +- internal/target/tcp/testing.go | 9 + internal/target/tcp/testing_test.go | 4 +- internal/target/testing.go | 13 + internal/tests/api/targets/target_test.go | 82 ++- .../boundary/_target_credential_sources.bash | 52 ++ .../boundary/target_credential_sources.bats | 174 +++++++ 27 files changed, 1951 insertions(+), 588 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/50/02_target.up.sql create mode 100644 internal/target/credential.go create mode 100644 internal/target/credential_test.go create mode 100644 internal/tests/cli/boundary/_target_credential_sources.bash create mode 100644 internal/tests/cli/boundary/target_credential_sources.bats diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index f27c75a78..69fb4b0d0 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/credential" + credstatic "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/daemon/controller/auth" "github.com/hashicorp/boundary/internal/daemon/controller/common" @@ -1443,12 +1444,16 @@ func (s Service) addCredentialSourcesInRepo(ctx context.Context, targetId string if err != nil { return nil, nil, nil, err } - credLibs, err := createCredLibs(targetId, applicationIds, nil, egressIds) - if err != nil { - return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err) + + var creds target.CredentialSources + if len(applicationIds) > 0 { + creds.ApplicationCredentialIds = strutil.RemoveDuplicates(applicationIds, false) + } + if len(egressIds) > 0 { + creds.EgressCredentialIds = strutil.RemoveDuplicates(egressIds, false) } - out, hs, credSources, err := repo.AddTargetCredentialSources(ctx, targetId, version, credLibs) + out, hs, credSources, err := repo.AddTargetCredentialSources(ctx, targetId, version, creds) if err != nil { // TODO: Figure out a way to surface more helpful error info beyond the Internal error. return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add credential sources to target: %v.", err) @@ -1466,12 +1471,15 @@ func (s Service) setCredentialSourcesInRepo(ctx context.Context, targetId string return nil, nil, nil, err } - credLibs, err := createCredLibs(targetId, applicationIds, nil, egressIds) - if err != nil { - return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err) + var ids target.CredentialSources + if len(applicationIds) > 0 { + ids.ApplicationCredentialIds = strutil.RemoveDuplicates(applicationIds, false) + } + if len(egressIds) > 0 { + ids.EgressCredentialIds = strutil.RemoveDuplicates(egressIds, false) } - _, _, _, err = repo.SetTargetCredentialSources(ctx, targetId, version, credLibs) + _, _, _, err = repo.SetTargetCredentialSources(ctx, targetId, version, ids) if err != nil { // TODO: Figure out a way to surface more helpful error info beyond the Internal error. return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err) @@ -1494,11 +1502,14 @@ func (s Service) removeCredentialSourcesInRepo(ctx context.Context, targetId str return nil, nil, nil, err } - credLibs, err := createCredLibs(targetId, applicationIds, nil, egressIds) - if err != nil { - return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set credential sources in target: %v.", err) + var ids target.CredentialSources + if len(applicationIds) > 0 { + ids.ApplicationCredentialIds = strutil.RemoveDuplicates(applicationIds, false) } - _, err = repo.DeleteTargetCredentialSources(ctx, targetId, version, credLibs) + if len(egressIds) > 0 { + ids.EgressCredentialIds = strutil.RemoveDuplicates(egressIds, false) + } + _, err = repo.DeleteTargetCredentialSources(ctx, targetId, version, ids) if err != nil { // TODO: Figure out a way to surface more helpful error info beyond the Internal error. return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove credential sources from target: %v.", err) @@ -1644,6 +1655,7 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou var appCredSources, egressCredSources []*pb.CredentialSource var appCredSourceIds, egressCredSourceIds []string var appCredLibraries []*pb.CredentialLibrary + var appCredLibIds []string for _, cs := range credSources { switch cs.CredentialPurpose() { @@ -1655,11 +1667,14 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou }) // ApplicationCredentialLibrariesField is deprecated and should only be populated - // for application purpose - appCredLibraries = append(appCredLibraries, &pb.CredentialLibrary{ - Id: cs.Id(), - CredentialStoreId: cs.CredentialStoreId(), - }) + // for library type sources + if cs.Type() == "library" { + appCredLibIds = append(appCredLibIds, cs.Id()) + appCredLibraries = append(appCredLibraries, &pb.CredentialLibrary{ + Id: cs.Id(), + CredentialStoreId: cs.CredentialStoreId(), + }) + } case credential.EgressPurpose: egressCredSources = append(egressCredSources, &pb.CredentialSource{ @@ -1674,7 +1689,7 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou } if outputFields.Has(globals.ApplicationCredentialLibraryIdsField) { - out.ApplicationCredentialLibraryIds = appCredSourceIds + out.ApplicationCredentialLibraryIds = appCredLibIds } if outputFields.Has(globals.ApplicationCredentialSourceIdsField) { out.ApplicationCredentialSourceIds = appCredSourceIds @@ -2040,13 +2055,13 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ badFields[globals.EgressCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided." } for _, cl := range req.GetApplicationCredentialSourceIds() { - if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { + if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) { badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl) break } } for _, cl := range req.GetEgressCredentialSourceIds() { - if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { + if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) { badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl) break } @@ -2066,13 +2081,13 @@ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequ badFields[globals.VersionField] = "Required field." } for _, cl := range req.GetApplicationCredentialSourceIds() { - if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { + if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) { badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl) break } } for _, cl := range req.GetEgressCredentialSourceIds() { - if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { + if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) { badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl) break } @@ -2096,13 +2111,13 @@ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourc badFields[globals.EgressCredentialSourceIdsField] = "Application or Egress Credential Source IDs must be provided." } for _, cl := range req.GetApplicationCredentialSourceIds() { - if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { + if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) { badFields[globals.ApplicationCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl) break } } for _, cl := range req.GetEgressCredentialSourceIds() { - if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { + if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix, credstatic.CredentialPrefix) { badFields[globals.EgressCredentialSourceIdsField] = fmt.Sprintf("Incorrectly formatted credential source identifier %q.", cl) break } @@ -2154,26 +2169,6 @@ func validateAuthorizeSessionRequest(req *pbs.AuthorizeSessionRequest) error { return nil } -func createCredLibs(targetId string, applicationIds, ingressIds, egressIds []string) ([]*target.CredentialLibrary, error) { - credLibs := make([]*target.CredentialLibrary, 0, len(applicationIds)+len(ingressIds)+len(egressIds)) - - byPurpose := map[credential.Purpose][]string{ - credential.ApplicationPurpose: strutil.RemoveDuplicates(applicationIds, false), - credential.IngressPurpose: strutil.RemoveDuplicates(ingressIds, false), - credential.EgressPurpose: strutil.RemoveDuplicates(egressIds, false), - } - for purpose, ids := range byPurpose { - for _, id := range ids { - l, err := target.NewCredentialLibrary(targetId, id, purpose) - if err != nil { - return nil, err - } - credLibs = append(credLibs, l) - } - } - return credLibs, nil -} - // credentialToProto converts the strongly typed credential.Credential into a known proto.Message. func credentialToProto(ctx context.Context, cred credential.Credential) (*serverpb.Credential, error) { const op = "targets.credentialToProto" diff --git a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go index 36a061dab..7e57aa99d 100644 --- a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go @@ -15,6 +15,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/credential" + credstatic "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/daemon/controller/auth" "github.com/hashicorp/boundary/internal/daemon/controller/handlers" @@ -2241,7 +2242,7 @@ func TestRemoveTargetLibraries(t *testing.T) { } } -func TestAddTargetSources(t *testing.T) { +func TestAddTargetCredentialSources(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) @@ -2256,8 +2257,11 @@ func TestAddTargetSources(t *testing.T) { s, err := testService(t, conn, kms, wrapper) require.NoError(t, err, "Error when getting new target service.") - store := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] - cls := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 2) + storeVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] + cls := vault.TestCredentialLibraries(t, conn, wrapper, storeVault.GetPublicId(), 2) + + storeStatic := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId()) + creds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", storeStatic.GetPublicId(), proj.GetPublicId(), 2) ctx := context.Background() addCases := []struct { @@ -2265,24 +2269,61 @@ func TestAddTargetSources(t *testing.T) { tar target.Target addSources []string resultSourceIds []string + resultLibIds []string }{ { - name: "Add source on empty target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty for sources"), + name: "Add library on empty target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty for lib sources"), addSources: []string{cls[1].GetPublicId()}, resultSourceIds: []string{cls[1].GetPublicId()}, + resultLibIds: []string{cls[1].GetPublicId()}, }, { - name: "Add source on populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), + name: "Add static cred on empty target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty for static sources"), + addSources: []string{creds[1].GetPublicId()}, + resultSourceIds: []string{creds[1].GetPublicId()}, + }, + { + name: "Add library on library populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for lib-lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), addSources: []string{cls[1].GetPublicId()}, resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, + resultLibIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, { - name: "Add duplicated sources on populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated for sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), - addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, - resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, + name: "Add library on static cred populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for lib-static sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose)})), + addSources: []string{cls[1].GetPublicId()}, + resultSourceIds: []string{creds[0].GetPublicId(), cls[1].GetPublicId()}, + resultLibIds: []string{cls[1].GetPublicId()}, + }, + { + name: "Add static cred on library populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for static-lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), + addSources: []string{creds[1].GetPublicId()}, + resultSourceIds: []string{cls[0].GetPublicId(), creds[1].GetPublicId()}, + resultLibIds: []string{cls[0].GetPublicId()}, + }, + { + name: "Add static cred on static cred populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for static-static sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose)})), + addSources: []string{creds[1].GetPublicId()}, + resultSourceIds: []string{creds[0].GetPublicId(), creds[1].GetPublicId()}, + }, + { + name: "Add duplicated sources on library populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated for lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), + addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId(), creds[1].GetPublicId()}, + resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId()}, + resultLibIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, + }, + { + name: "Add duplicated sources on static cred populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated for static sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose)})), + addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId(), creds[1].GetPublicId()}, + resultSourceIds: []string{creds[0].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId()}, + resultLibIds: []string{cls[1].GetPublicId()}, }, } @@ -2298,16 +2339,10 @@ func TestAddTargetSources(t *testing.T) { require.NoError(t, err, "Got error: %v", s) assert.ElementsMatch(t, tc.resultSourceIds, got.GetItem().GetApplicationCredentialSourceIds()) - assert.Equal(t, len(tc.resultSourceIds), len(got.GetItem().GetApplicationCredentialSources())) - wantTemplate := &pb.CredentialSource{ - CredentialStoreId: store.GetPublicId(), - } - for _, cl := range got.GetItem().GetApplicationCredentialSources() { - cl.Id = "" - assert.Empty(t, cmp.Diff(wantTemplate, cl, protocmp.Transform())) - } + assert.ElementsMatch(t, tc.resultLibIds, got.GetItem().GetApplicationCredentialLibraryIds()) + assert.Equal(t, len(tc.resultLibIds), len(got.GetItem().GetApplicationCredentialLibraries())) }) } @@ -2385,15 +2420,11 @@ func TestSetTargetCredentialSources(t *testing.T) { s, err := testService(t, conn, kms, wrapper) require.NoError(t, err, "Error when getting new target service.") - store := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] - cls := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 2) + storeVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] + cls := vault.TestCredentialLibraries(t, conn, wrapper, storeVault.GetPublicId(), 2) - resultingTargetSource := func(id string) *pb.CredentialSource { - return &pb.CredentialSource{ - Id: id, - CredentialStoreId: store.GetPublicId(), - } - } + storeStatic := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId()) + creds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", storeStatic.GetPublicId(), proj.GetPublicId(), 2) ctx := context.Background() setCases := []struct { @@ -2402,28 +2433,65 @@ func TestSetTargetCredentialSources(t *testing.T) { setCredentialSources []string resultCredentialSourceIds []string resultCredentialSources []*pb.CredentialLibrary + resultLibIds []string }{ { - name: "Set on empty target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty"), + name: "Set libary on empty target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty libary"), setCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, + resultLibIds: []string{cls[1].GetPublicId()}, }, { - name: "Set on populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), + name: "Set static on empty target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty static"), + setCredentialSources: []string{creds[1].GetPublicId()}, + resultCredentialSourceIds: []string{creds[1].GetPublicId()}, + }, + { + name: "Set library on library populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated libary-libary", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, + resultLibIds: []string{cls[1].GetPublicId()}, }, { - name: "Set duplicate sources on populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicate", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), + name: "Set static on library populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-libary", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), + setCredentialSources: []string{creds[1].GetPublicId()}, + resultCredentialSourceIds: []string{creds[1].GetPublicId()}, + }, + { + name: "Set library on static populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated library-static", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose)})), + setCredentialSources: []string{cls[1].GetPublicId()}, + resultCredentialSourceIds: []string{cls[1].GetPublicId()}, + resultLibIds: []string{cls[1].GetPublicId()}, + }, + { + name: "Set static on static populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-static", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose)})), + setCredentialSources: []string{creds[1].GetPublicId()}, + resultCredentialSourceIds: []string{creds[1].GetPublicId()}, + }, + { + name: "Set duplicate library on populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicate library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, + resultLibIds: []string{cls[1].GetPublicId()}, }, { - name: "Set empty on populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "another populated", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), + name: "Set duplicate static on populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicate static", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose)})), + setCredentialSources: []string{creds[1].GetPublicId(), creds[1].GetPublicId()}, + resultCredentialSourceIds: []string{creds[1].GetPublicId()}, + }, + { + name: "Set empty on populated target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "another populated", + target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)}), + target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{}, resultCredentialSourceIds: nil, }, @@ -2450,9 +2518,7 @@ func TestSetTargetCredentialSources(t *testing.T) { assert.Empty(t, cmp.Diff(tc.resultCredentialSources, got.GetItem().GetApplicationCredentialSourceIds(), protocmp.Transform())) } else { assert.Equal(t, len(tc.resultCredentialSourceIds), len(got.GetItem().GetApplicationCredentialSourceIds())) - for _, cl := range got.GetItem().GetApplicationCredentialSources() { - assert.Empty(t, cmp.Diff(resultingTargetSource(cl.GetId()), cl, protocmp.Transform())) - } + assert.Equal(t, len(tc.resultLibIds), len(got.GetItem().GetApplicationCredentialLibraries())) } }) } @@ -2519,8 +2585,11 @@ func TestRemoveTargetCredentialSources(t *testing.T) { s, err := testService(t, conn, kms, wrapper) require.NoError(t, err, "Error when getting new target service.") - store := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] - cls := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 2) + csVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] + cls := vault.TestCredentialLibraries(t, conn, wrapper, csVault.GetPublicId(), 2) + + csStatic := credstatic.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] + creds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "u", "p", csStatic.GetPublicId(), proj.GetPublicId(), 2) ctx := context.Background() removeCases := []struct { @@ -2529,32 +2598,99 @@ func TestRemoveTargetCredentialSources(t *testing.T) { removeCredentialSources []string resultCredentialSourceIds []string wantErr bool + resultLibIds []string }{ { - name: "Remove from empty", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty"), + name: "Remove library from empty", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty lib"), removeCredentialSources: []string{cls[1].GetPublicId()}, wantErr: true, }, { - name: "Remove 1 of 2 sources", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove partial", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), - removeCredentialSources: []string{cls[1].GetPublicId()}, - resultCredentialSourceIds: []string{cls[0].GetPublicId()}, + name: "Remove static from empty", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty static"), + removeCredentialSources: []string{creds[1].GetPublicId()}, + wantErr: true, }, { - name: "Remove 1 duplicate set of 2 sources", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove duplicate", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), + name: "Remove 1 of 2 libraries", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove partial lib", + target.WithCredentialLibraries([]*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose), + })), + removeCredentialSources: []string{cls[1].GetPublicId()}, + resultCredentialSourceIds: []string{cls[0].GetPublicId()}, + resultLibIds: []string{cls[0].GetPublicId()}, + }, + { + name: "Remove 1 of 2 static credentials", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove partial static", + target.WithStaticCredentials([]*target.StaticCredential{ + target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose), + target.TestNewStaticCredential("", creds[1].GetPublicId(), credential.ApplicationPurpose), + })), + removeCredentialSources: []string{creds[1].GetPublicId()}, + resultCredentialSourceIds: []string{creds[0].GetPublicId()}, + }, + { + name: "Remove 1 duplicate set of 2 libraries", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove duplicate lib", + target.WithCredentialLibraries([]*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose), + })), removeCredentialSources: []string{ cls[1].GetPublicId(), cls[1].GetPublicId(), }, resultCredentialSourceIds: []string{cls[0].GetPublicId()}, + resultLibIds: []string{cls[0].GetPublicId()}, + }, + { + name: "Remove 1 duplicate set of 2 static credentials", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove duplicate static", + target.WithStaticCredentials([]*target.StaticCredential{ + target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose), + target.TestNewStaticCredential("", creds[1].GetPublicId(), credential.ApplicationPurpose), + })), + removeCredentialSources: []string{ + creds[1].GetPublicId(), creds[1].GetPublicId(), + }, + resultCredentialSourceIds: []string{creds[0].GetPublicId()}, + }, + { + name: "Remove mixed sources from target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove mixed", + target.WithCredentialLibraries([]*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose), + }), + target.WithStaticCredentials([]*target.StaticCredential{ + target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose), + target.TestNewStaticCredential("", creds[1].GetPublicId(), credential.ApplicationPurpose), + })), + removeCredentialSources: []string{ + cls[1].GetPublicId(), creds[0].GetPublicId(), + }, + resultCredentialSourceIds: []string{ + cls[0].GetPublicId(), creds[1].GetPublicId(), + }, + resultLibIds: []string{cls[0].GetPublicId()}, }, { name: "Remove all sources from target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove all", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove all", + target.WithCredentialLibraries([]*target.CredentialLibrary{ + target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), + target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose), + }), + target.WithStaticCredentials([]*target.StaticCredential{ + target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose), + target.TestNewStaticCredential("", creds[1].GetPublicId(), credential.ApplicationPurpose), + })), removeCredentialSources: []string{ cls[0].GetPublicId(), cls[1].GetPublicId(), + creds[0].GetPublicId(), creds[1].GetPublicId(), }, resultCredentialSourceIds: []string{}, }, @@ -2576,6 +2712,10 @@ func TestRemoveTargetCredentialSources(t *testing.T) { require.NoError(t, err, "Got error: %v", s) assert.ElementsMatch(t, tc.resultCredentialSourceIds, got.GetItem().GetApplicationCredentialSourceIds()) + assert.Equal(t, len(tc.resultCredentialSourceIds), len(got.GetItem().GetApplicationCredentialSources())) + + assert.ElementsMatch(t, tc.resultLibIds, got.GetItem().GetApplicationCredentialLibraryIds()) + assert.Equal(t, len(tc.resultLibIds), len(got.GetItem().GetApplicationCredentialLibraries())) }) } diff --git a/internal/db/schema/migrations/oss/postgres/10/05_target.up.sql b/internal/db/schema/migrations/oss/postgres/10/05_target.up.sql index 58e4b97ce..02bab05dc 100644 --- a/internal/db/schema/migrations/oss/postgres/10/05_target.up.sql +++ b/internal/db/schema/migrations/oss/postgres/10/05_target.up.sql @@ -29,6 +29,7 @@ begin; create trigger immutable_columns before update on target_credential_library for each row execute procedure immutable_columns('target_id', 'credential_library_id', 'credential_purpose', 'create_time'); + -- replaced in 50/02_target.up.sql -- target_library provides the store id along with the other data stored in -- target_credential_library create view target_library diff --git a/internal/db/schema/migrations/oss/postgres/50/02_target.up.sql b/internal/db/schema/migrations/oss/postgres/50/02_target.up.sql new file mode 100644 index 000000000..c297565f7 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/50/02_target.up.sql @@ -0,0 +1,80 @@ +begin; + + -- target_static_credential does not follow the normal table naming convention of target_credential_static + create table target_static_credential ( + target_id wt_public_id not null + constraint target_fkey + references target (public_id) + on delete cascade + on update cascade, + credential_static_id wt_public_id not null + constraint credential_static_fkey + references credential_static (public_id) + on delete cascade + on update cascade, + credential_purpose text not null + constraint credential_purpose_enm_fkey + references credential_purpose_enm (name) + on delete restrict + on update cascade, + create_time wt_timestamp, + primary key(target_id, credential_static_id, credential_purpose) + ); + comment on table target_static_credential is + 'target_static_credential is a join table between the target, credential_static, and credential_purpose_enm tables. ' + 'A row in the target_static_credential table represents the assignment of a static credential to a target for the specified purpose.'; + + create trigger default_create_time_column before insert on target_static_credential + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on target_static_credential + for each row execute procedure immutable_columns('target_id', 'credential_static_id', 'credential_purpose', 'create_time'); + + -- replaces view from 10/05_target.up.sql + drop view target_library; + -- target_credential_source provides the store id along with the other data stored in + -- target_credential_library and target_static_credential + create view target_credential_source + as + select + tcl.target_id, + tcl.credential_library_id as credential_source_id, + tcl.credential_purpose, + cl.store_id + from + target_credential_library tcl, + credential_library cl + where + cl.public_id = tcl.credential_library_id + union + select + tcs.target_id, + tcs.credential_static_id as credential_source_id, + tcs.credential_purpose, + cst.store_id + from + target_static_credential tcs, + credential_static cst + where + cst.public_id = tcs.credential_static_id; + comment on view target_credential_source is + 'target_credential_source is a view where each row contains a credential source and the id of the parent credential store. ' + 'No encrypted data is returned. This view can be used to retrieve data which will be returned external to boundary.'; + + create view credential_source_all_types + as + select + public_id, + 'library' as type + from + credential_library + union + select + public_id, + 'static' as type + from + credential_static; + comment on view credential_source_all_types is + 'credential_source_all_types is a view where each row contains the credential source id and type.'; + +commit; diff --git a/internal/proto/controller/storage/target/store/v1/target.proto b/internal/proto/controller/storage/target/store/v1/target.proto index a005eff7c..9b5c38195 100644 --- a/internal/proto/controller/storage/target/store/v1/target.proto +++ b/internal/proto/controller/storage/target/store/v1/target.proto @@ -90,3 +90,49 @@ message CredentialLibrary { // @inject_tag: `gorm:"default:current_timestamp"` timestamp.v1.Timestamp create_time = 40; } + +message StaticCredential { + // target_id of the Target + // @inject_tag: gorm:"primary_key" + string target_id = 10; + + // credential_id of the static Credential + // @inject_tag: `gorm:"column:credential_static_id;primary_key" + string credential_id = 20; + + // credential_purpose is the purpose of the credential for the target + // @inject_tag: gorm:"primary_key" + string credential_purpose = 30; + + // create_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 40; +} + +message CredentialSource { + // target_id of the Target + // @inject_tag: gorm:"primary_key" + string target_id = 10; + + // credential_source_id of the Credential Library or static Credential + // @inject_tag: gorm:"primary_key" + string credential_source_id = 20; + + // credential_purpose is the purpose of the credential for the target + // @inject_tag: gorm:"primary_key" + string credential_purpose = 30; + + // create_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 40; +} + +message CredentialSourceView { + // public_id of the Credential source + // @inject_tag: gorm:"primary_key" + string public_id = 10; + + // type of credential source (library or static) + // @inject_tag: `gorm:"not_null"` + string type = 20; +} diff --git a/internal/session/repository_session_test.go b/internal/session/repository_session_test.go index 45309cd20..9ecf7a6ca 100644 --- a/internal/session/repository_session_test.go +++ b/internal/session/repository_session_test.go @@ -1442,12 +1442,11 @@ func testSessionCredentialParams(t *testing.T, conn *db.DB, wrapper wrapping.Wra stores := vault.TestCredentialStores(t, conn, wrapper, params.ScopeId, 1) libIds := vault.TestCredentialLibraries(t, conn, wrapper, stores[0].GetPublicId(), 2) - libs := []*target.CredentialLibrary{ - target.TestNewCredentialLibrary(tar.GetPublicId(), libIds[0].GetPublicId(), cred.ApplicationPurpose), - target.TestNewCredentialLibrary(tar.GetPublicId(), libIds[1].GetPublicId(), cred.ApplicationPurpose), - } - _, _, _, err = targetRepo.AddTargetCredentialSources(ctx, tar.GetPublicId(), tar.GetVersion(), libs) + ids := target.CredentialSources{ + ApplicationCredentialIds: []string{libIds[0].GetPublicId(), libIds[1].GetPublicId()}, + } + _, _, _, err = targetRepo.AddTargetCredentialSources(ctx, tar.GetPublicId(), tar.GetVersion(), ids) require.NoError(err) creds := []*DynamicCredential{ NewDynamicCredential(libIds[0].GetPublicId(), cred.ApplicationPurpose), diff --git a/internal/target/credential.go b/internal/target/credential.go new file mode 100644 index 000000000..078e8aeae --- /dev/null +++ b/internal/target/credential.go @@ -0,0 +1,68 @@ +package target + +import ( + "fmt" + + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/target/store" + "google.golang.org/protobuf/proto" +) + +// A StaticCredential is a CredentialSource that represents the relationship +// between a target and a static credential. +type StaticCredential struct { + *store.StaticCredential + tableName string `gorm:"-"` +} + +// NewStaticCredential creates a new in memory StaticCredential +// representing the relationship between targetId and credentialId. +func NewStaticCredential(targetId, credentialId string, purpose credential.Purpose) (*StaticCredential, error) { + const op = "target.StaticCredential" + if targetId == "" { + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "no target id") + } + if credentialId == "" { + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "no credential id") + } + + t := &StaticCredential{ + StaticCredential: &store.StaticCredential{ + TargetId: targetId, + CredentialId: credentialId, + CredentialPurpose: string(purpose), + }, + } + return t, nil +} + +func (t *StaticCredential) clone() *StaticCredential { + cp := proto.Clone(t.StaticCredential) + return &StaticCredential{ + StaticCredential: cp.(*store.StaticCredential), + } +} + +// TableName returns the table name. +func (t *StaticCredential) TableName() string { + if t.tableName != "" { + return t.tableName + } + return "target_static_credential" +} + +// SetTableName sets the table name. +func (t *StaticCredential) SetTableName(n string) { + t.tableName = n +} + +func (t *StaticCredential) oplog(op oplog.OpType) oplog.Metadata { + metadata := oplog.Metadata{ + "resource-public-id": []string{fmt.Sprintf("%s:%s:%s", t.TargetId, t.CredentialId, t.CredentialPurpose)}, + "resource-type": []string{"target-credential-static"}, + "op-type": []string{op.String()}, + } + return metadata +} diff --git a/internal/target/credential_library.go b/internal/target/credential_library.go index 8c013a4d8..102ec7963 100644 --- a/internal/target/credential_library.go +++ b/internal/target/credential_library.go @@ -10,8 +10,6 @@ import ( "google.golang.org/protobuf/proto" ) -var _ CredentialSource = (*TargetLibrary)(nil) - // A CredentialLibrary is a CredentialSource that represents the relationship // between a target and a credential library. type CredentialLibrary struct { @@ -74,38 +72,3 @@ func (t *CredentialLibrary) oplog(op oplog.OpType) oplog.Metadata { } return metadata } - -// A TargetLibrary represents the relationship between a target and a -// credential library and includes the id of the credential store that the -// library is a part of and the library's name and description. -// -// It implements the target.CredentialSource interface. -type TargetLibrary struct { - *store.CredentialLibrary - StoreId string -} - -// TableName returns the tablename to override the default gorm table name -func (ts *TargetLibrary) TableName() string { - return "target_library" -} - -// Id returns the ID of the library -func (ts *TargetLibrary) Id() string { - return ts.CredentialLibraryId -} - -// CredentialStoreId returns the ID of the store containing the library -func (ts *TargetLibrary) CredentialStoreId() string { - return ts.StoreId -} - -// CredentialPurpose returns the purpose of the credential -func (ts *TargetLibrary) CredentialPurpose() credential.Purpose { - return credential.Purpose(ts.GetCredentialPurpose()) -} - -// TargetId returns the target linked to this credential source -func (ts *TargetLibrary) TargetId() string { - return ts.GetTargetId() -} diff --git a/internal/target/credential_source.go b/internal/target/credential_source.go index 06e95bb73..88572a3e6 100644 --- a/internal/target/credential_source.go +++ b/internal/target/credential_source.go @@ -1,6 +1,11 @@ package target -import "github.com/hashicorp/boundary/internal/credential" +import ( + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/target/store" +) + +var _ CredentialSource = (*TargetCredentialSource)(nil) // CredentialSource is an interface that can be implemented by both a library // and a singular credential. @@ -10,3 +15,57 @@ type CredentialSource interface { CredentialPurpose() credential.Purpose TargetId() string } + +// CredentialSources contains slices of credential publicIds +// per purpose to be attacehd to the target. +type CredentialSources struct { + ApplicationCredentialIds []string + EgressCredentialIds []string +} + +// A TargetCredentialSource represents the relationship between a target and a +// credential library and includes the id of the credential store that the +// library is a part of and the library's name and description. +// +// It implements the target.CredentialSource interface. +type TargetCredentialSource struct { + *store.CredentialSource + StoreId string +} + +// TableName returns the tablename to override the default gorm table name +func (ts *TargetCredentialSource) TableName() string { + return "target_credential_source" +} + +// Id returns the ID of the credential source +func (ts *TargetCredentialSource) Id() string { + return ts.CredentialSourceId +} + +// CredentialStoreId returns the ID of the store containing the credential source +func (ts *TargetCredentialSource) CredentialStoreId() string { + return ts.StoreId +} + +// CredentialPurpose returns the purpose of the credential +func (ts *TargetCredentialSource) CredentialPurpose() credential.Purpose { + return credential.Purpose(ts.GetCredentialPurpose()) +} + +// TargetId returns the target linked to this credential source +func (ts *TargetCredentialSource) TargetId() string { + return ts.GetTargetId() +} + +// credentialSourceView provides a common way to return credential sources regardless of their +// underlying type (library or static). +type credentialSourceView struct { + *store.CredentialSourceView + tableName string `gorm:"-"` +} + +// TableName returns the tablename to override the default gorm table name +func (csv *credentialSourceView) TableName() string { + return "credential_source_all_types" +} diff --git a/internal/target/credential_test.go b/internal/target/credential_test.go new file mode 100644 index 000000000..7915f87a9 --- /dev/null +++ b/internal/target/credential_test.go @@ -0,0 +1,69 @@ +package target_test + +import ( + "testing" + + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStaticCredential_New(t *testing.T) { + type args struct { + targetId string + credId string + } + tests := []struct { + name string + args args + want *target.StaticCredential + wantErr errors.Code + }{ + { + name: "no-targetId", + args: args{ + credId: "cred_0000000", + }, + wantErr: errors.InvalidParameter, + }, + { + name: "no-credId", + args: args{ + targetId: "targ_0000000", + }, + wantErr: errors.InvalidParameter, + }, + { + name: "valid", + args: args{ + targetId: "targ_0000000", + credId: "cred_0000000", + }, + want: &target.StaticCredential{ + StaticCredential: &store.StaticCredential{ + TargetId: "targ_0000000", + CredentialId: "cred_0000000", + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + got, err := target.NewStaticCredential(tt.args.targetId, tt.args.credId, credential.ApplicationPurpose) + if tt.wantErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) + assert.Nil(got) + return + } + require.NoError(err) + require.NotNil(got) + assert.EqualValues(tt.want, got) + }) + } +} diff --git a/internal/target/options.go b/internal/target/options.go index e3d0421ac..593694df0 100644 --- a/internal/target/options.go +++ b/internal/target/options.go @@ -31,6 +31,7 @@ type options struct { WithType subtypes.Subtype WithHostSources []string WithCredentialLibraries []*CredentialLibrary + WithStaticCredentials []*StaticCredential WithSessionMaxSeconds uint32 WithSessionConnectionLimit int32 WithPublicId string @@ -51,6 +52,7 @@ func getDefaultOptions() options { WithType: "", WithHostSources: nil, WithCredentialLibraries: nil, + WithStaticCredentials: nil, WithSessionMaxSeconds: uint32((8 * time.Hour).Seconds()), WithSessionConnectionLimit: 1, WithPublicId: "", @@ -137,6 +139,13 @@ func WithCredentialLibraries(cl []*CredentialLibrary) Option { } } +// WithStaticCredentials provides an option for providing a list of static credentials. +func WithStaticCredentials(c []*StaticCredential) Option { + return func(o *options) { + o.WithStaticCredentials = c + } +} + func WithSessionMaxSeconds(dur uint32) Option { return func(o *options) { o.WithSessionMaxSeconds = dur diff --git a/internal/target/options_test.go b/internal/target/options_test.go index 0746102d6..fad53f0df 100644 --- a/internal/target/options_test.go +++ b/internal/target/options_test.go @@ -141,4 +141,37 @@ func Test_GetOpts(t *testing.T) { } assert.Equal(opts, testOpts) }) + t.Run("WithStaticCredentials", func(t *testing.T) { + assert := assert.New(t) + opts := GetOpts(WithStaticCredentials([]*StaticCredential{ + { + StaticCredential: &store.StaticCredential{ + CredentialId: "alice", + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + { + StaticCredential: &store.StaticCredential{ + CredentialId: "bob", + CredentialPurpose: string(credential.EgressPurpose), + }, + }, + })) + testOpts := getDefaultOptions() + testOpts.WithStaticCredentials = []*StaticCredential{ + { + StaticCredential: &store.StaticCredential{ + CredentialId: "alice", + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + { + StaticCredential: &store.StaticCredential{ + CredentialId: "bob", + CredentialPurpose: string(credential.EgressPurpose), + }, + }, + } + assert.Equal(opts, testOpts) + }) } diff --git a/internal/target/query.go b/internal/target/query.go index 4fd31c14c..198df571a 100644 --- a/internal/target/query.go +++ b/internal/target/query.go @@ -3,46 +3,51 @@ package target const ( setChangesQuery = ` with -set_libraries (library_id) as ( +set_sources (source_id, type) as ( -- returns the SET list - select public_id - from credential_library + select public_id, type + from credential_source_all_types where public_id in (%s) ), -current_libraries (library_id) as ( +current_sources (source_id, type) as ( -- returns the current list - select credential_library_id + select credential_library_id, 'library' from target_credential_library where target_id = @target_id and credential_purpose = @purpose + union + select credential_static_id, 'static' + from target_static_credential + where target_id = @target_id + and credential_purpose = @purpose ), -keep_libraries (library_id) as ( +keep_sources (source_id) as ( -- returns the KEEP list - select library_id - from current_libraries - where library_id in (select * from set_libraries) + select source_id + from current_sources + where source_id in (select source_id from set_sources) ), -delete_libraries (library_id) as ( +delete_sources (source_id, type) as ( -- returns the DELETE list - select library_id - from current_libraries - where library_id not in (select * from set_libraries) + select source_id, type + from current_sources + where source_id not in (select source_id from set_sources) ), -insert_libraries (library_id) as ( +insert_sources (source_id, type) as ( -- returns the ADD list - select library_id - from set_libraries - where library_id not in (select * from keep_libraries) + select source_id, type + from set_sources + where source_id not in (select * from keep_sources) ), -final (action, library_id) as ( - select 'delete', library_id - from delete_libraries +final (action, source_id, type) as ( + select 'delete', source_id, type + from delete_sources union - select 'add', library_id - from insert_libraries + select 'add', source_id, type + from insert_sources ) select * from final -order by action, library_id; +order by action, source_id; ` targetPublicIdList = ` diff --git a/internal/target/registry.go b/internal/target/registry.go index e54abb82f..060e27c90 100644 --- a/internal/target/registry.go +++ b/internal/target/registry.go @@ -25,9 +25,9 @@ type VetFunc func(context.Context, Target) error // 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 +// VetCredentialSourcesFunc is a function that checks the given CredentialLibraries +// and StaticCredentials to ensure that they are valid for a Target subtype. +type VetCredentialSourcesFunc func(context.Context, []*CredentialLibrary, []*StaticCredential) error // targetHooks defines the interface containing all the hooks needed for // managing target suptypes. @@ -43,9 +43,9 @@ type targetHooks interface { // 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 + // VetCredentialSources checks that the provided credential libriaries and + // static credentials are properly built for association with a target of this type. + VetCredentialSources(ctx context.Context, cls []*CredentialLibrary, creds []*StaticCredential) error } type registryEntry struct { @@ -118,12 +118,13 @@ func (r *registry) vetForUpdateFunc(s subtypes.Subtype) (VetForUpdateFunc, bool) return entry.targetHooks.VetForUpdate, ok } -func (r *registry) vetCredentialLibrariesFunc(s subtypes.Subtype) (VetCredentialLibrariesFunc, bool) { +func (r *registry) vetCredentialSourcesFunc(s subtypes.Subtype) (VetCredentialSourcesFunc, bool) { entry, ok := r.get(s) if !ok { return nil, ok } - return entry.targetHooks.VetCredentialLibraries, ok + + return entry.targetHooks.VetCredentialSources, ok } func (r *registry) idPrefix(s subtypes.Subtype) (string, bool) { diff --git a/internal/target/repository_credential_source.go b/internal/target/repository_credential_source.go index 486e4f278..a32c655b1 100644 --- a/internal/target/repository_credential_source.go +++ b/internal/target/repository_credential_source.go @@ -11,41 +11,15 @@ import ( "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" - "github.com/hashicorp/boundary/internal/target/store" + "github.com/hashicorp/boundary/internal/types/subtypes" + "github.com/hashicorp/go-secure-stdlib/strutil" ) -// credentialLibrariesPurposeMap is used to group CredentialLibraries by purpose. -type credentialLibrariesPurposeMap map[string][]*CredentialLibrary - -func (c credentialLibrariesPurposeMap) add(cl *CredentialLibrary) { - var s []*CredentialLibrary - var ok bool - - s, ok = c[cl.CredentialPurpose] - if !ok { - s = make([]*CredentialLibrary, 0, 1) - } - - s = append(s, cl) - c[cl.CredentialPurpose] = s -} - -func newCredentialLibrariesPurposeMap(cls []*CredentialLibrary) credentialLibrariesPurposeMap { - clpm := make(credentialLibrariesPurposeMap) - for _, p := range credential.ValidPurposes { - clpm[p.String()] = make([]*CredentialLibrary, 0) - } - for _, cl := range cls { - clpm.add(cl) - } - return clpm -} - -// AddTargetCredentialSources adds the cIds to the targetId in the repository. The target -// and the list of credential sources attached to the target, after cIds are added, +// AddTargetCredentialSources adds the credential source ids by purpose to the targetId in the repository. +// The target and the list of credential sources attached to the target, after ids are added, // will be returned on success. // The targetVersion must match the current version of the targetId in the repository. -func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, cls []*CredentialLibrary, _ ...Option) (Target, []HostSource, []CredentialSource, error) { +func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, idsByPurpose CredentialSources, _ ...Option) (Target, []HostSource, []CredentialSource, error) { const op = "target.(Repository).AddTargetCredentialSources" if targetId == "" { return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing target id") @@ -53,14 +27,6 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st if targetVersion == 0 { return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing version") } - if len(cls) == 0 { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing credential libraries") - } - - addCredLibs := make([]interface{}, 0, len(cls)) - for _, cl := range cls { - addCredLibs = append(addCredLibs, cl) - } t := allocTargetView() t.PublicId = targetId @@ -74,16 +40,15 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } - vetCredentialLibraries, ok := subtypeRegistry.vetCredentialLibrariesFunc(t.Subtype()) - if !ok { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) - } - if err := vetCredentialLibraries(ctx, cls); err != nil { - return nil, nil, nil, err + addCredLibs, addStaticCreds, err := r.createSources(ctx, targetId, t.Subtype(), idsByPurpose) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op) } target := alloc() - target.SetPublicId(ctx, t.PublicId) + if err := target.SetPublicId(ctx, t.PublicId); err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op) + } target.SetVersion(targetVersion + 1) metadata = target.Oplog(oplog.OpType_OP_TYPE_UPDATE) metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_CREATE.String()) @@ -101,7 +66,8 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st db.StdRetryCnt, db.ExpBackoff{}, func(reader db.Reader, w db.Writer) error { - msgs := make([]*oplog.Message, 0, 2) + numOplogMsgs := 1 + len(addCredLibs) + len(addStaticCreds) + msgs := make([]*oplog.Message, 0, numOplogMsgs) targetTicket, err := w.GetTicket(ctx, target) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) @@ -120,11 +86,29 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st } msgs = append(msgs, &targetOplogMsg) - credLibsOplogMsgs := make([]*oplog.Message, 0, len(addCredLibs)) - if err := w.CreateItems(ctx, addCredLibs, db.NewOplogMsgs(&credLibsOplogMsgs)); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target credential sources")) + if len(addCredLibs) > 0 { + i := make([]interface{}, 0, len(addCredLibs)) + for _, cl := range addCredLibs { + i = append(i, cl) + } + credLibsOplogMsgs := make([]*oplog.Message, 0, len(addCredLibs)) + if err := w.CreateItems(ctx, i, db.NewOplogMsgs(&credLibsOplogMsgs)); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target credential library")) + } + msgs = append(msgs, credLibsOplogMsgs...) + } + + if len(addStaticCreds) > 0 { + i := make([]interface{}, 0, len(addStaticCreds)) + for _, c := range addStaticCreds { + i = append(i, c) + } + credStaticOplogMsgs := make([]*oplog.Message, 0, len(addStaticCreds)) + if err := w.CreateItems(ctx, i, db.NewOplogMsgs(&credStaticOplogMsgs)); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target static credential")) + } + msgs = append(msgs, credStaticOplogMsgs...) } - msgs = append(msgs, credLibsOplogMsgs...) if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) @@ -148,7 +132,7 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st // DeleteTargetCredentialSources deletes credential sources from a target in the repository. // The target's current db version must match the targetVersion or an error will be returned. -func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, cls []*CredentialLibrary, _ ...Option) (int, error) { +func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, idsByPurpose CredentialSources, _ ...Option) (int, error) { const op = "target.(Repository).DeleteTargetCredentialSources" if targetId == "" { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target id") @@ -156,14 +140,6 @@ func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId if targetVersion == 0 { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version") } - if len(cls) == 0 { - return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing credential source ids") - } - - deleteCredLibs := make([]interface{}, 0, len(cls)) - for _, cl := range cls { - deleteCredLibs = append(deleteCredLibs, cl) - } t := allocTargetView() t.PublicId = targetId @@ -172,12 +148,19 @@ func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId } var metadata oplog.Metadata + deleteCredLibs, deleteStaticCred, err := r.createSources(ctx, targetId, t.Subtype(), idsByPurpose) + if err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + } + alloc, ok := subtypeRegistry.allocFunc(t.Subtype()) if !ok { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } target := alloc() - target.SetPublicId(ctx, t.PublicId) + if err := target.SetPublicId(ctx, t.PublicId); err != nil { + return db.NoRowsAffected, errors.Wrap(ctx, err, op) + } target.SetVersion(targetVersion + 1) metadata = target.Oplog(oplog.OpType_OP_TYPE_UPDATE) metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String()) @@ -212,15 +195,41 @@ func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId } msgs = append(msgs, &targetOplogMsg) - credLibsOplogMsgs := make([]*oplog.Message, 0, len(deleteCredLibs)) - rowsDeleted, err = w.DeleteItems(ctx, deleteCredLibs, db.NewOplogMsgs(&credLibsOplogMsgs)) - if err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete target credential sources")) + if len(deleteCredLibs) > 0 { + i := make([]interface{}, 0, len(deleteCredLibs)) + for _, cl := range deleteCredLibs { + i = append(i, cl) + } + + credLibsOplogMsgs := make([]*oplog.Message, 0, len(deleteCredLibs)) + cnt, err := w.DeleteItems(ctx, i, db.NewOplogMsgs(&credLibsOplogMsgs)) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete target credential libraries")) + } + if cnt != len(deleteCredLibs) { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("credential libraries deleted %d did not match request for %d", rowsDeleted, len(deleteCredLibs))) + } + rowsDeleted += cnt + msgs = append(msgs, credLibsOplogMsgs...) } - if rowsDeleted != len(deleteCredLibs) { - return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("credential sources deleted %d did not match request for %d", rowsDeleted, len(deleteCredLibs))) + + if len(deleteStaticCred) > 0 { + i := make([]interface{}, 0, len(deleteStaticCred)) + for _, cl := range deleteStaticCred { + i = append(i, cl) + } + + staticCredOplogMsgs := make([]*oplog.Message, 0, len(deleteStaticCred)) + cnt, err := w.DeleteItems(ctx, i, db.NewOplogMsgs(&staticCredOplogMsgs)) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete target static credential")) + } + if cnt != len(deleteStaticCred) { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("static credential deleted %d did not match request for %d", rowsDeleted, len(deleteCredLibs))) + } + rowsDeleted += cnt + msgs = append(msgs, staticCredOplogMsgs...) } - msgs = append(msgs, credLibsOplogMsgs...) if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) @@ -237,7 +246,7 @@ func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId // SetTargetCredentialSources will set the target's credential sources. Set will add // and/or delete credential sources as need to reconcile the existing credential sources // with the request. If clIds is empty, all the credential sources will be cleared from the target. -func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, cls []*CredentialLibrary, _ ...Option) ([]HostSource, []CredentialSource, int, error) { +func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId string, targetVersion uint32, ids CredentialSources, _ ...Option) ([]HostSource, []CredentialSource, int, error) { const op = "target.(Repository).SetTargetCredentialSources" if targetId == "" { return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target id") @@ -246,19 +255,29 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version") } - clpm := newCredentialLibrariesPurposeMap(cls) + var ( + addCredLibs []*CredentialLibrary + delCredLibs []*CredentialLibrary + addStaticCred []*StaticCredential + delStaticCred []*StaticCredential + ) - var changes []*change - - for purpose, cls := range clpm { - c, err := r.changes(ctx, targetId, cls, purpose) + byPurpose := map[credential.Purpose][]string{ + credential.ApplicationPurpose: ids.ApplicationCredentialIds, + credential.EgressPurpose: ids.EgressCredentialIds, + } + for p, ids := range byPurpose { + addL, delL, addS, delS, err := r.changes(ctx, targetId, ids, p) if err != nil { return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) } - changes = append(changes, c...) + addCredLibs = append(addCredLibs, addL...) + delCredLibs = append(delCredLibs, delL...) + addStaticCred = append(addStaticCred, addS...) + delStaticCred = append(delStaticCred, delS...) } - if len(changes) == 0 { + if len(addCredLibs)+len(delCredLibs)+len(addStaticCred)+len(delStaticCred) == 0 { // Nothing needs to be changed, return early hostSets, err := fetchHostSources(ctx, r.reader, targetId) if err != nil { @@ -271,16 +290,6 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st return hostSets, credSources, db.NoRowsAffected, nil } - var deleteCredLibs, addCredLibs []interface{} - for _, c := range changes { - switch c.action { - case "delete": - deleteCredLibs = append(deleteCredLibs, c.credentialLibrary) - case "add": - addCredLibs = append(addCredLibs, c.credentialLibrary) - } - } - t := allocTargetView() t.PublicId = targetId if err := r.reader.LookupByPublicId(ctx, &t); err != nil { @@ -293,16 +302,19 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } - vetCredentialLibraries, ok := subtypeRegistry.vetCredentialLibrariesFunc(t.Subtype()) + vetCredentialSources, ok := subtypeRegistry.vetCredentialSourcesFunc(t.Subtype()) if !ok { return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } - if err := vetCredentialLibraries(ctx, cls); err != nil { + // Validate add sources on target + if err := vetCredentialSources(ctx, addCredLibs, addStaticCred); err != nil { return nil, nil, db.NoRowsAffected, err } target := alloc() - target.SetPublicId(ctx, t.PublicId) + if err := target.SetPublicId(ctx, t.PublicId); err != nil { + return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op) + } target.SetVersion(targetVersion + 1) metadata = target.Oplog(oplog.OpType_OP_TYPE_UPDATE) @@ -340,8 +352,12 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st // add new credential libraries if len(addCredLibs) > 0 { + i := make([]interface{}, 0, len(addCredLibs)) + for _, cl := range addCredLibs { + i = append(i, cl) + } addMsgs := make([]*oplog.Message, 0, len(addCredLibs)) - if err := w.CreateItems(ctx, addCredLibs, db.NewOplogMsgs(&addMsgs)); err != nil { + if err := w.CreateItems(ctx, i, db.NewOplogMsgs(&addMsgs)); err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add target credential libraries")) } rowsAffected += len(addMsgs) @@ -350,19 +366,58 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st } // delete existing credential libraries not part of set - if len(deleteCredLibs) > 0 { - delMsgs := make([]*oplog.Message, 0, len(deleteCredLibs)) - rowsDeleted, err := w.DeleteItems(ctx, deleteCredLibs, db.NewOplogMsgs(&delMsgs)) + if len(delCredLibs) > 0 { + i := make([]interface{}, 0, len(delCredLibs)) + for _, cl := range delCredLibs { + i = append(i, cl) + } + delMsgs := make([]*oplog.Message, 0, len(delCredLibs)) + rowsDeleted, err := w.DeleteItems(ctx, i, db.NewOplogMsgs(&delMsgs)) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete target credential libraries")) } if rowsDeleted != len(delMsgs) { - return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("target credential libraries deleted %d did not match request for %d", rowsDeleted, len(deleteCredLibs))) + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("target credential libraries deleted %d did not match request for %d", rowsDeleted, len(delCredLibs))) } rowsAffected += rowsDeleted msgs = append(msgs, delMsgs...) metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String()) } + + // add new static credential + if len(addStaticCred) > 0 { + i := make([]interface{}, 0, len(addStaticCred)) + for _, cl := range addStaticCred { + i = append(i, cl) + } + addMsgs := make([]*oplog.Message, 0, len(addStaticCred)) + if err := w.CreateItems(ctx, i, db.NewOplogMsgs(&addMsgs)); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add target static credential ")) + } + rowsAffected += len(addMsgs) + msgs = append(msgs, addMsgs...) + metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_CREATE.String()) + } + + // delete existing static credentials not part of set + if len(delStaticCred) > 0 { + i := make([]interface{}, 0, len(delStaticCred)) + for _, cl := range delStaticCred { + i = append(i, cl) + } + delMsgs := make([]*oplog.Message, 0, len(delStaticCred)) + rowsDeleted, err := w.DeleteItems(ctx, i, db.NewOplogMsgs(&delMsgs)) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete target static credential")) + } + if rowsDeleted != len(delMsgs) { + return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("target static credential deleted %d did not match request for %d", rowsDeleted, len(delStaticCred))) + } + rowsAffected += rowsDeleted + msgs = append(msgs, delMsgs...) + metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String()) + } + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) } @@ -384,17 +439,17 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st return hostSources, credSources, rowsAffected, nil } -type change struct { - action string - credentialLibrary *CredentialLibrary -} - type changeQueryResult struct { - Action string - LibraryId string + Action string + Type string + SourceId string } -func (r *Repository) changes(ctx context.Context, targetId string, cls []*CredentialLibrary, purpose string) ([]*change, error) { +func (r *Repository) changes(ctx context.Context, targetId string, ids []string, purpose credential.Purpose) ( + addCredLib, delCredLib []*CredentialLibrary, + addStaticCred, delStaticCred []*StaticCredential, + err error, +) { const op = "target.(Repository).changes" // TODO ensure that all cls have the same purpose as the given purpose? @@ -402,8 +457,8 @@ func (r *Repository) changes(ctx context.Context, targetId string, cls []*Creden var inClauseSpots []string var params []interface{} params = append(params, sql.Named("target_id", targetId), sql.Named("purpose", purpose)) - for idx, cl := range cls { - params = append(params, sql.Named(fmt.Sprintf("%d", idx+1), cl.CredentialLibraryId)) + for idx, id := range ids { + params = append(params, sql.Named(fmt.Sprintf("%d", idx+1), id)) inClauseSpots = append(inClauseSpots, fmt.Sprintf("@%d", idx+1)) } inClause := strings.Join(inClauseSpots, ",") @@ -414,45 +469,121 @@ func (r *Repository) changes(ctx context.Context, targetId string, cls []*Creden query := fmt.Sprintf(setChangesQuery, inClause) rows, err := r.reader.Query(ctx, query, params) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("query failed")) + return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("query failed")) } defer rows.Close() - var changes []*change for rows.Next() { var chg changeQueryResult if err := r.reader.ScanRows(ctx, rows, &chg); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("scan row failed")) + return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("scan row failed")) + } + switch chg.Type { + case "library": + lib, err := NewCredentialLibrary(targetId, chg.SourceId, purpose) + if err != nil { + return nil, nil, nil, nil, errors.Wrap(ctx, err, op) + } + switch chg.Action { + case "delete": + delCredLib = append(delCredLib, lib) + default: + addCredLib = append(addCredLib, lib) + } + case "static": + cred, err := NewStaticCredential(targetId, chg.SourceId, purpose) + if err != nil { + return nil, nil, nil, nil, errors.Wrap(ctx, err, op) + } + switch chg.Action { + case "delete": + delStaticCred = append(delStaticCred, cred) + default: + addStaticCred = append(addStaticCred, cred) + } } - changes = append(changes, &change{ - action: chg.Action, - credentialLibrary: &CredentialLibrary{ - CredentialLibrary: &store.CredentialLibrary{ - TargetId: targetId, - CredentialLibraryId: chg.LibraryId, - CredentialPurpose: purpose, - }, - }, - }) } - return changes, nil + return addCredLib, delCredLib, addStaticCred, delStaticCred, nil } func fetchCredentialSources(ctx context.Context, r db.Reader, targetId string) ([]CredentialSource, error) { const op = "target.fetchCredentialSources" - var libraries []*TargetLibrary - if err := r.SearchWhere(ctx, &libraries, "target_id = ?", []interface{}{targetId}); err != nil { + var sources []*TargetCredentialSource + if err := r.SearchWhere(ctx, &sources, "target_id = ?", []interface{}{targetId}); err != nil { return nil, errors.Wrap(ctx, err, op) } - // FIXME: When we have static creds, there will need to be an updated view - // that unions between libs and creds, at which point the type above will - // change. For now we just take the libraries and wrap them. - if len(libraries) == 0 { + if len(sources) == 0 { return nil, nil } - ret := make([]CredentialSource, len(libraries)) - for i, lib := range libraries { - ret[i] = lib + ret := make([]CredentialSource, len(sources)) + for i, source := range sources { + ret[i] = source } return ret, nil } + +func (r *Repository) createSources(ctx context.Context, tId string, tSubtype subtypes.Subtype, credSources CredentialSources) ([]*CredentialLibrary, []*StaticCredential, error) { + const op = "target.(Repository).createSources" + + // Get a list of unique ids being attached to the target, to be used for looking up the source type (library or static) + ids := make([]string, 0, len(credSources.ApplicationCredentialIds)+len(credSources.EgressCredentialIds)) + ids = append(ids, credSources.ApplicationCredentialIds...) + ids = append(ids, credSources.EgressCredentialIds...) + totalCreds := len(ids) + ids = strutil.RemoveDuplicates(ids, false) + if len(ids) == 0 { + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing credential sources") + } + + // Fetch credentials from database to determine the type of credential + var credView []*credentialSourceView + if err := r.reader.SearchWhere(ctx, &credView, "public_id in (?)", []interface{}{ids}); err != nil { + return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("can't retrieve credentials")) + } + if len(ids) != len(credView) { + return nil, nil, errors.New(ctx, errors.NotSpecificIntegrity, op, + fmt.Sprintf("mismatch between request and returned source ids, expected %d got %d", len(ids), len(credView))) + } + + // Create a map between credential source ID and it's type (library or static). + // This will allow for a quick lookup when calling the corresponding New below + credTypeById := make(map[string]string, len(ids)) + for _, cv := range credView { + credTypeById[cv.GetPublicId()] = cv.GetType() + } + + credLibs := make([]*CredentialLibrary, 0, totalCreds) + staticCred := make([]*StaticCredential, 0, totalCreds) + byPurpose := map[credential.Purpose][]string{ + credential.ApplicationPurpose: credSources.ApplicationCredentialIds, + credential.EgressPurpose: credSources.EgressCredentialIds, + } + for purpose, ids := range byPurpose { + for _, id := range ids { + switch credTypeById[id] { + case "library": + lib, err := NewCredentialLibrary(tId, id, purpose) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + credLibs = append(credLibs, lib) + case "static": + cred, err := NewStaticCredential(tId, id, purpose) + if err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + staticCred = append(staticCred, cred) + } + } + } + + vetCredentialSources, ok := subtypeRegistry.vetCredentialSourcesFunc(tSubtype) + if !ok { + return nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("is an unsupported target type %s", tSubtype)) + } + if err := vetCredentialSources(ctx, credLibs, staticCred); err != nil { + return nil, nil, errors.Wrap(ctx, err, op) + } + + return credLibs, staticCred, nil +} diff --git a/internal/target/repository_credential_source_test.go b/internal/target/repository_credential_source_test.go index 96f262ad9..e399ffead 100644 --- a/internal/target/repository_credential_source_test.go +++ b/internal/target/repository_credential_source_test.go @@ -5,12 +5,14 @@ import ( "testing" "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/store" "github.com/hashicorp/boundary/internal/target/targettest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -34,8 +36,8 @@ func (h hooks) VetForUpdate(ctx context.Context, t target.Target, paths []string return targettest.VetForUpdate(ctx, t, paths) } -func (h hooks) VetCredentialLibraries(ctx context.Context, cls []*target.CredentialLibrary) error { - return targettest.VetCredentialLibraries(ctx, cls) +func (h hooks) VetCredentialSources(ctx context.Context, cls []*target.CredentialLibrary, creds []*target.StaticCredential) error { + return targettest.VetCredentialSources(ctx, cls, creds) } func TestRepository_SetTargetCredentialSources(t *testing.T) { @@ -52,31 +54,40 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { iamRepo := iam.TestRepo(t, conn, wrapper) _, proj := iam.TestScopes(t, iamRepo) - cs := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] - credLibs := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 2) + storeVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] + credLibs := vault.TestCredentialLibraries(t, conn, wrapper, storeVault.GetPublicId(), 2) lib1 := credLibs[0] lib2 := credLibs[1] - setupFn := func(tar target.Target) ([]target.CredentialSource, []*target.CredentialLibrary) { - credLibs := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 10) - cls := make([]*target.CredentialLibrary, 0, len(credLibs)) + storeStatic := static.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] + credsStatic := static.TestUsernamePasswordCredentials(t, conn, wrapper, "u", "p", storeStatic.GetPublicId(), proj.GetPublicId(), 2) + cred1 := credsStatic[0] + cred2 := credsStatic[1] + + setupFn := func(tar target.Target) ([]target.CredentialSource, target.CredentialSources) { + credLibs := vault.TestCredentialLibraries(t, conn, wrapper, storeVault.GetPublicId(), 5) + var ids target.CredentialSources for _, cl := range credLibs { - cls = append(cls, target.TestNewCredentialLibrary(tar.GetPublicId(), cl.PublicId, credential.ApplicationPurpose)) + ids.ApplicationCredentialIds = append(ids.ApplicationCredentialIds, cl.GetPublicId()) + } + creds := static.TestUsernamePasswordCredentials(t, conn, wrapper, "u", "p", storeStatic.GetPublicId(), proj.GetPublicId(), 5) + for _, cred := range creds { + ids.ApplicationCredentialIds = append(ids.ApplicationCredentialIds, cred.GetPublicId()) } - _, _, created, err := repo.AddTargetCredentialSources(context.Background(), tar.GetPublicId(), 1, cls) + _, _, created, err := repo.AddTargetCredentialSources(context.Background(), tar.GetPublicId(), 1, ids) require.NoError(t, err) require.Equal(t, 10, len(created)) - return created, cls + return created, ids } type args struct { - targetVersion uint32 - cls []*target.CredentialLibrary - addToOrigLibs bool + targetVersion uint32 + ids target.CredentialSources + addToOrigSources bool } tests := []struct { name string - setup func(target.Target) ([]target.CredentialSource, []*target.CredentialLibrary) + setup func(target.Target) ([]target.CredentialSource, target.CredentialSources) args args wantAffectedRows int wantErr bool @@ -87,7 +98,6 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 2, - cls: []*target.CredentialLibrary{}, }, wantErr: false, wantAffectedRows: 10, @@ -96,62 +106,47 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { name: "no-change", setup: setupFn, args: args{ - targetVersion: 2, - cls: []*target.CredentialLibrary{}, - addToOrigLibs: true, + targetVersion: 2, + addToOrigSources: true, }, wantErr: false, wantAffectedRows: 0, }, { - name: "add-cred-sources", + name: "add-cred-library", setup: setupFn, args: args{ targetVersion: 2, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId, lib2.PublicId}, }, - addToOrigLibs: true, + addToOrigSources: true, }, wantErr: false, wantAffectedRows: 2, }, { - name: "add-multiple-purposes", + name: "add-cred-static", setup: setupFn, args: args{ targetVersion: 2, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib1.PublicId, credential.EgressPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{cred1.PublicId, cred2.PublicId}, }, - addToOrigLibs: true, + addToOrigSources: true, }, wantErr: false, - wantAffectedRows: 3, + wantAffectedRows: 2, }, { - name: "remove-add-change-purpose", - setup: func(tar target.Target) ([]target.CredentialSource, []*target.CredentialLibrary) { - cls := []*target.CredentialLibrary{ - target.TestNewCredentialLibrary(tar.GetPublicId(), lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary(tar.GetPublicId(), lib2.PublicId, credential.ApplicationPurpose), - } - - _, _, created, err := repo.AddTargetCredentialSources(context.Background(), tar.GetPublicId(), 1, cls) - require.NoError(t, err) - require.Equal(t, 2, len(created)) - return created, cls - }, + name: "add-cred-lib-and-static", + setup: setupFn, args: args{ targetVersion: 2, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.EgressPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.EgressPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{cred1.PublicId, lib1.PublicId, cred2.PublicId, lib2.PublicId}, }, - addToOrigLibs: false, + addToOrigSources: true, }, wantErr: false, wantAffectedRows: 4, @@ -161,11 +156,10 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 0, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId, lib2.PublicId}, }, - addToOrigLibs: true, + addToOrigSources: true, }, wantErr: true, wantErrCode: errors.InvalidParameter, @@ -175,11 +169,10 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 1000, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId, lib2.PublicId}, }, - addToOrigLibs: true, + addToOrigSources: true, }, wantErr: true, wantErrCode: errors.VersionMismatch, @@ -189,15 +182,52 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 2, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId, lib2.PublicId}, }, - addToOrigLibs: false, + addToOrigSources: false, }, wantErr: false, wantAffectedRows: 12, }, + { + name: "remove existing and add cred static", + setup: setupFn, + args: args{ + targetVersion: 2, + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{cred1.PublicId, cred2.PublicId}, + }, + addToOrigSources: false, + }, + wantErr: false, + wantAffectedRows: 12, + }, + { + name: "remove existing and add cred static and lib", + setup: setupFn, + args: args{ + targetVersion: 2, + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{cred1.PublicId, lib2.PublicId}, + }, + addToOrigSources: false, + }, + wantErr: false, + wantAffectedRows: 12, + }, + { + name: "egress-credential-purpose", + setup: setupFn, + args: args{ + targetVersion: 2, + ids: target.CredentialSources{ + EgressCredentialIds: []string{lib1.PublicId}, + }, + addToOrigSources: true, + }, + wantAffectedRows: 1, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -207,23 +237,28 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { tar := targettest.TestNewTestTarget(ctx, t, conn, proj.PublicId, tt.name) var origCredSources []target.CredentialSource - var origCredLibraries []*target.CredentialLibrary - if tt.setup != nil { - origCredSources, origCredLibraries = tt.setup(tar) - } - wantCredSources := make(map[string]target.CredentialSource) - for _, cl := range tt.args.cls { - cl.TargetId = tar.GetPublicId() - wantCredSources[cl.CredentialLibraryId+"_"+cl.CredentialPurpose] = &target.TargetLibrary{ - CredentialLibrary: cl.CredentialLibrary, + if tt.setup != nil { + var origCredIds target.CredentialSources + origCredSources, origCredIds = tt.setup(tar) + + if tt.args.addToOrigSources { + tt.args.ids.ApplicationCredentialIds = append(tt.args.ids.ApplicationCredentialIds, origCredIds.ApplicationCredentialIds...) + tt.args.ids.EgressCredentialIds = append(tt.args.ids.EgressCredentialIds, origCredIds.EgressCredentialIds...) } } - if tt.args.addToOrigLibs { - tt.args.cls = append(tt.args.cls, origCredLibraries...) - for _, cl := range origCredLibraries { - wantCredSources[cl.CredentialLibraryId+"_"+cl.CredentialPurpose] = &target.TargetLibrary{ - CredentialLibrary: cl.CredentialLibrary, + + byPurpose := map[credential.Purpose][]string{ + credential.ApplicationPurpose: tt.args.ids.ApplicationCredentialIds, + credential.EgressPurpose: tt.args.ids.EgressCredentialIds, + } + for purpose, ids := range byPurpose { + for _, id := range ids { + wantCredSources[id+"_"+string(purpose)] = &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: id, + CredentialPurpose: string(purpose), + }, } } } @@ -232,7 +267,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { require.NoError(err) assert.Equal(origCredSources, lookupCredSources) - _, got, affectedRows, err := repo.SetTargetCredentialSources(ctx, tar.GetPublicId(), tt.args.targetVersion, tt.args.cls) + _, gotSources, affectedRows, err := repo.SetTargetCredentialSources(ctx, tar.GetPublicId(), tt.args.targetVersion, tt.args.ids) if tt.wantErr { require.Error(err) assert.Equal(0, affectedRows) @@ -243,9 +278,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { require.NoError(err) assert.Equal(tt.wantAffectedRows, affectedRows) - assert.Equal(len(wantCredSources), len(got)) - - for _, cs := range got { + for _, cs := range gotSources { w, ok := wantCredSources[cs.Id()+"_"+string(cs.CredentialPurpose())] assert.True(ok, "got unexpected credentialsource %v", cs) assert.Equal(w.Id(), cs.Id()) @@ -263,8 +296,8 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { t.Run("missing-target-id", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - cl1 := target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose) - _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "", 1, []*target.CredentialLibrary{cl1}) + _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "", 1, + target.CredentialSources{ApplicationCredentialIds: []string{lib1.PublicId}}) require.Error(err) assert.Truef(errors.Match(errors.T(errors.InvalidParameter), err), "unexpected error %s", err.Error()) @@ -272,8 +305,8 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { t.Run("target-not-found", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - cl1 := target.TestNewCredentialLibrary("fake-target-id", lib1.PublicId, credential.ApplicationPurpose) - _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "fake-target-id", 1, []*target.CredentialLibrary{cl1}) + _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "fake-target-id", 1, + target.CredentialSources{ApplicationCredentialIds: []string{lib1.PublicId}}) require.Error(err) assert.Truef(errors.Match(errors.T(errors.RecordNotFound), err), "unexpected error %s", err.Error()) diff --git a/internal/target/store/target.pb.go b/internal/target/store/target.pb.go index a6f784a00..9df9a13d2 100644 --- a/internal/target/store/target.pb.go +++ b/internal/target/store/target.pb.go @@ -331,6 +331,223 @@ func (x *CredentialLibrary) GetCreateTime() *timestamp.Timestamp { return nil } +type StaticCredential struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // target_id of the Target + // @inject_tag: gorm:"primary_key" + TargetId string `protobuf:"bytes,10,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty" gorm:"primary_key"` + // credential_id of the static Credential + // @inject_tag: `gorm:"column:credential_static_id;primary_key" + CredentialId string `protobuf:"bytes,20,opt,name=credential_id,json=credentialId,proto3" json:"credential_id,omitempty" gorm:"column:credential_static_id;primary_key"` + // credential_purpose is the purpose of the credential for the target + // @inject_tag: gorm:"primary_key" + CredentialPurpose string `protobuf:"bytes,30,opt,name=credential_purpose,json=credentialPurpose,proto3" json:"credential_purpose,omitempty" gorm:"primary_key"` + // create_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,40,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` +} + +func (x *StaticCredential) Reset() { + *x = StaticCredential{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StaticCredential) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StaticCredential) ProtoMessage() {} + +func (x *StaticCredential) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StaticCredential.ProtoReflect.Descriptor instead. +func (*StaticCredential) Descriptor() ([]byte, []int) { + return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{3} +} + +func (x *StaticCredential) GetTargetId() string { + if x != nil { + return x.TargetId + } + return "" +} + +func (x *StaticCredential) GetCredentialId() string { + if x != nil { + return x.CredentialId + } + return "" +} + +func (x *StaticCredential) GetCredentialPurpose() string { + if x != nil { + return x.CredentialPurpose + } + return "" +} + +func (x *StaticCredential) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +type CredentialSource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // target_id of the Target + // @inject_tag: gorm:"primary_key" + TargetId string `protobuf:"bytes,10,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty" gorm:"primary_key"` + // credential_source_id of the Credential Library or static Credential + // @inject_tag: gorm:"primary_key" + CredentialSourceId string `protobuf:"bytes,20,opt,name=credential_source_id,json=credentialSourceId,proto3" json:"credential_source_id,omitempty" gorm:"primary_key"` + // credential_purpose is the purpose of the credential for the target + // @inject_tag: gorm:"primary_key" + CredentialPurpose string `protobuf:"bytes,30,opt,name=credential_purpose,json=credentialPurpose,proto3" json:"credential_purpose,omitempty" gorm:"primary_key"` + // create_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,40,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` +} + +func (x *CredentialSource) Reset() { + *x = CredentialSource{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CredentialSource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CredentialSource) ProtoMessage() {} + +func (x *CredentialSource) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CredentialSource.ProtoReflect.Descriptor instead. +func (*CredentialSource) Descriptor() ([]byte, []int) { + return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{4} +} + +func (x *CredentialSource) GetTargetId() string { + if x != nil { + return x.TargetId + } + return "" +} + +func (x *CredentialSource) GetCredentialSourceId() string { + if x != nil { + return x.CredentialSourceId + } + return "" +} + +func (x *CredentialSource) GetCredentialPurpose() string { + if x != nil { + return x.CredentialPurpose + } + return "" +} + +func (x *CredentialSource) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +type CredentialSourceView struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id of the Credential source + // @inject_tag: gorm:"primary_key" + PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // type of credential source (library or static) + // @inject_tag: `gorm:"not_null"` + Type string `protobuf:"bytes,20,opt,name=type,proto3" json:"type,omitempty" gorm:"not_null"` +} + +func (x *CredentialSourceView) Reset() { + *x = CredentialSourceView{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CredentialSourceView) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CredentialSourceView) ProtoMessage() {} + +func (x *CredentialSourceView) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CredentialSourceView.ProtoReflect.Descriptor instead. +func (*CredentialSourceView) Descriptor() ([]byte, []int) { + return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{5} +} + +func (x *CredentialSourceView) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *CredentialSourceView) GetType() string { + if x != nil { + return x.Type + } + return "" +} + var File_controller_storage_target_store_v1_target_proto protoreflect.FileDescriptor var file_controller_storage_target_store_v1_target_proto_rawDesc = []byte{ @@ -397,12 +614,43 @@ var file_controller_storage_target_store_v1_target_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x3b, 0x5a, 0x39, - 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, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xd0, 0x01, 0x0a, + 0x10, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x23, + 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, + 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x5f, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x75, 0x72, 0x70, 0x6f, + 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, + 0xdd, 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, + 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x5f, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x75, 0x72, 0x70, 0x6f, + 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, + 0x47, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x42, 0x3b, 0x5a, 0x39, 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, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -417,23 +665,28 @@ func file_controller_storage_target_store_v1_target_proto_rawDescGZIP() []byte { return file_controller_storage_target_store_v1_target_proto_rawDescData } -var file_controller_storage_target_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_controller_storage_target_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_controller_storage_target_store_v1_target_proto_goTypes = []interface{}{ - (*TargetView)(nil), // 0: controller.storage.target.store.v1.TargetView - (*TargetHostSet)(nil), // 1: controller.storage.target.store.v1.TargetHostSet - (*CredentialLibrary)(nil), // 2: controller.storage.target.store.v1.CredentialLibrary - (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp + (*TargetView)(nil), // 0: controller.storage.target.store.v1.TargetView + (*TargetHostSet)(nil), // 1: controller.storage.target.store.v1.TargetHostSet + (*CredentialLibrary)(nil), // 2: controller.storage.target.store.v1.CredentialLibrary + (*StaticCredential)(nil), // 3: controller.storage.target.store.v1.StaticCredential + (*CredentialSource)(nil), // 4: controller.storage.target.store.v1.CredentialSource + (*CredentialSourceView)(nil), // 5: controller.storage.target.store.v1.CredentialSourceView + (*timestamp.Timestamp)(nil), // 6: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_target_store_v1_target_proto_depIdxs = []int32{ - 3, // 0: controller.storage.target.store.v1.TargetView.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 1: controller.storage.target.store.v1.TargetView.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 2: controller.storage.target.store.v1.TargetHostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 3, // 3: controller.storage.target.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 6, // 0: controller.storage.target.store.v1.TargetView.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 1: controller.storage.target.store.v1.TargetView.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 2: controller.storage.target.store.v1.TargetHostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 3: controller.storage.target.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 4: controller.storage.target.store.v1.StaticCredential.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // 5: controller.storage.target.store.v1.CredentialSource.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_controller_storage_target_store_v1_target_proto_init() } @@ -478,6 +731,42 @@ func file_controller_storage_target_store_v1_target_proto_init() { return nil } } + file_controller_storage_target_store_v1_target_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StaticCredential); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_target_store_v1_target_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CredentialSource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_storage_target_store_v1_target_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CredentialSourceView); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -485,7 +774,7 @@ func file_controller_storage_target_store_v1_target_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_target_store_v1_target_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/target/targettest/target.go b/internal/target/targettest/target.go index 741add0a2..f79dc1536 100644 --- a/internal/target/targettest/target.go +++ b/internal/target/targettest/target.go @@ -230,8 +230,8 @@ func VetForUpdate(ctx context.Context, t target.Target, paths []string) error { return nil } -// VetCredentialLibraries allows for any CredentialLibraries. -func VetCredentialLibraries(_ context.Context, _ []*target.CredentialLibrary) error { +// VetCredentialSources allows for any CredentialLibraries. +func VetCredentialSources(_ context.Context, _ []*target.CredentialLibrary, _ []*target.StaticCredential) error { return nil } diff --git a/internal/target/tcp/register.go b/internal/target/tcp/register.go index db8119c01..ca4d81339 100644 --- a/internal/target/tcp/register.go +++ b/internal/target/tcp/register.go @@ -71,13 +71,18 @@ func (h targetHooks) VetForUpdate(ctx context.Context, t target.Target, paths [] return nil } -// VetCredentialLibraries checks that all of the provided credential libriaries have a CredentialPurpose +// VetCredentialSources checks that all the provided credential sources have a CredentialPurpose // of ApplicationPurpose. Any other CredentialPurpose will result in an error. -func (h targetHooks) VetCredentialLibraries(ctx context.Context, cls []*target.CredentialLibrary) error { - const op = "tcp.vetCredentialLibraries" +func (h targetHooks) VetCredentialSources(ctx context.Context, libs []*target.CredentialLibrary, creds []*target.StaticCredential) error { + const op = "tcp.VetCredentialSources" - for _, cl := range cls { - if cl.CredentialPurpose != string(credential.ApplicationPurpose) { + for _, c := range libs { + if c.GetCredentialPurpose() != string(credential.ApplicationPurpose) { + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("tcp.Target only supports credential purpose: %q", credential.ApplicationPurpose)) + } + } + for _, c := range creds { + if c.GetCredentialPurpose() != string(credential.ApplicationPurpose) { return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("tcp.Target only supports credential purpose: %q", credential.ApplicationPurpose)) } } diff --git a/internal/target/tcp/repository_credential_source_test.go b/internal/target/tcp/repository_credential_source_test.go index 0380bfb76..0576feda6 100644 --- a/internal/target/tcp/repository_credential_source_test.go +++ b/internal/target/tcp/repository_credential_source_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -31,16 +32,23 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { repo, err := target.NewRepository(rw, rw, testKms) require.NoError(t, err) - cs := vault.TestCredentialStores(t, conn, wrapper, staticProj.GetPublicId(), 1)[0] - libs := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 3) + csVault := vault.TestCredentialStores(t, conn, wrapper, staticProj.GetPublicId(), 1)[0] + libs := vault.TestCredentialLibraries(t, conn, wrapper, csVault.GetPublicId(), 3) require.Len(t, libs, 3) lib1 := libs[0] lib2 := libs[1] lib3 := libs[2] + csStatic := static.TestCredentialStore(t, conn, wrapper, staticProj.GetPublicId()) + creds := static.TestUsernamePasswordCredentials(t, conn, wrapper, "user", "pass", csStatic.GetPublicId(), staticProj.GetPublicId(), 3) + require.Len(t, creds, 3) + cred1 := creds[0] + cred2 := creds[1] + cred3 := creds[2] + type args struct { targetVersion uint32 - cls []*target.CredentialLibrary + ids target.CredentialSources } tests := []struct { name string @@ -66,18 +74,88 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { wantErrCode: errors.InvalidParameter, }, { - name: "valid-single-source", + name: "valid-single-library", args: args{ targetVersion: 1, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - }, + ids: target.CredentialSources{ApplicationCredentialIds: []string{lib1.PublicId}}, }, wantCredSources: map[string]target.CredentialSource{ - lib1.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetLibrary{ - CredentialLibrary: &store.CredentialLibrary{ - CredentialLibraryId: lib1.PublicId, - CredentialPurpose: string(credential.ApplicationPurpose), + lib1.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: lib1.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + }, + wantErr: false, + }, + { + name: "valid-single-static", + args: args{ + targetVersion: 1, + ids: target.CredentialSources{ApplicationCredentialIds: []string{cred1.PublicId}}, + }, + wantCredSources: map[string]target.CredentialSource{ + cred1.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: cred1.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + }, + wantErr: false, + }, + { + name: "valid-multiple-libraries", + args: args{ + targetVersion: 1, + ids: target.CredentialSources{ApplicationCredentialIds: []string{lib1.PublicId, lib2.PublicId, lib3.PublicId}}, + }, + wantCredSources: map[string]target.CredentialSource{ + lib1.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: lib1.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + lib2.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: lib2.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + lib3.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: lib3.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + }, + wantErr: false, + }, + { + name: "valid-multiple-static", + args: args{ + targetVersion: 1, + ids: target.CredentialSources{ApplicationCredentialIds: []string{cred1.PublicId, cred2.PublicId, cred3.PublicId}}, + }, + wantCredSources: map[string]target.CredentialSource{ + cred1.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: cred1.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + cred2.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: cred2.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + cred3.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: cred3.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), }, }, }, @@ -87,29 +165,33 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { name: "valid-multiple-sources", args: args{ targetVersion: 1, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib3.PublicId, credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{cred1.PublicId, cred2.PublicId, lib1.PublicId, lib2.PublicId}, }, }, wantCredSources: map[string]target.CredentialSource{ - lib1.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetLibrary{ - CredentialLibrary: &store.CredentialLibrary{ - CredentialLibraryId: lib1.PublicId, - CredentialPurpose: string(credential.ApplicationPurpose), + cred1.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: cred1.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), }, }, - lib2.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetLibrary{ - CredentialLibrary: &store.CredentialLibrary{ - CredentialLibraryId: lib2.PublicId, - CredentialPurpose: string(credential.ApplicationPurpose), + cred2.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: cred2.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), }, }, - lib3.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetLibrary{ - CredentialLibrary: &store.CredentialLibrary{ - CredentialLibraryId: lib3.PublicId, - CredentialPurpose: string(credential.ApplicationPurpose), + lib1.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: lib1.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), + }, + }, + lib2.PublicId + "_" + string(credential.ApplicationPurpose): &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: lib2.PublicId, + CredentialPurpose: string(credential.ApplicationPurpose), }, }, }, @@ -119,34 +201,27 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { name: "invalid-source-id", args: args{ targetVersion: 1, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib3.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", "invalid-source-id", credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId, cred2.PublicId, "invalid-source-id", lib3.PublicId, cred3.PublicId}, }, }, wantErr: true, wantErrCode: errors.NotSpecificIntegrity, }, { - name: "egress-credential-purpose", + name: "egress-credential-purpose-library", args: args{ targetVersion: 1, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.EgressPurpose), - }, + ids: target.CredentialSources{EgressCredentialIds: []string{lib1.PublicId}}, }, wantErr: true, wantErrCode: errors.InvalidParameter, }, { - name: "ingress-credential-purpose", + name: "egress-credential-purpose-static", args: args{ targetVersion: 1, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.IngressPurpose), - }, + ids: target.CredentialSources{EgressCredentialIds: []string{cred1.PublicId}}, }, wantErr: true, wantErrCode: errors.InvalidParameter, @@ -155,9 +230,7 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { name: "bad-version", args: args{ targetVersion: 1000, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - }, + ids: target.CredentialSources{ApplicationCredentialIds: []string{lib1.PublicId}}, }, wantErr: true, wantErrCode: errors.VersionMismatch, @@ -170,11 +243,7 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { ctx := context.Background() projTarget := tcp.TestTarget(ctx, t, conn, staticProj.PublicId, tt.name) - for _, cl := range tt.args.cls { - cl.TargetId = projTarget.GetPublicId() - } - - gotTarget, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.GetPublicId(), tt.args.targetVersion, tt.args.cls) + gotTarget, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.GetPublicId(), tt.args.targetVersion, tt.args.ids) if tt.wantErr { require.Error(err) assert.Truef(errors.Match(errors.T(tt.wantErrCode), err), "unexpected error %s", err.Error()) @@ -215,22 +284,28 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { ctx := context.Background() projTarget := tcp.TestTarget(ctx, t, conn, staticProj.PublicId, "add-existing") - cl1 := target.TestNewCredentialLibrary(projTarget.GetPublicId(), lib1.PublicId, credential.ApplicationPurpose) - cl2 := target.TestNewCredentialLibrary(projTarget.GetPublicId(), lib2.PublicId, credential.ApplicationPurpose) - cl3 := target.TestNewCredentialLibrary(projTarget.GetPublicId(), lib3.PublicId, credential.ApplicationPurpose) - _, _, gotCredSources, err := repo.AddTargetCredentialSources(ctx, projTarget.GetPublicId(), 1, []*target.CredentialLibrary{cl1}) + ids := target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId}, + } + _, _, gotCredSources, err := repo.AddTargetCredentialSources(ctx, projTarget.GetPublicId(), 1, ids) require.NoError(err) assert.Len(gotCredSources, 1) assert.Equal(lib1.PublicId, gotCredSources[0].Id()) // Adding lib1 again should error - _, _, _, err = repo.AddTargetCredentialSources(ctx, projTarget.GetPublicId(), 2, []*target.CredentialLibrary{cl1}) + ids = target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId}, + } + _, _, _, err = repo.AddTargetCredentialSources(ctx, projTarget.GetPublicId(), 2, ids) require.Error(err) assert.True(errors.Match(errors.T(errors.NotUnique), err)) // Adding multiple with lib1 in set should error - _, _, _, err = repo.AddTargetCredentialSources(ctx, projTarget.GetPublicId(), 2, []*target.CredentialLibrary{cl3, cl2, cl1}) + ids = target.CredentialSources{ + ApplicationCredentialIds: []string{lib3.PublicId, lib2.PublicId, lib1.PublicId}, + } + _, _, _, err = repo.AddTargetCredentialSources(ctx, projTarget.GetPublicId(), 2, ids) require.Error(err) assert.True(errors.Match(errors.T(errors.NotUnique), err)) @@ -243,8 +318,10 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { t.Run("target-not-found", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - cl1 := target.TestNewCredentialLibrary("fake-target-id", lib1.PublicId, credential.ApplicationPurpose) - _, _, _, err := repo.AddTargetCredentialSources(context.Background(), "fake-target-id", 1, []*target.CredentialLibrary{cl1}) + ids := target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId}, + } + _, _, _, err := repo.AddTargetCredentialSources(context.Background(), "fake-target-id", 1, ids) require.Error(err) assert.Truef(errors.Match(errors.T(errors.RecordNotFound), err), "unexpected error %s", err.Error()) @@ -265,8 +342,10 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { type args struct { targetIdOverride *string targetVersionOverride *uint32 - createCnt int - deleteCnt int + createLibCnt int + createStaticCnt int + deleteLibCnt int + deleteStaticCnt int } tests := []struct { name string @@ -276,27 +355,50 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { wantErrCode errors.Code }{ { - name: "valid", + name: "valid-lib-only", args: args{ - createCnt: 5, - deleteCnt: 5, + createLibCnt: 5, + deleteLibCnt: 5, }, wantRowsDeleted: 5, wantErr: false, }, + { + name: "valid-static-only", + args: args{ + createStaticCnt: 5, + deleteStaticCnt: 5, + }, + wantRowsDeleted: 5, + wantErr: false, + }, + { + name: "valid-mixed", + args: args{ + createLibCnt: 5, + deleteLibCnt: 5, + createStaticCnt: 5, + deleteStaticCnt: 5, + }, + wantRowsDeleted: 10, + wantErr: false, + }, { name: "valid-keeping-some", args: args{ - createCnt: 5, - deleteCnt: 2, + createLibCnt: 5, + deleteLibCnt: 3, + createStaticCnt: 5, + deleteStaticCnt: 2, }, - wantRowsDeleted: 2, + wantRowsDeleted: 5, wantErr: false, }, { name: "no-deletes", args: args{ - createCnt: 5, + createLibCnt: 5, + createStaticCnt: 5, }, wantRowsDeleted: 0, wantErr: true, @@ -306,8 +408,8 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { name: "not-found", args: args{ targetIdOverride: func() *string { id := tcp.TestId(t); return &id }(), - createCnt: 5, - deleteCnt: 5, + createLibCnt: 5, + deleteLibCnt: 5, }, wantRowsDeleted: 0, wantErr: true, @@ -317,8 +419,8 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { name: "missing-target-id", args: args{ targetIdOverride: func() *string { id := ""; return &id }(), - createCnt: 5, - deleteCnt: 5, + createLibCnt: 5, + deleteLibCnt: 5, }, wantRowsDeleted: 0, wantErr: true, @@ -328,8 +430,8 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { name: "zero-version", args: args{ targetVersionOverride: func() *uint32 { v := uint32(0); return &v }(), - createCnt: 5, - deleteCnt: 5, + createLibCnt: 5, + deleteLibCnt: 5, }, wantRowsDeleted: 0, wantErr: true, @@ -339,38 +441,47 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { name: "bad-version", args: args{ targetVersionOverride: func() *uint32 { v := uint32(1000); return &v }(), - createCnt: 5, - deleteCnt: 5, + createLibCnt: 5, + deleteLibCnt: 5, }, wantRowsDeleted: 0, wantErr: true, wantErrCode: errors.VersionMismatch, }, } - css := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), len(tests)) + csVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), len(tests)) + csStatic := static.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), len(tests)) for i, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - cs := css[i] + csv := csVault[i] + css := csStatic[i] ctx := context.Background() tar := tcp.TestTarget(ctx, t, conn, proj.PublicId, tt.name) - cls := make([]*target.CredentialLibrary, 0, tt.args.createCnt) - if tt.args.createCnt > 0 { - credLibs := vault.TestCredentialLibraries(t, conn, wrapper, cs.PublicId, tt.args.createCnt) - for _, cl := range credLibs { - cls = append(cls, target.TestNewCredentialLibrary(tar.GetPublicId(), cl.PublicId, credential.ApplicationPurpose)) - } + var ids target.CredentialSources + credLibs := vault.TestCredentialLibraries(t, conn, wrapper, csv.PublicId, tt.args.createLibCnt) + for _, cl := range credLibs { + ids.ApplicationCredentialIds = append(ids.ApplicationCredentialIds, cl.GetPublicId()) + } + creds := static.TestUsernamePasswordCredentials(t, conn, wrapper, "u", "p", css.PublicId, proj.GetPublicId(), tt.args.createStaticCnt) + for _, c := range creds { + ids.ApplicationCredentialIds = append(ids.ApplicationCredentialIds, c.GetPublicId()) } - _, _, addedCredSources, err := repo.AddTargetCredentialSources(ctx, tar.GetPublicId(), 1, cls) - require.NoError(err) - assert.Equal(tt.args.createCnt, len(addedCredSources)) - deleteCredSources := make([]*target.CredentialLibrary, 0, tt.args.deleteCnt) - for i := 0; i < tt.args.deleteCnt; i++ { - deleteCredSources = append(deleteCredSources, cls[i]) + _, _, addedCredSources, err := repo.AddTargetCredentialSources(ctx, tar.GetPublicId(), 1, ids) + require.NoError(err) + assert.Equal(tt.args.createLibCnt+tt.args.createStaticCnt, len(addedCredSources)) + + var deleteIds target.CredentialSources + for i := 0; i < tt.args.deleteLibCnt; i++ { + deleteIds.ApplicationCredentialIds = append(deleteIds.ApplicationCredentialIds, credLibs[i].GetPublicId()) } + for i := 0; i < tt.args.deleteStaticCnt; i++ { + deleteIds.ApplicationCredentialIds = append(deleteIds.ApplicationCredentialIds, creds[i].GetPublicId()) + } + var targetId string switch { case tt.args.targetIdOverride != nil: @@ -385,7 +496,7 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { default: targetVersion = 2 } - deletedRows, err := repo.DeleteTargetCredentialSources(ctx, targetId, targetVersion, deleteCredSources) + deletedRows, err := repo.DeleteTargetCredentialSources(ctx, targetId, targetVersion, deleteIds) if tt.wantErr { require.Error(err) assert.Equal(0, deletedRows) @@ -413,22 +524,28 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { ctx := context.Background() projTarget := tcp.TestTarget(ctx, t, conn, proj.PublicId, "add-existing") - cl1 := target.TestNewCredentialLibrary(projTarget.GetPublicId(), lib1.PublicId, credential.ApplicationPurpose) - cl2 := target.TestNewCredentialLibrary(projTarget.GetPublicId(), lib2.PublicId, credential.ApplicationPurpose) - cl3 := target.TestNewCredentialLibrary(projTarget.GetPublicId(), lib3.PublicId, credential.ApplicationPurpose) - _, _, gotCredSources, err := repo.AddTargetCredentialSources(ctx, projTarget.GetPublicId(), 1, []*target.CredentialLibrary{cl1, cl2}) + ids := target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.GetPublicId(), lib2.GetPublicId()}, + } + _, _, gotCredSources, err := repo.AddTargetCredentialSources(ctx, projTarget.GetPublicId(), 1, ids) require.NoError(err) assert.Len(gotCredSources, 2) // Deleting an unassociated source should return an error - delCount, err := repo.DeleteTargetCredentialSources(ctx, projTarget.GetPublicId(), 2, []*target.CredentialLibrary{cl3}) + delCount, err := repo.DeleteTargetCredentialSources(ctx, projTarget.GetPublicId(), 2, + target.CredentialSources{ + ApplicationCredentialIds: []string{lib3.GetPublicId()}, + }) require.Error(err) assert.True(errors.Match(errors.T(errors.MultipleRecords), err)) assert.Equal(0, delCount) // Deleting sources which includes an unassociated source should return an error - delCount, err = repo.DeleteTargetCredentialSources(ctx, projTarget.GetPublicId(), 2, []*target.CredentialLibrary{cl1, cl2, cl3}) + delCount, err = repo.DeleteTargetCredentialSources(ctx, projTarget.GetPublicId(), 2, + target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.GetPublicId(), lib2.GetPublicId(), lib3.GetPublicId()}, + }) require.Error(err) assert.True(errors.Match(errors.T(errors.MultipleRecords), err)) assert.Equal(0, delCount) @@ -452,31 +569,40 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { iamRepo := iam.TestRepo(t, conn, wrapper) _, proj := iam.TestScopes(t, iamRepo) - cs := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] - credLibs := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 2) + storeVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] + credLibs := vault.TestCredentialLibraries(t, conn, wrapper, storeVault.GetPublicId(), 2) lib1 := credLibs[0] lib2 := credLibs[1] - setupFn := func(tar target.Target) ([]target.CredentialSource, []*target.CredentialLibrary) { - credLibs := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 10) - cls := make([]*target.CredentialLibrary, 0, len(credLibs)) + storeStatic := static.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] + credsStatic := static.TestUsernamePasswordCredentials(t, conn, wrapper, "u", "p", storeStatic.GetPublicId(), proj.GetPublicId(), 2) + cred1 := credsStatic[0] + cred2 := credsStatic[1] + + setupFn := func(tar target.Target) ([]target.CredentialSource, target.CredentialSources) { + credLibs := vault.TestCredentialLibraries(t, conn, wrapper, storeVault.GetPublicId(), 5) + var ids target.CredentialSources for _, cl := range credLibs { - cls = append(cls, target.TestNewCredentialLibrary(tar.GetPublicId(), cl.PublicId, credential.ApplicationPurpose)) + ids.ApplicationCredentialIds = append(ids.ApplicationCredentialIds, cl.GetPublicId()) + } + creds := static.TestUsernamePasswordCredentials(t, conn, wrapper, "u", "p", storeStatic.GetPublicId(), proj.GetPublicId(), 5) + for _, cred := range creds { + ids.ApplicationCredentialIds = append(ids.ApplicationCredentialIds, cred.GetPublicId()) } - _, _, created, err := repo.AddTargetCredentialSources(context.Background(), tar.GetPublicId(), 1, cls) + _, _, created, err := repo.AddTargetCredentialSources(context.Background(), tar.GetPublicId(), 1, ids) require.NoError(t, err) require.Equal(t, 10, len(created)) - return created, cls + return created, ids } type args struct { - targetVersion uint32 - cls []*target.CredentialLibrary - addToOrigLibs bool + targetVersion uint32 + ids target.CredentialSources + addToOrigSources bool } tests := []struct { name string - setup func(target.Target) ([]target.CredentialSource, []*target.CredentialLibrary) + setup func(target.Target) ([]target.CredentialSource, target.CredentialSources) args args wantAffectedRows int wantErr bool @@ -487,7 +613,6 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 2, - cls: []*target.CredentialLibrary{}, }, wantErr: false, wantAffectedRows: 10, @@ -496,37 +621,60 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { name: "no-change", setup: setupFn, args: args{ - targetVersion: 2, - cls: []*target.CredentialLibrary{}, - addToOrigLibs: true, + targetVersion: 2, + addToOrigSources: true, }, wantErr: false, wantAffectedRows: 0, }, { - name: "add-cred-sources", + name: "add-cred-library", setup: setupFn, args: args{ targetVersion: 2, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId, lib2.PublicId}, }, - addToOrigLibs: true, + addToOrigSources: true, }, wantErr: false, wantAffectedRows: 2, }, + { + name: "add-cred-static", + setup: setupFn, + args: args{ + targetVersion: 2, + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{cred1.PublicId, cred2.PublicId}, + }, + addToOrigSources: true, + }, + wantErr: false, + wantAffectedRows: 2, + }, + { + name: "add-cred-lib-and-static", + setup: setupFn, + args: args{ + targetVersion: 2, + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{cred1.PublicId, lib1.PublicId, cred2.PublicId, lib2.PublicId}, + }, + addToOrigSources: true, + }, + wantErr: false, + wantAffectedRows: 4, + }, { name: "zero version", setup: setupFn, args: args{ targetVersion: 0, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId, lib2.PublicId}, }, - addToOrigLibs: true, + addToOrigSources: true, }, wantErr: true, wantErrCode: errors.InvalidParameter, @@ -536,11 +684,10 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 1000, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId, lib2.PublicId}, }, - addToOrigLibs: true, + addToOrigSources: true, }, wantErr: true, wantErrCode: errors.VersionMismatch, @@ -550,38 +697,39 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { setup: setupFn, args: args{ targetVersion: 2, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose), - target.TestNewCredentialLibrary("", lib2.PublicId, credential.ApplicationPurpose), + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{lib1.PublicId, lib2.PublicId}, }, - addToOrigLibs: false, + addToOrigSources: false, }, wantErr: false, wantAffectedRows: 12, }, { - name: "egress-credential-purpose", + name: "remove existing and add cred static", setup: setupFn, args: args{ - targetVersion: 1, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.EgressPurpose), + targetVersion: 2, + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{cred1.PublicId, cred2.PublicId}, }, + addToOrigSources: false, }, - wantErr: true, - wantErrCode: errors.InvalidParameter, + wantErr: false, + wantAffectedRows: 12, }, { - name: "ingress-credential-purpose", + name: "remove existing and add cred static and lib", setup: setupFn, args: args{ - targetVersion: 1, - cls: []*target.CredentialLibrary{ - target.TestNewCredentialLibrary("", lib1.PublicId, credential.IngressPurpose), + targetVersion: 2, + ids: target.CredentialSources{ + ApplicationCredentialIds: []string{cred1.PublicId, lib2.PublicId}, }, + addToOrigSources: false, }, - wantErr: true, - wantErrCode: errors.InvalidParameter, + wantErr: false, + wantAffectedRows: 12, }, } for _, tt := range tests { @@ -592,23 +740,28 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { tar := tcp.TestTarget(ctx, t, conn, proj.PublicId, tt.name) var origCredSources []target.CredentialSource - var origCredLibraries []*target.CredentialLibrary - if tt.setup != nil { - origCredSources, origCredLibraries = tt.setup(tar) - } - wantCredSources := make(map[string]target.CredentialSource) - for _, cl := range tt.args.cls { - cl.TargetId = tar.GetPublicId() - wantCredSources[cl.CredentialLibraryId+"_"+cl.CredentialPurpose] = &target.TargetLibrary{ - CredentialLibrary: cl.CredentialLibrary, + if tt.setup != nil { + var origCredIds target.CredentialSources + origCredSources, origCredIds = tt.setup(tar) + + if tt.args.addToOrigSources { + tt.args.ids.ApplicationCredentialIds = append(tt.args.ids.ApplicationCredentialIds, origCredIds.ApplicationCredentialIds...) + tt.args.ids.EgressCredentialIds = append(tt.args.ids.EgressCredentialIds, origCredIds.EgressCredentialIds...) } } - if tt.args.addToOrigLibs { - tt.args.cls = append(tt.args.cls, origCredLibraries...) - for _, cl := range origCredLibraries { - wantCredSources[cl.CredentialLibraryId+"_"+cl.CredentialPurpose] = &target.TargetLibrary{ - CredentialLibrary: cl.CredentialLibrary, + + byPurpose := map[credential.Purpose][]string{ + credential.ApplicationPurpose: tt.args.ids.ApplicationCredentialIds, + credential.EgressPurpose: tt.args.ids.EgressCredentialIds, + } + for purpose, ids := range byPurpose { + for _, id := range ids { + wantCredSources[id+"_"+string(purpose)] = &target.TargetCredentialSource{ + CredentialSource: &store.CredentialSource{ + CredentialSourceId: id, + CredentialPurpose: string(purpose), + }, } } } @@ -617,7 +770,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { require.NoError(err) assert.Equal(origCredSources, lookupCredSources) - _, got, affectedRows, err := repo.SetTargetCredentialSources(ctx, tar.GetPublicId(), tt.args.targetVersion, tt.args.cls) + _, gotSources, affectedRows, err := repo.SetTargetCredentialSources(ctx, tar.GetPublicId(), tt.args.targetVersion, tt.args.ids) if tt.wantErr { require.Error(err) assert.Equal(0, affectedRows) @@ -628,9 +781,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { require.NoError(err) assert.Equal(tt.wantAffectedRows, affectedRows) - assert.Equal(len(wantCredSources), len(got)) - - for _, cs := range got { + for _, cs := range gotSources { w, ok := wantCredSources[cs.Id()+"_"+string(cs.CredentialPurpose())] assert.True(ok, "got unexpected credentialsource %v", cs) assert.Equal(w.Id(), cs.Id()) @@ -648,8 +799,8 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { t.Run("missing-target-id", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - cl1 := target.TestNewCredentialLibrary("", lib1.PublicId, credential.ApplicationPurpose) - _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "", 1, []*target.CredentialLibrary{cl1}) + _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "", 1, + target.CredentialSources{ApplicationCredentialIds: []string{lib1.PublicId}}) require.Error(err) assert.Truef(errors.Match(errors.T(errors.InvalidParameter), err), "unexpected error %s", err.Error()) @@ -657,8 +808,8 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { t.Run("target-not-found", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - cl1 := target.TestNewCredentialLibrary("fake-target-id", lib1.PublicId, credential.ApplicationPurpose) - _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "fake-target-id", 1, []*target.CredentialLibrary{cl1}) + _, _, _, err := repo.SetTargetCredentialSources(context.Background(), "fake-target-id", 1, + target.CredentialSources{ApplicationCredentialIds: []string{lib1.PublicId}}) require.Error(err) assert.Truef(errors.Match(errors.T(errors.RecordNotFound), err), "unexpected error %s", err.Error()) diff --git a/internal/target/tcp/repository_tcp_target_test.go b/internal/target/tcp/repository_tcp_target_test.go index cb4bafec7..eaa690d48 100644 --- a/internal/target/tcp/repository_tcp_target_test.go +++ b/internal/target/tcp/repository_tcp_target_test.go @@ -465,7 +465,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { for _, cl := range credSources { afterUpdateIds = append(afterUpdateIds, cl.Id()) } - assert.Equal(testClIds, afterUpdateIds) + assert.ElementsMatch(testClIds, afterUpdateIds) switch tt.name { case "valid-no-op": diff --git a/internal/target/tcp/testing.go b/internal/target/tcp/testing.go index 0e73faa38..be1647348 100644 --- a/internal/target/tcp/testing.go +++ b/internal/target/tcp/testing.go @@ -45,6 +45,15 @@ func TestTarget(ctx context.Context, t testing.TB, conn *db.DB, scopeId, name st err := rw.CreateItems(context.Background(), newCredLibs) require.NoError(err) } + if len(opts.WithStaticCredentials) > 0 { + newCreds := make([]interface{}, 0, len(opts.WithStaticCredentials)) + for _, c := range opts.WithStaticCredentials { + c.TargetId = tar.GetPublicId() + newCreds = append(newCreds, c) + } + err := rw.CreateItems(context.Background(), newCreds) + require.NoError(err) + } return tar } diff --git a/internal/target/tcp/testing_test.go b/internal/target/tcp/testing_test.go index 9716a3f52..bcc402f5a 100644 --- a/internal/target/tcp/testing_test.go +++ b/internal/target/tcp/testing_test.go @@ -45,7 +45,7 @@ func Test_TestTcpTarget(t *testing.T) { for _, s := range foundSources { foundIds = append(foundIds, s.Id()) } - require.Equal(sets, foundIds) + require.ElementsMatch(sets, foundIds) } func Test_TestCredentialLibrary(t *testing.T) { @@ -80,5 +80,5 @@ func Test_TestCredentialLibrary(t *testing.T) { for _, s := range foundSources { foundIds = append(foundIds, s.Id()) } - require.Equal(libIds, foundIds) + require.ElementsMatch(libIds, foundIds) } diff --git a/internal/target/testing.go b/internal/target/testing.go index 964a66dd1..b7776733d 100644 --- a/internal/target/testing.go +++ b/internal/target/testing.go @@ -23,6 +23,19 @@ func TestNewCredentialLibrary(targetId, credentialLibraryId string, purpose cred } } +// TestNewStaticCredential creates a new in memory StaticCredential +// representing the relationship between targetId and credentialId with +// the given purpose. +func TestNewStaticCredential(targetId, credentialId string, purpose credential.Purpose) *StaticCredential { + return &StaticCredential{ + StaticCredential: &store.StaticCredential{ + TargetId: targetId, + CredentialId: credentialId, + CredentialPurpose: string(purpose), + }, + } +} + // TestCredentialLibrary creates a CredentialLibrary for targetId and // libraryId with the credential purpose of application. func TestCredentialLibrary(t testing.TB, conn *db.DB, targetId, libraryId string) *CredentialLibrary { diff --git a/internal/tests/api/targets/target_test.go b/internal/tests/api/targets/target_test.go index 9943b702f..d6b4810e5 100644 --- a/internal/tests/api/targets/target_test.go +++ b/internal/tests/api/targets/target_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/boundary/api" "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/hostsets" @@ -66,11 +67,7 @@ func TestHostSetASD(t *testing.T) { assert.Empty(tar.Item.HostSetIds) } -func defaultCredLibrary(r *credentiallibraries.CredentialLibrary) *targets.CredentialLibrary { - return &targets.CredentialLibrary{Id: r.Id, CredentialStoreId: r.CredentialStoreId} -} - -func TestCredentialLibraryASD(t *testing.T) { +func TestCredentialSourcesASD(t *testing.T) { assert, require := assert.New(t), require.New(t) tc := controller.NewTestController(t, nil) defer tc.Shutdown() @@ -82,44 +79,85 @@ func TestCredentialLibraryASD(t *testing.T) { client := tc.Client().Clone() client.SetToken(token.Token) - cs, err := credentialstores.NewClient(client).Create(tc.Context(), "vault", proj.GetPublicId(), + csVault, err := credentialstores.NewClient(client).Create(tc.Context(), "vault", proj.GetPublicId(), credentialstores.WithVaultCredentialStoreAddress(vaultServ.Addr), credentialstores.WithVaultCredentialStoreToken(vaultTok)) require.NoError(err) - require.NotNil(cs) + require.NotNil(csVault) lClient := credentiallibraries.NewClient(client) - r1, err := lClient.Create(tc.Context(), cs.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something1")) + lib1, err := lClient.Create(tc.Context(), csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something1")) require.NoError(err) - require.NotNil(r1) + require.NotNil(lib1) - r2, err := lClient.Create(tc.Context(), cs.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something2")) + lib2, err := lClient.Create(tc.Context(), csVault.Item.Id, credentiallibraries.WithVaultCredentialLibraryPath("something2")) require.NoError(err) - require.NotNil(r1) + 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.ApplicationCredentialLibraryIds) + assert.Empty(tar.Item.ApplicationCredentialSourceIds) - tar, err = tarClient.AddCredentialLibraries(tc.Context(), tar.Item.Id, tar.Item.Version, targets.WithApplicationCredentialLibraryIds([]string{r1.Item.Id})) + // Add first Vault library + tar, err = tarClient.AddCredentialSources(tc.Context(), tar.Item.Id, tar.Item.Version, + targets.WithApplicationCredentialSourceIds([]string{lib1.Item.Id})) require.NoError(err) require.NotNil(tar) - assert.ElementsMatch(tar.Item.ApplicationCredentialLibraryIds, []string{r1.Item.Id}) - assert.ElementsMatch(tar.Item.ApplicationCredentialLibraries, []*targets.CredentialLibrary{defaultCredLibrary(r1.Item)}) + assert.ElementsMatch(tar.Item.ApplicationCredentialSourceIds, []string{lib1.Item.Id}) + assert.ElementsMatch(tar.Item.ApplicationCredentialSources, []*targets.CredentialSource{ + { + Id: lib1.Item.Id, + CredentialStoreId: csVault.Item.Id, + }, + }) - tar, err = tarClient.SetCredentialLibraries(tc.Context(), tar.Item.Id, tar.Item.Version, - targets.WithApplicationCredentialLibraryIds([]string{r2.Item.Id})) + // Set second Vault library and a static credential + tar, err = tarClient.SetCredentialSources(tc.Context(), tar.Item.Id, tar.Item.Version, + targets.WithApplicationCredentialSourceIds([]string{lib2.Item.Id, cred.Item.Id})) require.NoError(err) require.NotNil(tar) - assert.ElementsMatch(tar.Item.ApplicationCredentialLibraryIds, []string{r2.Item.Id}) - assert.ElementsMatch(tar.Item.ApplicationCredentialLibraries, []*targets.CredentialLibrary{defaultCredLibrary(r2.Item)}) + assert.ElementsMatch(tar.Item.ApplicationCredentialSourceIds, []string{lib2.Item.Id, cred.Item.Id}) + assert.ElementsMatch(tar.Item.ApplicationCredentialSources, []*targets.CredentialSource{ + { + Id: lib2.Item.Id, + CredentialStoreId: csVault.Item.Id, + }, + { + Id: cred.Item.Id, + CredentialStoreId: csStatic.Item.Id, + }, + }) - tar, err = tarClient.RemoveCredentialLibraries(tc.Context(), tar.Item.Id, tar.Item.Version, - targets.WithApplicationCredentialLibraryIds([]string{r2.Item.Id})) + // Remove second Vault library + tar, err = tarClient.RemoveCredentialSources(tc.Context(), tar.Item.Id, tar.Item.Version, + targets.WithApplicationCredentialSourceIds([]string{lib2.Item.Id})) require.NoError(err) require.NotNil(tar) - assert.Empty(tar.Item.ApplicationCredentialLibraryIds) + assert.ElementsMatch(tar.Item.ApplicationCredentialSourceIds, []string{cred.Item.Id}) + assert.ElementsMatch(tar.Item.ApplicationCredentialSources, []*targets.CredentialSource{ + { + Id: cred.Item.Id, + CredentialStoreId: csStatic.Item.Id, + }, + }) + + // Set empty credential sources + tar, err = tarClient.SetCredentialSources(tc.Context(), tar.Item.Id, tar.Item.Version, + targets.WithApplicationCredentialSourceIds([]string{})) + require.NoError(err) + require.NotNil(tar) + assert.Empty(tar.Item.ApplicationCredentialSourceIds) } func TestList(t *testing.T) { diff --git a/internal/tests/cli/boundary/_target_credential_sources.bash b/internal/tests/cli/boundary/_target_credential_sources.bash new file mode 100644 index 000000000..b2097ca75 --- /dev/null +++ b/internal/tests/cli/boundary/_target_credential_sources.bash @@ -0,0 +1,52 @@ +load _authorized_actions + +function add_target_application_credential_sources() { + for i in "${@:2}" + do + cred+="-application-credential-source $i " + done + + boundary targets add-credential-sources -id $1 $cred +} + +function remove_target_application_credential_sources() { + for i in "${@:2}" + do + cred+="-application-credential-source $i " + done + + boundary targets remove-credential-sources -id $1 $cred +} + +function set_target_application_credential_sources() { + for i in "${@:2}" + do + cred+="-application-credential-source $i " + done + + boundary targets set-credential-sources -id $1 $cred +} + +function validate_credential_sources() { + targetData=$(boundary targets read -id $1 -format json) + for i in "${@:2}" + do + if ! echo "$targetData" | grep -q $i; then + echo "Credential source id '$i' not found on target" + echo "$targetData" + exit 1 + fi + done +} + +function validate_credential_sources_not_present() { + targetData=$(boundary targets read -id $1 -format json) + for i in "${@:2}" + do + if echo "$targetData" | grep -q $i; then + echo "Credential source id '$i' unexpectedly found on target" + echo "$targetData" + exit 1 + fi + done +} diff --git a/internal/tests/cli/boundary/target_credential_sources.bats b/internal/tests/cli/boundary/target_credential_sources.bats new file mode 100644 index 000000000..4da9204e8 --- /dev/null +++ b/internal/tests/cli/boundary/target_credential_sources.bats @@ -0,0 +1,174 @@ +#!/usr/bin/env bats + +load _auth +load _connect +load _targets +load _helpers +load _credential_stores +load _credentials +load _target_credential_sources + +export NEW_STORE='test-for-add-credential-sources' +export NEW_CREDENTIAL='first-credential' +export NEW_CREDENTIAL1='second-credential' +export NEW_CREDENTIAL2='third-credential' + +@test "boundary/login: can login as admin user" { + run login $DEFAULT_LOGIN + [ "$status" -eq 0 ] +} + +@test "boundary/credential-stores: can create $NEW_STORE static store in default project" { + run create_static_credential_store $NEW_STORE $DEFAULT_P_ID + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/credentials: can create $NEW_CREDENTIAL credential in $NEW_STORE store" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + run create_username_password_credential $NEW_CREDENTIAL $csid 'username' 'password' + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: can add $NEW_CREDENTIAL credential source" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + run add_target_application_credential_sources $DEFAULT_TARGET $cid + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: validate only $NEW_CREDENTIAL credential source present" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + run validate_credential_sources $DEFAULT_TARGET $cid + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: cannot add duplicate credential source" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + run add_target_application_credential_sources $DEFAULT_TARGET $cid + echo "$output" + [ "$status" -eq 1 ] +} + +@test "boundary/target: can delete $NEW_CREDENTIAL credential source" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + run remove_target_application_credential_sources $DEFAULT_TARGET $cid + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: validate $NEW_CREDENTIAL credential source removed" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + run validate_credential_sources_not_present $DEFAULT_TARGET $cid + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/credentials: can create additional credentials in $NEW_STORE store" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + run create_username_password_credential $NEW_CREDENTIAL1 $csid 'username' 'password' + echo "$output" + [ "$status" -eq 0 ] + + run create_username_password_credential $NEW_CREDENTIAL2 $csid 'username' 'password' + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: can add multiple credential sources" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + local cid1=$(credential_id $NEW_CREDENTIAL1 $csid) + local cid2=$(credential_id $NEW_CREDENTIAL2 $csid) + run add_target_application_credential_sources $DEFAULT_TARGET $cid $cid1 $cid2 + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: validate added credential sources present" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + local cid1=$(credential_id $NEW_CREDENTIAL1 $csid) + local cid2=$(credential_id $NEW_CREDENTIAL2 $csid) + run validate_credential_sources $DEFAULT_TARGET $cid $cid1 $cid2 + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: can delete multiple credential sources" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + local cid1=$(credential_id $NEW_CREDENTIAL1 $csid) + run remove_target_application_credential_sources $DEFAULT_TARGET $cid $cid1 + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: validate $NEW_CREDENTIAL2 credential source present" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid2=$(credential_id $NEW_CREDENTIAL2 $csid) + run validate_credential_sources $DEFAULT_TARGET $cid2 + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: validate deleted credential sources not present" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + local cid1=$(credential_id $NEW_CREDENTIAL $csid) + run validate_credential_sources_not_present $DEFAULT_TARGET $cid $cid1 + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: can set multiple credential sources" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + local cid1=$(credential_id $NEW_CREDENTIAL1 $csid) + local cid2=$(credential_id $NEW_CREDENTIAL2 $csid) + run set_target_application_credential_sources $DEFAULT_TARGET $cid $cid1 $cid2 + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: validate set credential sources present" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + local cid1=$(credential_id $NEW_CREDENTIAL1 $csid) + local cid2=$(credential_id $NEW_CREDENTIAL2 $csid) + run validate_credential_sources $DEFAULT_TARGET $cid2 + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: can set just $NEW_CREDENTIAL credential source" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + run set_target_application_credential_sources $DEFAULT_TARGET $cid + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: validate $NEW_CREDENTIAL credential source present" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid=$(credential_id $NEW_CREDENTIAL $csid) + run validate_credential_sources $DEFAULT_TARGET $cid + echo "$output" + [ "$status" -eq 0 ] +} + +@test "boundary/target: validate not set credential sources not present" { + local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) + local cid1=$(credential_id $NEW_CREDENTIAL1 $csid) + local cid2=$(credential_id $NEW_CREDENTIAL2 $csid) + run validate_credential_sources_not_present $DEFAULT_TARGET $cid1 $cid2 + echo "$output" + [ "$status" -eq 0 ] +} From 58d9d42a8858c54623b8f628c7a61939564bf6dd Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Thu, 9 Jun 2022 15:32:48 -0700 Subject: [PATCH 09/15] feat(session): Add support for session static credentials --- .../oss/postgres/50/03_session.up.sql | 32 +++++++++++++ internal/session/repository_session.go | 36 +++++++++++++-- internal/session/repository_session_test.go | 29 +++++++++--- internal/session/session.go | 16 +++++++ internal/session/session_test.go | 2 + internal/session/static_credential.go | 45 +++++++++++++++++++ 6 files changed, 151 insertions(+), 9 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/50/03_session.up.sql create mode 100644 internal/session/static_credential.go diff --git a/internal/db/schema/migrations/oss/postgres/50/03_session.up.sql b/internal/db/schema/migrations/oss/postgres/50/03_session.up.sql new file mode 100644 index 000000000..71bbdad95 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/50/03_session.up.sql @@ -0,0 +1,32 @@ +begin; + + create table session_credential_static ( + session_id wt_public_id not null + constraint session_fkey + references session (public_id) + on delete cascade + on update cascade, + credential_static_id wt_public_id + constraint credential_static_fkey + references credential_static (public_id) + on delete cascade + on update cascade, + credential_purpose text not null + constraint credential_purpose_fkey + references credential_purpose_enm (name) + on delete restrict + on update cascade, + primary key(session_id, credential_static_id, credential_purpose), + create_time wt_timestamp + ); + comment on table session_credential_dynamic is + 'session_credential_static is a join table between the session and static credential tables. ' + 'It also contains the credential purpose the relationship represents.'; + + create trigger default_create_time_column before insert on session_credential_static + for each row execute procedure default_create_time(); + + create trigger immutable_columns before update on session_credential_static + for each row execute procedure immutable_columns('session_id', 'credential_static_id', 'credential_purpose', 'create_time'); + +commit; diff --git a/internal/session/repository_session.go b/internal/session/repository_session.go index 07cfd76fb..b225409fc 100644 --- a/internal/session/repository_session.go +++ b/internal/session/repository_session.go @@ -93,6 +93,7 @@ func (r *Repository) CreateSession(ctx context.Context, sessionWrapper wrapping. func(read db.Reader, w db.Writer) error { returnedSession = newSession.Clone().(*Session) returnedSession.DynamicCredentials = nil + returnedSession.StaticCredentials = nil if err = w.Create(ctx, returnedSession); err != nil { return errors.Wrap(ctx, err, op) } @@ -101,6 +102,25 @@ func (r *Repository) CreateSession(ctx context.Context, sessionWrapper wrapping. cred.SessionId = newSession.PublicId } + var staticCreds []interface{} + for _, cred := range newSession.StaticCredentials { + cred.SessionId = newSession.PublicId + staticCreds = append(staticCreds, cred) + } + + if len(staticCreds) > 0 { + if err = w.CreateItems(ctx, staticCreds); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("failed to create static credentials")) + } + + // Get static creds back from the db for return + var c []*StaticCredential + if err := read.SearchWhere(ctx, &c, "session_id = ?", []interface{}{newSession.PublicId}); err != nil { + return errors.Wrap(ctx, err, op) + } + returnedSession.StaticCredentials = c + } + // TODO: after upgrading to gorm v2 this batch insert can be replaced, since gorm v2 supports batch inserts q, batchInsertArgs, err := batchInsertsessionCredentialDynamic(newSession.DynamicCredentials) if err == nil { @@ -165,12 +185,20 @@ func (r *Repository) LookupSession(ctx context.Context, sessionId string, _ ...O } session.States = states - var creds []*DynamicCredential - if err := read.SearchWhere(ctx, &creds, "session_id = ?", []interface{}{sessionId}); err != nil { + var dynamicCreds []*DynamicCredential + if err := read.SearchWhere(ctx, &dynamicCreds, "session_id = ?", []interface{}{sessionId}); err != nil { return errors.Wrap(ctx, err, op) } - if len(creds) > 0 { - session.DynamicCredentials = creds + if len(dynamicCreds) > 0 { + session.DynamicCredentials = dynamicCreds + } + + var staticCreds []*StaticCredential + if err := read.SearchWhere(ctx, &staticCreds, "session_id = ?", []interface{}{sessionId}); err != nil { + return errors.Wrap(ctx, err, op) + } + if len(staticCreds) > 0 { + session.StaticCredentials = staticCreds } connections, err := fetchConnections(ctx, read, sessionId, db.WithOrder("create_time desc")) diff --git a/internal/session/repository_session_test.go b/internal/session/repository_session_test.go index 9ecf7a6ca..3a259348c 100644 --- a/internal/session/repository_session_test.go +++ b/internal/session/repository_session_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/boundary/internal/authtoken" authtokenStore "github.com/hashicorp/boundary/internal/authtoken/store" cred "github.com/hashicorp/boundary/internal/credential" + credstatic "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/timestamp" @@ -406,6 +407,7 @@ func TestRepository_CreateSession(t *testing.T) { ExpirationTime: tt.args.composedOf.ExpirationTime, ConnectionLimit: tt.args.composedOf.ConnectionLimit, DynamicCredentials: tt.args.composedOf.DynamicCredentials, + StaticCredentials: tt.args.composedOf.StaticCredentials, } ses, privKey, err := repo.CreateSession(context.Background(), wrapper, s, tt.args.workerAddresses) if tt.wantErr { @@ -425,6 +427,7 @@ func TestRepository_CreateSession(t *testing.T) { require.NoError(err) assert.Equal(keyId, ses.KeyId) assert.Len(ses.DynamicCredentials, len(s.DynamicCredentials)) + assert.Len(ses.StaticCredentials, len(s.StaticCredentials)) foundSession, _, err := repo.LookupSession(context.Background(), ses.PublicId) assert.NoError(err) assert.Equal(keyId, foundSession.KeyId) @@ -447,6 +450,13 @@ func TestRepository_CreateSession(t *testing.T) { assert.NotEmpty(cred.LibraryId) assert.NotEmpty(cred.CredentialPurpose) } + + assert.Equal(s.StaticCredentials, foundSession.StaticCredentials) + for _, cred := range foundSession.StaticCredentials { + assert.NotEmpty(cred.CredentialStaticId) + assert.NotEmpty(cred.SessionId) + assert.NotEmpty(cred.CredentialPurpose) + } }) } } @@ -1440,19 +1450,28 @@ func testSessionCredentialParams(t *testing.T, conn *db.DB, wrapper wrapping.Wra require.NoError(err) require.NotNil(tar) - stores := vault.TestCredentialStores(t, conn, wrapper, params.ScopeId, 1) - libIds := vault.TestCredentialLibraries(t, conn, wrapper, stores[0].GetPublicId(), 2) + vaultStore := vault.TestCredentialStores(t, conn, wrapper, params.ScopeId, 1)[0] + libIds := vault.TestCredentialLibraries(t, conn, wrapper, vaultStore.GetPublicId(), 2) + + staticStore := credstatic.TestCredentialStore(t, conn, wrapper, params.ScopeId) + upCreds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, "u", "p", staticStore.GetPublicId(), params.ScopeId, 2) ids := target.CredentialSources{ - ApplicationCredentialIds: []string{libIds[0].GetPublicId(), libIds[1].GetPublicId()}, + ApplicationCredentialIds: []string{libIds[0].GetPublicId(), libIds[1].GetPublicId(), upCreds[0].GetPublicId(), upCreds[1].GetPublicId()}, } _, _, _, err = targetRepo.AddTargetCredentialSources(ctx, tar.GetPublicId(), tar.GetVersion(), ids) require.NoError(err) - creds := []*DynamicCredential{ + dynamicCreds := []*DynamicCredential{ NewDynamicCredential(libIds[0].GetPublicId(), cred.ApplicationPurpose), NewDynamicCredential(libIds[1].GetPublicId(), cred.ApplicationPurpose), } - params.DynamicCredentials = creds + params.DynamicCredentials = dynamicCreds + + staticCreds := []*StaticCredential{ + NewStaticCredential(upCreds[0].GetPublicId(), cred.ApplicationPurpose), + NewStaticCredential(upCreds[1].GetPublicId(), cred.ApplicationPurpose), + } + params.StaticCredentials = staticCreds return params } diff --git a/internal/session/session.go b/internal/session/session.go index 5d6faf74a..f497b3728 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -54,6 +54,9 @@ type ComposedOf struct { // DynamicCredentials are dynamic credentials that will be retrieved // for the session. DynamicCredentials optional. DynamicCredentials []*DynamicCredential + // StaticCredentials are static credentials that will be retrieved + // for the session. StaticCredentials optional. + StaticCredentials []*StaticCredential } // Session contains information about a user's session with a target @@ -114,6 +117,9 @@ type Session struct { // DynamicCredentials for the session. DynamicCredentials []*DynamicCredential `gorm:"-"` + // StaticCredentials for the session. + StaticCredentials []*StaticCredential `gorm:"-"` + // Connections for the session are for read only and are ignored during write operations Connections []*Connection `gorm:"-"` @@ -153,6 +159,7 @@ func New(c ComposedOf, _ ...Option) (*Session, error) { ConnectionLimit: c.ConnectionLimit, WorkerFilter: c.WorkerFilter, DynamicCredentials: c.DynamicCredentials, + StaticCredentials: c.StaticCredentials, } if err := s.validateNewSession(); err != nil { return nil, errors.WrapDeprecated(err, op) @@ -198,6 +205,13 @@ func (s *Session) Clone() interface{} { clone.DynamicCredentials = append(clone.DynamicCredentials, cp) } } + if len(s.StaticCredentials) > 0 { + clone.StaticCredentials = make([]*StaticCredential, 0, len(s.StaticCredentials)) + for _, sc := range s.StaticCredentials { + cp := sc.clone() + clone.StaticCredentials = append(clone.StaticCredentials, cp) + } + } if s.TofuToken != nil { clone.TofuToken = make([]byte, len(s.TofuToken)) copy(clone.TofuToken, s.TofuToken) @@ -283,6 +297,8 @@ func (s *Session) VetForWrite(ctx context.Context, _ db.Reader, opType db.OpType return errors.New(ctx, errors.InvalidParameter, op, "worker filter is immutable") case contains(opts.WithFieldMaskPaths, "DynamicCredentials"): return errors.New(ctx, errors.InvalidParameter, op, "dynamic credentials are immutable") + case contains(opts.WithFieldMaskPaths, "StaticCredentials"): + return errors.New(ctx, errors.InvalidParameter, op, "static credentials are immutable") case contains(opts.WithFieldMaskPaths, "TerminationReason"): if _, err := convertToReason(s.TerminationReason); err != nil { return errors.Wrap(ctx, err, op) diff --git a/internal/session/session_test.go b/internal/session/session_test.go index a5699f14d..1d33d9a43 100644 --- a/internal/session/session_test.go +++ b/internal/session/session_test.go @@ -58,6 +58,7 @@ func TestSession_Create(t *testing.T) { ExpirationTime: composedOf.ExpirationTime, ConnectionLimit: composedOf.ConnectionLimit, DynamicCredentials: composedOf.DynamicCredentials, + StaticCredentials: composedOf.StaticCredentials, }, create: true, }, @@ -158,6 +159,7 @@ func TestSession_Create(t *testing.T) { ExpirationTime: composedOf.ExpirationTime, ConnectionLimit: composedOf.ConnectionLimit, DynamicCredentials: composedOf.DynamicCredentials, + StaticCredentials: composedOf.StaticCredentials, }, wantAddrErr: true, wantIsErr: errors.InvalidParameter, diff --git a/internal/session/static_credential.go b/internal/session/static_credential.go new file mode 100644 index 000000000..4cae6a1f7 --- /dev/null +++ b/internal/session/static_credential.go @@ -0,0 +1,45 @@ +package session + +import ( + cred "github.com/hashicorp/boundary/internal/credential" +) + +// A StaticCredential represents the relationship between a session, a +// credential and the purpose of the credential. +type StaticCredential struct { + SessionId string `json:"session_id,omitempty" gorm:"primary_key"` + CredentialPurpose string `json:"credential_purpose,omitempty" gorm:"primary_key"` + CredentialStaticId string `json:"credential_id,omitempty" gorm:"default:null"` + + tableName string `gorm:"-"` +} + +// NewStaticCredential creates a new in memory Credential representing the +// relationship between session a credential and the purpose of the credential. +func NewStaticCredential(id string, purpose cred.Purpose) *StaticCredential { + return &StaticCredential{ + CredentialStaticId: id, + CredentialPurpose: string(purpose), + } +} + +func (c *StaticCredential) clone() *StaticCredential { + return &StaticCredential{ + SessionId: c.SessionId, + CredentialPurpose: c.CredentialPurpose, + CredentialStaticId: c.CredentialStaticId, + } +} + +// TableName returns the table name. +func (c *StaticCredential) TableName() string { + if c.tableName != "" { + return c.tableName + } + return "session_credential_static" +} + +// SetTableName sets the table name. +func (c *StaticCredential) SetTableName(n string) { + c.tableName = n +} From c14fe050364a6992dfe35b2bbfd2d66260dd23da Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Thu, 9 Jun 2022 15:35:34 -0700 Subject: [PATCH 10/15] feat(credential): Add support for retrieving static credentials --- .../static/repository_credentials.go | 45 ++++++++ .../static/repository_credentials_test.go | 106 ++++++++++++++++++ .../static/usernamepassword_credential.go | 2 + 3 files changed, 153 insertions(+) create mode 100644 internal/credential/static/repository_credentials.go create mode 100644 internal/credential/static/repository_credentials_test.go diff --git a/internal/credential/static/repository_credentials.go b/internal/credential/static/repository_credentials.go new file mode 100644 index 000000000..de3edae83 --- /dev/null +++ b/internal/credential/static/repository_credentials.go @@ -0,0 +1,45 @@ +package static + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" +) + +// Retrieve retrieves and returns static credentials from Boundary for all the provided +// ids. All the returned static credentials will have their secret fields decrypted. +func (r *Repository) Retrieve(ctx context.Context, scopeId string, ids []string) ([]credential.Static, error) { + const op = "static.(Repository).Retrieve" + if len(ids) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "no ids") + } + + var creds []*UsernamePasswordCredential + err := r.reader.SearchWhere(ctx, &creds, "public_id in (?)", []interface{}{ids}) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + if len(creds) != len(ids) { + return nil, errors.New(ctx, errors.NotSpecificIntegrity, op, + fmt.Sprintf("mismatch between creds and number of ids requested, expected %d got %d", len(ids), len(creds))) + } + + out := make([]credential.Static, 0, len(ids)) + for _, c := range creds { + // decrypt credential + databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper")) + } + if err := c.decrypt(ctx, databaseWrapper); err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + out = append(out, c) + } + + return out, nil +} diff --git a/internal/credential/static/repository_credentials_test.go b/internal/credential/static/repository_credentials_test.go new file mode 100644 index 000000000..64374090a --- /dev/null +++ b/internal/credential/static/repository_credentials_test.go @@ -0,0 +1,106 @@ +package static + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/boundary/internal/credential" + "github.com/hashicorp/boundary/internal/credential/static/store" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepository_Retrieve(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + assert, require := assert.New(t), require.New(t) + repo, err := NewRepository(context.Background(), rw, rw, kms) + assert.NoError(err) + require.NotNil(repo) + + org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + staticStore := TestCredentialStore(t, conn, wrapper, prj.GetPublicId()) + cred1 := TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", staticStore.GetPublicId(), prj.GetPublicId()) + cred2 := TestUsernamePasswordCredential(t, conn, wrapper, "different user", "better password", staticStore.GetPublicId(), prj.GetPublicId()) + cred3 := TestUsernamePasswordCredential(t, conn, wrapper, "final user", "horrible password", staticStore.GetPublicId(), prj.GetPublicId()) + + type args struct { + credIds []string + scopeId string + } + tests := []struct { + name string + args args + wantErr bool + wantCreds []credential.Static + }{ + { + name: "no-scope", + args: args{ + credIds: []string{cred1.GetPublicId()}, + }, + wantErr: true, + }, + { + name: "invalid-scope", + args: args{ + scopeId: org.GetPublicId(), + credIds: []string{cred1.GetPublicId()}, + }, + wantErr: true, + }, + { + name: "valid-one-cred", + args: args{ + scopeId: prj.GetPublicId(), + credIds: []string{cred1.GetPublicId()}, + }, + wantCreds: []credential.Static{ + cred1, + }, + }, + { + name: "valid-multiple-creds", + args: args{ + scopeId: prj.GetPublicId(), + credIds: []string{cred1.GetPublicId(), cred2.GetPublicId(), cred3.GetPublicId()}, + }, + wantCreds: []credential.Static{ + cred1, cred2, cred3, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + gotCreds, err := repo.Retrieve(context.Background(), tt.args.scopeId, tt.args.credIds) + if tt.wantErr { + require.Error(err) + assert.Nil(gotCreds) + return + } + require.NoError(err) + assert.Empty( + cmp.Diff( + tt.wantCreds, + gotCreds, + cmpopts.IgnoreUnexported(UsernamePasswordCredential{}, store.UsernamePasswordCredential{}), + cmpopts.IgnoreTypes(×tamp.Timestamp{}), + cmpopts.SortSlices(func(x, y credential.Static) bool { + return x.GetPublicId() < y.GetPublicId() + }), + ), + ) + }) + } +} diff --git a/internal/credential/static/usernamepassword_credential.go b/internal/credential/static/usernamepassword_credential.go index b5ad2fd73..61f5f498c 100644 --- a/internal/credential/static/usernamepassword_credential.go +++ b/internal/credential/static/usernamepassword_credential.go @@ -13,6 +13,8 @@ import ( "google.golang.org/protobuf/proto" ) +var _ credential.Static = (*UsernamePasswordCredential)(nil) + // A UsernamePasswordCredential contains the credential with a username and password. // It is owned by a credential store. type UsernamePasswordCredential struct { From 4b365db6341889a919a842b73b3ca8aa7300c650 Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Thu, 9 Jun 2022 15:35:55 -0700 Subject: [PATCH 11/15] feat(target): Add support for static credentials during authorize-session --- internal/cmd/commands/connect/funcs.go | 2 +- internal/cmd/commands/targetscmd/funcs.go | 2 +- internal/daemon/controller/handler.go | 3 +- .../handlers/targets/credentials.go | 198 ++++++++++++ .../handlers/targets/target_service.go | 191 +++++------- .../targets/tcp/target_service_test.go | 284 ++++++++++++------ .../oss/postgres/50/02_target.up.sql | 6 +- .../storage/target/store/v1/target.proto | 4 + internal/target/credential_source.go | 17 +- .../target/repository_credential_source.go | 14 +- internal/target/store/target.pb.go | 34 ++- 11 files changed, 519 insertions(+), 236 deletions(-) create mode 100644 internal/daemon/controller/handlers/targets/credentials.go diff --git a/internal/cmd/commands/connect/funcs.go b/internal/cmd/commands/connect/funcs.go index 3cf884b58..f44c789a5 100644 --- a/internal/cmd/commands/connect/funcs.go +++ b/internal/cmd/commands/connect/funcs.go @@ -78,7 +78,7 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string { prefixStr := strings.Repeat(" ", indent) origSecret := []string{fmt.Sprintf("%s %s", prefixStr, sc.Secret.Raw)} switch sc.CredentialSource.Type { - case "vault": + case "vault", "static": if sc.Credential != nil { maxLength := 0 for k := range sc.Credential { diff --git a/internal/cmd/commands/targetscmd/funcs.go b/internal/cmd/commands/targetscmd/funcs.go index 385c2c174..77f8fed40 100644 --- a/internal/cmd/commands/targetscmd/funcs.go +++ b/internal/cmd/commands/targetscmd/funcs.go @@ -888,7 +888,7 @@ func printCustomActionOutputImpl(c *Command) (bool, error) { var secretStr []string switch cred.CredentialSource.Type { - case "vault": + case "vault", "static": switch { case cred.Credential != nil: maxLength := 0 diff --git a/internal/daemon/controller/handler.go b/internal/daemon/controller/handler.go index 0c9c0f973..54494928b 100644 --- a/internal/daemon/controller/handler.go +++ b/internal/daemon/controller/handler.go @@ -175,7 +175,8 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error { c.SessionRepoFn, c.PluginHostRepoFn, c.StaticHostRepoFn, - c.VaultCredentialRepoFn) + c.VaultCredentialRepoFn, + c.StaticCredentialRepoFn) if err != nil { return fmt.Errorf("failed to create target handler service: %w", err) } diff --git a/internal/daemon/controller/handlers/targets/credentials.go b/internal/daemon/controller/handlers/targets/credentials.go new file mode 100644 index 000000000..a67977b9e --- /dev/null +++ b/internal/daemon/controller/handlers/targets/credentials.go @@ -0,0 +1,198 @@ +package targets + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + + "github.com/hashicorp/boundary/internal/credential" + credstatic "github.com/hashicorp/boundary/internal/credential/static" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers" + "github.com/hashicorp/boundary/internal/errors" + serverpb "github.com/hashicorp/boundary/internal/gen/controller/servers/services" + "github.com/hashicorp/boundary/internal/session" + "github.com/hashicorp/boundary/internal/types/subtypes" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" +) + +// dynamicToWorkerCredential converts the strongly typed credential.Dynamic into +// a session.Credential suitable for passing to a Boundary worker. +func dynamicToWorkerCredential(ctx context.Context, cred credential.Dynamic) (session.Credential, error) { + const op = "targets.dynamicToWorkerCredential" + var workerCred *serverpb.Credential + switch c := cred.(type) { + case credential.UsernamePassword: + workerCred = &serverpb.Credential{ + Credential: &serverpb.Credential_UserPassword{ + UserPassword: &serverpb.UserPassword{ + Username: c.Username(), + Password: string(c.Password()), + }, + }, + } + + default: + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c)) + } + + data, err := proto.Marshal(workerCred) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling dynamic secret to proto")) + } + return data, nil +} + +func dynamicToSessionCredential(ctx context.Context, cred credential.Dynamic) (*pb.SessionCredential, error) { + const op = "targets.dynamicToSessionCredential" + l := cred.Library() + secret := cred.Secret() + // TODO: Access the json directly from the vault response instead of re-marshalling it. + jSecret, err := json.Marshal(secret) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling secret to json")) + } + var sSecret *structpb.Struct + switch secret.(type) { + case map[string]interface{}: + // In this case we actually have to re-decode it. The proto wrappers + // choke on json.Number and at the time I'm writing this I don't + // have time to write a walk function to dig through with reflect + // and find all json.Numbers and replace them. So we eat the + // inefficiency. So note that we are specifically _not_ using a + // decoder with UseNumber here. + var dSecret map[string]interface{} + if err := json.Unmarshal(jSecret, &dSecret); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("decoding json for proto marshaling")) + } + sSecret, err = structpb.NewStruct(dSecret) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for secret")) + } + } + + var credType string + var credData *structpb.Struct + if l.CredentialType() != credential.UnspecifiedType { + credType = string(l.CredentialType()) + + switch c := cred.(type) { + case credential.UsernamePassword: + credData, err = handlers.ProtoToStruct( + &pb.UserPasswordCredential{ + Username: c.Username(), + Password: string(c.Password()), + }, + ) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential")) + } + + default: + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c)) + } + } + + return &pb.SessionCredential{ + CredentialLibrary: &pb.CredentialLibrary{ + Id: l.GetPublicId(), + Name: l.GetName(), + Description: l.GetDescription(), + CredentialStoreId: l.GetStoreId(), + Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(), + }, + CredentialSource: &pb.CredentialSource{ + Id: l.GetPublicId(), + Name: l.GetName(), + Description: l.GetDescription(), + CredentialStoreId: l.GetStoreId(), + Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(), + CredentialType: credType, + }, + Secret: &pb.SessionSecret{ + Raw: base64.StdEncoding.EncodeToString(jSecret), + Decoded: sSecret, + }, + Credential: credData, + }, nil +} + +// staticToWorkerCredential converts the credential.Static into +// a session.Credential suitable for passing to a Boundary worker. +func staticToWorkerCredential(ctx context.Context, cred credential.Static) (session.Credential, error) { + const op = "targets.staticToWorkerCredential" + var workerCred *serverpb.Credential + switch c := cred.(type) { + case credstatic.UsernamePasswordCredential: + workerCred = &serverpb.Credential{ + Credential: &serverpb.Credential_UserPassword{ + UserPassword: &serverpb.UserPassword{ + Username: c.GetUsername(), + Password: string(c.GetPassword()), + }, + }, + } + + default: + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c)) + } + + data, err := proto.Marshal(workerCred) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling dynamic secret to proto")) + } + return data, nil +} + +func staticToSessionCredential(ctx context.Context, cred credential.Static) (*pb.SessionCredential, error) { + const op = "targets.staticToSessionCredential" + + var credType string + var credData *structpb.Struct + var secret map[string]interface{} + switch c := cred.(type) { + case *credstatic.UsernamePasswordCredential: + var err error + credType = string(credential.UsernamePasswordType) + credData, err = handlers.ProtoToStruct( + &pb.UserPasswordCredential{ + Username: c.GetUsername(), + Password: string(c.GetPassword()), + }, + ) + secret = map[string]interface{}{ + "username": c.GetUsername(), + "password": string(c.GetPassword()), + } + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for static credential")) + } + + default: + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c)) + } + + jSecret, err := json.Marshal(secret) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling static secret to json")) + } + sSecret, err := structpb.NewStruct(secret) + + return &pb.SessionCredential{ + CredentialSource: &pb.CredentialSource{ + Id: cred.GetPublicId(), + Name: cred.GetName(), + Description: cred.GetDescription(), + CredentialStoreId: cred.GetStoreId(), + Type: credstatic.Subtype.String(), + CredentialType: credType, + }, + Secret: &pb.SessionSecret{ + Raw: base64.StdEncoding.EncodeToString(jSecret), + Decoded: sSecret, + }, + Credential: credData, + }, nil +} diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index 69fb4b0d0..9c85c058e 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -2,8 +2,6 @@ package targets import ( "context" - "encoding/base64" - "encoding/json" stderrors "errors" "fmt" "math/rand" @@ -23,7 +21,6 @@ import ( "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" - serverpb "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/host" "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" @@ -44,7 +41,6 @@ import ( "github.com/mr-tron/base58" "google.golang.org/grpc/codes" "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -97,6 +93,7 @@ type Service struct { pluginHostRepoFn common.PluginHostRepoFactory staticHostRepoFn common.StaticRepoFactory vaultCredRepoFn common.VaultCredentialRepoFactory + staticCredRepoFn common.StaticCredentialRepoFactory kmsCache *kms.Kms } @@ -111,6 +108,7 @@ func NewService( pluginHostRepoFn common.PluginHostRepoFactory, staticHostRepoFn common.StaticRepoFactory, vaultCredRepoFn common.VaultCredentialRepoFactory, + staticCredRepoFn common.StaticCredentialRepoFactory, ) (Service, error) { const op = "targets.NewService" if repoFn == nil { @@ -134,6 +132,9 @@ func NewService( if vaultCredRepoFn == nil { return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing vault credential repository") } + if staticCredRepoFn == nil { + return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static credential repository") + } return Service{ repoFn: repoFn, iamRepoFn: iamRepoFn, @@ -142,6 +143,7 @@ func NewService( pluginHostRepoFn: pluginHostRepoFn, staticHostRepoFn: staticHostRepoFn, vaultCredRepoFn: vaultCredRepoFn, + staticCredRepoFn: staticCredRepoFn, kmsCache: kmsCache, }, nil } @@ -873,7 +875,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession if err != nil { return nil, err } - t, hostSources, libs, err := repo.LookupTarget(ctx, t.GetPublicId()) + t, hostSources, credSources, err := repo.LookupTarget(ctx, t.GetPublicId()) if err != nil { if errors.IsNotFoundError(err) { return nil, handlers.NotFoundErrorf("Target %q not found.", t.GetPublicId()) @@ -1049,14 +1051,22 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession Host: net.JoinHostPort(h, p), } - var reqs []credential.Request + var vaultReqs []credential.Request + var staticIds []string var dynCreds []*session.DynamicCredential - for _, l := range libs { - reqs = append(reqs, credential.Request{ - SourceId: l.Id(), - Purpose: l.CredentialPurpose(), - }) - dynCreds = append(dynCreds, session.NewDynamicCredential(l.Id(), l.CredentialPurpose())) + var staticCreds []*session.StaticCredential + for _, cs := range credSources { + switch cs.Type() { + case target.LibraryCredentialSourceType: + vaultReqs = append(vaultReqs, credential.Request{ + SourceId: cs.Id(), + Purpose: cs.CredentialPurpose(), + }) + dynCreds = append(dynCreds, session.NewDynamicCredential(cs.Id(), cs.CredentialPurpose())) + case target.StaticCredentialSourceType: + staticIds = append(staticIds, cs.Id()) + staticCreds = append(staticCreds, session.NewStaticCredential(cs.Id(), cs.CredentialPurpose())) + } } expTime := timestamppb.Now() @@ -1073,6 +1083,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession ConnectionLimit: t.GetSessionConnectionLimit(), WorkerFilter: t.GetWorkerFilter(), DynamicCredentials: dynCreds, + StaticCredentials: staticCreds, } sess, err := session.New(sessionComposition) @@ -1088,111 +1099,82 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession return nil, err } - var cs []credential.Dynamic - if len(reqs) > 0 { + var dynamic []credential.Dynamic + var staticCredsById map[string]credential.Static + if len(vaultReqs) > 0 { credRepo, err := s.vaultCredRepoFn() if err != nil { return nil, errors.Wrap(ctx, err, op) } - cs, err = credRepo.Issue(ctx, sess.GetPublicId(), reqs) + dynamic, err = credRepo.Issue(ctx, sess.GetPublicId(), vaultReqs) if err != nil { return nil, errors.Wrap(ctx, err, op) } } + if len(staticIds) > 0 { + credRepo, err := s.staticCredRepoFn() + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + // Remove duplicate requests + staticIds = strutil.RemoveDuplicates(staticIds, false) + creds, err := credRepo.Retrieve(ctx, t.GetScopeId(), staticIds) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + + staticCredsById = make(map[string]credential.Static) + for _, c := range creds { + staticCredsById[c.GetPublicId()] = c + } + } + var creds []*pb.SessionCredential var workerCreds []session.Credential - for _, cred := range cs { + for _, cred := range dynamic { switch cred.Purpose() { case credential.EgressPurpose: - m, err := credentialToProto(ctx, cred) + c, err := dynamicToWorkerCredential(ctx, cred) if err != nil { return nil, errors.Wrap(ctx, err, op) } - data, err := proto.Marshal(m) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling secret to proto")) - } - workerCreds = append(workerCreds, data) + workerCreds = append(workerCreds, c) case credential.ApplicationPurpose: - l := cred.Library() - secret := cred.Secret() - // TODO: Access the json directly from the vault response instead of re-marshalling it. - jSecret, err := json.Marshal(secret) + c, err := dynamicToSessionCredential(ctx, cred) if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("marshalling secret to json")) + return nil, errors.Wrap(ctx, err, op) } - var sSecret *structpb.Struct - switch secret.(type) { - case map[string]interface{}: - // In this case we actually have to re-decode it. The proto wrappers - // choke on json.Number and at the time I'm writing this I don't - // have time to write a walk function to dig through with reflect - // and find all json.Numbers and replace them. So we eat the - // inefficiency. So note that we are specifically _not_ using a - // decoder with UseNumber here. - var dSecret map[string]interface{} - if err := json.Unmarshal(jSecret, &dSecret); err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("decoding json for proto marshaling")) - } - sSecret, err = structpb.NewStruct(dSecret) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for secret")) - } - } - - var credType string - var credData *structpb.Struct - if l.CredentialType() != credential.UnspecifiedType { - credType = string(l.CredentialType()) - - switch c := cred.(type) { - case credential.UserPassword: - credData, err = handlers.ProtoToStruct( - &pb.UserPasswordCredential{ - Username: c.Username(), - Password: string(c.Password()), - }, - ) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("creating proto struct for credential")) - } - - default: - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unsupported credential type")) - } - - } - - creds = append(creds, &pb.SessionCredential{ - CredentialLibrary: &pb.CredentialLibrary{ - Id: l.GetPublicId(), - Name: l.GetName(), - Description: l.GetDescription(), - CredentialStoreId: l.GetStoreId(), - Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(), - }, - CredentialSource: &pb.CredentialSource{ - Id: l.GetPublicId(), - Name: l.GetName(), - Description: l.GetDescription(), - CredentialStoreId: l.GetStoreId(), - Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(), - CredentialType: credType, - }, - Secret: &pb.SessionSecret{ - Raw: base64.StdEncoding.EncodeToString(jSecret), - Decoded: sSecret, - }, - Credential: credData, - }) + creds = append(creds, c) default: return nil, errors.New(ctx, errors.Unknown, op, fmt.Sprintf("unsupported credential purpose %s", cred.Purpose())) } } + for _, sc := range staticCreds { + switch sc.CredentialPurpose { + case string(credential.EgressPurpose): + c, err := staticToWorkerCredential(ctx, staticCredsById[sc.CredentialStaticId]) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + workerCreds = append(workerCreds, c) + + case string(credential.ApplicationPurpose): + c, err := staticToSessionCredential(ctx, staticCredsById[sc.CredentialStaticId]) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + creds = append(creds, c) + + default: + return nil, errors.New(ctx, errors.Unknown, op, fmt.Sprintf("unsupported credential purpose %s", sc.CredentialPurpose)) + } + } + if len(workerCreds) > 0 { // store credentials in repo, worker will request creds when a connection is established err = sessionRepo.AddSessionCredentials(ctx, sess.ScopeId, sess.PublicId, workerCreds) @@ -1226,7 +1208,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession Scope: authResults.Scope, CreatedTime: sess.CreateTime.GetTimestamp(), Type: t.GetType().String(), - AuthorizationToken: string(encodedMarshaledSad), + AuthorizationToken: encodedMarshaledSad, UserId: authResults.UserId, HostId: chosenEndpoint.HostId, HostSetId: chosenEndpoint.SetId, @@ -1327,7 +1309,9 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st if err != nil { return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build target for update: %v.", err) } - u.SetPublicId(ctx, id) + if err := u.SetPublicId(ctx, id); err != nil { + return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set target id: %v.", err) + } maskManager, err := subtypeRegistry.maskManager(subtype) if err != nil { @@ -1668,7 +1652,7 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou // ApplicationCredentialLibrariesField is deprecated and should only be populated // for library type sources - if cs.Type() == "library" { + if cs.Type() == target.LibraryCredentialSourceType { appCredLibIds = append(appCredLibIds, cs.Id()) appCredLibraries = append(appCredLibraries, &pb.CredentialLibrary{ Id: cs.Id(), @@ -2168,22 +2152,3 @@ func validateAuthorizeSessionRequest(req *pbs.AuthorizeSessionRequest) error { } return nil } - -// credentialToProto converts the strongly typed credential.Credential into a known proto.Message. -func credentialToProto(ctx context.Context, cred credential.Credential) (*serverpb.Credential, error) { - const op = "targets.credentialToProto" - switch c := cred.(type) { - case credential.UserPassword: - return &serverpb.Credential{ - Credential: &serverpb.Credential_UserPassword{ - UserPassword: &serverpb.UserPassword{ - Username: c.Username(), - Password: string(c.Password()), - }, - }, - }, nil - - default: - return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported credential %T", c)) - } -} diff --git a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go index 7e57aa99d..e8fe57ae8 100644 --- a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/boundary/internal/daemon/controller/auth" "github.com/hashicorp/boundary/internal/daemon/controller/handlers" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentials" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers" "github.com/hashicorp/boundary/internal/db" @@ -39,7 +40,8 @@ import ( "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/tcp" "github.com/hashicorp/boundary/internal/types/scope" - credpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries" + credlibpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentiallibraries" + credpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" @@ -99,10 +101,13 @@ func testService(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapp pluginHostRepoFn := func() (*plugin.Repository, error) { return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } - credentialRepoFn := func() (*vault.Repository, error) { + vaultCredRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } - return targets.NewService(context.Background(), kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn) + staticCredRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(context.Background(), rw, rw, kms) + } + return targets.NewService(context.Background(), kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn) } func TestGet(t *testing.T) { @@ -2436,8 +2441,8 @@ func TestSetTargetCredentialSources(t *testing.T) { resultLibIds []string }{ { - name: "Set libary on empty target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty libary"), + name: "Set library on empty target", + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty library"), setCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, resultLibIds: []string{cls[1].GetPublicId()}, @@ -2450,14 +2455,14 @@ func TestSetTargetCredentialSources(t *testing.T) { }, { name: "Set library on library populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated libary-libary", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated library-library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, resultLibIds: []string{cls[1].GetPublicId()}, }, { name: "Set static on library populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-libary", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), + tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated static-library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{creds[1].GetPublicId()}, resultCredentialSourceIds: []string{creds[1].GetPublicId()}, }, @@ -2813,9 +2818,12 @@ func TestAuthorizeSession(t *testing.T) { staticHostRepoFn := func() (*static.Repository, error) { return staticRepo, nil } - credentialRepoFn := func() (*vault.Repository, error) { + vaultCredRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } + staticCredRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(ctx, rw, rw, kms) + } atRepoFn := func() (*authtoken.Repository, error) { return authtoken.NewRepository(rw, rw, kms) } @@ -2889,15 +2897,15 @@ func TestAuthorizeSession(t *testing.T) { v.MountPKI(t) sec, tok := v.CreateToken(t, vault.WithPolicies([]string{"default", "boundary-controller", "pki"})) - store := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor) - credService, err := credentiallibraries.NewService(credentialRepoFn, iamRepoFn) + vaultStore := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor) + credService, err := credentiallibraries.NewService(vaultCredRepoFn, iamRepoFn) require.NoError(t, err) - clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{ - CredentialStoreId: store.GetPublicId(), + clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{ + CredentialStoreId: vaultStore.GetPublicId(), Name: wrapperspb.String("Library Name"), Description: wrapperspb.String("Library Description"), - Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{ - VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{ + Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{ + VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{ Path: &wrapperspb.StringValue{ Value: path.Join("pki", "issue", "boundary"), }, @@ -2943,7 +2951,7 @@ func TestAuthorizeSession(t *testing.T) { }, } - s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn) + s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn) require.NoError(t, err) for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { @@ -3013,14 +3021,14 @@ func TestAuthorizeSession(t *testing.T) { Id: clsResp.GetItem().GetId(), Name: clsResp.GetItem().GetName().GetValue(), Description: clsResp.GetItem().GetDescription().GetValue(), - CredentialStoreId: store.GetPublicId(), + CredentialStoreId: vaultStore.GetPublicId(), Type: vault.Subtype.String(), }, CredentialSource: &pb.CredentialSource{ Id: clsResp.GetItem().GetId(), Name: clsResp.GetItem().GetName().GetValue(), Description: clsResp.GetItem().GetDescription().GetValue(), - CredentialStoreId: store.GetPublicId(), + CredentialStoreId: vaultStore.GetPublicId(), Type: vault.Subtype.String(), }, }}, @@ -3085,9 +3093,12 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { staticHostRepoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } - credentialRepoFn := func() (*vault.Repository, error) { + vaultCredRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } + staticCredRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(ctx, rw, rw, kms) + } atRepoFn := func() (*authtoken.Repository, error) { return authtoken.NewRepository(rw, rw, kms) } @@ -3112,7 +3123,7 @@ func TestAuthorizeSessionTypedCredentials(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) + s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn) require.NoError(t, err) hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -3124,20 +3135,20 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { v.AddKVPolicy(t) sec, tok := v.CreateToken(t, vault.WithPolicies([]string{"default", "boundary-controller", "secret"})) - store := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor) - credService, err := credentiallibraries.NewService(credentialRepoFn, iamRepoFn) + vaultStore := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor) + credLibService, err := credentiallibraries.NewService(vaultCredRepoFn, iamRepoFn) require.NoError(t, err) // Create secret in vault with default username and password fields defaultUserPass := v.CreateKVSecret(t, "default-userpass", []byte(`{"data": {"username": "my-user", "password": "my-pass"}}`)) require.NotNil(t, defaultUserPass) - clsRespUserPassword, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{ - CredentialStoreId: store.GetPublicId(), + clsRespUserPassword, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{ + CredentialStoreId: vaultStore.GetPublicId(), Name: wrapperspb.String("Userpassword Library"), Description: wrapperspb.String("Userpassword Library Description"), - Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{ - VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{ + Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{ + VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{ Path: wrapperspb.String(path.Join("secret", "data", "default-userpass")), HttpMethod: wrapperspb.String("GET"), }, @@ -3146,12 +3157,12 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { }}) require.NoError(t, err) - clsRespUnspecified, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{ - CredentialStoreId: store.GetPublicId(), + clsRespUnspecified, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{ + CredentialStoreId: vaultStore.GetPublicId(), Name: wrapperspb.String("Unspecified Library"), Description: wrapperspb.String("Unspecified Library Description"), - Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{ - VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{ + Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{ + VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{ Path: wrapperspb.String(path.Join("secret", "data", "default-userpass")), HttpMethod: wrapperspb.String("GET"), }, @@ -3163,12 +3174,12 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { nonDefaultUserPass := v.CreateKVSecret(t, "non-default-userpass", []byte(`{"data": {"non-default-user": "my-user", "non-default-pass": "my-pass"}}`)) require.NotNil(t, nonDefaultUserPass) - clsRespUserPasswordWithMapping, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{ - CredentialStoreId: store.GetPublicId(), + clsRespUserPasswordWithMapping, err := credLibService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{ + CredentialStoreId: vaultStore.GetPublicId(), Name: wrapperspb.String("Userpassword Mapping Library"), Description: wrapperspb.String("Userpassword Mapping Library Description"), - Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{ - VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{ + Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{ + VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{ Path: wrapperspb.String(path.Join("secret", "data", "non-default-userpass")), HttpMethod: wrapperspb.String("GET"), }, @@ -3181,55 +3192,146 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { }}) require.NoError(t, err) + staticStore := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId()) + credService, err := credentials.NewService(staticCredRepoFn, iamRepoFn) + require.NoError(t, err) + credResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{ + CredentialStoreId: staticStore.GetPublicId(), + Type: credstatic.UsernamePasswordSubtype.String(), + Name: wrapperspb.String("Cred Name"), + Description: wrapperspb.String("Cred Description"), + Attrs: &credpb.Credential_UsernamePasswordAttributes{ + UsernamePasswordAttributes: &credpb.UsernamePasswordAttributes{ + Username: wrapperspb.String("static-username"), + Password: wrapperspb.String("static-password"), + }, + }, + }}) + require.NoError(t, err) + require.NotNil(t, credResp) + cases := []struct { name string hostSourceId string - credLib *credpb.CredentialLibrary + credSourceId string wantedHostId string wantedEndpoint string - wantedCredType string - wantedCred *structpb.Struct + wantedCred *pb.SessionCredential }{ { - name: "unspecified", + name: "vault-unspecified", hostSourceId: shs.GetPublicId(), + credSourceId: clsRespUnspecified.GetItem().GetId(), wantedHostId: h.GetPublicId(), wantedEndpoint: h.GetAddress(), - credLib: clsRespUnspecified.GetItem(), + wantedCred: &pb.SessionCredential{ + CredentialLibrary: &pb.CredentialLibrary{ + Id: clsRespUnspecified.GetItem().GetId(), + Name: clsRespUnspecified.GetItem().GetName().GetValue(), + Description: clsRespUnspecified.GetItem().GetDescription().GetValue(), + CredentialStoreId: vaultStore.GetPublicId(), + Type: vault.Subtype.String(), + }, + CredentialSource: &pb.CredentialSource{ + Id: clsRespUnspecified.GetItem().GetId(), + Name: clsRespUnspecified.GetItem().GetName().GetValue(), + Description: clsRespUnspecified.GetItem().GetDescription().GetValue(), + CredentialStoreId: vaultStore.GetPublicId(), + Type: vault.Subtype.String(), + }, + }, }, { - name: "userpassword", + name: "vault-userpassword", hostSourceId: shs.GetPublicId(), + credSourceId: clsRespUserPassword.GetItem().GetId(), wantedHostId: h.GetPublicId(), wantedEndpoint: h.GetAddress(), - credLib: clsRespUserPassword.GetItem(), - wantedCred: func() *structpb.Struct { - data := map[string]interface{}{ - "username": "my-user", - "password": "my-pass", - } - st, err := structpb.NewStruct(data) - require.NoError(t, err) - return st - }(), - wantedCredType: string(credential.UsernamePasswordType), + wantedCred: &pb.SessionCredential{ + CredentialLibrary: &pb.CredentialLibrary{ + Id: clsRespUserPassword.GetItem().GetId(), + Name: clsRespUserPassword.GetItem().GetName().GetValue(), + Description: clsRespUserPassword.GetItem().GetDescription().GetValue(), + CredentialStoreId: vaultStore.GetPublicId(), + Type: vault.Subtype.String(), + }, + CredentialSource: &pb.CredentialSource{ + Id: clsRespUserPassword.GetItem().GetId(), + Name: clsRespUserPassword.GetItem().GetName().GetValue(), + Description: clsRespUserPassword.GetItem().GetDescription().GetValue(), + CredentialStoreId: vaultStore.GetPublicId(), + Type: vault.Subtype.String(), + CredentialType: string(credential.UsernamePasswordType), + }, + Credential: func() *structpb.Struct { + data := map[string]interface{}{ + "password": "my-pass", + "username": "my-user", + } + st, err := structpb.NewStruct(data) + require.NoError(t, err) + return st + }(), + }, }, { - name: "userpassword-with-mapping", + name: "vault-userpassword-with-mapping", hostSourceId: shs.GetPublicId(), + credSourceId: clsRespUserPasswordWithMapping.GetItem().GetId(), wantedHostId: h.GetPublicId(), wantedEndpoint: h.GetAddress(), - credLib: clsRespUserPasswordWithMapping.GetItem(), - wantedCred: func() *structpb.Struct { - data := map[string]interface{}{ - "password": "my-pass", - "username": "my-user", - } - st, err := structpb.NewStruct(data) - require.NoError(t, err) - return st - }(), - wantedCredType: string(credential.UsernamePasswordType), + wantedCred: &pb.SessionCredential{ + CredentialLibrary: &pb.CredentialLibrary{ + Id: clsRespUserPasswordWithMapping.GetItem().GetId(), + Name: clsRespUserPasswordWithMapping.GetItem().GetName().GetValue(), + Description: clsRespUserPasswordWithMapping.GetItem().GetDescription().GetValue(), + CredentialStoreId: vaultStore.GetPublicId(), + Type: vault.Subtype.String(), + }, + CredentialSource: &pb.CredentialSource{ + Id: clsRespUserPasswordWithMapping.GetItem().GetId(), + Name: clsRespUserPasswordWithMapping.GetItem().GetName().GetValue(), + Description: clsRespUserPasswordWithMapping.GetItem().GetDescription().GetValue(), + CredentialStoreId: vaultStore.GetPublicId(), + Type: vault.Subtype.String(), + CredentialType: string(credential.UsernamePasswordType), + }, + Credential: func() *structpb.Struct { + data := map[string]interface{}{ + "password": "my-pass", + "username": "my-user", + } + st, err := structpb.NewStruct(data) + require.NoError(t, err) + return st + }(), + }, + }, + { + name: "static-userpassword", + hostSourceId: shs.GetPublicId(), + credSourceId: credResp.GetItem().GetId(), + wantedHostId: h.GetPublicId(), + wantedEndpoint: h.GetAddress(), + wantedCred: &pb.SessionCredential{ + CredentialSource: &pb.CredentialSource{ + Id: credResp.GetItem().GetId(), + Name: credResp.GetItem().GetName().GetValue(), + Description: credResp.GetItem().GetDescription().GetValue(), + CredentialStoreId: staticStore.GetPublicId(), + Type: credstatic.Subtype.String(), + CredentialType: string(credential.UsernamePasswordType), + }, + Credential: func() *structpb.Struct { + data := map[string]interface{}{ + "password": "static-password", + "username": "static-username", + } + st, err := structpb.NewStruct(data) + require.NoError(t, err) + return st + }(), + }, }, } @@ -3246,7 +3348,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { _, err = s.AddTargetCredentialSources(ctx, &pbs.AddTargetCredentialSourcesRequest{ Id: tar.GetPublicId(), - ApplicationCredentialSourceIds: []string{tc.credLib.GetId()}, + ApplicationCredentialSourceIds: []string{tc.credSourceId}, Version: apiTar.GetItem().GetVersion(), }) require.NoError(t, err) @@ -3274,32 +3376,13 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { Description: proj.GetDescription(), ParentScopeId: proj.GetParentId(), }, - TargetId: tar.GetPublicId(), - UserId: at.GetIamUserId(), - HostSetId: tc.hostSourceId, - HostId: tc.wantedHostId, - Type: "tcp", - Endpoint: fmt.Sprintf("tcp://%s:%d", tc.wantedEndpoint, defaultPort), - Credentials: []*pb.SessionCredential{ - { - CredentialLibrary: &pb.CredentialLibrary{ - Id: tc.credLib.GetId(), - Name: tc.credLib.GetName().GetValue(), - Description: tc.credLib.GetDescription().GetValue(), - CredentialStoreId: store.GetPublicId(), - Type: vault.Subtype.String(), - }, - CredentialSource: &pb.CredentialSource{ - Id: tc.credLib.GetId(), - Name: tc.credLib.GetName().GetValue(), - Description: tc.credLib.GetDescription().GetValue(), - CredentialStoreId: store.GetPublicId(), - Type: vault.Subtype.String(), - CredentialType: tc.wantedCredType, - }, - Credential: tc.wantedCred, - }, - }, + TargetId: tar.GetPublicId(), + UserId: at.GetIamUserId(), + HostSetId: tc.hostSourceId, + HostId: tc.wantedHostId, + Type: "tcp", + Endpoint: fmt.Sprintf("tcp://%s:%d", tc.wantedEndpoint, defaultPort), + Credentials: []*pb.SessionCredential{tc.wantedCred}, // TODO: validate the contents of the authorization token is what is expected } got := asRes.GetItem() @@ -3350,15 +3433,18 @@ func TestAuthorizeSession_Errors(t *testing.T) { pluginHostRepoFn := func() (*plugin.Repository, error) { return plugin.NewRepository(rw, rw, kms, sche, map[string]plgpb.HostPluginServiceClient{}) } - credentialRepoFn := func() (*vault.Repository, error) { + vaultCredRepoFn := func() (*vault.Repository, error) { return vault.NewRepository(rw, rw, kms, sche) } + staticCredRepoFn := func() (*credstatic.Repository, error) { + return credstatic.NewRepository(ctx, rw, rw, kms) + } atRepoFn := func() (*authtoken.Repository, error) { return authtoken.NewRepository(rw, rw, kms) } org, proj := iam.TestScopes(t, iamRepo) - s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn) + s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn) require.NoError(t, err) // Authorized user gets full permissions @@ -3441,13 +3527,13 @@ func TestAuthorizeSession_Errors(t *testing.T) { } libraryExists := func(tar target.Target) (version uint32) { - credService, err := credentiallibraries.NewService(credentialRepoFn, iamRepoFn) + credService, err := credentiallibraries.NewService(vaultCredRepoFn, iamRepoFn) require.NoError(t, err) - clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{ + clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{ CredentialStoreId: store.GetPublicId(), Description: wrapperspb.String(fmt.Sprintf("Library Description for target %q", tar.GetName())), - Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{ - VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{ + Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{ + VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{ Path: wrapperspb.String(path.Join("database", "creds", "opened")), }, }, @@ -3465,13 +3551,13 @@ func TestAuthorizeSession_Errors(t *testing.T) { } misConfiguredlibraryExists := func(tar target.Target) (version uint32) { - credService, err := credentiallibraries.NewService(credentialRepoFn, iamRepoFn) + credService, err := credentiallibraries.NewService(vaultCredRepoFn, iamRepoFn) require.NoError(t, err) - clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{ + clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credlibpb.CredentialLibrary{ CredentialStoreId: store.GetPublicId(), Description: wrapperspb.String(fmt.Sprintf("Library Description for target %q", tar.GetName())), - Attrs: &credpb.CredentialLibrary_VaultCredentialLibraryAttributes{ - VaultCredentialLibraryAttributes: &credpb.VaultCredentialLibraryAttributes{ + Attrs: &credlibpb.CredentialLibrary_VaultCredentialLibraryAttributes{ + VaultCredentialLibraryAttributes: &credlibpb.VaultCredentialLibraryAttributes{ Path: wrapperspb.String("bad path"), }, }, diff --git a/internal/db/schema/migrations/oss/postgres/50/02_target.up.sql b/internal/db/schema/migrations/oss/postgres/50/02_target.up.sql index c297565f7..8b3e51330 100644 --- a/internal/db/schema/migrations/oss/postgres/50/02_target.up.sql +++ b/internal/db/schema/migrations/oss/postgres/50/02_target.up.sql @@ -40,7 +40,8 @@ begin; tcl.target_id, tcl.credential_library_id as credential_source_id, tcl.credential_purpose, - cl.store_id + cl.store_id, + 'library' as type from target_credential_library tcl, credential_library cl @@ -51,7 +52,8 @@ begin; tcs.target_id, tcs.credential_static_id as credential_source_id, tcs.credential_purpose, - cst.store_id + cst.store_id, + 'static' as type from target_static_credential tcs, credential_static cst diff --git a/internal/proto/controller/storage/target/store/v1/target.proto b/internal/proto/controller/storage/target/store/v1/target.proto index 9b5c38195..afc02628b 100644 --- a/internal/proto/controller/storage/target/store/v1/target.proto +++ b/internal/proto/controller/storage/target/store/v1/target.proto @@ -125,6 +125,10 @@ message CredentialSource { // create_time from the RDBMS // @inject_tag: `gorm:"default:current_timestamp"` timestamp.v1.Timestamp create_time = 40; + + // type of credential source (library or static) + // @inject_tag: `gorm:"not_null"` + string type = 50; } message CredentialSourceView { diff --git a/internal/target/credential_source.go b/internal/target/credential_source.go index 88572a3e6..fcad085b7 100644 --- a/internal/target/credential_source.go +++ b/internal/target/credential_source.go @@ -5,6 +5,15 @@ import ( "github.com/hashicorp/boundary/internal/target/store" ) +// CredentialSourceType is the type of credential source. +type CredentialSourceType string + +// Credential source type values. +const ( + LibraryCredentialSourceType CredentialSourceType = "library" + StaticCredentialSourceType CredentialSourceType = "static" +) + var _ CredentialSource = (*TargetCredentialSource)(nil) // CredentialSource is an interface that can be implemented by both a library @@ -14,10 +23,11 @@ type CredentialSource interface { Id() string CredentialPurpose() credential.Purpose TargetId() string + Type() CredentialSourceType } // CredentialSources contains slices of credential publicIds -// per purpose to be attacehd to the target. +// per purpose to be attached to the target. type CredentialSources struct { ApplicationCredentialIds []string EgressCredentialIds []string @@ -58,6 +68,11 @@ func (ts *TargetCredentialSource) TargetId() string { return ts.GetTargetId() } +// Type returns the type of the credential source (library or static) +func (ts *TargetCredentialSource) Type() CredentialSourceType { + return CredentialSourceType(ts.GetType()) +} + // credentialSourceView provides a common way to return credential sources regardless of their // underlying type (library or static). type credentialSourceView struct { diff --git a/internal/target/repository_credential_source.go b/internal/target/repository_credential_source.go index a32c655b1..bcd2c65a7 100644 --- a/internal/target/repository_credential_source.go +++ b/internal/target/repository_credential_source.go @@ -478,8 +478,8 @@ func (r *Repository) changes(ctx context.Context, targetId string, ids []string, if err := r.reader.ScanRows(ctx, rows, &chg); err != nil { return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("scan row failed")) } - switch chg.Type { - case "library": + switch CredentialSourceType(chg.Type) { + case LibraryCredentialSourceType: lib, err := NewCredentialLibrary(targetId, chg.SourceId, purpose) if err != nil { return nil, nil, nil, nil, errors.Wrap(ctx, err, op) @@ -490,7 +490,7 @@ func (r *Repository) changes(ctx context.Context, targetId string, ids []string, default: addCredLib = append(addCredLib, lib) } - case "static": + case StaticCredentialSourceType: cred, err := NewStaticCredential(targetId, chg.SourceId, purpose) if err != nil { return nil, nil, nil, nil, errors.Wrap(ctx, err, op) @@ -547,9 +547,9 @@ func (r *Repository) createSources(ctx context.Context, tId string, tSubtype sub // Create a map between credential source ID and it's type (library or static). // This will allow for a quick lookup when calling the corresponding New below - credTypeById := make(map[string]string, len(ids)) + credTypeById := make(map[string]CredentialSourceType, len(ids)) for _, cv := range credView { - credTypeById[cv.GetPublicId()] = cv.GetType() + credTypeById[cv.GetPublicId()] = CredentialSourceType(cv.GetType()) } credLibs := make([]*CredentialLibrary, 0, totalCreds) @@ -561,13 +561,13 @@ func (r *Repository) createSources(ctx context.Context, tId string, tSubtype sub for purpose, ids := range byPurpose { for _, id := range ids { switch credTypeById[id] { - case "library": + case LibraryCredentialSourceType: lib, err := NewCredentialLibrary(tId, id, purpose) if err != nil { return nil, nil, errors.Wrap(ctx, err, op) } credLibs = append(credLibs, lib) - case "static": + case StaticCredentialSourceType: cred, err := NewStaticCredential(tId, id, purpose) if err != nil { return nil, nil, errors.Wrap(ctx, err, op) diff --git a/internal/target/store/target.pb.go b/internal/target/store/target.pb.go index 9df9a13d2..4eed83939 100644 --- a/internal/target/store/target.pb.go +++ b/internal/target/store/target.pb.go @@ -427,6 +427,9 @@ type CredentialSource struct { // create_time from the RDBMS // @inject_tag: `gorm:"default:current_timestamp"` CreateTime *timestamp.Timestamp `protobuf:"bytes,40,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // type of credential source (library or static) + // @inject_tag: `gorm:"not_null"` + Type string `protobuf:"bytes,50,opt,name=type,proto3" json:"type,omitempty" gorm:"not_null"` } func (x *CredentialSource) Reset() { @@ -489,6 +492,13 @@ func (x *CredentialSource) GetCreateTime() *timestamp.Timestamp { return nil } +func (x *CredentialSource) GetType() string { + if x != nil { + return x.Type + } + return "" +} + type CredentialSourceView struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -628,7 +638,7 @@ var file_controller_storage_target_store_v1_target_proto_rawDesc = []byte{ 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, - 0xdd, 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, + 0xf1, 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, @@ -641,16 +651,18 @@ var file_controller_storage_target_store_v1_target_proto_rawDesc = []byte{ 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, - 0x47, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x14, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x42, 0x3b, 0x5a, 0x39, 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, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x22, 0x47, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x42, 0x3b, 0x5a, 0x39, + 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, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( From 631de0e5e2a7626aee370a4ad7b7316b0bb1c0f1 Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Sat, 11 Jun 2022 21:15:05 -0700 Subject: [PATCH 12/15] chore(changelog): Update changelog --- CHANGELOG.md | 7 +++++++ .../db/schema/migrations/oss/postgres/10/05_target.up.sql | 2 +- .../oss/postgres/{50 => 33}/01_static_credential.up.sql | 0 .../migrations/oss/postgres/{50 => 33}/02_target.up.sql | 0 .../migrations/oss/postgres/{50 => 33}/03_session.up.sql | 0 5 files changed, 8 insertions(+), 1 deletion(-) rename internal/db/schema/migrations/oss/postgres/{50 => 33}/01_static_credential.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{50 => 33}/02_target.up.sql (100%) rename internal/db/schema/migrations/oss/postgres/{50 => 33}/03_session.up.sql (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4181635f..93aea82e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. ### New and Improved +* Credentials: This release introduces a new credential store type `static`, + which simply takes in a user-supplied credential and stores it (encrypted) + directly in Boundary. Currently, the `static` credential store can hold + credentials of type `username_password`. These credentials can act as + credential sources for targets, similar to credential libraries from the + `vault` credential store, and thus can be brokered to users at session + authorization time. [PR](https://github.com/hashicorp/boundary/pull/2174) * controller: Improve response time for listing sessions. This also creates a new periodic job that will delete terminated sessions after 1 hour. diff --git a/internal/db/schema/migrations/oss/postgres/10/05_target.up.sql b/internal/db/schema/migrations/oss/postgres/10/05_target.up.sql index 02bab05dc..12a879130 100644 --- a/internal/db/schema/migrations/oss/postgres/10/05_target.up.sql +++ b/internal/db/schema/migrations/oss/postgres/10/05_target.up.sql @@ -29,7 +29,7 @@ begin; create trigger immutable_columns before update on target_credential_library for each row execute procedure immutable_columns('target_id', 'credential_library_id', 'credential_purpose', 'create_time'); - -- replaced in 50/02_target.up.sql + -- replaced in 33/02_target.up.sql -- target_library provides the store id along with the other data stored in -- target_credential_library create view target_library diff --git a/internal/db/schema/migrations/oss/postgres/50/01_static_credential.up.sql b/internal/db/schema/migrations/oss/postgres/33/01_static_credential.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/50/01_static_credential.up.sql rename to internal/db/schema/migrations/oss/postgres/33/01_static_credential.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/50/02_target.up.sql b/internal/db/schema/migrations/oss/postgres/33/02_target.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/50/02_target.up.sql rename to internal/db/schema/migrations/oss/postgres/33/02_target.up.sql diff --git a/internal/db/schema/migrations/oss/postgres/50/03_session.up.sql b/internal/db/schema/migrations/oss/postgres/33/03_session.up.sql similarity index 100% rename from internal/db/schema/migrations/oss/postgres/50/03_session.up.sql rename to internal/db/schema/migrations/oss/postgres/33/03_session.up.sql From 68eb6e2bed78238e7af4076a1235aaf3d145f915 Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Wed, 15 Jun 2022 08:56:24 -0700 Subject: [PATCH 13/15] chore(targets): remove deprecated credential libraries on target resources (#1533) --- CHANGELOG.md | 4 + api/targets/credential_library.gen.go | 10 - api/targets/option.gen.go | 12 - api/targets/session_credential.gen.go | 7 +- api/targets/target.gen.go | 238 +-- globals/fields.go | 142 +- internal/api/genapi/input.go | 12 +- internal/cmd/commands.go | 18 - internal/cmd/commands/connect/postgres.go | 3 +- internal/cmd/commands/targetscmd/funcs.go | 141 +- .../cmd/commands/targetscmd/targets.gen.go | 24 - internal/cmd/gencli/input.go | 2 +- .../credential/vault/setup_manual_test.go | 2 +- internal/daemon/controller/handler_test.go | 6 + .../handlers/targets/credentials.go | 7 - .../handlers/targets/target_service.go | 230 +-- .../targets/tcp/target_service_test.go | 826 ++++------- internal/gen/controller.swagger.json | 213 --- .../api/services/target_service.pb.go | 1286 +++++------------ .../api/services/target_service.pb.gw.go | 378 ----- .../api/services/target_service_grpc.pb.go | 159 -- .../api/resources/targets/v1/target.proto | 39 +- .../api/services/v1/target_service.proto | 96 -- .../tests/api/targets/classification_test.go | 46 +- internal/types/action/action.go | 6 +- .../api/resources/targets/target.pb.go | 730 ++++------ 26 files changed, 1138 insertions(+), 3499 deletions(-) delete mode 100644 api/targets/credential_library.gen.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 93aea82e6..a7b62adf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. ### Deprecations/Changes +* Targets: Removes support for `credential libraries` with respect to Target resources. + The `library` `fields` and `actions` were deprecated in [Boundary 0.5.0](#050-20210802), + please use `credential sources` instead. See changelog referenced above for + more details ([PR](https://github.com/hashicorp/boundary/pull/1533)). * Credential Libraries: The `user_password` credential type has been renamed to `username_password` to remove any inconsistency over what the credential type is. All existing `user_password` typed credential libraries will be migrated to diff --git a/api/targets/credential_library.gen.go b/api/targets/credential_library.gen.go deleted file mode 100644 index c7ac04e2f..000000000 --- a/api/targets/credential_library.gen.go +++ /dev/null @@ -1,10 +0,0 @@ -// Code generated by "make api"; DO NOT EDIT. -package targets - -type CredentialLibrary struct { - Id string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - CredentialStoreId string `json:"credential_store_id,omitempty"` - Type string `json:"type,omitempty"` -} diff --git a/api/targets/option.gen.go b/api/targets/option.gen.go index 5c47abf6d..225b52e99 100644 --- a/api/targets/option.gen.go +++ b/api/targets/option.gen.go @@ -87,18 +87,6 @@ func WithRecursive(recurse bool) Option { } } -func WithApplicationCredentialLibraryIds(inApplicationCredentialLibraryIds []string) Option { - return func(o *options) { - o.postMap["application_credential_library_ids"] = inApplicationCredentialLibraryIds - } -} - -func DefaultApplicationCredentialLibraryIds() Option { - return func(o *options) { - o.postMap["application_credential_library_ids"] = nil - } -} - func WithApplicationCredentialSourceIds(inApplicationCredentialSourceIds []string) Option { return func(o *options) { o.postMap["application_credential_source_ids"] = inApplicationCredentialSourceIds diff --git a/api/targets/session_credential.gen.go b/api/targets/session_credential.gen.go index 5b251ec8e..06a2739cd 100644 --- a/api/targets/session_credential.gen.go +++ b/api/targets/session_credential.gen.go @@ -2,8 +2,7 @@ package targets type SessionCredential struct { - CredentialSource *CredentialSource `json:"credential_source,omitempty"` - CredentialLibrary *CredentialLibrary `json:"credential_library,omitempty"` - Secret *SessionSecret `json:"secret,omitempty"` - Credential map[string]interface{} `json:"credential,omitempty"` + CredentialSource *CredentialSource `json:"credential_source,omitempty"` + Secret *SessionSecret `json:"secret,omitempty"` + Credential map[string]interface{} `json:"credential,omitempty"` } diff --git a/api/targets/target.gen.go b/api/targets/target.gen.go index ca5c961aa..d930d7798 100644 --- a/api/targets/target.gen.go +++ b/api/targets/target.gen.go @@ -13,30 +13,28 @@ import ( ) type Target struct { - Id string `json:"id,omitempty"` - ScopeId string `json:"scope_id,omitempty"` - Scope *scopes.ScopeInfo `json:"scope,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - CreatedTime time.Time `json:"created_time,omitempty"` - UpdatedTime time.Time `json:"updated_time,omitempty"` - Version uint32 `json:"version,omitempty"` - Type string `json:"type,omitempty"` - HostSetIds []string `json:"host_set_ids,omitempty"` - HostSets []*HostSet `json:"host_sets,omitempty"` - HostSourceIds []string `json:"host_source_ids,omitempty"` - HostSources []*HostSource `json:"host_sources,omitempty"` - SessionMaxSeconds uint32 `json:"session_max_seconds,omitempty"` - SessionConnectionLimit int32 `json:"session_connection_limit,omitempty"` - WorkerFilter string `json:"worker_filter,omitempty"` - ApplicationCredentialLibraryIds []string `json:"application_credential_library_ids,omitempty"` - ApplicationCredentialLibraries []*CredentialLibrary `json:"application_credential_libraries,omitempty"` - ApplicationCredentialSourceIds []string `json:"application_credential_source_ids,omitempty"` - ApplicationCredentialSources []*CredentialSource `json:"application_credential_sources,omitempty"` - EgressCredentialSourceIds []string `json:"egress_credential_source_ids,omitempty"` - EgressCredentialSources []*CredentialSource `json:"egress_credential_sources,omitempty"` - Attributes map[string]interface{} `json:"attributes,omitempty"` - AuthorizedActions []string `json:"authorized_actions,omitempty"` + Id string `json:"id,omitempty"` + ScopeId string `json:"scope_id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + CreatedTime time.Time `json:"created_time,omitempty"` + UpdatedTime time.Time `json:"updated_time,omitempty"` + Version uint32 `json:"version,omitempty"` + Type string `json:"type,omitempty"` + HostSetIds []string `json:"host_set_ids,omitempty"` + HostSets []*HostSet `json:"host_sets,omitempty"` + HostSourceIds []string `json:"host_source_ids,omitempty"` + HostSources []*HostSource `json:"host_sources,omitempty"` + SessionMaxSeconds uint32 `json:"session_max_seconds,omitempty"` + SessionConnectionLimit int32 `json:"session_connection_limit,omitempty"` + WorkerFilter string `json:"worker_filter,omitempty"` + ApplicationCredentialSourceIds []string `json:"application_credential_source_ids,omitempty"` + ApplicationCredentialSources []*CredentialSource `json:"application_credential_sources,omitempty"` + EgressCredentialSourceIds []string `json:"egress_credential_source_ids,omitempty"` + EgressCredentialSources []*CredentialSource `json:"egress_credential_sources,omitempty"` + Attributes map[string]interface{} `json:"attributes,omitempty"` + AuthorizedActions []string `json:"authorized_actions,omitempty"` response *api.Response } @@ -337,70 +335,6 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Targ return target, nil } -func (c *Client) AddCredentialLibraries(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) { - if id == "" { - return nil, fmt.Errorf("empty id value passed into AddCredentialLibraries request") - } - - if c.client == nil { - return nil, errors.New("nil client") - } - - opts, apiOpts := getOpts(opt...) - - if version == 0 { - if !opts.withAutomaticVersioning { - return nil, errors.New("zero version number passed into AddCredentialLibraries request") - } - existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...) - if existingErr != nil { - if api.AsServerError(existingErr) != nil { - return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) - } - return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) - } - if existingTarget == nil { - return nil, errors.New("nil resource response found when performing initial check-and-set read") - } - if existingTarget.Item == nil { - return nil, errors.New("nil resource found when performing initial check-and-set read") - } - version = existingTarget.Item.Version - } - - opts.postMap["version"] = version - - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("targets/%s:add-credential-libraries", url.PathEscape(id)), opts.postMap, apiOpts...) - if err != nil { - return nil, fmt.Errorf("error creating AddCredentialLibraries request: %w", err) - } - - if len(opts.queryMap) > 0 { - q := url.Values{} - for k, v := range opts.queryMap { - q.Add(k, v) - } - req.URL.RawQuery = q.Encode() - } - - resp, err := c.client.Do(req) - if err != nil { - return nil, fmt.Errorf("error performing client request during AddCredentialLibraries call: %w", err) - } - - target := new(TargetUpdateResult) - target.Item = new(Target) - apiErr, err := resp.Decode(target.Item) - if err != nil { - return nil, fmt.Errorf("error decoding AddCredentialLibraries response: %w", err) - } - if apiErr != nil { - return nil, apiErr - } - target.response = resp - return target, nil -} - func (c *Client) AddCredentialSources(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) { if id == "" { return nil, fmt.Errorf("empty id value passed into AddCredentialSources request") @@ -605,70 +539,6 @@ func (c *Client) AddHostSources(ctx context.Context, id string, version uint32, return target, nil } -func (c *Client) SetCredentialLibraries(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) { - if id == "" { - return nil, fmt.Errorf("empty id value passed into SetCredentialLibraries request") - } - - if c.client == nil { - return nil, errors.New("nil client") - } - - opts, apiOpts := getOpts(opt...) - - if version == 0 { - if !opts.withAutomaticVersioning { - return nil, errors.New("zero version number passed into SetCredentialLibraries request") - } - existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...) - if existingErr != nil { - if api.AsServerError(existingErr) != nil { - return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) - } - return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) - } - if existingTarget == nil { - return nil, errors.New("nil resource response found when performing initial check-and-set read") - } - if existingTarget.Item == nil { - return nil, errors.New("nil resource found when performing initial check-and-set read") - } - version = existingTarget.Item.Version - } - - opts.postMap["version"] = version - - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("targets/%s:set-credential-libraries", url.PathEscape(id)), opts.postMap, apiOpts...) - if err != nil { - return nil, fmt.Errorf("error creating SetCredentialLibraries request: %w", err) - } - - if len(opts.queryMap) > 0 { - q := url.Values{} - for k, v := range opts.queryMap { - q.Add(k, v) - } - req.URL.RawQuery = q.Encode() - } - - resp, err := c.client.Do(req) - if err != nil { - return nil, fmt.Errorf("error performing client request during SetCredentialLibraries call: %w", err) - } - - target := new(TargetUpdateResult) - target.Item = new(Target) - apiErr, err := resp.Decode(target.Item) - if err != nil { - return nil, fmt.Errorf("error decoding SetCredentialLibraries response: %w", err) - } - if apiErr != nil { - return nil, apiErr - } - target.response = resp - return target, nil -} - func (c *Client) SetCredentialSources(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) { if id == "" { return nil, fmt.Errorf("empty id value passed into SetCredentialSources request") @@ -865,70 +735,6 @@ func (c *Client) SetHostSources(ctx context.Context, id string, version uint32, return target, nil } -func (c *Client) RemoveCredentialLibraries(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) { - if id == "" { - return nil, fmt.Errorf("empty id value passed into RemoveCredentialLibraries request") - } - - if c.client == nil { - return nil, errors.New("nil client") - } - - opts, apiOpts := getOpts(opt...) - - if version == 0 { - if !opts.withAutomaticVersioning { - return nil, errors.New("zero version number passed into RemoveCredentialLibraries request") - } - existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...) - if existingErr != nil { - if api.AsServerError(existingErr) != nil { - return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) - } - return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) - } - if existingTarget == nil { - return nil, errors.New("nil resource response found when performing initial check-and-set read") - } - if existingTarget.Item == nil { - return nil, errors.New("nil resource found when performing initial check-and-set read") - } - version = existingTarget.Item.Version - } - - opts.postMap["version"] = version - - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("targets/%s:remove-credential-libraries", url.PathEscape(id)), opts.postMap, apiOpts...) - if err != nil { - return nil, fmt.Errorf("error creating RemoveCredentialLibraries request: %w", err) - } - - if len(opts.queryMap) > 0 { - q := url.Values{} - for k, v := range opts.queryMap { - q.Add(k, v) - } - req.URL.RawQuery = q.Encode() - } - - resp, err := c.client.Do(req) - if err != nil { - return nil, fmt.Errorf("error performing client request during RemoveCredentialLibraries call: %w", err) - } - - target := new(TargetUpdateResult) - target.Item = new(Target) - apiErr, err := resp.Decode(target.Item) - if err != nil { - return nil, fmt.Errorf("error decoding RemoveCredentialLibraries response: %w", err) - } - if apiErr != nil { - return nil, apiErr - } - target.response = resp - return target, nil -} - func (c *Client) RemoveCredentialSources(ctx context.Context, id string, version uint32, opt ...Option) (*TargetUpdateResult, error) { if id == "" { return nil, fmt.Errorf("empty id value passed into RemoveCredentialSources request") diff --git a/globals/fields.go b/globals/fields.go index 00589c64e..84c687501 100644 --- a/globals/fields.go +++ b/globals/fields.go @@ -1,76 +1,74 @@ package globals const ( - IdField = "id" - VersionField = "version" - NameField = "name" - DescriptionField = "description" - CreatedTimeField = "created_time" - UpdatedTimeField = "updated_time" - TypeField = "type" - AttributesField = "attributes" - ScopeIdField = "scope_id" - ScopeField = "scope" - AuthMethodIdField = "auth_method_id" - AccountIdField = "account_id" - UserIdField = "user_id" - IsPrimaryField = "is_primary" - AuthorizedActionsField = "authorized_actions" - AuthorizedCollectionActionsField = "authorized_collection_actions" - ExpirationTimeField = "expiration_time" - ApproximateLastUsedTimeField = "approximate_last_used_time" - MembersField = "members" - MemberIdsField = "member_ids" - HostCatalogIdField = "host_catalog_id" - HostSetIdsField = "host_set_ids" - HostSourceIdsField = "host_source_ids" - HostIdsField = "host_ids" - PrincipalIdsField = "principal_ids" - PrincipalsField = "principals" - GrantScopeIdField = "grant_scope_id" - GrantsField = "grants" - GrantStringsField = "grant_strings" - PrimaryAuthMethodIdField = "primary_auth_method_id" - TargetIdField = "target_id" - HostIdField = "host_id" - HostSetIdField = "host_set_id" - HostSetsField = "host_sets" - HostSourcesField = "host_sources" - AuthTokenIdField = "auth_token_id" - EndpointField = "endpoint" - CertificateField = "certificate" - TerminationReasonField = "termination_reason" - StatusField = "status" - StatesField = "states" - SessionConnectionLimitField = "session_connection_limit" - SessionMaxSecondsField = "session_max_seconds" - WorkerFilterField = "worker_filter" - AccountIdsField = "account_ids" - AccountsField = "accounts" - LoginNameField = "login_name" - FullNameField = "full_name" - PrimaryAccountIdField = "primary_account_id" - EmailField = "email" - ManagedGroupIdsField = "managed_group_ids" - FilterField = "filter" - CredentialStoreIdField = "credential_store_id" - ApplicationCredentialLibraryIdsField = "application_credential_library_ids" - ApplicationCredentialLibrariesField = "application_credential_libraries" - ApplicationCredentialSourceIdsField = "application_credential_source_ids" - ApplicationCredentialSourcesField = "application_credential_sources" - PreferredEndpointsField = "preferred_endpoints" - SyncIntervalSecondsField = "sync_interval_seconds" - PluginIdField = "plugin_id" - PluginField = "plugin" - PluginNameField = "plugin_name" - IpAddressesField = "ip_addresses" - DnsNamesField = "dns_names" - SecretsHmacField = "secrets_hmac" - ExternalIdField = "external_id" - EgressCredentialSourceIdsField = "egress_credential_source_ids" - EgressCredentialSourcesField = "egress_credential_sources" - ConnectionsField = "connections" - CredentialTypeField = "credential_type" - CredentialMappingOverridesField = "credential_mapping_overrides" - MetricNamespace = "boundary" + IdField = "id" + VersionField = "version" + NameField = "name" + DescriptionField = "description" + CreatedTimeField = "created_time" + UpdatedTimeField = "updated_time" + TypeField = "type" + AttributesField = "attributes" + ScopeIdField = "scope_id" + ScopeField = "scope" + AuthMethodIdField = "auth_method_id" + AccountIdField = "account_id" + UserIdField = "user_id" + IsPrimaryField = "is_primary" + AuthorizedActionsField = "authorized_actions" + AuthorizedCollectionActionsField = "authorized_collection_actions" + ExpirationTimeField = "expiration_time" + ApproximateLastUsedTimeField = "approximate_last_used_time" + MembersField = "members" + MemberIdsField = "member_ids" + HostCatalogIdField = "host_catalog_id" + HostSetIdsField = "host_set_ids" + HostSourceIdsField = "host_source_ids" + HostIdsField = "host_ids" + PrincipalIdsField = "principal_ids" + PrincipalsField = "principals" + GrantScopeIdField = "grant_scope_id" + GrantsField = "grants" + GrantStringsField = "grant_strings" + PrimaryAuthMethodIdField = "primary_auth_method_id" + TargetIdField = "target_id" + HostIdField = "host_id" + HostSetIdField = "host_set_id" + HostSetsField = "host_sets" + HostSourcesField = "host_sources" + AuthTokenIdField = "auth_token_id" + EndpointField = "endpoint" + CertificateField = "certificate" + TerminationReasonField = "termination_reason" + StatusField = "status" + StatesField = "states" + SessionConnectionLimitField = "session_connection_limit" + SessionMaxSecondsField = "session_max_seconds" + WorkerFilterField = "worker_filter" + AccountIdsField = "account_ids" + AccountsField = "accounts" + LoginNameField = "login_name" + FullNameField = "full_name" + PrimaryAccountIdField = "primary_account_id" + EmailField = "email" + ManagedGroupIdsField = "managed_group_ids" + FilterField = "filter" + CredentialStoreIdField = "credential_store_id" + ApplicationCredentialSourceIdsField = "application_credential_source_ids" + ApplicationCredentialSourcesField = "application_credential_sources" + PreferredEndpointsField = "preferred_endpoints" + SyncIntervalSecondsField = "sync_interval_seconds" + PluginIdField = "plugin_id" + PluginField = "plugin" + PluginNameField = "plugin_name" + IpAddressesField = "ip_addresses" + DnsNamesField = "dns_names" + SecretsHmacField = "secrets_hmac" + ExternalIdField = "external_id" + EgressCredentialSourceIdsField = "egress_credential_source_ids" + EgressCredentialSourcesField = "egress_credential_sources" + ConnectionsField = "connections" + CredentialTypeField = "credential_type" + CredentialMappingOverridesField = "credential_mapping_overrides" + MetricNamespace = "boundary" ) diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index 0ae65b2e7..1f387b654 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -553,10 +553,6 @@ var inputStructs = []*structInfo{ inProto: &targets.HostSource{}, outFile: "targets/host_source.gen.go", }, - { - inProto: &targets.CredentialLibrary{}, - outFile: "targets/credential_library.gen.go", - }, { inProto: &targets.CredentialSource{}, outFile: "targets/credential_source.gen.go", @@ -611,8 +607,7 @@ var inputStructs = []*structInfo{ SliceType: "[]string", VarName: "hostSourceIds", }, - "CredentialLibraries": {}, - "CredentialSources": {}, + "CredentialSources": {}, }, extraFields: []fieldInfo{ { @@ -633,11 +628,6 @@ var inputStructs = []*structInfo{ FieldType: "string", SkipDefault: true, }, - { - Name: "ApplicationCredentialLibraryIds", - ProtoName: "application_credential_library_ids", - FieldType: "[]string", - }, { Name: "ApplicationCredentialSourceIds", ProtoName: "application_credential_source_ids", diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index 0a78ba796..13eb7db21 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -999,24 +999,6 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Func: "set-host-sources", }, nil }, - "targets add-credential-libraries": func() (cli.Command, error) { - return &targetscmd.Command{ - Command: base.NewCommand(ui), - Func: "add-credential-libraries", - }, nil - }, - "targets remove-credential-libraries": func() (cli.Command, error) { - return &targetscmd.Command{ - Command: base.NewCommand(ui), - Func: "remove-credential-libraries", - }, nil - }, - "targets set-credential-libraries": func() (cli.Command, error) { - return &targetscmd.Command{ - Command: base.NewCommand(ui), - Func: "set-credential-libraries", - }, nil - }, "targets add-credential-sources": func() (cli.Command, error) { return &targetscmd.Command{ Command: base.NewCommand(ui), diff --git a/internal/cmd/commands/connect/postgres.go b/internal/cmd/commands/connect/postgres.go index 75ea76788..8ef73ffee 100644 --- a/internal/cmd/commands/connect/postgres.go +++ b/internal/cmd/commands/connect/postgres.go @@ -64,8 +64,7 @@ func (p *postgresFlags) buildArgs(c *Command, port, ip, addr string) (args, envs if cred.Secret == nil || cred.Secret.Decoded == nil { continue } - // TODO: Could allow switching on library ID or name - switch cred.CredentialLibrary.Type { + switch cred.CredentialSource.Type { case "vault": // Attempt unmarshaling into creds if err := mapstructure.Decode(cred.Secret.Decoded, &creds); err != nil { diff --git a/internal/cmd/commands/targetscmd/funcs.go b/internal/cmd/commands/targetscmd/funcs.go index 77f8fed40..740854889 100644 --- a/internal/cmd/commands/targetscmd/funcs.go +++ b/internal/cmd/commands/targetscmd/funcs.go @@ -30,30 +30,26 @@ func init() { } type extraCmdVars struct { - flagHostSets []string - flagHostSources []string - flagApplicationCredentialLibraries []string - flagApplicationCredentialSources []string - flagEgressCredentialSources []string - flagHostId string - sar *targets.SessionAuthorizationResult + flagHostSets []string + flagHostSources []string + flagApplicationCredentialSources []string + flagEgressCredentialSources []string + flagHostId string + sar *targets.SessionAuthorizationResult } func extraActionsFlagsMapFuncImpl() map[string][]string { return map[string][]string{ - "authorize-session": {"id", "host-id"}, - "add-host-sets": {"id", "host-set", "version"}, - "remove-host-sets": {"id", "host-set", "version"}, - "set-host-sets": {"id", "host-set", "version"}, - "add-host-sources": {"id", "host-source", "version"}, - "remove-host-sources": {"id", "host-source", "version"}, - "set-host-sources": {"id", "host-source", "version"}, - "add-credential-libraries": {"id", "application-credential-library", "version"}, - "remove-credential-libraries": {"id", "application-credential-library", "version"}, - "set-credential-libraries": {"id", "application-credential-library", "version"}, - "add-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"}, - "remove-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"}, - "set-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"}, + "authorize-session": {"id", "host-id"}, + "add-host-sets": {"id", "host-set", "version"}, + "remove-host-sets": {"id", "host-set", "version"}, + "set-host-sets": {"id", "host-set", "version"}, + "add-host-sources": {"id", "host-source", "version"}, + "remove-host-sources": {"id", "host-source", "version"}, + "set-host-sources": {"id", "host-source", "version"}, + "add-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"}, + "remove-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"}, + "set-credential-sources": {"id", "application-credential-source", "egress-credential-source", "version"}, } } @@ -83,18 +79,6 @@ func extraSynopsisFuncImpl(c *Command) string { } return wordwrap.WrapString(fmt.Sprintf("%s a target", in), base.TermWidth) - case "add-credential-libraries", "set-credential-libraries", "remove-credential-libraries": - var in string - switch { - case strings.HasPrefix(c.Func, "add"): - in = "Add credential libraries to" - case strings.HasPrefix(c.Func, "set"): - in = "Set the full contents of the credential libraries on" - case strings.HasPrefix(c.Func, "remove"): - in = "Remove credential libraries from" - } - return wordwrap.WrapString(fmt.Sprintf("%s a target. DEPRECATED; use the -sources version instead.", in), base.TermWidth) - case "add-credential-sources", "set-credential-sources", "remove-credential-sources": var in string switch { @@ -232,48 +216,6 @@ func (c *Command) extraHelpFunc(helpMap map[string]func() string) string { "", "", }) - case "add-credential-libraries": - helpStr = base.WrapForHelpText([]string{ - "Usage: boundary target add-credential-libraries [options] [args]", - "", - " DEPRECATED: Use add-credential-sources instead.", - "", - " This command allows adding credential-library resources to target resources. Example:", - "", - " Add credential-library resources to a tcp-type target:", - "", - ` $ boundary targets add-credential-libraries -id ttcp_1234567890 -application-credential-library clvlt_1234567890 -application-credential-library clvlt_0987654321`, - "", - "", - }) - case "remove-credential-libraries": - helpStr = base.WrapForHelpText([]string{ - "Usage: boundary target remove-credential-libraries [options] [args]", - "", - " DEPRECATED: Use remove-credential-sources instead.", - "", - " This command allows removing credential-library resources from target resources. Example:", - "", - " Remove credential-library resources from a tcp-type target:", - "", - ` $ boundary targets remove-credential-libraries -id ttcp_1234567890 -application-credential-library clvlt_1234567890 -application-credential-library clvlt_0987654321`, - "", - "", - }) - case "set-credential-libraries": - helpStr = base.WrapForHelpText([]string{ - "Usage: boundary target set-credential-libraries [options] [args]", - "", - " DEPRECATED: Use set-credential-sources instead.", - "", - " This command allows setting the complete set of credential-library resources on a target resource. Example:", - "", - " Set credential-library resources on a tcp-type target:", - "", - ` $ boundary targets set-credential-libraries -id ttcp_1234567890 -application-credential-library clvlt_1234567890`, - "", - "", - }) case "add-credential-sources": helpStr = base.WrapForHelpText([]string{ "Usage: boundary target add-credential-sources [options] [args]", @@ -351,12 +293,6 @@ func extraFlagsFuncImpl(c *Command, _ *base.FlagSets, f *base.FlagSet) { Target: &c.flagHostId, Usage: "The ID of a specific host to connect to out of the hosts from the target's host sets. If not specified, one is chosen at random.", }) - case "application-credential-library": - f.StringSliceVar(&base.StringSliceVar{ - Name: "application-credential-library", - Target: &c.flagApplicationCredentialLibraries, - Usage: "The credential-library resource for application purpose to add, set, or remove. May be specified multiple times.", - }) case "application-credential-source": f.StringSliceVar(&base.StringSliceVar{ Name: "application-credential-source", @@ -467,13 +403,6 @@ func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]targets.Op } } - case "add-credential-libraries", "remove-credential-libraries": - if len(c.flagApplicationCredentialLibraries) == 0 { - c.UI.Error("No credential-libraries supplied via -application-credential-library") - return false - } - *opts = append(*opts, targets.WithApplicationCredentialLibraryIds(c.flagApplicationCredentialLibraries)) - case "add-credential-sources", "remove-credential-sources": // TODO: As we add other purposes, add them to this check if len(c.flagApplicationCredentialSources)+len(c.flagEgressCredentialSources) == 0 { @@ -488,21 +417,6 @@ func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]targets.Op *opts = append(*opts, targets.WithEgressCredentialSourceIds(c.flagEgressCredentialSources)) } - case "set-credential-libraries": - switch len(c.flagApplicationCredentialLibraries) { - case 0: - c.UI.Error("No credential-libraries supplied via -application-credential-library") - return false - case 1: - if c.flagApplicationCredentialLibraries[0] == "null" { - *opts = append(*opts, targets.DefaultApplicationCredentialLibraryIds()) - break - } - fallthrough - default: - *opts = append(*opts, targets.WithApplicationCredentialLibraryIds(c.flagApplicationCredentialLibraries)) - } - case "set-credential-sources": // TODO: As we add other purposes, add them to this check if len(c.flagApplicationCredentialSources)+len(c.flagEgressCredentialSources) == 0 { @@ -558,12 +472,6 @@ func executeExtraActionsImpl(c *Command, origResult api.GenericResult, origError return targetClient.RemoveHostSources(c.Context, c.FlagId, version, c.flagHostSources, opts...) case "set-host-sources": return targetClient.SetHostSources(c.Context, c.FlagId, version, c.flagHostSources, opts...) - case "add-credential-libraries": - return targetClient.AddCredentialLibraries(c.Context, c.FlagId, version, opts...) - case "remove-credential-libraries": - return targetClient.RemoveCredentialLibraries(c.Context, c.FlagId, version, opts...) - case "set-credential-libraries": - return targetClient.SetCredentialLibraries(c.Context, c.FlagId, version, opts...) case "add-credential-sources": return targetClient.AddCredentialSources(c.Context, c.FlagId, version, opts...) case "remove-credential-sources": @@ -702,23 +610,6 @@ func printItemTable(result api.GenericResult) string { } var credentialSourceMaps map[credential.Purpose][]map[string]interface{} - if len(item.ApplicationCredentialLibraries) > 0 { - if credentialSourceMaps == nil { - credentialSourceMaps = make(map[credential.Purpose][]map[string]interface{}) - } - var applicationCredentialSourceMaps []map[string]interface{} - for _, lib := range item.ApplicationCredentialLibraries { - m := map[string]interface{}{ - "ID": lib.Id, - "Credential Store ID": lib.CredentialStoreId, - } - applicationCredentialSourceMaps = append(applicationCredentialSourceMaps, m) - } - credentialSourceMaps[credential.ApplicationPurpose] = applicationCredentialSourceMaps - if l := len("Credential Store ID"); l > maxLength { - maxLength = l - } - } if len(item.ApplicationCredentialSources) > 0 { if credentialSourceMaps == nil { credentialSourceMaps = make(map[credential.Purpose][]map[string]interface{}) diff --git a/internal/cmd/commands/targetscmd/targets.gen.go b/internal/cmd/commands/targetscmd/targets.gen.go index 10e00939d..4d6a2d72e 100644 --- a/internal/cmd/commands/targetscmd/targets.gen.go +++ b/internal/cmd/commands/targetscmd/targets.gen.go @@ -246,30 +246,6 @@ func (c *Command) Run(args []string) int { version = uint32(c.FlagVersion) } - case "add-credential-libraries": - switch c.FlagVersion { - case 0: - opts = append(opts, targets.WithAutomaticVersioning(true)) - default: - version = uint32(c.FlagVersion) - } - - case "remove-credential-libraries": - switch c.FlagVersion { - case 0: - opts = append(opts, targets.WithAutomaticVersioning(true)) - default: - version = uint32(c.FlagVersion) - } - - case "set-credential-libraries": - switch c.FlagVersion { - case 0: - opts = append(opts, targets.WithAutomaticVersioning(true)) - default: - version = uint32(c.FlagVersion) - } - case "add-credential-sources": switch c.FlagVersion { case 0: diff --git a/internal/cmd/gencli/input.go b/internal/cmd/gencli/input.go index 519613496..e9536467c 100644 --- a/internal/cmd/gencli/input.go +++ b/internal/cmd/gencli/input.go @@ -472,7 +472,7 @@ var inputStructs = map[string][]*cmdInfo{ HasName: true, HasDescription: true, Container: "Scope", - VersionedActions: []string{"add-host-sets", "remove-host-sets", "set-host-sets", "add-host-sources", "remove-host-sources", "set-host-sources", "add-credential-libraries", "remove-credential-libraries", "set-credential-libraries", "add-credential-sources", "remove-credential-sources", "set-credential-sources"}, + VersionedActions: []string{"add-host-sets", "remove-host-sets", "set-host-sets", "add-host-sources", "remove-host-sources", "set-host-sources", "add-credential-sources", "remove-credential-sources", "set-credential-sources"}, }, { ResourceType: resource.Target.String(), diff --git a/internal/credential/vault/setup_manual_test.go b/internal/credential/vault/setup_manual_test.go index cbd01a81f..f8a5cb97c 100644 --- a/internal/credential/vault/setup_manual_test.go +++ b/internal/credential/vault/setup_manual_test.go @@ -79,7 +79,7 @@ func TestSetupSleepyDevEnvironment(t *testing.T) { ) require.NoError(t, err) require.NotNil(t, lib) - tgt, err = tcl.AddCredentialLibraries(ctx, tgt.Item.Id, 0, targets.WithAutomaticVersioning(true), targets.WithApplicationCredentialLibraryIds([]string{lib.Item.Id})) + tgt, err = tcl.AddCredentialSources(ctx, tgt.Item.Id, 0, targets.WithAutomaticVersioning(true), targets.WithApplicationCredentialSourceIds([]string{lib.Item.Id})) require.NoError(t, err) require.NotNil(t, tgt) // Worker 1 diff --git a/internal/daemon/controller/handler_test.go b/internal/daemon/controller/handler_test.go index dca2c877f..b7927b3e9 100644 --- a/internal/daemon/controller/handler_test.go +++ b/internal/daemon/controller/handler_test.go @@ -163,6 +163,12 @@ func TestHandleImplementedPaths(t *testing.T) { "v1/targets/someid:add-host-sets", "v1/targets/someid:set-host-sets", "v1/targets/someid:remove-host-sets", + "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/users/someid:add-accounts", "v1/users/someid:set-accounts", "v1/users/someid:remove-accounts", diff --git a/internal/daemon/controller/handlers/targets/credentials.go b/internal/daemon/controller/handlers/targets/credentials.go index a67977b9e..8de97f4d1 100644 --- a/internal/daemon/controller/handlers/targets/credentials.go +++ b/internal/daemon/controller/handlers/targets/credentials.go @@ -96,13 +96,6 @@ func dynamicToSessionCredential(ctx context.Context, cred credential.Dynamic) (* } return &pb.SessionCredential{ - CredentialLibrary: &pb.CredentialLibrary{ - Id: l.GetPublicId(), - Name: l.GetName(), - Description: l.GetDescription(), - CredentialStoreId: l.GetStoreId(), - Type: subtypes.SubtypeFromId(credentialDomain, l.GetPublicId()).String(), - }, CredentialSource: &pb.CredentialSource{ Id: l.GetPublicId(), Name: l.GetName(), diff --git a/internal/daemon/controller/handlers/targets/target_service.go b/internal/daemon/controller/handlers/targets/target_service.go index 9c85c058e..8f7aba70e 100644 --- a/internal/daemon/controller/handlers/targets/target_service.go +++ b/internal/daemon/controller/handlers/targets/target_service.go @@ -65,9 +65,6 @@ var ( action.AddHostSources, action.SetHostSources, action.RemoveHostSources, - action.AddCredentialLibraries, - action.SetCredentialLibraries, - action.RemoveCredentialLibraries, action.AddCredentialSources, action.SetCredentialSources, action.RemoveCredentialSources, @@ -492,7 +489,7 @@ func (s Service) AddTargetHostSources(ctx context.Context, req *pbs.AddTargetHos if err := validateAddHostSourcesRequest(req); err != nil { return nil, err } - authResults := s.authResult(ctx, req.GetId(), action.AddHostSets) + authResults := s.authResult(ctx, req.GetId(), action.AddHostSources) if authResults.Error != nil { return nil, authResults.Error } @@ -530,7 +527,7 @@ func (s Service) SetTargetHostSources(ctx context.Context, req *pbs.SetTargetHos if err := validateSetHostSourcesRequest(req); err != nil { return nil, err } - authResults := s.authResult(ctx, req.GetId(), action.SetHostSets) + authResults := s.authResult(ctx, req.GetId(), action.SetHostSources) if authResults.Error != nil { return nil, authResults.Error } @@ -563,12 +560,12 @@ func (s Service) SetTargetHostSources(ctx context.Context, req *pbs.SetTargetHos // RemoveTargetHostSources implements the interface pbs.TargetServiceServer. func (s Service) RemoveTargetHostSources(ctx context.Context, req *pbs.RemoveTargetHostSourcesRequest) (*pbs.RemoveTargetHostSourcesResponse, error) { - const op = "targets.(Service).RemoveTargetHostSets" + const op = "targets.(Service).RemoveTargetHostSources" if err := validateRemoveHostSourcesRequest(req); err != nil { return nil, err } - authResults := s.authResult(ctx, req.GetId(), action.RemoveHostSets) + authResults := s.authResult(ctx, req.GetId(), action.RemoveHostSources) if authResults.Error != nil { return nil, authResults.Error } @@ -599,120 +596,6 @@ func (s Service) RemoveTargetHostSources(ctx context.Context, req *pbs.RemoveTar return &pbs.RemoveTargetHostSourcesResponse{Item: item}, nil } -// DEPRECATED: AddTargetCredentialLibraries implements the interface pbs.TargetServiceServer. -func (s Service) AddTargetCredentialLibraries(ctx context.Context, req *pbs.AddTargetCredentialLibrariesRequest) (*pbs.AddTargetCredentialLibrariesResponse, error) { - const op = "targets.(Service).AddTargetCredentialLibraries" - - if err := validateAddLibrariesRequest(req); err != nil { - return nil, err - } - authResults := s.authResult(ctx, req.GetId(), action.AddCredentialLibraries) - if authResults.Error != nil { - return nil, authResults.Error - } - t, ts, cl, err := s.addCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion()) - if err != nil { - return nil, err - } - - outputFields, ok := requests.OutputFields(ctx) - if !ok { - return nil, errors.New(ctx, errors.Internal, op, "no request context found") - } - - outputOpts := make([]handlers.Option, 0, 3) - outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) - if outputFields.Has(globals.ScopeField) { - outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) - } - if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings())) - } - - item, err := toProto(ctx, t, ts, cl, outputOpts...) - if err != nil { - return nil, err - } - - return &pbs.AddTargetCredentialLibrariesResponse{Item: item}, nil -} - -// DEPRECATED: SetTargetCredentialLibraries implements the interface pbs.TargetServiceServer. -func (s Service) SetTargetCredentialLibraries(ctx context.Context, req *pbs.SetTargetCredentialLibrariesRequest) (*pbs.SetTargetCredentialLibrariesResponse, error) { - const op = "targets.(Service).SetTargetCredentialLibraries" - - if err := validateSetLibrariesRequest(req); err != nil { - return nil, err - } - authResults := s.authResult(ctx, req.GetId(), action.SetCredentialLibraries) - if authResults.Error != nil { - return nil, authResults.Error - } - t, ts, cl, err := s.setCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion()) - if err != nil { - return nil, err - } - - outputFields, ok := requests.OutputFields(ctx) - if !ok { - return nil, errors.New(ctx, errors.Internal, op, "no request context found") - } - - outputOpts := make([]handlers.Option, 0, 3) - outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) - if outputFields.Has(globals.ScopeField) { - outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) - } - if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings())) - } - - item, err := toProto(ctx, t, ts, cl, outputOpts...) - if err != nil { - return nil, err - } - - return &pbs.SetTargetCredentialLibrariesResponse{Item: item}, nil -} - -// DEPRECATED: RemoveTargetCredentialLibraries implements the interface pbs.TargetServiceServer. -func (s Service) RemoveTargetCredentialLibraries(ctx context.Context, req *pbs.RemoveTargetCredentialLibrariesRequest) (*pbs.RemoveTargetCredentialLibrariesResponse, error) { - const op = "targets.(Service).RemoveTargetCredentialLibraries" - - if err := validateRemoveLibrariesRequest(req); err != nil { - return nil, err - } - authResults := s.authResult(ctx, req.GetId(), action.RemoveCredentialLibraries) - if authResults.Error != nil { - return nil, authResults.Error - } - t, ts, cl, err := s.removeCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialLibraryIds(), nil, req.GetVersion()) - if err != nil { - return nil, err - } - - outputFields, ok := requests.OutputFields(ctx) - if !ok { - return nil, errors.New(ctx, errors.Internal, op, "no request context found") - } - - outputOpts := make([]handlers.Option, 0, 3) - outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields)) - if outputFields.Has(globals.ScopeField) { - outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope)) - } - if outputFields.Has(globals.AuthorizedActionsField) { - outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings())) - } - - item, err := toProto(ctx, t, ts, cl, outputOpts...) - if err != nil { - return nil, err - } - - return &pbs.RemoveTargetCredentialLibrariesResponse{Item: item}, nil -} - // AddTargetCredentialSources implements the interface pbs.TargetServiceServer. func (s Service) AddTargetCredentialSources(ctx context.Context, req *pbs.AddTargetCredentialSourcesRequest) (*pbs.AddTargetCredentialSourcesResponse, error) { const op = "targets.(Service).AddTargetCredentialSources" @@ -722,7 +605,12 @@ func (s Service) AddTargetCredentialSources(ctx context.Context, req *pbs.AddTar } authResults := s.authResult(ctx, req.GetId(), action.AddCredentialSources) if authResults.Error != nil { - return nil, authResults.Error + // TODO AddCredentialLibraries was deprecated but grant actions were never migrated + // remove this check once actions have been migrated + authResults = s.authResult(ctx, req.GetId(), action.AddCredentialLibraries) + if authResults.Error != nil { + return nil, authResults.Error + } } t, ts, cl, err := s.addCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion()) if err != nil { @@ -760,7 +648,12 @@ func (s Service) SetTargetCredentialSources(ctx context.Context, req *pbs.SetTar } authResults := s.authResult(ctx, req.GetId(), action.SetCredentialSources) if authResults.Error != nil { - return nil, authResults.Error + // TODO SetCredentialLibraries was deprecated but grant actions were never migrated + // remove this check once actions have been migrated + authResults = s.authResult(ctx, req.GetId(), action.SetCredentialLibraries) + if authResults.Error != nil { + return nil, authResults.Error + } } t, ts, cl, err := s.setCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion()) if err != nil { @@ -798,7 +691,12 @@ func (s Service) RemoveTargetCredentialSources(ctx context.Context, req *pbs.Rem } authResults := s.authResult(ctx, req.GetId(), action.RemoveCredentialSources) if authResults.Error != nil { - return nil, authResults.Error + // TODO RemoveCredentialLibraries was deprecated but grant actions were never migrated + // remove this check once actions have been migrated + authResults = s.authResult(ctx, req.GetId(), action.RemoveCredentialLibraries) + if authResults.Error != nil { + return nil, authResults.Error + } } t, ts, cl, err := s.removeCredentialSourcesInRepo(ctx, req.GetId(), req.GetApplicationCredentialSourceIds(), req.GetEgressCredentialSourceIds(), req.GetVersion()) if err != nil { @@ -1638,8 +1536,6 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou var appCredSources, egressCredSources []*pb.CredentialSource var appCredSourceIds, egressCredSourceIds []string - var appCredLibraries []*pb.CredentialLibrary - var appCredLibIds []string for _, cs := range credSources { switch cs.CredentialPurpose() { @@ -1650,16 +1546,6 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou CredentialStoreId: cs.CredentialStoreId(), }) - // ApplicationCredentialLibrariesField is deprecated and should only be populated - // for library type sources - if cs.Type() == target.LibraryCredentialSourceType { - appCredLibIds = append(appCredLibIds, cs.Id()) - appCredLibraries = append(appCredLibraries, &pb.CredentialLibrary{ - Id: cs.Id(), - CredentialStoreId: cs.CredentialStoreId(), - }) - } - case credential.EgressPurpose: egressCredSources = append(egressCredSources, &pb.CredentialSource{ Id: cs.Id(), @@ -1672,15 +1558,9 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou } } - if outputFields.Has(globals.ApplicationCredentialLibraryIdsField) { - out.ApplicationCredentialLibraryIds = appCredLibIds - } if outputFields.Has(globals.ApplicationCredentialSourceIdsField) { out.ApplicationCredentialSourceIds = appCredSourceIds } - if outputFields.Has(globals.ApplicationCredentialLibrariesField) { - out.ApplicationCredentialLibraries = appCredLibraries - } if outputFields.Has(globals.ApplicationCredentialSourcesField) { out.ApplicationCredentialSources = appCredSources } @@ -1960,72 +1840,6 @@ func validateRemoveHostSourcesRequest(req *pbs.RemoveTargetHostSourcesRequest) e return nil } -func validateAddLibrariesRequest(req *pbs.AddTargetCredentialLibrariesRequest) error { - badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) { - badFields[globals.IdField] = "Incorrectly formatted identifier." - } - if req.GetVersion() == 0 { - badFields[globals.VersionField] = "Required field." - } - if len(req.GetApplicationCredentialLibraryIds()) == 0 { - badFields[globals.ApplicationCredentialLibraryIdsField] = "Must be non-empty." - } - for _, cl := range req.GetApplicationCredentialLibraryIds() { - if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { - badFields[globals.ApplicationCredentialLibraryIdsField] = fmt.Sprintf("Incorrectly formatted credential library identifier %q.", cl) - break - } - } - if len(badFields) > 0 { - return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) - } - return nil -} - -func validateSetLibrariesRequest(req *pbs.SetTargetCredentialLibrariesRequest) error { - badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) { - badFields[globals.IdField] = "Incorrectly formatted identifier." - } - if req.GetVersion() == 0 { - badFields[globals.VersionField] = "Required field." - } - for _, cl := range req.GetApplicationCredentialLibraryIds() { - if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { - badFields[globals.ApplicationCredentialLibraryIdsField] = fmt.Sprintf("Incorrectly formatted credential library identifier %q.", cl) - break - } - } - if len(badFields) > 0 { - return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) - } - return nil -} - -func validateRemoveLibrariesRequest(req *pbs.RemoveTargetCredentialLibrariesRequest) error { - badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) { - badFields[globals.IdField] = "Incorrectly formatted identifier." - } - if req.GetVersion() == 0 { - badFields[globals.VersionField] = "Required field." - } - if len(req.GetApplicationCredentialLibraryIds()) == 0 { - badFields[globals.ApplicationCredentialLibraryIdsField] = "Must be non-empty." - } - for _, cl := range req.GetApplicationCredentialLibraryIds() { - if !handlers.ValidId(handlers.Id(cl), vault.CredentialLibraryPrefix) { - badFields[globals.ApplicationCredentialLibraryIdsField] = fmt.Sprintf("Incorrectly formatted credential library identifier %q.", cl) - break - } - } - if len(badFields) > 0 { - return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) - } - return nil -} - func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequest) error { badFields := map[string]string{} if !handlers.ValidId(handlers.Id(req.GetId()), target.Prefixes()...) { diff --git a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go index e8fe57ae8..cdd60632a 100644 --- a/internal/daemon/controller/handlers/targets/tcp/target_service_test.go +++ b/internal/daemon/controller/handlers/targets/tcp/target_service_test.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "path" - "sort" "strings" "sync" "testing" @@ -71,16 +70,13 @@ var testAuthorizedActions = []string{ "add-host-sources", "set-host-sources", "remove-host-sources", - "add-credential-libraries", - "set-credential-libraries", - "remove-credential-libraries", "add-credential-sources", "set-credential-sources", "remove-credential-sources", "authorize-session", } -func testService(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapper) (targets.Service, error) { +func testService(t *testing.T, ctx context.Context, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapper) (targets.Service, error) { rw := db.New(conn) sche := scheduler.TestScheduler(t, conn, wrapper) repoFn := func() (*target.Repository, error) { @@ -107,7 +103,7 @@ func testService(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapp staticCredRepoFn := func() (*credstatic.Repository, error) { return credstatic.NewRepository(context.Background(), rw, rw, kms) } - return targets.NewService(context.Background(), kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn) + return targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, vaultCredRepoFn, staticCredRepoFn) } func TestGet(t *testing.T) { @@ -183,7 +179,7 @@ func TestGet(t *testing.T) { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(err, "Couldn't create a new host set service.") got, gErr := s.GetTarget(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) @@ -296,7 +292,7 @@ func TestList(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(err, "Couldn't create new host set service.") // Test with non-anon user @@ -350,7 +346,7 @@ func TestDelete(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test") - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Couldn't create a new target service.") cases := []struct { @@ -414,7 +410,7 @@ func TestDelete_twice(t *testing.T) { tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test") - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(err, "Couldn't create a new target service.") req := &pbs.DeleteTargetRequest{ Id: tar.GetPublicId(), @@ -559,7 +555,7 @@ func TestCreate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(err, "Failed to create a new host set service.") got, gErr := s.CreateTarget(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) @@ -647,7 +643,7 @@ func TestUpdate(t *testing.T) { Id: tar.GetPublicId(), } - tested, err := testService(t, conn, kms, wrapper) + tested, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Failed to create a new host set service.") cases := []struct { @@ -1021,7 +1017,7 @@ func TestUpdate_BadVersion(t *testing.T) { gtar, _, _, err := repo.CreateTarget(context.Background(), tar) require.NoError(t, err) - tested, err := testService(t, conn, kms, wrapper) + tested, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Failed to create a new host set service.") upTar, err := tested.UpdateTarget(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), &pbs.UpdateTargetRequest{ @@ -1050,7 +1046,7 @@ func TestAddTargetHostSets(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Error when getting new target service.") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -1186,7 +1182,7 @@ func TestSetTargetHostSets(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Error when getting new host set service.") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -1316,7 +1312,7 @@ func TestRemoveTargetHostSets(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Error when getting new host set service.") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -1458,7 +1454,7 @@ func TestAddTargetHostSources(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Error when getting new target service.") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -1594,7 +1590,7 @@ func TestSetTargetHostSources(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Error when getting new host set service.") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -1718,7 +1714,7 @@ func TestRemoveTargetHostSources(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Error when getting new host set service.") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] @@ -1847,406 +1843,6 @@ func TestRemoveTargetHostSources(t *testing.T) { } } -func TestAddTargetLibraries(t *testing.T) { - conn, _ := db.TestSetup(t, "postgres") - wrapper := db.TestWrapper(t) - kms := kms.TestKms(t, conn, wrapper) - - iamRepo := iam.TestRepo(t, conn, wrapper) - iamRepoFn := func() (*iam.Repository, error) { - return iamRepo, nil - } - - _, proj := iam.TestScopes(t, iamRepo) - - s, err := testService(t, conn, kms, wrapper) - require.NoError(t, err, "Error when getting new target service.") - - store := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] - cls := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 2) - - ctx := context.Background() - addCases := []struct { - name string - tar target.Target - addLibraries []string - resultLibraryIds []string - }{ - { - name: "Add library on empty target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty for libraries"), - addLibraries: []string{cls[1].GetPublicId()}, - resultLibraryIds: []string{cls[1].GetPublicId()}, - }, - { - name: "Add library on populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for libraries", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), - addLibraries: []string{cls[1].GetPublicId()}, - resultLibraryIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, - }, - { - name: "Add duplicated libraries on populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated for libraries", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), - addLibraries: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, - resultLibraryIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, - }, - } - - for _, tc := range addCases { - t.Run(tc.name, func(t *testing.T) { - req := &pbs.AddTargetCredentialLibrariesRequest{ - Id: tc.tar.GetPublicId(), - Version: tc.tar.GetVersion(), - ApplicationCredentialLibraryIds: tc.addLibraries, - } - - got, err := s.AddTargetCredentialLibraries(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req) - require.NoError(t, err, "Got error: %v", s) - - assert.ElementsMatch(t, tc.resultLibraryIds, got.GetItem().GetApplicationCredentialLibraryIds()) - - assert.Equal(t, len(tc.resultLibraryIds), len(got.GetItem().GetApplicationCredentialLibraries())) - - wantTemplate := &pb.CredentialLibrary{ - CredentialStoreId: store.GetPublicId(), - } - for _, cl := range got.GetItem().GetApplicationCredentialLibraries() { - cl.Id = "" - assert.Empty(t, cmp.Diff(wantTemplate, cl, protocmp.Transform())) - } - }) - } - - tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test") - - failCases := []struct { - name string - req *pbs.AddTargetCredentialLibrariesRequest - err error - }{ - { - name: "Bad target id", - req: &pbs.AddTargetCredentialLibrariesRequest{ - Id: "bad id", - Version: tar.GetVersion(), - ApplicationCredentialLibraryIds: []string{ - cls[0].GetPublicId(), - }, - }, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, - { - name: "Bad version", - req: &pbs.AddTargetCredentialLibrariesRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion() + 2, - ApplicationCredentialLibraryIds: []string{ - cls[0].GetPublicId(), - }, - }, - err: handlers.ApiErrorWithCode(codes.Internal), - }, - { - name: "Empty library list", - req: &pbs.AddTargetCredentialLibrariesRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion(), - }, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, - { - name: "Incorrect library id", - req: &pbs.AddTargetCredentialLibrariesRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion(), - ApplicationCredentialLibraryIds: []string{"incorrect"}, - }, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, - } - for _, tc := range failCases { - t.Run(tc.name, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) - _, gErr := s.AddTargetCredentialLibraries(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) - if tc.err != nil { - require.Error(gErr) - assert.True(errors.Is(gErr, tc.err), "AddTargetCredentialLibraries(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) - } - }) - } -} - -func TestSetTargetLibraries(t *testing.T) { - conn, _ := db.TestSetup(t, "postgres") - wrapper := db.TestWrapper(t) - kms := kms.TestKms(t, conn, wrapper) - - iamRepo := iam.TestRepo(t, conn, wrapper) - iamRepoFn := func() (*iam.Repository, error) { - return iamRepo, nil - } - - _, proj := iam.TestScopes(t, iamRepo) - - s, err := testService(t, conn, kms, wrapper) - require.NoError(t, err, "Error when getting new target service.") - - store := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] - cls := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 2) - - resultingTargetLibrary := func(id string) *pb.CredentialLibrary { - return &pb.CredentialLibrary{ - Id: id, - CredentialStoreId: store.GetPublicId(), - } - } - - ctx := context.Background() - setCases := []struct { - name string - tar target.Target - setLibraries []string - resultLibraryIds []string - resultLibraries []*pb.CredentialLibrary - }{ - { - name: "Set on empty target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty"), - setLibraries: []string{cls[1].GetPublicId()}, - resultLibraryIds: []string{cls[1].GetPublicId()}, - }, - { - name: "Set on populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), - setLibraries: []string{cls[1].GetPublicId()}, - resultLibraryIds: []string{cls[1].GetPublicId()}, - }, - { - name: "Set duplicate libraries on populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicate", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), - setLibraries: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, - resultLibraryIds: []string{cls[1].GetPublicId()}, - }, - { - name: "Set empty on populated target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "another populated", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), - setLibraries: []string{}, - resultLibraryIds: nil, - }, - } - for _, tc := range setCases { - t.Run(tc.name, func(t *testing.T) { - req := &pbs.SetTargetCredentialLibrariesRequest{ - Id: tc.tar.GetPublicId(), - Version: tc.tar.GetVersion(), - ApplicationCredentialLibraryIds: tc.setLibraries, - } - - got, err := s.SetTargetCredentialLibraries(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req) - require.NoError(t, err, "Got error: %v", s) - assert.ElementsMatch(t, tc.resultLibraryIds, got.GetItem().GetApplicationCredentialLibraryIds()) - - if len(tc.resultLibraries) != 0 { - sort.Slice(tc.resultLibraries, func(i, j int) bool { - return tc.resultLibraries[i].GetId() < tc.resultLibraries[j].GetId() - }) - sort.Slice(got.GetItem().ApplicationCredentialLibraryIds, func(i, j int) bool { - return got.GetItem().ApplicationCredentialLibraryIds[i] < got.GetItem().ApplicationCredentialLibraryIds[j] - }) - assert.Empty(t, cmp.Diff(tc.resultLibraries, got.GetItem().GetApplicationCredentialLibraryIds(), protocmp.Transform())) - } else { - assert.Equal(t, len(tc.resultLibraryIds), len(got.GetItem().GetApplicationCredentialLibraryIds())) - for _, cl := range got.GetItem().GetApplicationCredentialLibraries() { - assert.Empty(t, cmp.Diff(resultingTargetLibrary(cl.GetId()), cl, protocmp.Transform())) - } - } - }) - } - - tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test name") - - failCases := []struct { - name string - req *pbs.SetTargetCredentialLibrariesRequest - err error - }{ - { - name: "Bad target Id", - req: &pbs.SetTargetCredentialLibrariesRequest{ - Id: "bad id", - Version: tar.GetVersion(), - ApplicationCredentialLibraryIds: []string{cls[0].GetPublicId()}, - }, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, - { - name: "Bad version", - req: &pbs.SetTargetCredentialLibrariesRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion() + 3, - ApplicationCredentialLibraryIds: []string{cls[0].GetPublicId()}, - }, - err: handlers.ApiErrorWithCode(codes.Internal), - }, - { - name: "Bad library id", - req: &pbs.SetTargetCredentialLibrariesRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion(), - ApplicationCredentialLibraryIds: []string{"invalid"}, - }, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, - } - for _, tc := range failCases { - t.Run(tc.name, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) - _, gErr := s.SetTargetCredentialLibraries(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) - if tc.err != nil { - require.Error(gErr) - assert.True(errors.Is(gErr, tc.err), "SetTargetCredentialLibraries(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) - } - }) - } -} - -func TestRemoveTargetLibraries(t *testing.T) { - conn, _ := db.TestSetup(t, "postgres") - wrapper := db.TestWrapper(t) - kms := kms.TestKms(t, conn, wrapper) - - iamRepo := iam.TestRepo(t, conn, wrapper) - iamRepoFn := func() (*iam.Repository, error) { - return iamRepo, nil - } - - _, proj := iam.TestScopes(t, iamRepo) - - s, err := testService(t, conn, kms, wrapper) - require.NoError(t, err, "Error when getting new target service.") - - store := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] - cls := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 2) - - ctx := context.Background() - removeCases := []struct { - name string - tar target.Target - removeLibs []string - resultLibIds []string - wantErr bool - }{ - { - name: "Remove from empty", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty"), - removeLibs: []string{cls[1].GetPublicId()}, - wantErr: true, - }, - { - name: "Remove 1 of 2 libraries", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove partial", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), - removeLibs: []string{cls[1].GetPublicId()}, - resultLibIds: []string{cls[0].GetPublicId()}, - }, - { - name: "Remove 1 duplicate set of 2 libraries", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove duplicate", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), - removeLibs: []string{ - cls[1].GetPublicId(), cls[1].GetPublicId(), - }, - resultLibIds: []string{cls[0].GetPublicId()}, - }, - { - name: "Remove all libraries from target", - tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "remove all", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose), target.TestNewCredentialLibrary("", cls[1].GetPublicId(), credential.ApplicationPurpose)})), - removeLibs: []string{ - cls[0].GetPublicId(), cls[1].GetPublicId(), - }, - resultLibIds: []string{}, - }, - } - - for _, tc := range removeCases { - t.Run(tc.name, func(t *testing.T) { - req := &pbs.RemoveTargetCredentialLibrariesRequest{ - Id: tc.tar.GetPublicId(), - Version: tc.tar.GetVersion(), - ApplicationCredentialLibraryIds: tc.removeLibs, - } - - got, err := s.RemoveTargetCredentialLibraries(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req) - if tc.wantErr { - assert.Error(t, err) - return - } - require.NoError(t, err, "Got error: %v", s) - - assert.ElementsMatch(t, tc.resultLibIds, got.GetItem().GetApplicationCredentialLibraryIds()) - }) - } - - tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "testing") - - failCases := []struct { - name string - req *pbs.RemoveTargetCredentialLibrariesRequest - err error - }{ - { - name: "Bad version", - req: &pbs.RemoveTargetCredentialLibrariesRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion() + 3, - ApplicationCredentialLibraryIds: []string{ - cls[0].GetPublicId(), - }, - }, - err: handlers.ApiErrorWithCode(codes.Internal), - }, - { - name: "Bad target Id", - req: &pbs.RemoveTargetCredentialLibrariesRequest{ - Id: "bad id", - Version: tar.GetVersion(), - ApplicationCredentialLibraryIds: []string{ - cls[0].GetPublicId(), - }, - }, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, - { - name: "Empty libraries", - req: &pbs.RemoveTargetCredentialLibrariesRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion(), - ApplicationCredentialLibraryIds: []string{}, - }, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, - { - name: "Invalid library ids", - req: &pbs.RemoveTargetCredentialLibrariesRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion(), - ApplicationCredentialLibraryIds: []string{ - "invalid", - }, - }, - err: handlers.ApiErrorWithCode(codes.InvalidArgument), - }, - } - for _, tc := range failCases { - t.Run(tc.name, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) - _, gErr := s.RemoveTargetCredentialLibraries(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req) - if tc.err != nil { - require.Error(gErr) - assert.True(errors.Is(gErr, tc.err), "RemoveTargetCredentialLibraries(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) - } - }) - } -} - func TestAddTargetCredentialSources(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) @@ -2259,7 +1855,7 @@ func TestAddTargetCredentialSources(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Error when getting new target service.") storeVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] @@ -2274,14 +1870,12 @@ func TestAddTargetCredentialSources(t *testing.T) { tar target.Target addSources []string resultSourceIds []string - resultLibIds []string }{ { name: "Add library on empty target", tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty for lib sources"), addSources: []string{cls[1].GetPublicId()}, resultSourceIds: []string{cls[1].GetPublicId()}, - resultLibIds: []string{cls[1].GetPublicId()}, }, { name: "Add static cred on empty target", @@ -2294,21 +1888,18 @@ func TestAddTargetCredentialSources(t *testing.T) { tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for lib-lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), addSources: []string{cls[1].GetPublicId()}, resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, - resultLibIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, { name: "Add library on static cred populated target", tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for lib-static sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose)})), addSources: []string{cls[1].GetPublicId()}, resultSourceIds: []string{creds[0].GetPublicId(), cls[1].GetPublicId()}, - resultLibIds: []string{cls[1].GetPublicId()}, }, { name: "Add static cred on library populated target", tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated for static-lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), addSources: []string{creds[1].GetPublicId()}, resultSourceIds: []string{cls[0].GetPublicId(), creds[1].GetPublicId()}, - resultLibIds: []string{cls[0].GetPublicId()}, }, { name: "Add static cred on static cred populated target", @@ -2321,14 +1912,12 @@ func TestAddTargetCredentialSources(t *testing.T) { tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated for lib sources", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId(), creds[1].GetPublicId()}, resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId()}, - resultLibIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, { name: "Add duplicated sources on static cred populated target", tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicated for static sources", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose)})), addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId(), creds[1].GetPublicId()}, resultSourceIds: []string{creds[0].GetPublicId(), cls[1].GetPublicId(), creds[1].GetPublicId()}, - resultLibIds: []string{cls[1].GetPublicId()}, }, } @@ -2345,9 +1934,6 @@ func TestAddTargetCredentialSources(t *testing.T) { assert.ElementsMatch(t, tc.resultSourceIds, got.GetItem().GetApplicationCredentialSourceIds()) assert.Equal(t, len(tc.resultSourceIds), len(got.GetItem().GetApplicationCredentialSources())) - - assert.ElementsMatch(t, tc.resultLibIds, got.GetItem().GetApplicationCredentialLibraryIds()) - assert.Equal(t, len(tc.resultLibIds), len(got.GetItem().GetApplicationCredentialLibraries())) }) } @@ -2422,7 +2008,7 @@ func TestSetTargetCredentialSources(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Error when getting new target service.") storeVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] @@ -2437,15 +2023,12 @@ func TestSetTargetCredentialSources(t *testing.T) { tar target.Target setCredentialSources []string resultCredentialSourceIds []string - resultCredentialSources []*pb.CredentialLibrary - resultLibIds []string }{ { name: "Set library on empty target", tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "empty library"), setCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, - resultLibIds: []string{cls[1].GetPublicId()}, }, { name: "Set static on empty target", @@ -2458,7 +2041,6 @@ func TestSetTargetCredentialSources(t *testing.T) { tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated library-library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, - resultLibIds: []string{cls[1].GetPublicId()}, }, { name: "Set static on library populated target", @@ -2471,7 +2053,6 @@ func TestSetTargetCredentialSources(t *testing.T) { tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "populated library-static", target.WithStaticCredentials([]*target.StaticCredential{target.TestNewStaticCredential("", creds[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, - resultLibIds: []string{cls[1].GetPublicId()}, }, { name: "Set static on static populated target", @@ -2484,7 +2065,6 @@ func TestSetTargetCredentialSources(t *testing.T) { tar: tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "duplicate library", target.WithCredentialLibraries([]*target.CredentialLibrary{target.TestNewCredentialLibrary("", cls[0].GetPublicId(), credential.ApplicationPurpose)})), setCredentialSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, - resultLibIds: []string{cls[1].GetPublicId()}, }, { name: "Set duplicate static on populated target", @@ -2512,19 +2092,7 @@ func TestSetTargetCredentialSources(t *testing.T) { got, err := s.SetTargetCredentialSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req) require.NoError(t, err, "Got error: %v", s) assert.ElementsMatch(t, tc.resultCredentialSourceIds, got.GetItem().GetApplicationCredentialSourceIds()) - - if len(tc.resultCredentialSources) != 0 { - sort.Slice(tc.resultCredentialSources, func(i, j int) bool { - return tc.resultCredentialSources[i].GetId() < tc.resultCredentialSources[j].GetId() - }) - sort.Slice(got.GetItem().ApplicationCredentialSourceIds, func(i, j int) bool { - return got.GetItem().ApplicationCredentialSourceIds[i] < got.GetItem().ApplicationCredentialSourceIds[j] - }) - assert.Empty(t, cmp.Diff(tc.resultCredentialSources, got.GetItem().GetApplicationCredentialSourceIds(), protocmp.Transform())) - } else { - assert.Equal(t, len(tc.resultCredentialSourceIds), len(got.GetItem().GetApplicationCredentialSourceIds())) - assert.Equal(t, len(tc.resultLibIds), len(got.GetItem().GetApplicationCredentialLibraries())) - } + assert.Equal(t, len(tc.resultCredentialSourceIds), len(got.GetItem().GetApplicationCredentialSources())) }) } @@ -2587,7 +2155,7 @@ func TestRemoveTargetCredentialSources(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) - s, err := testService(t, conn, kms, wrapper) + s, err := testService(t, context.Background(), conn, kms, wrapper) require.NoError(t, err, "Error when getting new target service.") csVault := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] @@ -2603,7 +2171,6 @@ func TestRemoveTargetCredentialSources(t *testing.T) { removeCredentialSources []string resultCredentialSourceIds []string wantErr bool - resultLibIds []string }{ { name: "Remove library from empty", @@ -2626,7 +2193,6 @@ func TestRemoveTargetCredentialSources(t *testing.T) { })), removeCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[0].GetPublicId()}, - resultLibIds: []string{cls[0].GetPublicId()}, }, { name: "Remove 1 of 2 static credentials", @@ -2649,7 +2215,6 @@ func TestRemoveTargetCredentialSources(t *testing.T) { cls[1].GetPublicId(), cls[1].GetPublicId(), }, resultCredentialSourceIds: []string{cls[0].GetPublicId()}, - resultLibIds: []string{cls[0].GetPublicId()}, }, { name: "Remove 1 duplicate set of 2 static credentials", @@ -2680,7 +2245,6 @@ func TestRemoveTargetCredentialSources(t *testing.T) { resultCredentialSourceIds: []string{ cls[0].GetPublicId(), creds[1].GetPublicId(), }, - resultLibIds: []string{cls[0].GetPublicId()}, }, { name: "Remove all sources from target", @@ -2718,9 +2282,6 @@ func TestRemoveTargetCredentialSources(t *testing.T) { assert.ElementsMatch(t, tc.resultCredentialSourceIds, got.GetItem().GetApplicationCredentialSourceIds()) assert.Equal(t, len(tc.resultCredentialSourceIds), len(got.GetItem().GetApplicationCredentialSources())) - - assert.ElementsMatch(t, tc.resultLibIds, got.GetItem().GetApplicationCredentialLibraryIds()) - assert.Equal(t, len(tc.resultLibIds), len(got.GetItem().GetApplicationCredentialLibraries())) }) } @@ -2956,10 +2517,10 @@ func TestAuthorizeSession(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, target.WithDefaultPort(defaultPort)) - apiTar, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion(), - HostSetIds: []string{tc.hostSourceId}, + apiTar, err := s.AddTargetHostSources(ctx, &pbs.AddTargetHostSourcesRequest{ + Id: tar.GetPublicId(), + Version: tar.GetVersion(), + HostSourceIds: []string{tc.hostSourceId}, }) require.NoError(t, err) _, err = s.AddTargetCredentialSources(ctx, @@ -3017,13 +2578,6 @@ func TestAuthorizeSession(t *testing.T) { Type: "tcp", Endpoint: fmt.Sprintf("tcp://%s", tc.wantedEndpoint), Credentials: []*pb.SessionCredential{{ - CredentialLibrary: &pb.CredentialLibrary{ - Id: clsResp.GetItem().GetId(), - Name: clsResp.GetItem().GetName().GetValue(), - Description: clsResp.GetItem().GetDescription().GetValue(), - CredentialStoreId: vaultStore.GetPublicId(), - Type: vault.Subtype.String(), - }, CredentialSource: &pb.CredentialSource{ Id: clsResp.GetItem().GetId(), Name: clsResp.GetItem().GetName().GetValue(), @@ -3225,13 +2779,6 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { wantedHostId: h.GetPublicId(), wantedEndpoint: h.GetAddress(), wantedCred: &pb.SessionCredential{ - CredentialLibrary: &pb.CredentialLibrary{ - Id: clsRespUnspecified.GetItem().GetId(), - Name: clsRespUnspecified.GetItem().GetName().GetValue(), - Description: clsRespUnspecified.GetItem().GetDescription().GetValue(), - CredentialStoreId: vaultStore.GetPublicId(), - Type: vault.Subtype.String(), - }, CredentialSource: &pb.CredentialSource{ Id: clsRespUnspecified.GetItem().GetId(), Name: clsRespUnspecified.GetItem().GetName().GetValue(), @@ -3248,13 +2795,6 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { wantedHostId: h.GetPublicId(), wantedEndpoint: h.GetAddress(), wantedCred: &pb.SessionCredential{ - CredentialLibrary: &pb.CredentialLibrary{ - Id: clsRespUserPassword.GetItem().GetId(), - Name: clsRespUserPassword.GetItem().GetName().GetValue(), - Description: clsRespUserPassword.GetItem().GetDescription().GetValue(), - CredentialStoreId: vaultStore.GetPublicId(), - Type: vault.Subtype.String(), - }, CredentialSource: &pb.CredentialSource{ Id: clsRespUserPassword.GetItem().GetId(), Name: clsRespUserPassword.GetItem().GetName().GetValue(), @@ -3281,13 +2821,6 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { wantedHostId: h.GetPublicId(), wantedEndpoint: h.GetAddress(), wantedCred: &pb.SessionCredential{ - CredentialLibrary: &pb.CredentialLibrary{ - Id: clsRespUserPasswordWithMapping.GetItem().GetId(), - Name: clsRespUserPasswordWithMapping.GetItem().GetName().GetValue(), - Description: clsRespUserPasswordWithMapping.GetItem().GetDescription().GetValue(), - CredentialStoreId: vaultStore.GetPublicId(), - Type: vault.Subtype.String(), - }, CredentialSource: &pb.CredentialSource{ Id: clsRespUserPasswordWithMapping.GetItem().GetId(), Name: clsRespUserPasswordWithMapping.GetItem().GetName().GetValue(), @@ -3339,10 +2872,10 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) { t.Run(tc.name, func(t *testing.T) { const defaultPort = 2 tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), tc.name, target.WithDefaultPort(defaultPort)) - apiTar, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion(), - HostSetIds: []string{tc.hostSourceId}, + apiTar, err := s.AddTargetHostSources(ctx, &pbs.AddTargetHostSourcesRequest{ + Id: tar.GetPublicId(), + Version: tar.GetVersion(), + HostSourceIds: []string{tc.hostSourceId}, }) require.NoError(t, err) _, err = s.AddTargetCredentialSources(ctx, @@ -3484,10 +3017,10 @@ func TestAuthorizeSession_Errors(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] - tr, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{ - Id: tar.GetPublicId(), - Version: tar.GetVersion(), - HostSetIds: []string{hs.GetPublicId()}, + tr, err := s.AddTargetHostSources(ctx, &pbs.AddTargetHostSourcesRequest{ + Id: tar.GetPublicId(), + Version: tar.GetVersion(), + HostSourceIds: []string{hs.GetPublicId()}, }) require.NoError(t, err) return tr.GetItem().GetVersion() @@ -3498,10 +3031,10 @@ func TestAuthorizeSession_Errors(t *testing.T) { 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()}, + apiTar, err := s.AddTargetHostSources(ctx, &pbs.AddTargetHostSourcesRequest{ + Id: tar.GetPublicId(), + Version: tar.GetVersion(), + HostSourceIds: []string{hs.GetPublicId()}, }) require.NoError(t, err) h.Address = fmt.Sprintf("%s:54321", h.GetAddress()) @@ -3517,10 +3050,10 @@ func TestAuthorizeSession_Errors(t *testing.T) { 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()}, + apiTar, err := s.AddTargetHostSources(ctx, &pbs.AddTargetHostSourcesRequest{ + Id: tar.GetPublicId(), + Version: tar.GetVersion(), + HostSourceIds: []string{hs.GetPublicId()}, }) require.NoError(t, err) return apiTar.GetItem().GetVersion() @@ -3540,11 +3073,11 @@ func TestAuthorizeSession_Errors(t *testing.T) { }}) require.NoError(t, err) - tr, err := s.AddTargetCredentialLibraries(ctx, - &pbs.AddTargetCredentialLibrariesRequest{ - Id: tar.GetPublicId(), - ApplicationCredentialLibraryIds: []string{clsResp.GetItem().GetId()}, - Version: tar.GetVersion(), + tr, err := s.AddTargetCredentialSources(ctx, + &pbs.AddTargetCredentialSourcesRequest{ + Id: tar.GetPublicId(), + ApplicationCredentialSourceIds: []string{clsResp.GetItem().GetId()}, + Version: tar.GetVersion(), }) require.NoError(t, err) return tr.GetItem().GetVersion() @@ -3564,11 +3097,11 @@ func TestAuthorizeSession_Errors(t *testing.T) { }}) require.NoError(t, err) - tr, err := s.AddTargetCredentialLibraries(ctx, - &pbs.AddTargetCredentialLibrariesRequest{ - Id: tar.GetPublicId(), - ApplicationCredentialLibraryIds: []string{clsResp.GetItem().GetId()}, - Version: tar.GetVersion(), + tr, err := s.AddTargetCredentialSources(ctx, + &pbs.AddTargetCredentialSourcesRequest{ + Id: tar.GetPublicId(), + ApplicationCredentialSourceIds: []string{clsResp.GetItem().GetId()}, + Version: tar.GetVersion(), }) require.NoError(t, err) return tr.GetItem().GetVersion() @@ -3635,3 +3168,264 @@ func decodeJsonSecret(t *testing.T, in string) map[string]interface{} { require.NoError(t, dec.Decode(&ret)) return ret } + +// Test that deprecated action add-credential-libraries still works +// TODO: this test can be removed when grants are migrated +func TestAddTargetCredentialLibraryPerms(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + atRepoFn := func() (*authtoken.Repository, error) { + return authtoken.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } + serversRepoFn := func() (*servers.Repository, error) { + return servers.NewRepository(rw, rw, kms) + } + + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + org, proj := iam.TestScopes(t, iamRepo) + + storeStatic := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId()) + cred := credstatic.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", storeStatic.GetPublicId(), proj.GetPublicId()) + + cases := []struct { + name string + grant string + wantErr bool + }{ + { + name: "valid-deprecated", + grant: "id=*;type=*;actions=add-credential-libraries", + }, + { + name: "invalid", + grant: "id=*;type=*;actions=authorize-session", + wantErr: true, + }, + { + name: "valid-sources", + grant: "id=*;type=*;actions=add-credential-sources", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) + ctx := auth.NewVerifierContext(requests.NewRequestContext(context.Background()), + iamRepoFn, + atRepoFn, + serversRepoFn, + kms, + &authpb.RequestInfo{ + Token: at.GetToken(), + TokenFormat: uint32(auth.AuthTokenTypeBearer), + PublicId: at.GetPublicId(), + }) + + r := iam.TestRole(t, conn, proj.GetPublicId()) + _ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId()) + _ = iam.TestRoleGrant(t, conn, r.GetPublicId(), tc.grant) + + s, err := testService(t, context.Background(), conn, kms, wrapper) + require.NoError(t, err) + + tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), tc.name) + req := &pbs.AddTargetCredentialSourcesRequest{ + Id: tar.GetPublicId(), + Version: tar.GetVersion(), + ApplicationCredentialSourceIds: []string{cred.GetPublicId()}, + } + + got, err := s.AddTargetCredentialSources(ctx, req) + if tc.wantErr { + require.Error(t, err) + require.Nil(t, got) + return + } + status, ok := status.FromError(err) + require.True(t, ok) + require.NoError(t, err, "Got error: %v", status) + require.NotNil(t, got) + }) + } +} + +// Test that deprecated action add-credential-libraries still works +// TODO: this test can be removed when grants are migrated +func TestSetTargetCredentialLibraryPerms(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + atRepoFn := func() (*authtoken.Repository, error) { + return authtoken.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } + serversRepoFn := func() (*servers.Repository, error) { + return servers.NewRepository(rw, rw, kms) + } + + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + org, proj := iam.TestScopes(t, iamRepo) + + storeStatic := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId()) + cred := credstatic.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", storeStatic.GetPublicId(), proj.GetPublicId()) + + cases := []struct { + name string + grant string + wantErr bool + }{ + { + name: "valid-deprecated", + grant: "id=*;type=*;actions=set-credential-libraries", + }, + { + name: "invalid", + grant: "id=*;type=*;actions=authorize-session", + wantErr: true, + }, + { + name: "valid-sources", + grant: "id=*;type=*;actions=set-credential-sources", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) + ctx := auth.NewVerifierContext(requests.NewRequestContext(context.Background()), + iamRepoFn, + atRepoFn, + serversRepoFn, + kms, + &authpb.RequestInfo{ + Token: at.GetToken(), + TokenFormat: uint32(auth.AuthTokenTypeBearer), + PublicId: at.GetPublicId(), + }) + + r := iam.TestRole(t, conn, proj.GetPublicId()) + _ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId()) + _ = iam.TestRoleGrant(t, conn, r.GetPublicId(), tc.grant) + + s, err := testService(t, context.Background(), conn, kms, wrapper) + require.NoError(t, err) + + tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), tc.name) + req := &pbs.SetTargetCredentialSourcesRequest{ + Id: tar.GetPublicId(), + Version: tar.GetVersion(), + ApplicationCredentialSourceIds: []string{cred.GetPublicId()}, + } + + got, err := s.SetTargetCredentialSources(ctx, req) + if tc.wantErr { + require.Error(t, err) + require.Nil(t, got) + return + } + status, ok := status.FromError(err) + require.True(t, ok) + require.NoError(t, err, "Got error: %v", status) + require.NotNil(t, got) + }) + } +} + +// Test that deprecated action remove-credential-libraries still works +// TODO: this test can be removed when grants are migrated +func TestRemoveTargetCredentialLibraryPerms(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + atRepoFn := func() (*authtoken.Repository, error) { + return authtoken.NewRepository(rw, rw, kms) + } + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } + serversRepoFn := func() (*servers.Repository, error) { + return servers.NewRepository(rw, rw, kms) + } + + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + org, proj := iam.TestScopes(t, iamRepo) + + storeStatic := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId()) + cred := credstatic.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", storeStatic.GetPublicId(), proj.GetPublicId()) + + cases := []struct { + name string + grant string + wantErr bool + }{ + { + name: "valid-deprecated", + grant: "id=*;type=*;actions=remove-credential-libraries", + }, + { + name: "invalid", + grant: "id=*;type=*;actions=authorize-session", + wantErr: true, + }, + { + name: "valid-sources", + grant: "id=*;type=*;actions=remove-credential-sources", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) + ctx := auth.NewVerifierContext(requests.NewRequestContext(context.Background()), + iamRepoFn, + atRepoFn, + serversRepoFn, + kms, + &authpb.RequestInfo{ + Token: at.GetToken(), + TokenFormat: uint32(auth.AuthTokenTypeBearer), + PublicId: at.GetPublicId(), + }) + + r := iam.TestRole(t, conn, proj.GetPublicId()) + _ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId()) + _ = iam.TestRoleGrant(t, conn, r.GetPublicId(), tc.grant) + + s, err := testService(t, context.Background(), conn, kms, wrapper) + require.NoError(t, err) + + tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), tc.name, target.WithStaticCredentials([]*target.StaticCredential{ + target.TestNewStaticCredential("", cred.GetPublicId(), credential.ApplicationPurpose), + })) + + req := &pbs.RemoveTargetCredentialSourcesRequest{ + Id: tar.GetPublicId(), + Version: tar.GetVersion(), + ApplicationCredentialSourceIds: []string{cred.GetPublicId()}, + } + + got, err := s.RemoveTargetCredentialSources(ctx, req) + if tc.wantErr { + require.Error(t, err) + require.Nil(t, got) + return + } + status, ok := status.FromError(err) + require.True(t, ok) + require.NoError(t, err, "Got error: %v", status) + require.NotNil(t, got) + }) + } +} diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 1da680fc7..e83b08ab8 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -2892,52 +2892,6 @@ ] } }, - "/v1/targets/{id}:add-credential-libraries": { - "post": { - "summary": "Deprecated: use AddTargetCredentialSources instead.", - "operationId": "TargetService_AddTargetCredentialLibraries", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/controller.api.resources.targets.v1.Target" - } - } - }, - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "properties": { - "version": { - "type": "integer", - "format": "int64", - "description": "Version is used to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version." - }, - "application_credential_library_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - ], - "tags": [ - "controller.api.services.v1.TargetService" - ] - } - }, "/v1/targets/{id}:add-credential-sources": { "post": { "summary": "Adds existing Credential Sources to a Target.", @@ -3134,52 +3088,6 @@ ] } }, - "/v1/targets/{id}:remove-credential-libraries": { - "post": { - "summary": "Deprecated: use RemoveTargetCredentialSources instead.", - "operationId": "TargetService_RemoveTargetCredentialLibraries", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/controller.api.resources.targets.v1.Target" - } - } - }, - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "properties": { - "version": { - "type": "integer", - "format": "int64", - "description": "Version is used to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version." - }, - "application_credential_library_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - ], - "tags": [ - "controller.api.services.v1.TargetService" - ] - } - }, "/v1/targets/{id}:remove-credential-sources": { "post": { "summary": "Removes Credential Sources from the Target.", @@ -3324,53 +3232,6 @@ ] } }, - "/v1/targets/{id}:set-credential-libraries": { - "post": { - "summary": "Deprecated: use SetTargetCredentialSources instead.", - "operationId": "TargetService_SetTargetCredentialLibraries", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/controller.api.resources.targets.v1.Target" - } - } - }, - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "properties": { - "version": { - "type": "integer", - "format": "int64", - "description": "Version is used to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version." - }, - "application_credential_library_ids": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "description": "Sets the values for application_credential_library_ids. If any\ncredential_library_id field that is not set in the request will result in\nthose fields being cleared." - } - } - ], - "tags": [ - "controller.api.services.v1.TargetService" - ] - } - }, "/v1/targets/{id}:set-credential-sources": { "post": { "summary": "Sets the Credential Sources on the Target.", @@ -5116,35 +4977,6 @@ } } }, - "controller.api.resources.targets.v1.CredentialLibrary": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The ID of the Credential Library." - }, - "name": { - "type": "string", - "description": "Output only. The name of the Credential Library.", - "readOnly": true - }, - "description": { - "type": "string", - "description": "Output only. The description of the Credential Library.", - "readOnly": true - }, - "credential_store_id": { - "type": "string", - "description": "Output only. The Credential Store to which this Credential Library belongs.", - "readOnly": true - }, - "type": { - "type": "string", - "description": "Output only. The type of the credential library.", - "readOnly": true - } - } - }, "controller.api.resources.targets.v1.CredentialSource": { "type": "object", "properties": { @@ -5282,11 +5114,6 @@ "description": "Output only. The credential source information.", "readOnly": true }, - "credential_library": { - "$ref": "#/definitions/controller.api.resources.targets.v1.CredentialLibrary", - "description": "Output only. The library which generated this credential. Deprecated: use credential_source instead.", - "readOnly": true - }, "secret": { "$ref": "#/definitions/controller.api.resources.targets.v1.SessionSecret", "description": "Output only. The secret of this credential base64 encoded.", @@ -5408,22 +5235,6 @@ "type": "string", "description": "Optional boolean expression to filter the workers that are allowed to satisfy this request." }, - "application_credential_library_ids": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Output only. The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead.", - "readOnly": true - }, - "application_credential_libraries": { - "type": "array", - "items": { - "$ref": "#/definitions/controller.api.resources.targets.v1.CredentialLibrary" - }, - "description": "Output only. The application credential libraries associated with this Target. Deprecated: use application_credential_sources instead.", - "readOnly": true - }, "application_credential_source_ids": { "type": "array", "items": { @@ -5607,14 +5418,6 @@ } } }, - "controller.api.services.v1.AddTargetCredentialLibrariesResponse": { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/controller.api.resources.targets.v1.Target" - } - } - }, "controller.api.services.v1.AddTargetCredentialSourcesResponse": { "type": "object", "properties": { @@ -6231,14 +6034,6 @@ } } }, - "controller.api.services.v1.RemoveTargetCredentialLibrariesResponse": { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/controller.api.resources.targets.v1.Target" - } - } - }, "controller.api.services.v1.RemoveTargetCredentialSourcesResponse": { "type": "object", "properties": { @@ -6311,14 +6106,6 @@ } } }, - "controller.api.services.v1.SetTargetCredentialLibrariesResponse": { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/controller.api.resources.targets.v1.Target" - } - } - }, "controller.api.services.v1.SetTargetCredentialSourcesResponse": { "type": "object", "properties": { diff --git a/internal/gen/controller/api/services/target_service.pb.go b/internal/gen/controller/api/services/target_service.pb.go index a94f7172b..bbb76af3d 100644 --- a/internal/gen/controller/api/services/target_service.pb.go +++ b/internal/gen/controller/api/services/target_service.pb.go @@ -1198,345 +1198,6 @@ func (x *RemoveTargetHostSourcesResponse) GetItem() *targets.Target { return nil } -type AddTargetCredentialLibrariesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` - // Version is used to ensure this resource has not changed. - // The mutation will fail if the version does not match the latest known good version. - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"` - ApplicationCredentialLibraryIds []string `protobuf:"bytes,3,rep,name=application_credential_library_ids,proto3" json:"application_credential_library_ids,omitempty" class:"public"` // @gotags: `class:"public"` -} - -func (x *AddTargetCredentialLibrariesRequest) Reset() { - *x = AddTargetCredentialLibrariesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AddTargetCredentialLibrariesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddTargetCredentialLibrariesRequest) ProtoMessage() {} - -func (x *AddTargetCredentialLibrariesRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[22] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddTargetCredentialLibrariesRequest.ProtoReflect.Descriptor instead. -func (*AddTargetCredentialLibrariesRequest) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{22} -} - -func (x *AddTargetCredentialLibrariesRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *AddTargetCredentialLibrariesRequest) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *AddTargetCredentialLibrariesRequest) GetApplicationCredentialLibraryIds() []string { - if x != nil { - return x.ApplicationCredentialLibraryIds - } - return nil -} - -type AddTargetCredentialLibrariesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Item *targets.Target `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` -} - -func (x *AddTargetCredentialLibrariesResponse) Reset() { - *x = AddTargetCredentialLibrariesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AddTargetCredentialLibrariesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddTargetCredentialLibrariesResponse) ProtoMessage() {} - -func (x *AddTargetCredentialLibrariesResponse) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[23] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddTargetCredentialLibrariesResponse.ProtoReflect.Descriptor instead. -func (*AddTargetCredentialLibrariesResponse) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{23} -} - -func (x *AddTargetCredentialLibrariesResponse) GetItem() *targets.Target { - if x != nil { - return x.Item - } - return nil -} - -// Sets the values for application_credential_library_ids. If any -// credential_library_id field that is not set in the request will result in -// those fields being cleared. -type SetTargetCredentialLibrariesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` - // Version is used to ensure this resource has not changed. - // The mutation will fail if the version does not match the latest known good version. - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"` - ApplicationCredentialLibraryIds []string `protobuf:"bytes,3,rep,name=application_credential_library_ids,proto3" json:"application_credential_library_ids,omitempty" class:"public"` // @gotags: `class:"public"` -} - -func (x *SetTargetCredentialLibrariesRequest) Reset() { - *x = SetTargetCredentialLibrariesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[24] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetTargetCredentialLibrariesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetTargetCredentialLibrariesRequest) ProtoMessage() {} - -func (x *SetTargetCredentialLibrariesRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[24] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetTargetCredentialLibrariesRequest.ProtoReflect.Descriptor instead. -func (*SetTargetCredentialLibrariesRequest) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{24} -} - -func (x *SetTargetCredentialLibrariesRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *SetTargetCredentialLibrariesRequest) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *SetTargetCredentialLibrariesRequest) GetApplicationCredentialLibraryIds() []string { - if x != nil { - return x.ApplicationCredentialLibraryIds - } - return nil -} - -type SetTargetCredentialLibrariesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Item *targets.Target `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` -} - -func (x *SetTargetCredentialLibrariesResponse) Reset() { - *x = SetTargetCredentialLibrariesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetTargetCredentialLibrariesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetTargetCredentialLibrariesResponse) ProtoMessage() {} - -func (x *SetTargetCredentialLibrariesResponse) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[25] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetTargetCredentialLibrariesResponse.ProtoReflect.Descriptor instead. -func (*SetTargetCredentialLibrariesResponse) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{25} -} - -func (x *SetTargetCredentialLibrariesResponse) GetItem() *targets.Target { - if x != nil { - return x.Item - } - return nil -} - -type RemoveTargetCredentialLibrariesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` - // Version is used to ensure this resource has not changed. - // The mutation will fail if the version does not match the latest known good version. - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"` - ApplicationCredentialLibraryIds []string `protobuf:"bytes,3,rep,name=application_credential_library_ids,proto3" json:"application_credential_library_ids,omitempty" class:"public"` // @gotags: `class:"public"` -} - -func (x *RemoveTargetCredentialLibrariesRequest) Reset() { - *x = RemoveTargetCredentialLibrariesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[26] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RemoveTargetCredentialLibrariesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveTargetCredentialLibrariesRequest) ProtoMessage() {} - -func (x *RemoveTargetCredentialLibrariesRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[26] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveTargetCredentialLibrariesRequest.ProtoReflect.Descriptor instead. -func (*RemoveTargetCredentialLibrariesRequest) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{26} -} - -func (x *RemoveTargetCredentialLibrariesRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *RemoveTargetCredentialLibrariesRequest) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *RemoveTargetCredentialLibrariesRequest) GetApplicationCredentialLibraryIds() []string { - if x != nil { - return x.ApplicationCredentialLibraryIds - } - return nil -} - -type RemoveTargetCredentialLibrariesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Item *targets.Target `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` -} - -func (x *RemoveTargetCredentialLibrariesResponse) Reset() { - *x = RemoveTargetCredentialLibrariesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[27] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RemoveTargetCredentialLibrariesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveTargetCredentialLibrariesResponse) ProtoMessage() {} - -func (x *RemoveTargetCredentialLibrariesResponse) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[27] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveTargetCredentialLibrariesResponse.ProtoReflect.Descriptor instead. -func (*RemoveTargetCredentialLibrariesResponse) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{27} -} - -func (x *RemoveTargetCredentialLibrariesResponse) GetItem() *targets.Target { - if x != nil { - return x.Item - } - return nil -} - type AddTargetCredentialSourcesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1553,7 +1214,7 @@ type AddTargetCredentialSourcesRequest struct { func (x *AddTargetCredentialSourcesRequest) Reset() { *x = AddTargetCredentialSourcesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[28] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1566,7 +1227,7 @@ func (x *AddTargetCredentialSourcesRequest) String() string { func (*AddTargetCredentialSourcesRequest) ProtoMessage() {} func (x *AddTargetCredentialSourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[28] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1579,7 +1240,7 @@ func (x *AddTargetCredentialSourcesRequest) ProtoReflect() protoreflect.Message // Deprecated: Use AddTargetCredentialSourcesRequest.ProtoReflect.Descriptor instead. func (*AddTargetCredentialSourcesRequest) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{28} + return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{22} } func (x *AddTargetCredentialSourcesRequest) GetId() string { @@ -1621,7 +1282,7 @@ type AddTargetCredentialSourcesResponse struct { func (x *AddTargetCredentialSourcesResponse) Reset() { *x = AddTargetCredentialSourcesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[29] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1634,7 +1295,7 @@ func (x *AddTargetCredentialSourcesResponse) String() string { func (*AddTargetCredentialSourcesResponse) ProtoMessage() {} func (x *AddTargetCredentialSourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[29] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1647,7 +1308,7 @@ func (x *AddTargetCredentialSourcesResponse) ProtoReflect() protoreflect.Message // Deprecated: Use AddTargetCredentialSourcesResponse.ProtoReflect.Descriptor instead. func (*AddTargetCredentialSourcesResponse) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{29} + return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{23} } func (x *AddTargetCredentialSourcesResponse) GetItem() *targets.Target { @@ -1675,7 +1336,7 @@ type SetTargetCredentialSourcesRequest struct { func (x *SetTargetCredentialSourcesRequest) Reset() { *x = SetTargetCredentialSourcesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[30] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1688,7 +1349,7 @@ func (x *SetTargetCredentialSourcesRequest) String() string { func (*SetTargetCredentialSourcesRequest) ProtoMessage() {} func (x *SetTargetCredentialSourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[30] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1701,7 +1362,7 @@ func (x *SetTargetCredentialSourcesRequest) ProtoReflect() protoreflect.Message // Deprecated: Use SetTargetCredentialSourcesRequest.ProtoReflect.Descriptor instead. func (*SetTargetCredentialSourcesRequest) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{30} + return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{24} } func (x *SetTargetCredentialSourcesRequest) GetId() string { @@ -1743,7 +1404,7 @@ type SetTargetCredentialSourcesResponse struct { func (x *SetTargetCredentialSourcesResponse) Reset() { *x = SetTargetCredentialSourcesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[31] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1756,7 +1417,7 @@ func (x *SetTargetCredentialSourcesResponse) String() string { func (*SetTargetCredentialSourcesResponse) ProtoMessage() {} func (x *SetTargetCredentialSourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[31] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1769,7 +1430,7 @@ func (x *SetTargetCredentialSourcesResponse) ProtoReflect() protoreflect.Message // Deprecated: Use SetTargetCredentialSourcesResponse.ProtoReflect.Descriptor instead. func (*SetTargetCredentialSourcesResponse) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{31} + return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{25} } func (x *SetTargetCredentialSourcesResponse) GetItem() *targets.Target { @@ -1795,7 +1456,7 @@ type RemoveTargetCredentialSourcesRequest struct { func (x *RemoveTargetCredentialSourcesRequest) Reset() { *x = RemoveTargetCredentialSourcesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[32] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1808,7 +1469,7 @@ func (x *RemoveTargetCredentialSourcesRequest) String() string { func (*RemoveTargetCredentialSourcesRequest) ProtoMessage() {} func (x *RemoveTargetCredentialSourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[32] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1821,7 +1482,7 @@ func (x *RemoveTargetCredentialSourcesRequest) ProtoReflect() protoreflect.Messa // Deprecated: Use RemoveTargetCredentialSourcesRequest.ProtoReflect.Descriptor instead. func (*RemoveTargetCredentialSourcesRequest) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{32} + return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{26} } func (x *RemoveTargetCredentialSourcesRequest) GetId() string { @@ -1863,7 +1524,7 @@ type RemoveTargetCredentialSourcesResponse struct { func (x *RemoveTargetCredentialSourcesResponse) Reset() { *x = RemoveTargetCredentialSourcesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[33] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1876,7 +1537,7 @@ func (x *RemoveTargetCredentialSourcesResponse) String() string { func (*RemoveTargetCredentialSourcesResponse) ProtoMessage() {} func (x *RemoveTargetCredentialSourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[33] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1889,7 +1550,7 @@ func (x *RemoveTargetCredentialSourcesResponse) ProtoReflect() protoreflect.Mess // Deprecated: Use RemoveTargetCredentialSourcesResponse.ProtoReflect.Descriptor instead. func (*RemoveTargetCredentialSourcesResponse) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{33} + return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{27} } func (x *RemoveTargetCredentialSourcesResponse) GetItem() *targets.Target { @@ -1919,7 +1580,7 @@ type AuthorizeSessionRequest struct { func (x *AuthorizeSessionRequest) Reset() { *x = AuthorizeSessionRequest{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[34] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1932,7 +1593,7 @@ func (x *AuthorizeSessionRequest) String() string { func (*AuthorizeSessionRequest) ProtoMessage() {} func (x *AuthorizeSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[34] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1945,7 +1606,7 @@ func (x *AuthorizeSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthorizeSessionRequest.ProtoReflect.Descriptor instead. func (*AuthorizeSessionRequest) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{34} + return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{28} } func (x *AuthorizeSessionRequest) GetId() string { @@ -1994,7 +1655,7 @@ type AuthorizeSessionResponse struct { func (x *AuthorizeSessionResponse) Reset() { *x = AuthorizeSessionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[35] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2007,7 +1668,7 @@ func (x *AuthorizeSessionResponse) String() string { func (*AuthorizeSessionResponse) ProtoMessage() {} func (x *AuthorizeSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_target_service_proto_msgTypes[35] + mi := &file_controller_api_services_v1_target_service_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2020,7 +1681,7 @@ func (x *AuthorizeSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthorizeSessionResponse.ProtoReflect.Descriptor instead. func (*AuthorizeSessionResponse) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{35} + return file_controller_api_services_v1_target_service_proto_rawDescGZIP(), []int{29} } func (x *AuthorizeSessionResponse) GetItem() *targets.SessionAuthorization { @@ -2180,78 +1841,7 @@ var file_controller_api_services_v1_target_service_proto_rawDesc = []byte{ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x9f, 0x01, 0x0a, 0x23, 0x41, 0x64, 0x64, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x22, 0x61, 0x70, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, - 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x67, 0x0a, 0x24, 0x41, 0x64, - 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x22, 0x9f, 0x01, 0x0a, 0x23, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, - 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, - 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, - 0x79, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x67, 0x0a, 0x24, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, - 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xa2, - 0x01, 0x0a, 0x26, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, - 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, - 0x69, 0x64, 0x73, 0x22, 0x6a, 0x0a, 0x27, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, - 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, - 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, - 0xdf, 0x01, 0x0a, 0x21, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x4c, 0x0a, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x21, 0x61, 0x70, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x42, 0x0a, - 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x73, 0x22, 0x65, 0x0a, 0x22, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xdf, 0x01, 0x0a, 0x21, 0x53, 0x65, 0x74, + 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xdf, 0x01, 0x0a, 0x21, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, @@ -2265,314 +1855,281 @@ var file_controller_api_services_v1_target_service_proto_rawDesc = []byte{ 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x65, 0x0a, 0x22, 0x53, 0x65, - 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x65, 0x0a, 0x22, 0x41, 0x64, + 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, - 0x6d, 0x22, 0xe1, 0x01, 0x0a, 0x24, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, + 0x6d, 0x22, 0xdf, 0x01, 0x0a, 0x21, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x4c, 0x0a, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x21, 0x61, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x12, + 0x42, 0x0a, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x73, 0x22, 0x65, 0x0a, 0x22, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xe1, 0x01, 0x0a, 0x24, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, + 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x12, 0x42, 0x0a, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, - 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x68, 0x0a, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, - 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, - 0x91, 0x01, 0x0a, 0x17, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, - 0x6f, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x73, 0x63, 0x6f, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x6f, 0x73, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x6f, 0x73, 0x74, - 0x5f, 0x69, 0x64, 0x22, 0x69, 0x0a, 0x18, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x4d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x97, - 0x20, 0x0a, 0x0d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0xa2, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2c, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x92, 0x41, 0x17, - 0x12, 0x15, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x10, 0x2f, - 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x9a, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x92, 0x41, 0x14, 0x12, 0x12, 0x4c, 0x69, 0x73, - 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x73, 0x12, 0xaf, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, 0x92, 0x41, 0x1a, 0x12, 0x18, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x0b, 0x2f, 0x76, 0x31, - 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x12, 0xad, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x92, 0x41, 0x13, 0x12, 0x11, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x32, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x12, 0xa1, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x92, 0x41, 0x13, 0x12, 0x11, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x2a, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xcc, 0x01, 0x0a, 0x10, 0x41, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x42, 0x0a, 0x1c, 0x65, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x68, + 0x0a, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x91, 0x01, 0x0a, 0x17, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x69, 0x0a, 0x18, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4d, 0x92, 0x41, 0x17, 0x12, 0x15, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x73, 0x20, 0x61, 0x20, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x2f, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x2d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x01, - 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xda, 0x01, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x12, 0x34, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x92, 0x41, 0x26, 0x12, - 0x24, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x48, - 0x6f, 0x73, 0x74, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x1e, 0x2f, 0x76, 0x31, - 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, - 0x64, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x12, 0xd7, 0x01, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x55, 0x92, 0x41, 0x23, 0x12, 0x21, 0x53, 0x65, - 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x65, 0x74, 0x73, - 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x68, 0x6f, 0x73, 0x74, - 0x2d, 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xe4, - 0x01, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, - 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0xb7, 0x19, 0x0a, 0x0d, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xa2, 0x01, 0x0a, 0x09, 0x47, 0x65, + 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x92, 0x41, 0x17, 0x12, 0x15, 0x47, 0x65, 0x74, 0x73, 0x20, + 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x9a, + 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x2e, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x2a, 0x92, 0x41, 0x14, 0x12, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, + 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0xaf, 0x01, 0x0a, 0x0c, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x3c, 0x92, 0x41, 0x1a, 0x12, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, + 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x19, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xad, 0x01, + 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x3a, 0x92, 0x41, 0x13, 0x12, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, + 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x32, + 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xa1, 0x01, + 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2f, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x2e, 0x92, 0x41, 0x13, 0x12, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, + 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x2a, + 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x12, 0xcc, 0x01, 0x0a, 0x10, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x4d, 0x92, 0x41, 0x17, 0x12, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x65, 0x73, 0x20, 0x61, 0x20, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x2d, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, + 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x2d, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x12, 0xda, 0x01, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, + 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, + 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x58, 0x92, 0x41, 0x26, 0x12, 0x24, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, + 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x65, 0x74, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x29, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, + 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xd7, 0x01, + 0x0a, 0x11, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, + 0x65, 0x74, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x55, 0x92, 0x41, 0x23, 0x12, 0x21, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x1e, + 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, + 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xe4, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, + 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x59, 0x92, 0x41, 0x24, 0x12, - 0x22, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x65, - 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xe9, 0x01, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, - 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x5e, 0x92, 0x41, 0x29, 0x12, 0x27, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, - 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x68, 0x6f, 0x73, 0x74, - 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, - 0x6d, 0x12, 0xe9, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, - 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e, 0x92, - 0x41, 0x29, 0x12, 0x27, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, - 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xf3, 0x01, - 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, - 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, - 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x27, 0x12, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, - 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66, 0x72, - 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x2f, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x68, 0x6f, - 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x12, 0x98, 0x02, 0x0a, 0x1c, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, - 0x72, 0x69, 0x65, 0x73, 0x12, 0x3f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x40, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x75, 0x88, 0x02, 0x01, 0x92, 0x41, 0x35, 0x12, - 0x33, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x75, 0x73, 0x65, - 0x20, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74, - 0x65, 0x61, 0x64, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x2f, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, - 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x6c, 0x69, 0x62, 0x72, - 0x61, 0x72, 0x69, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x98, - 0x02, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, - 0x3f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x40, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x75, 0x88, 0x02, 0x01, 0x92, 0x41, 0x35, 0x12, 0x33, 0x44, 0x65, 0x70, 0x72, - 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x75, 0x73, 0x65, 0x20, 0x53, 0x65, 0x74, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x2e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, - 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xa7, 0x02, 0x0a, 0x1f, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x42, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x43, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7b, 0x88, 0x02, 0x01, 0x92, 0x41, 0x38, 0x12, 0x36, - 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x75, 0x73, 0x65, 0x20, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x69, 0x6e, - 0x73, 0x74, 0x65, 0x61, 0x64, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x37, 0x22, 0x2c, 0x2f, 0x76, - 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x12, 0x87, 0x02, 0x0a, 0x1a, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x12, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x3e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x6a, 0x92, 0x41, 0x2f, 0x12, 0x2d, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, - 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x27, 0x2f, 0x76, 0x31, - 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, - 0x64, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x84, 0x02, - 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3d, 0x2e, 0x63, + 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x59, 0x92, 0x41, 0x24, 0x12, 0x22, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x73, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, + 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x68, 0x6f, 0x73, 0x74, + 0x2d, 0x73, 0x65, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xe9, + 0x01, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, + 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, + 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e, 0x92, 0x41, 0x29, 0x12, + 0x27, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x48, + 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, + 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21, + 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xe9, 0x01, 0x0a, 0x14, 0x53, + 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x63, 0x6f, + 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e, 0x92, 0x41, 0x29, 0x12, 0x27, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, + 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, + 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0xf3, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x12, 0x3a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x27, + 0x12, 0x25, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x22, 0x24, 0x2f, + 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, + 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x87, 0x02, 0x0a, + 0x1a, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x92, 0x41, 0x2c, - 0x12, 0x2a, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x32, 0x22, 0x27, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, - 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x12, 0x91, 0x02, 0x0a, 0x1d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x40, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6b, 0x92, 0x41, 0x2d, - 0x12, 0x2b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, - 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x35, 0x22, 0x2a, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, - 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x63, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, - 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x42, 0x57, 0x5a, 0x4b, 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, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0xa2, 0xe3, 0x29, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6a, 0x92, 0x41, 0x2f, 0x12, + 0x2d, 0x41, 0x64, 0x64, 0x73, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x32, 0x22, 0x27, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, + 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x84, 0x02, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x92, 0x41, 0x2c, 0x12, 0x2a, 0x53, 0x65, 0x74, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x27, 0x2f, 0x76, 0x31, + 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, + 0x74, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x91, 0x02, + 0x0a, 0x1d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, + 0x40, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x41, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6b, 0x92, 0x41, 0x2d, 0x12, 0x2b, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x73, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x22, 0x2a, 0x2f, 0x76, + 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x42, 0x57, 0x5a, 0x4b, 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, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0xa2, 0xe3, 0x29, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -2587,110 +2144,95 @@ func file_controller_api_services_v1_target_service_proto_rawDescGZIP() []byte { return file_controller_api_services_v1_target_service_proto_rawDescData } -var file_controller_api_services_v1_target_service_proto_msgTypes = make([]protoimpl.MessageInfo, 36) +var file_controller_api_services_v1_target_service_proto_msgTypes = make([]protoimpl.MessageInfo, 30) var file_controller_api_services_v1_target_service_proto_goTypes = []interface{}{ - (*GetTargetRequest)(nil), // 0: controller.api.services.v1.GetTargetRequest - (*GetTargetResponse)(nil), // 1: controller.api.services.v1.GetTargetResponse - (*ListTargetsRequest)(nil), // 2: controller.api.services.v1.ListTargetsRequest - (*ListTargetsResponse)(nil), // 3: controller.api.services.v1.ListTargetsResponse - (*CreateTargetRequest)(nil), // 4: controller.api.services.v1.CreateTargetRequest - (*CreateTargetResponse)(nil), // 5: controller.api.services.v1.CreateTargetResponse - (*UpdateTargetRequest)(nil), // 6: controller.api.services.v1.UpdateTargetRequest - (*UpdateTargetResponse)(nil), // 7: controller.api.services.v1.UpdateTargetResponse - (*DeleteTargetRequest)(nil), // 8: controller.api.services.v1.DeleteTargetRequest - (*DeleteTargetResponse)(nil), // 9: controller.api.services.v1.DeleteTargetResponse - (*AddTargetHostSetsRequest)(nil), // 10: controller.api.services.v1.AddTargetHostSetsRequest - (*AddTargetHostSetsResponse)(nil), // 11: controller.api.services.v1.AddTargetHostSetsResponse - (*SetTargetHostSetsRequest)(nil), // 12: controller.api.services.v1.SetTargetHostSetsRequest - (*SetTargetHostSetsResponse)(nil), // 13: controller.api.services.v1.SetTargetHostSetsResponse - (*RemoveTargetHostSetsRequest)(nil), // 14: controller.api.services.v1.RemoveTargetHostSetsRequest - (*RemoveTargetHostSetsResponse)(nil), // 15: controller.api.services.v1.RemoveTargetHostSetsResponse - (*AddTargetHostSourcesRequest)(nil), // 16: controller.api.services.v1.AddTargetHostSourcesRequest - (*AddTargetHostSourcesResponse)(nil), // 17: controller.api.services.v1.AddTargetHostSourcesResponse - (*SetTargetHostSourcesRequest)(nil), // 18: controller.api.services.v1.SetTargetHostSourcesRequest - (*SetTargetHostSourcesResponse)(nil), // 19: controller.api.services.v1.SetTargetHostSourcesResponse - (*RemoveTargetHostSourcesRequest)(nil), // 20: controller.api.services.v1.RemoveTargetHostSourcesRequest - (*RemoveTargetHostSourcesResponse)(nil), // 21: controller.api.services.v1.RemoveTargetHostSourcesResponse - (*AddTargetCredentialLibrariesRequest)(nil), // 22: controller.api.services.v1.AddTargetCredentialLibrariesRequest - (*AddTargetCredentialLibrariesResponse)(nil), // 23: controller.api.services.v1.AddTargetCredentialLibrariesResponse - (*SetTargetCredentialLibrariesRequest)(nil), // 24: controller.api.services.v1.SetTargetCredentialLibrariesRequest - (*SetTargetCredentialLibrariesResponse)(nil), // 25: controller.api.services.v1.SetTargetCredentialLibrariesResponse - (*RemoveTargetCredentialLibrariesRequest)(nil), // 26: controller.api.services.v1.RemoveTargetCredentialLibrariesRequest - (*RemoveTargetCredentialLibrariesResponse)(nil), // 27: controller.api.services.v1.RemoveTargetCredentialLibrariesResponse - (*AddTargetCredentialSourcesRequest)(nil), // 28: controller.api.services.v1.AddTargetCredentialSourcesRequest - (*AddTargetCredentialSourcesResponse)(nil), // 29: controller.api.services.v1.AddTargetCredentialSourcesResponse - (*SetTargetCredentialSourcesRequest)(nil), // 30: controller.api.services.v1.SetTargetCredentialSourcesRequest - (*SetTargetCredentialSourcesResponse)(nil), // 31: controller.api.services.v1.SetTargetCredentialSourcesResponse - (*RemoveTargetCredentialSourcesRequest)(nil), // 32: controller.api.services.v1.RemoveTargetCredentialSourcesRequest - (*RemoveTargetCredentialSourcesResponse)(nil), // 33: controller.api.services.v1.RemoveTargetCredentialSourcesResponse - (*AuthorizeSessionRequest)(nil), // 34: controller.api.services.v1.AuthorizeSessionRequest - (*AuthorizeSessionResponse)(nil), // 35: controller.api.services.v1.AuthorizeSessionResponse - (*targets.Target)(nil), // 36: controller.api.resources.targets.v1.Target - (*fieldmaskpb.FieldMask)(nil), // 37: google.protobuf.FieldMask - (*targets.SessionAuthorization)(nil), // 38: controller.api.resources.targets.v1.SessionAuthorization + (*GetTargetRequest)(nil), // 0: controller.api.services.v1.GetTargetRequest + (*GetTargetResponse)(nil), // 1: controller.api.services.v1.GetTargetResponse + (*ListTargetsRequest)(nil), // 2: controller.api.services.v1.ListTargetsRequest + (*ListTargetsResponse)(nil), // 3: controller.api.services.v1.ListTargetsResponse + (*CreateTargetRequest)(nil), // 4: controller.api.services.v1.CreateTargetRequest + (*CreateTargetResponse)(nil), // 5: controller.api.services.v1.CreateTargetResponse + (*UpdateTargetRequest)(nil), // 6: controller.api.services.v1.UpdateTargetRequest + (*UpdateTargetResponse)(nil), // 7: controller.api.services.v1.UpdateTargetResponse + (*DeleteTargetRequest)(nil), // 8: controller.api.services.v1.DeleteTargetRequest + (*DeleteTargetResponse)(nil), // 9: controller.api.services.v1.DeleteTargetResponse + (*AddTargetHostSetsRequest)(nil), // 10: controller.api.services.v1.AddTargetHostSetsRequest + (*AddTargetHostSetsResponse)(nil), // 11: controller.api.services.v1.AddTargetHostSetsResponse + (*SetTargetHostSetsRequest)(nil), // 12: controller.api.services.v1.SetTargetHostSetsRequest + (*SetTargetHostSetsResponse)(nil), // 13: controller.api.services.v1.SetTargetHostSetsResponse + (*RemoveTargetHostSetsRequest)(nil), // 14: controller.api.services.v1.RemoveTargetHostSetsRequest + (*RemoveTargetHostSetsResponse)(nil), // 15: controller.api.services.v1.RemoveTargetHostSetsResponse + (*AddTargetHostSourcesRequest)(nil), // 16: controller.api.services.v1.AddTargetHostSourcesRequest + (*AddTargetHostSourcesResponse)(nil), // 17: controller.api.services.v1.AddTargetHostSourcesResponse + (*SetTargetHostSourcesRequest)(nil), // 18: controller.api.services.v1.SetTargetHostSourcesRequest + (*SetTargetHostSourcesResponse)(nil), // 19: controller.api.services.v1.SetTargetHostSourcesResponse + (*RemoveTargetHostSourcesRequest)(nil), // 20: controller.api.services.v1.RemoveTargetHostSourcesRequest + (*RemoveTargetHostSourcesResponse)(nil), // 21: controller.api.services.v1.RemoveTargetHostSourcesResponse + (*AddTargetCredentialSourcesRequest)(nil), // 22: controller.api.services.v1.AddTargetCredentialSourcesRequest + (*AddTargetCredentialSourcesResponse)(nil), // 23: controller.api.services.v1.AddTargetCredentialSourcesResponse + (*SetTargetCredentialSourcesRequest)(nil), // 24: controller.api.services.v1.SetTargetCredentialSourcesRequest + (*SetTargetCredentialSourcesResponse)(nil), // 25: controller.api.services.v1.SetTargetCredentialSourcesResponse + (*RemoveTargetCredentialSourcesRequest)(nil), // 26: controller.api.services.v1.RemoveTargetCredentialSourcesRequest + (*RemoveTargetCredentialSourcesResponse)(nil), // 27: controller.api.services.v1.RemoveTargetCredentialSourcesResponse + (*AuthorizeSessionRequest)(nil), // 28: controller.api.services.v1.AuthorizeSessionRequest + (*AuthorizeSessionResponse)(nil), // 29: controller.api.services.v1.AuthorizeSessionResponse + (*targets.Target)(nil), // 30: controller.api.resources.targets.v1.Target + (*fieldmaskpb.FieldMask)(nil), // 31: google.protobuf.FieldMask + (*targets.SessionAuthorization)(nil), // 32: controller.api.resources.targets.v1.SessionAuthorization } var file_controller_api_services_v1_target_service_proto_depIdxs = []int32{ - 36, // 0: controller.api.services.v1.GetTargetResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 1: controller.api.services.v1.ListTargetsResponse.items:type_name -> controller.api.resources.targets.v1.Target - 36, // 2: controller.api.services.v1.CreateTargetRequest.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 3: controller.api.services.v1.CreateTargetResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 4: controller.api.services.v1.UpdateTargetRequest.item:type_name -> controller.api.resources.targets.v1.Target - 37, // 5: controller.api.services.v1.UpdateTargetRequest.update_mask:type_name -> google.protobuf.FieldMask - 36, // 6: controller.api.services.v1.UpdateTargetResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 7: controller.api.services.v1.AddTargetHostSetsResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 8: controller.api.services.v1.SetTargetHostSetsResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 9: controller.api.services.v1.RemoveTargetHostSetsResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 10: controller.api.services.v1.AddTargetHostSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 11: controller.api.services.v1.SetTargetHostSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 12: controller.api.services.v1.RemoveTargetHostSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 13: controller.api.services.v1.AddTargetCredentialLibrariesResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 14: controller.api.services.v1.SetTargetCredentialLibrariesResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 15: controller.api.services.v1.RemoveTargetCredentialLibrariesResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 16: controller.api.services.v1.AddTargetCredentialSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 17: controller.api.services.v1.SetTargetCredentialSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target - 36, // 18: controller.api.services.v1.RemoveTargetCredentialSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target - 38, // 19: controller.api.services.v1.AuthorizeSessionResponse.item:type_name -> controller.api.resources.targets.v1.SessionAuthorization - 0, // 20: controller.api.services.v1.TargetService.GetTarget:input_type -> controller.api.services.v1.GetTargetRequest - 2, // 21: controller.api.services.v1.TargetService.ListTargets:input_type -> controller.api.services.v1.ListTargetsRequest - 4, // 22: controller.api.services.v1.TargetService.CreateTarget:input_type -> controller.api.services.v1.CreateTargetRequest - 6, // 23: controller.api.services.v1.TargetService.UpdateTarget:input_type -> controller.api.services.v1.UpdateTargetRequest - 8, // 24: controller.api.services.v1.TargetService.DeleteTarget:input_type -> controller.api.services.v1.DeleteTargetRequest - 34, // 25: controller.api.services.v1.TargetService.AuthorizeSession:input_type -> controller.api.services.v1.AuthorizeSessionRequest - 10, // 26: controller.api.services.v1.TargetService.AddTargetHostSets:input_type -> controller.api.services.v1.AddTargetHostSetsRequest - 12, // 27: controller.api.services.v1.TargetService.SetTargetHostSets:input_type -> controller.api.services.v1.SetTargetHostSetsRequest - 14, // 28: controller.api.services.v1.TargetService.RemoveTargetHostSets:input_type -> controller.api.services.v1.RemoveTargetHostSetsRequest - 16, // 29: controller.api.services.v1.TargetService.AddTargetHostSources:input_type -> controller.api.services.v1.AddTargetHostSourcesRequest - 18, // 30: controller.api.services.v1.TargetService.SetTargetHostSources:input_type -> controller.api.services.v1.SetTargetHostSourcesRequest - 20, // 31: controller.api.services.v1.TargetService.RemoveTargetHostSources:input_type -> controller.api.services.v1.RemoveTargetHostSourcesRequest - 22, // 32: controller.api.services.v1.TargetService.AddTargetCredentialLibraries:input_type -> controller.api.services.v1.AddTargetCredentialLibrariesRequest - 24, // 33: controller.api.services.v1.TargetService.SetTargetCredentialLibraries:input_type -> controller.api.services.v1.SetTargetCredentialLibrariesRequest - 26, // 34: controller.api.services.v1.TargetService.RemoveTargetCredentialLibraries:input_type -> controller.api.services.v1.RemoveTargetCredentialLibrariesRequest - 28, // 35: controller.api.services.v1.TargetService.AddTargetCredentialSources:input_type -> controller.api.services.v1.AddTargetCredentialSourcesRequest - 30, // 36: controller.api.services.v1.TargetService.SetTargetCredentialSources:input_type -> controller.api.services.v1.SetTargetCredentialSourcesRequest - 32, // 37: controller.api.services.v1.TargetService.RemoveTargetCredentialSources:input_type -> controller.api.services.v1.RemoveTargetCredentialSourcesRequest - 1, // 38: controller.api.services.v1.TargetService.GetTarget:output_type -> controller.api.services.v1.GetTargetResponse - 3, // 39: controller.api.services.v1.TargetService.ListTargets:output_type -> controller.api.services.v1.ListTargetsResponse - 5, // 40: controller.api.services.v1.TargetService.CreateTarget:output_type -> controller.api.services.v1.CreateTargetResponse - 7, // 41: controller.api.services.v1.TargetService.UpdateTarget:output_type -> controller.api.services.v1.UpdateTargetResponse - 9, // 42: controller.api.services.v1.TargetService.DeleteTarget:output_type -> controller.api.services.v1.DeleteTargetResponse - 35, // 43: controller.api.services.v1.TargetService.AuthorizeSession:output_type -> controller.api.services.v1.AuthorizeSessionResponse - 11, // 44: controller.api.services.v1.TargetService.AddTargetHostSets:output_type -> controller.api.services.v1.AddTargetHostSetsResponse - 13, // 45: controller.api.services.v1.TargetService.SetTargetHostSets:output_type -> controller.api.services.v1.SetTargetHostSetsResponse - 15, // 46: controller.api.services.v1.TargetService.RemoveTargetHostSets:output_type -> controller.api.services.v1.RemoveTargetHostSetsResponse - 17, // 47: controller.api.services.v1.TargetService.AddTargetHostSources:output_type -> controller.api.services.v1.AddTargetHostSourcesResponse - 19, // 48: controller.api.services.v1.TargetService.SetTargetHostSources:output_type -> controller.api.services.v1.SetTargetHostSourcesResponse - 21, // 49: controller.api.services.v1.TargetService.RemoveTargetHostSources:output_type -> controller.api.services.v1.RemoveTargetHostSourcesResponse - 23, // 50: controller.api.services.v1.TargetService.AddTargetCredentialLibraries:output_type -> controller.api.services.v1.AddTargetCredentialLibrariesResponse - 25, // 51: controller.api.services.v1.TargetService.SetTargetCredentialLibraries:output_type -> controller.api.services.v1.SetTargetCredentialLibrariesResponse - 27, // 52: controller.api.services.v1.TargetService.RemoveTargetCredentialLibraries:output_type -> controller.api.services.v1.RemoveTargetCredentialLibrariesResponse - 29, // 53: controller.api.services.v1.TargetService.AddTargetCredentialSources:output_type -> controller.api.services.v1.AddTargetCredentialSourcesResponse - 31, // 54: controller.api.services.v1.TargetService.SetTargetCredentialSources:output_type -> controller.api.services.v1.SetTargetCredentialSourcesResponse - 33, // 55: controller.api.services.v1.TargetService.RemoveTargetCredentialSources:output_type -> controller.api.services.v1.RemoveTargetCredentialSourcesResponse - 38, // [38:56] is the sub-list for method output_type - 20, // [20:38] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name + 30, // 0: controller.api.services.v1.GetTargetResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 1: controller.api.services.v1.ListTargetsResponse.items:type_name -> controller.api.resources.targets.v1.Target + 30, // 2: controller.api.services.v1.CreateTargetRequest.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 3: controller.api.services.v1.CreateTargetResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 4: controller.api.services.v1.UpdateTargetRequest.item:type_name -> controller.api.resources.targets.v1.Target + 31, // 5: controller.api.services.v1.UpdateTargetRequest.update_mask:type_name -> google.protobuf.FieldMask + 30, // 6: controller.api.services.v1.UpdateTargetResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 7: controller.api.services.v1.AddTargetHostSetsResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 8: controller.api.services.v1.SetTargetHostSetsResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 9: controller.api.services.v1.RemoveTargetHostSetsResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 10: controller.api.services.v1.AddTargetHostSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 11: controller.api.services.v1.SetTargetHostSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 12: controller.api.services.v1.RemoveTargetHostSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 13: controller.api.services.v1.AddTargetCredentialSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 14: controller.api.services.v1.SetTargetCredentialSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target + 30, // 15: controller.api.services.v1.RemoveTargetCredentialSourcesResponse.item:type_name -> controller.api.resources.targets.v1.Target + 32, // 16: controller.api.services.v1.AuthorizeSessionResponse.item:type_name -> controller.api.resources.targets.v1.SessionAuthorization + 0, // 17: controller.api.services.v1.TargetService.GetTarget:input_type -> controller.api.services.v1.GetTargetRequest + 2, // 18: controller.api.services.v1.TargetService.ListTargets:input_type -> controller.api.services.v1.ListTargetsRequest + 4, // 19: controller.api.services.v1.TargetService.CreateTarget:input_type -> controller.api.services.v1.CreateTargetRequest + 6, // 20: controller.api.services.v1.TargetService.UpdateTarget:input_type -> controller.api.services.v1.UpdateTargetRequest + 8, // 21: controller.api.services.v1.TargetService.DeleteTarget:input_type -> controller.api.services.v1.DeleteTargetRequest + 28, // 22: controller.api.services.v1.TargetService.AuthorizeSession:input_type -> controller.api.services.v1.AuthorizeSessionRequest + 10, // 23: controller.api.services.v1.TargetService.AddTargetHostSets:input_type -> controller.api.services.v1.AddTargetHostSetsRequest + 12, // 24: controller.api.services.v1.TargetService.SetTargetHostSets:input_type -> controller.api.services.v1.SetTargetHostSetsRequest + 14, // 25: controller.api.services.v1.TargetService.RemoveTargetHostSets:input_type -> controller.api.services.v1.RemoveTargetHostSetsRequest + 16, // 26: controller.api.services.v1.TargetService.AddTargetHostSources:input_type -> controller.api.services.v1.AddTargetHostSourcesRequest + 18, // 27: controller.api.services.v1.TargetService.SetTargetHostSources:input_type -> controller.api.services.v1.SetTargetHostSourcesRequest + 20, // 28: controller.api.services.v1.TargetService.RemoveTargetHostSources:input_type -> controller.api.services.v1.RemoveTargetHostSourcesRequest + 22, // 29: controller.api.services.v1.TargetService.AddTargetCredentialSources:input_type -> controller.api.services.v1.AddTargetCredentialSourcesRequest + 24, // 30: controller.api.services.v1.TargetService.SetTargetCredentialSources:input_type -> controller.api.services.v1.SetTargetCredentialSourcesRequest + 26, // 31: controller.api.services.v1.TargetService.RemoveTargetCredentialSources:input_type -> controller.api.services.v1.RemoveTargetCredentialSourcesRequest + 1, // 32: controller.api.services.v1.TargetService.GetTarget:output_type -> controller.api.services.v1.GetTargetResponse + 3, // 33: controller.api.services.v1.TargetService.ListTargets:output_type -> controller.api.services.v1.ListTargetsResponse + 5, // 34: controller.api.services.v1.TargetService.CreateTarget:output_type -> controller.api.services.v1.CreateTargetResponse + 7, // 35: controller.api.services.v1.TargetService.UpdateTarget:output_type -> controller.api.services.v1.UpdateTargetResponse + 9, // 36: controller.api.services.v1.TargetService.DeleteTarget:output_type -> controller.api.services.v1.DeleteTargetResponse + 29, // 37: controller.api.services.v1.TargetService.AuthorizeSession:output_type -> controller.api.services.v1.AuthorizeSessionResponse + 11, // 38: controller.api.services.v1.TargetService.AddTargetHostSets:output_type -> controller.api.services.v1.AddTargetHostSetsResponse + 13, // 39: controller.api.services.v1.TargetService.SetTargetHostSets:output_type -> controller.api.services.v1.SetTargetHostSetsResponse + 15, // 40: controller.api.services.v1.TargetService.RemoveTargetHostSets:output_type -> controller.api.services.v1.RemoveTargetHostSetsResponse + 17, // 41: controller.api.services.v1.TargetService.AddTargetHostSources:output_type -> controller.api.services.v1.AddTargetHostSourcesResponse + 19, // 42: controller.api.services.v1.TargetService.SetTargetHostSources:output_type -> controller.api.services.v1.SetTargetHostSourcesResponse + 21, // 43: controller.api.services.v1.TargetService.RemoveTargetHostSources:output_type -> controller.api.services.v1.RemoveTargetHostSourcesResponse + 23, // 44: controller.api.services.v1.TargetService.AddTargetCredentialSources:output_type -> controller.api.services.v1.AddTargetCredentialSourcesResponse + 25, // 45: controller.api.services.v1.TargetService.SetTargetCredentialSources:output_type -> controller.api.services.v1.SetTargetCredentialSourcesResponse + 27, // 46: controller.api.services.v1.TargetService.RemoveTargetCredentialSources:output_type -> controller.api.services.v1.RemoveTargetCredentialSourcesResponse + 32, // [32:47] is the sub-list for method output_type + 17, // [17:32] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_controller_api_services_v1_target_service_proto_init() } @@ -2964,78 +2506,6 @@ func file_controller_api_services_v1_target_service_proto_init() { } } file_controller_api_services_v1_target_service_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddTargetCredentialLibrariesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_services_v1_target_service_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddTargetCredentialLibrariesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_services_v1_target_service_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetTargetCredentialLibrariesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_services_v1_target_service_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetTargetCredentialLibrariesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_services_v1_target_service_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveTargetCredentialLibrariesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_services_v1_target_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveTargetCredentialLibrariesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_services_v1_target_service_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddTargetCredentialSourcesRequest); i { case 0: return &v.state @@ -3047,7 +2517,7 @@ func file_controller_api_services_v1_target_service_proto_init() { return nil } } - file_controller_api_services_v1_target_service_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_services_v1_target_service_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddTargetCredentialSourcesResponse); i { case 0: return &v.state @@ -3059,7 +2529,7 @@ func file_controller_api_services_v1_target_service_proto_init() { return nil } } - file_controller_api_services_v1_target_service_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_services_v1_target_service_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetTargetCredentialSourcesRequest); i { case 0: return &v.state @@ -3071,7 +2541,7 @@ func file_controller_api_services_v1_target_service_proto_init() { return nil } } - file_controller_api_services_v1_target_service_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_services_v1_target_service_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetTargetCredentialSourcesResponse); i { case 0: return &v.state @@ -3083,7 +2553,7 @@ func file_controller_api_services_v1_target_service_proto_init() { return nil } } - file_controller_api_services_v1_target_service_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_services_v1_target_service_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveTargetCredentialSourcesRequest); i { case 0: return &v.state @@ -3095,7 +2565,7 @@ func file_controller_api_services_v1_target_service_proto_init() { return nil } } - file_controller_api_services_v1_target_service_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_services_v1_target_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveTargetCredentialSourcesResponse); i { case 0: return &v.state @@ -3107,7 +2577,7 @@ func file_controller_api_services_v1_target_service_proto_init() { return nil } } - file_controller_api_services_v1_target_service_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_services_v1_target_service_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AuthorizeSessionRequest); i { case 0: return &v.state @@ -3119,7 +2589,7 @@ func file_controller_api_services_v1_target_service_proto_init() { return nil } } - file_controller_api_services_v1_target_service_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_services_v1_target_service_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AuthorizeSessionResponse); i { case 0: return &v.state @@ -3138,7 +2608,7 @@ func file_controller_api_services_v1_target_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_api_services_v1_target_service_proto_rawDesc, NumEnums: 0, - NumMessages: 36, + NumMessages: 30, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/gen/controller/api/services/target_service.pb.gw.go b/internal/gen/controller/api/services/target_service.pb.gw.go index e242e6c24..78e073d22 100644 --- a/internal/gen/controller/api/services/target_service.pb.gw.go +++ b/internal/gen/controller/api/services/target_service.pb.gw.go @@ -781,210 +781,6 @@ func local_request_TargetService_RemoveTargetHostSources_0(ctx context.Context, } -func request_TargetService_AddTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, client TargetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq AddTargetCredentialLibrariesRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") - } - - protoReq.Id, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) - } - - msg, err := client.AddTargetCredentialLibraries(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_TargetService_AddTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, server TargetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq AddTargetCredentialLibrariesRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") - } - - protoReq.Id, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) - } - - msg, err := server.AddTargetCredentialLibraries(ctx, &protoReq) - return msg, metadata, err - -} - -func request_TargetService_SetTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, client TargetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq SetTargetCredentialLibrariesRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") - } - - protoReq.Id, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) - } - - msg, err := client.SetTargetCredentialLibraries(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_TargetService_SetTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, server TargetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq SetTargetCredentialLibrariesRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") - } - - protoReq.Id, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) - } - - msg, err := server.SetTargetCredentialLibraries(ctx, &protoReq) - return msg, metadata, err - -} - -func request_TargetService_RemoveTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, client TargetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq RemoveTargetCredentialLibrariesRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") - } - - protoReq.Id, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) - } - - msg, err := client.RemoveTargetCredentialLibraries(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_TargetService_RemoveTargetCredentialLibraries_0(ctx context.Context, marshaler runtime.Marshaler, server TargetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq RemoveTargetCredentialLibrariesRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") - } - - protoReq.Id, err = runtime.String(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) - } - - msg, err := server.RemoveTargetCredentialLibraries(ctx, &protoReq) - return msg, metadata, err - -} - func request_TargetService_AddTargetCredentialSources_0(ctx context.Context, marshaler runtime.Marshaler, client TargetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq AddTargetCredentialSourcesRequest var metadata runtime.ServerMetadata @@ -1483,78 +1279,6 @@ func RegisterTargetServiceHandlerServer(ctx context.Context, mux *runtime.ServeM }) - mux.Handle("POST", pattern_TargetService_AddTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - var err error - ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.TargetService/AddTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:add-credential-libraries")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_TargetService_AddTargetCredentialLibraries_0(ctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_TargetService_AddTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_AddTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_TargetService_SetTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - var err error - ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.TargetService/SetTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:set-credential-libraries")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_TargetService_SetTargetCredentialLibraries_0(ctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_TargetService_SetTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_SetTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_TargetService_RemoveTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - var err error - ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.TargetService/RemoveTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:remove-credential-libraries")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_TargetService_RemoveTargetCredentialLibraries_0(ctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_TargetService_RemoveTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_RemoveTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("POST", pattern_TargetService_AddTargetCredentialSources_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1920,69 +1644,6 @@ func RegisterTargetServiceHandlerClient(ctx context.Context, mux *runtime.ServeM }) - mux.Handle("POST", pattern_TargetService_AddTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - var err error - ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.TargetService/AddTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:add-credential-libraries")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_TargetService_AddTargetCredentialLibraries_0(ctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_TargetService_AddTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_AddTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_TargetService_SetTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - var err error - ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.TargetService/SetTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:set-credential-libraries")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_TargetService_SetTargetCredentialLibraries_0(ctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_TargetService_SetTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_SetTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_TargetService_RemoveTargetCredentialLibraries_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - var err error - ctx, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.TargetService/RemoveTargetCredentialLibraries", runtime.WithHTTPPathPattern("/v1/targets/{id}:remove-credential-libraries")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_TargetService_RemoveTargetCredentialLibraries_0(ctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_TargetService_RemoveTargetCredentialLibraries_0(ctx, mux, outboundMarshaler, w, req, response_TargetService_RemoveTargetCredentialLibraries_0{resp}, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("POST", pattern_TargetService_AddTargetCredentialSources_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -2139,33 +1800,6 @@ func (m response_TargetService_RemoveTargetHostSources_0) XXX_ResponseBody() int return response.Item } -type response_TargetService_AddTargetCredentialLibraries_0 struct { - proto.Message -} - -func (m response_TargetService_AddTargetCredentialLibraries_0) XXX_ResponseBody() interface{} { - response := m.Message.(*AddTargetCredentialLibrariesResponse) - return response.Item -} - -type response_TargetService_SetTargetCredentialLibraries_0 struct { - proto.Message -} - -func (m response_TargetService_SetTargetCredentialLibraries_0) XXX_ResponseBody() interface{} { - response := m.Message.(*SetTargetCredentialLibrariesResponse) - return response.Item -} - -type response_TargetService_RemoveTargetCredentialLibraries_0 struct { - proto.Message -} - -func (m response_TargetService_RemoveTargetCredentialLibraries_0) XXX_ResponseBody() interface{} { - response := m.Message.(*RemoveTargetCredentialLibrariesResponse) - return response.Item -} - type response_TargetService_AddTargetCredentialSources_0 struct { proto.Message } @@ -2218,12 +1852,6 @@ var ( pattern_TargetService_RemoveTargetHostSources_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "remove-host-sources")) - pattern_TargetService_AddTargetCredentialLibraries_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "add-credential-libraries")) - - pattern_TargetService_SetTargetCredentialLibraries_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "set-credential-libraries")) - - pattern_TargetService_RemoveTargetCredentialLibraries_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "remove-credential-libraries")) - pattern_TargetService_AddTargetCredentialSources_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "add-credential-sources")) pattern_TargetService_SetTargetCredentialSources_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "targets", "id"}, "set-credential-sources")) @@ -2256,12 +1884,6 @@ var ( forward_TargetService_RemoveTargetHostSources_0 = runtime.ForwardResponseMessage - forward_TargetService_AddTargetCredentialLibraries_0 = runtime.ForwardResponseMessage - - forward_TargetService_SetTargetCredentialLibraries_0 = runtime.ForwardResponseMessage - - forward_TargetService_RemoveTargetCredentialLibraries_0 = runtime.ForwardResponseMessage - forward_TargetService_AddTargetCredentialSources_0 = runtime.ForwardResponseMessage forward_TargetService_SetTargetCredentialSources_0 = runtime.ForwardResponseMessage diff --git a/internal/gen/controller/api/services/target_service_grpc.pb.go b/internal/gen/controller/api/services/target_service_grpc.pb.go index 6b4aa54aa..f8ae7a60d 100644 --- a/internal/gen/controller/api/services/target_service_grpc.pb.go +++ b/internal/gen/controller/api/services/target_service_grpc.pb.go @@ -94,33 +94,6 @@ type TargetServiceClient interface { // is returned if a Host Source is attempted to be removed from the Target // when the Target does not have the Host Set. RemoveTargetHostSources(ctx context.Context, in *RemoveTargetHostSourcesRequest, opts ...grpc.CallOption) (*RemoveTargetHostSourcesResponse, error) - // Deprecated: Do not use. - // AddTargetCredentialLibraries adds Credential Libraries to this Target. - // The provided request must include the Target ID to which the Credential - // Libraries will be added. All Credential Libraries added to the provided - // Target must be a child of a Store that is in the same scope as this - // Target. If the scope or Target IDs are missing, malformed, or reference - // non-existing resources, an error is returned. An error is returned if a - // Credential Library is attempted to be added to a target that is already - // present on the Target. - AddTargetCredentialLibraries(ctx context.Context, in *AddTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*AddTargetCredentialLibrariesResponse, error) - // Deprecated: Do not use. - // SetTargetCredentialLibraries sets the Target's Credential Libraries. - // Any existing Credential Libraries on the Target are deleted if they are - // not included in this request. The provided request must include the scope, - // and the Target ID on which the Credential Libraries will be set. All - // Credential Libraries in the request must be a child of a Store that is - // in the same scope as the provided Target. If any IDs are missing, - // malformed, or references a non-existing resource, an error is returned. - SetTargetCredentialLibraries(ctx context.Context, in *SetTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*SetTargetCredentialLibrariesResponse, error) - // Deprecated: Do not use. - // RemoveTargetCredentialLibraries removes the Credential Libraries from the - // specified Target. The provided request must include the Target ID for the - // Target from which the Credential Libraries will be removed. If the ID is - // missing, or malformed, an error is returned. An error is returned if a - // Credential Library is attempted to be removed from the Target when the - // Target does not have the Credential Library. - RemoveTargetCredentialLibraries(ctx context.Context, in *RemoveTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*RemoveTargetCredentialLibrariesResponse, error) // AddTargetCredentialSources adds Credential Sources to this Target. // The provided request must include the Target ID to which the Credential // Sources will be added. All Credential Sources added to the provided @@ -263,36 +236,6 @@ func (c *targetServiceClient) RemoveTargetHostSources(ctx context.Context, in *R return out, nil } -// Deprecated: Do not use. -func (c *targetServiceClient) AddTargetCredentialLibraries(ctx context.Context, in *AddTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*AddTargetCredentialLibrariesResponse, error) { - out := new(AddTargetCredentialLibrariesResponse) - err := c.cc.Invoke(ctx, "/controller.api.services.v1.TargetService/AddTargetCredentialLibraries", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Deprecated: Do not use. -func (c *targetServiceClient) SetTargetCredentialLibraries(ctx context.Context, in *SetTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*SetTargetCredentialLibrariesResponse, error) { - out := new(SetTargetCredentialLibrariesResponse) - err := c.cc.Invoke(ctx, "/controller.api.services.v1.TargetService/SetTargetCredentialLibraries", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Deprecated: Do not use. -func (c *targetServiceClient) RemoveTargetCredentialLibraries(ctx context.Context, in *RemoveTargetCredentialLibrariesRequest, opts ...grpc.CallOption) (*RemoveTargetCredentialLibrariesResponse, error) { - out := new(RemoveTargetCredentialLibrariesResponse) - err := c.cc.Invoke(ctx, "/controller.api.services.v1.TargetService/RemoveTargetCredentialLibraries", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *targetServiceClient) AddTargetCredentialSources(ctx context.Context, in *AddTargetCredentialSourcesRequest, opts ...grpc.CallOption) (*AddTargetCredentialSourcesResponse, error) { out := new(AddTargetCredentialSourcesResponse) err := c.cc.Invoke(ctx, "/controller.api.services.v1.TargetService/AddTargetCredentialSources", in, out, opts...) @@ -400,33 +343,6 @@ type TargetServiceServer interface { // is returned if a Host Source is attempted to be removed from the Target // when the Target does not have the Host Set. RemoveTargetHostSources(context.Context, *RemoveTargetHostSourcesRequest) (*RemoveTargetHostSourcesResponse, error) - // Deprecated: Do not use. - // AddTargetCredentialLibraries adds Credential Libraries to this Target. - // The provided request must include the Target ID to which the Credential - // Libraries will be added. All Credential Libraries added to the provided - // Target must be a child of a Store that is in the same scope as this - // Target. If the scope or Target IDs are missing, malformed, or reference - // non-existing resources, an error is returned. An error is returned if a - // Credential Library is attempted to be added to a target that is already - // present on the Target. - AddTargetCredentialLibraries(context.Context, *AddTargetCredentialLibrariesRequest) (*AddTargetCredentialLibrariesResponse, error) - // Deprecated: Do not use. - // SetTargetCredentialLibraries sets the Target's Credential Libraries. - // Any existing Credential Libraries on the Target are deleted if they are - // not included in this request. The provided request must include the scope, - // and the Target ID on which the Credential Libraries will be set. All - // Credential Libraries in the request must be a child of a Store that is - // in the same scope as the provided Target. If any IDs are missing, - // malformed, or references a non-existing resource, an error is returned. - SetTargetCredentialLibraries(context.Context, *SetTargetCredentialLibrariesRequest) (*SetTargetCredentialLibrariesResponse, error) - // Deprecated: Do not use. - // RemoveTargetCredentialLibraries removes the Credential Libraries from the - // specified Target. The provided request must include the Target ID for the - // Target from which the Credential Libraries will be removed. If the ID is - // missing, or malformed, an error is returned. An error is returned if a - // Credential Library is attempted to be removed from the Target when the - // Target does not have the Credential Library. - RemoveTargetCredentialLibraries(context.Context, *RemoveTargetCredentialLibrariesRequest) (*RemoveTargetCredentialLibrariesResponse, error) // AddTargetCredentialSources adds Credential Sources to this Target. // The provided request must include the Target ID to which the Credential // Sources will be added. All Credential Sources added to the provided @@ -494,15 +410,6 @@ func (UnimplementedTargetServiceServer) SetTargetHostSources(context.Context, *S func (UnimplementedTargetServiceServer) RemoveTargetHostSources(context.Context, *RemoveTargetHostSourcesRequest) (*RemoveTargetHostSourcesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemoveTargetHostSources not implemented") } -func (UnimplementedTargetServiceServer) AddTargetCredentialLibraries(context.Context, *AddTargetCredentialLibrariesRequest) (*AddTargetCredentialLibrariesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method AddTargetCredentialLibraries not implemented") -} -func (UnimplementedTargetServiceServer) SetTargetCredentialLibraries(context.Context, *SetTargetCredentialLibrariesRequest) (*SetTargetCredentialLibrariesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetTargetCredentialLibraries not implemented") -} -func (UnimplementedTargetServiceServer) RemoveTargetCredentialLibraries(context.Context, *RemoveTargetCredentialLibrariesRequest) (*RemoveTargetCredentialLibrariesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RemoveTargetCredentialLibraries not implemented") -} func (UnimplementedTargetServiceServer) AddTargetCredentialSources(context.Context, *AddTargetCredentialSourcesRequest) (*AddTargetCredentialSourcesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddTargetCredentialSources not implemented") } @@ -741,60 +648,6 @@ func _TargetService_RemoveTargetHostSources_Handler(srv interface{}, ctx context return interceptor(ctx, in, info, handler) } -func _TargetService_AddTargetCredentialLibraries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AddTargetCredentialLibrariesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TargetServiceServer).AddTargetCredentialLibraries(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/controller.api.services.v1.TargetService/AddTargetCredentialLibraries", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TargetServiceServer).AddTargetCredentialLibraries(ctx, req.(*AddTargetCredentialLibrariesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _TargetService_SetTargetCredentialLibraries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetTargetCredentialLibrariesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TargetServiceServer).SetTargetCredentialLibraries(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/controller.api.services.v1.TargetService/SetTargetCredentialLibraries", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TargetServiceServer).SetTargetCredentialLibraries(ctx, req.(*SetTargetCredentialLibrariesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _TargetService_RemoveTargetCredentialLibraries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveTargetCredentialLibrariesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TargetServiceServer).RemoveTargetCredentialLibraries(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/controller.api.services.v1.TargetService/RemoveTargetCredentialLibraries", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TargetServiceServer).RemoveTargetCredentialLibraries(ctx, req.(*RemoveTargetCredentialLibrariesRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _TargetService_AddTargetCredentialSources_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AddTargetCredentialSourcesRequest) if err := dec(in); err != nil { @@ -904,18 +757,6 @@ var TargetService_ServiceDesc = grpc.ServiceDesc{ MethodName: "RemoveTargetHostSources", Handler: _TargetService_RemoveTargetHostSources_Handler, }, - { - MethodName: "AddTargetCredentialLibraries", - Handler: _TargetService_AddTargetCredentialLibraries_Handler, - }, - { - MethodName: "SetTargetCredentialLibraries", - Handler: _TargetService_SetTargetCredentialLibraries_Handler, - }, - { - MethodName: "RemoveTargetCredentialLibraries", - Handler: _TargetService_RemoveTargetCredentialLibraries_Handler, - }, { MethodName: "AddTargetCredentialSources", Handler: _TargetService_AddTargetCredentialSources_Handler, diff --git a/internal/proto/controller/api/resources/targets/v1/target.proto b/internal/proto/controller/api/resources/targets/v1/target.proto index 0b646941a..42577ec13 100644 --- a/internal/proto/controller/api/resources/targets/v1/target.proto +++ b/internal/proto/controller/api/resources/targets/v1/target.proto @@ -47,23 +47,6 @@ message CredentialSource { string credential_type = 70; // @gotags: `class:"public"` } -message CredentialLibrary { - // The ID of the Credential Library. - string id = 10; // @gotags: `class:"public"` - - // Output only. The name of the Credential Library. - string name = 20; // @gotags: `class:"public"` - - // Output only. The description of the Credential Library. - string description = 30; // @gotags: `class:"public"` - - // Output only. The Credential Store to which this Credential Library belongs. - string credential_store_id = 40 [json_name = "credential_store_id"]; // @gotags: `class:"public"` - - // Output only. The type of the credential library. - string type = 60; // @gotags: `class:"public"` -} - // The actual secret for a session credential. message SessionSecret { // Output only. The base64-encoded value representing the raw bytes from the @@ -79,15 +62,16 @@ message SessionCredential { // Output only. The credential source information. CredentialSource credential_source = 1; - // Output only. The library which generated this credential. Deprecated: use credential_source instead. - CredentialLibrary credential_library = 10 [deprecated = true]; - // Output only. The secret of this credential base64 encoded. SessionSecret secret = 20; // Output only. The fields of the strongly typed credential, empty if the credential type of the // credential source is unspecified. google.protobuf.Struct credential = 40; + + // Deprecated fields + reserved "credential_library"; + reserved 10; } // Target contains all fields related to a Target resource @@ -174,17 +158,6 @@ message Target { } ]; // @gotags: `class:"public"` - // Output only. The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead. - repeated string application_credential_library_ids = 150 [ - json_name = "application_credential_library_ids", - deprecated = true - ]; // @gotags: `class:"public"` - // Output only. The application credential libraries associated with this Target. Deprecated: use application_credential_sources instead. - repeated CredentialLibrary application_credential_libraries = 180 [ - json_name = "application_credential_libraries", - deprecated = true - ]; - // Output only. The IDs of the application credential source ids associated with this Target. repeated string application_credential_source_ids = 400 [json_name = "application_credential_source_ids"]; // @gotags: `class:"public"` // Output only. The application credential sources associated with this Target. @@ -210,6 +183,10 @@ message Target { // Output only. The available actions on this resource for this user. repeated string authorized_actions = 300 [json_name = "authorized_actions"]; // @gotags: `class:"public"` + + // Deprecated fields + reserved "application_credential_library_ids", "application_credential_libraries"; + reserved 150, 180; } // TcpTargetAttributes contains attributes relevant to Targets of type "tcp" diff --git a/internal/proto/controller/api/services/v1/target_service.proto b/internal/proto/controller/api/services/v1/target_service.proto index eafa19645..e255325ff 100644 --- a/internal/proto/controller/api/services/v1/target_service.proto +++ b/internal/proto/controller/api/services/v1/target_service.proto @@ -203,63 +203,6 @@ service TargetService { }; } - // AddTargetCredentialLibraries adds Credential Libraries to this Target. - // The provided request must include the Target ID to which the Credential - // Libraries will be added. All Credential Libraries added to the provided - // Target must be a child of a Store that is in the same scope as this - // Target. If the scope or Target IDs are missing, malformed, or reference - // non-existing resources, an error is returned. An error is returned if a - // Credential Library is attempted to be added to a target that is already - // present on the Target. - rpc AddTargetCredentialLibraries(AddTargetCredentialLibrariesRequest) returns (AddTargetCredentialLibrariesResponse) { - option (google.api.http) = { - post: "/v1/targets/{id}:add-credential-libraries" - body: "*" - response_body: "item" - }; - option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { - summary: "Deprecated: use AddTargetCredentialSources instead." - }; - option deprecated = true; - } - - // SetTargetCredentialLibraries sets the Target's Credential Libraries. - // Any existing Credential Libraries on the Target are deleted if they are - // not included in this request. The provided request must include the scope, - // and the Target ID on which the Credential Libraries will be set. All - // Credential Libraries in the request must be a child of a Store that is - // in the same scope as the provided Target. If any IDs are missing, - // malformed, or references a non-existing resource, an error is returned. - rpc SetTargetCredentialLibraries(SetTargetCredentialLibrariesRequest) returns (SetTargetCredentialLibrariesResponse) { - option (google.api.http) = { - post: "/v1/targets/{id}:set-credential-libraries" - body: "*" - response_body: "item" - }; - option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { - summary: "Deprecated: use SetTargetCredentialSources instead." - }; - option deprecated = true; - } - - // RemoveTargetCredentialLibraries removes the Credential Libraries from the - // specified Target. The provided request must include the Target ID for the - // Target from which the Credential Libraries will be removed. If the ID is - // missing, or malformed, an error is returned. An error is returned if a - // Credential Library is attempted to be removed from the Target when the - // Target does not have the Credential Library. - rpc RemoveTargetCredentialLibraries(RemoveTargetCredentialLibrariesRequest) returns (RemoveTargetCredentialLibrariesResponse) { - option (google.api.http) = { - post: "/v1/targets/{id}:remove-credential-libraries" - body: "*" - response_body: "item" - }; - option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { - summary: "Deprecated: use RemoveTargetCredentialSources instead." - }; - option deprecated = true; - } - // AddTargetCredentialSources adds Credential Sources to this Target. // The provided request must include the Target ID to which the Credential // Sources will be added. All Credential Sources added to the provided @@ -430,45 +373,6 @@ message RemoveTargetHostSourcesResponse { api.resources.targets.v1.Target item = 1; } -message AddTargetCredentialLibrariesRequest { - string id = 1; // @gotags: `class:"public"` - // Version is used to ensure this resource has not changed. - // The mutation will fail if the version does not match the latest known good version. - uint32 version = 2; // @gotags: `class:"public"` - repeated string application_credential_library_ids = 3 [json_name = "application_credential_library_ids"]; // @gotags: `class:"public"` -} - -message AddTargetCredentialLibrariesResponse { - api.resources.targets.v1.Target item = 1; -} - -// Sets the values for application_credential_library_ids. If any -// credential_library_id field that is not set in the request will result in -// those fields being cleared. -message SetTargetCredentialLibrariesRequest { - string id = 1; // @gotags: `class:"public"` - // Version is used to ensure this resource has not changed. - // The mutation will fail if the version does not match the latest known good version. - uint32 version = 2; // @gotags: `class:"public"` - repeated string application_credential_library_ids = 3 [json_name = "application_credential_library_ids"]; // @gotags: `class:"public"` -} - -message SetTargetCredentialLibrariesResponse { - api.resources.targets.v1.Target item = 1; -} - -message RemoveTargetCredentialLibrariesRequest { - string id = 1; // @gotags: `class:"public"` - // Version is used to ensure this resource has not changed. - // The mutation will fail if the version does not match the latest known good version. - uint32 version = 2; // @gotags: `class:"public"` - repeated string application_credential_library_ids = 3 [json_name = "application_credential_library_ids"]; // @gotags: `class:"public"` -} - -message RemoveTargetCredentialLibrariesResponse { - api.resources.targets.v1.Target item = 1; -} - message AddTargetCredentialSourcesRequest { string id = 1; // @gotags: `class:"public"` // Version is used to ensure this resource has not changed. diff --git a/internal/tests/api/targets/classification_test.go b/internal/tests/api/targets/classification_test.go index a0086553a..3255a9e1a 100644 --- a/internal/tests/api/targets/classification_test.go +++ b/internal/tests/api/targets/classification_test.go @@ -71,19 +71,9 @@ func TestTargets(t *testing.T) { HostCatalogId: "host-catalog-id", }, }, - SessionMaxSeconds: &wrapperspb.UInt32Value{Value: 0}, - SessionConnectionLimit: &wrapperspb.Int32Value{Value: 0}, - WorkerFilter: &wrapperspb.StringValue{Value: "worker-filter"}, - ApplicationCredentialLibraryIds: []string{"application-credential-library-id"}, - ApplicationCredentialLibraries: []*pb.CredentialLibrary{ - { - Id: "id", - Name: "name", - Description: "description", - CredentialStoreId: "credential-store-id", - Type: "type", - }, - }, + SessionMaxSeconds: &wrapperspb.UInt32Value{Value: 0}, + SessionConnectionLimit: &wrapperspb.Int32Value{Value: 0}, + WorkerFilter: &wrapperspb.StringValue{Value: "worker-filter"}, ApplicationCredentialSourceIds: []string{"application-credential-source-id"}, ApplicationCredentialSources: []*pb.CredentialSource{ { @@ -151,19 +141,9 @@ func TestTargets(t *testing.T) { HostCatalogId: "host-catalog-id", }, }, - SessionMaxSeconds: &wrapperspb.UInt32Value{Value: 0}, - SessionConnectionLimit: &wrapperspb.Int32Value{Value: 0}, - WorkerFilter: &wrapperspb.StringValue{Value: "worker-filter"}, - ApplicationCredentialLibraryIds: []string{"application-credential-library-id"}, - ApplicationCredentialLibraries: []*pb.CredentialLibrary{ - { - Id: "id", - Name: "name", - Description: "description", - CredentialStoreId: "credential-store-id", - Type: "type", - }, - }, + SessionMaxSeconds: &wrapperspb.UInt32Value{Value: 0}, + SessionConnectionLimit: &wrapperspb.Int32Value{Value: 0}, + WorkerFilter: &wrapperspb.StringValue{Value: "worker-filter"}, ApplicationCredentialSourceIds: []string{"application-credential-source-id"}, ApplicationCredentialSources: []*pb.CredentialSource{ { @@ -253,13 +233,6 @@ func TestTargets(t *testing.T) { Type: "type", CredentialType: "credential-type", }, - CredentialLibrary: &pb.CredentialLibrary{ - Id: "id", - Name: "name", - Description: "description", - CredentialStoreId: "credential-store-id", - Type: "type", - }, Secret: &pb.SessionSecret{ Raw: "raw-secrets", Decoded: &structpb.Struct{ @@ -309,13 +282,6 @@ func TestTargets(t *testing.T) { Type: "type", CredentialType: "credential-type", }, - CredentialLibrary: &pb.CredentialLibrary{ - Id: "id", - Name: "name", - Description: "description", - CredentialStoreId: "credential-store-id", - Type: "type", - }, Secret: &pb.SessionSecret{ Raw: encrypt.RedactedData, Decoded: &structpb.Struct{ diff --git a/internal/types/action/action.go b/internal/types/action/action.go index d7986c3a4..f93b86c22 100644 --- a/internal/types/action/action.go +++ b/internal/types/action/action.go @@ -144,9 +144,9 @@ func (a Type) String() string { "change-state", "delete:self", "no-op", - "add-credential-libraries", - "set-credential-libraries", - "remove-credential-libraries", + "add-credential-libraries", // DEPRECATED + "set-credential-libraries", // DEPRECATED + "remove-credential-libraries", // DEPRECATED "add-credential-sources", "set-credential-sources", "remove-credential-sources", diff --git a/sdk/pbs/controller/api/resources/targets/target.pb.go b/sdk/pbs/controller/api/resources/targets/target.pb.go index 912d885d0..4e8b1c1c7 100644 --- a/sdk/pbs/controller/api/resources/targets/target.pb.go +++ b/sdk/pbs/controller/api/resources/targets/target.pb.go @@ -233,90 +233,6 @@ func (x *CredentialSource) GetCredentialType() string { return "" } -type CredentialLibrary struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The ID of the Credential Library. - Id string `protobuf:"bytes,10,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"` - // Output only. The name of the Credential Library. - Name string `protobuf:"bytes,20,opt,name=name,proto3" json:"name,omitempty" class:"public"` // @gotags: `class:"public"` - // Output only. The description of the Credential Library. - Description string `protobuf:"bytes,30,opt,name=description,proto3" json:"description,omitempty" class:"public"` // @gotags: `class:"public"` - // Output only. The Credential Store to which this Credential Library belongs. - CredentialStoreId string `protobuf:"bytes,40,opt,name=credential_store_id,proto3" json:"credential_store_id,omitempty" class:"public"` // @gotags: `class:"public"` - // Output only. The type of the credential library. - Type string `protobuf:"bytes,60,opt,name=type,proto3" json:"type,omitempty" class:"public"` // @gotags: `class:"public"` -} - -func (x *CredentialLibrary) Reset() { - *x = CredentialLibrary{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CredentialLibrary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CredentialLibrary) ProtoMessage() {} - -func (x *CredentialLibrary) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CredentialLibrary.ProtoReflect.Descriptor instead. -func (*CredentialLibrary) Descriptor() ([]byte, []int) { - return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{3} -} - -func (x *CredentialLibrary) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *CredentialLibrary) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *CredentialLibrary) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *CredentialLibrary) GetCredentialStoreId() string { - if x != nil { - return x.CredentialStoreId - } - return "" -} - -func (x *CredentialLibrary) GetType() string { - if x != nil { - return x.Type - } - return "" -} - // The actual secret for a session credential. type SessionSecret struct { state protoimpl.MessageState @@ -333,7 +249,7 @@ type SessionSecret struct { func (x *SessionSecret) Reset() { *x = SessionSecret{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[4] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -346,7 +262,7 @@ func (x *SessionSecret) String() string { func (*SessionSecret) ProtoMessage() {} func (x *SessionSecret) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[4] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -359,7 +275,7 @@ func (x *SessionSecret) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionSecret.ProtoReflect.Descriptor instead. func (*SessionSecret) Descriptor() ([]byte, []int) { - return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{4} + return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{3} } func (x *SessionSecret) GetRaw() string { @@ -384,10 +300,6 @@ type SessionCredential struct { // Output only. The credential source information. CredentialSource *CredentialSource `protobuf:"bytes,1,opt,name=credential_source,json=credentialSource,proto3" json:"credential_source,omitempty"` - // Output only. The library which generated this credential. Deprecated: use credential_source instead. - // - // Deprecated: Do not use. - CredentialLibrary *CredentialLibrary `protobuf:"bytes,10,opt,name=credential_library,json=credentialLibrary,proto3" json:"credential_library,omitempty"` // Output only. The secret of this credential base64 encoded. Secret *SessionSecret `protobuf:"bytes,20,opt,name=secret,proto3" json:"secret,omitempty"` // Output only. The fields of the strongly typed credential, empty if the credential type of the @@ -398,7 +310,7 @@ type SessionCredential struct { func (x *SessionCredential) Reset() { *x = SessionCredential{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[5] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -411,7 +323,7 @@ func (x *SessionCredential) String() string { func (*SessionCredential) ProtoMessage() {} func (x *SessionCredential) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[5] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -424,7 +336,7 @@ func (x *SessionCredential) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionCredential.ProtoReflect.Descriptor instead. func (*SessionCredential) Descriptor() ([]byte, []int) { - return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{5} + return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{4} } func (x *SessionCredential) GetCredentialSource() *CredentialSource { @@ -434,14 +346,6 @@ func (x *SessionCredential) GetCredentialSource() *CredentialSource { return nil } -// Deprecated: Do not use. -func (x *SessionCredential) GetCredentialLibrary() *CredentialLibrary { - if x != nil { - return x.CredentialLibrary - } - return nil -} - func (x *SessionCredential) GetSecret() *SessionSecret { if x != nil { return x.Secret @@ -495,14 +399,6 @@ type Target struct { SessionConnectionLimit *wrapperspb.Int32Value `protobuf:"bytes,130,opt,name=session_connection_limit,proto3" json:"session_connection_limit,omitempty" class:"public"` // @gotags: `class:"public"` // Optional boolean expression to filter the workers that are allowed to satisfy this request. WorkerFilter *wrapperspb.StringValue `protobuf:"bytes,140,opt,name=worker_filter,proto3" json:"worker_filter,omitempty" class:"public"` // @gotags: `class:"public"` - // Output only. The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead. - // - // Deprecated: Do not use. - ApplicationCredentialLibraryIds []string `protobuf:"bytes,150,rep,name=application_credential_library_ids,proto3" json:"application_credential_library_ids,omitempty" class:"public"` // @gotags: `class:"public"` - // Output only. The application credential libraries associated with this Target. Deprecated: use application_credential_sources instead. - // - // Deprecated: Do not use. - ApplicationCredentialLibraries []*CredentialLibrary `protobuf:"bytes,180,rep,name=application_credential_libraries,proto3" json:"application_credential_libraries,omitempty"` // Output only. The IDs of the application credential source ids associated with this Target. ApplicationCredentialSourceIds []string `protobuf:"bytes,400,rep,name=application_credential_source_ids,proto3" json:"application_credential_source_ids,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The application credential sources associated with this Target. @@ -522,7 +418,7 @@ type Target struct { func (x *Target) Reset() { *x = Target{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[6] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -535,7 +431,7 @@ func (x *Target) String() string { func (*Target) ProtoMessage() {} func (x *Target) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[6] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -548,7 +444,7 @@ func (x *Target) ProtoReflect() protoreflect.Message { // Deprecated: Use Target.ProtoReflect.Descriptor instead. func (*Target) Descriptor() ([]byte, []int) { - return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{6} + return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{5} } func (x *Target) GetId() string { @@ -663,22 +559,6 @@ func (x *Target) GetWorkerFilter() *wrapperspb.StringValue { return nil } -// Deprecated: Do not use. -func (x *Target) GetApplicationCredentialLibraryIds() []string { - if x != nil { - return x.ApplicationCredentialLibraryIds - } - return nil -} - -// Deprecated: Do not use. -func (x *Target) GetApplicationCredentialLibraries() []*CredentialLibrary { - if x != nil { - return x.ApplicationCredentialLibraries - } - return nil -} - func (x *Target) GetApplicationCredentialSourceIds() []string { if x != nil { return x.ApplicationCredentialSourceIds @@ -765,7 +645,7 @@ type TcpTargetAttributes struct { func (x *TcpTargetAttributes) Reset() { *x = TcpTargetAttributes{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[7] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -778,7 +658,7 @@ func (x *TcpTargetAttributes) String() string { func (*TcpTargetAttributes) ProtoMessage() {} func (x *TcpTargetAttributes) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[7] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -791,7 +671,7 @@ func (x *TcpTargetAttributes) ProtoReflect() protoreflect.Message { // Deprecated: Use TcpTargetAttributes.ProtoReflect.Descriptor instead. func (*TcpTargetAttributes) Descriptor() ([]byte, []int) { - return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{7} + return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{6} } func (x *TcpTargetAttributes) GetDefaultPort() *wrapperspb.UInt32Value { @@ -814,7 +694,7 @@ type WorkerInfo struct { func (x *WorkerInfo) Reset() { *x = WorkerInfo{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[8] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -827,7 +707,7 @@ func (x *WorkerInfo) String() string { func (*WorkerInfo) ProtoMessage() {} func (x *WorkerInfo) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[8] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -840,7 +720,7 @@ func (x *WorkerInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerInfo.ProtoReflect.Descriptor instead. func (*WorkerInfo) Descriptor() ([]byte, []int) { - return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{8} + return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{7} } func (x *WorkerInfo) GetAddress() string { @@ -883,7 +763,7 @@ type SessionAuthorizationData struct { func (x *SessionAuthorizationData) Reset() { *x = SessionAuthorizationData{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[9] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -896,7 +776,7 @@ func (x *SessionAuthorizationData) String() string { func (*SessionAuthorizationData) ProtoMessage() {} func (x *SessionAuthorizationData) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[9] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -909,7 +789,7 @@ func (x *SessionAuthorizationData) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionAuthorizationData.ProtoReflect.Descriptor instead. func (*SessionAuthorizationData) Descriptor() ([]byte, []int) { - return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{9} + return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{8} } func (x *SessionAuthorizationData) GetSessionId() string { @@ -1022,7 +902,7 @@ type SessionAuthorization struct { func (x *SessionAuthorization) Reset() { *x = SessionAuthorization{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[10] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1035,7 +915,7 @@ func (x *SessionAuthorization) String() string { func (*SessionAuthorization) ProtoMessage() {} func (x *SessionAuthorization) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[10] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1048,7 +928,7 @@ func (x *SessionAuthorization) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionAuthorization.ProtoReflect.Descriptor instead. func (*SessionAuthorization) Descriptor() ([]byte, []int) { - return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{10} + return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{9} } func (x *SessionAuthorization) GetSessionId() string { @@ -1143,7 +1023,7 @@ type UserPasswordCredential struct { func (x *UserPasswordCredential) Reset() { *x = UserPasswordCredential{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[11] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1156,7 +1036,7 @@ func (x *UserPasswordCredential) String() string { func (*UserPasswordCredential) ProtoMessage() {} func (x *UserPasswordCredential) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[11] + mi := &file_controller_api_resources_targets_v1_target_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1169,7 +1049,7 @@ func (x *UserPasswordCredential) ProtoReflect() protoreflect.Message { // Deprecated: Use UserPasswordCredential.ProtoReflect.Descriptor instead. func (*UserPasswordCredential) Descriptor() ([]byte, []int) { - return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{11} + return file_controller_api_resources_targets_v1_target_proto_rawDescGZIP(), []int{10} } func (x *UserPasswordCredential) GetUsername() string { @@ -1229,183 +1109,190 @@ var file_controller_api_resources_targets_v1_target_proto_rawDesc = []byte{ 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0x9f, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x5f, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x3c, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x54, 0x0a, 0x0d, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x31, 0x0a, 0x07, 0x64, - 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x22, 0xe7, - 0x02, 0x0a, 0x11, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x12, 0x62, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x10, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x69, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, - 0x61, 0x72, 0x79, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x14, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, - 0x37, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x28, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, 0x63, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x22, 0xc9, 0x0f, 0x0a, 0x06, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, - 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, - 0x63, 0x6f, 0x70, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x42, 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, - 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x22, - 0x0a, 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x64, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, - 0x64, 0x73, 0x12, 0x4a, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x18, - 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, - 0x53, 0x65, 0x74, 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x12, 0x29, - 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0xa4, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x54, 0x0a, 0x0c, 0x68, 0x6f, 0x73, - 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0xae, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, - 0x80, 0x01, 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, - 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x78, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x30, 0xa0, 0xda, 0x29, - 0x01, 0xc2, 0xdd, 0x29, 0x28, 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, - 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x11, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x13, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x73, 0x12, 0x94, 0x01, 0x0a, 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x82, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x42, 0x3a, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x32, 0x0a, 0x18, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, - 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x6a, 0x0a, 0x0d, 0x77, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, - 0x25, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1d, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x66, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x53, 0x0a, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, - 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x96, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, - 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x20, 0x61, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, - 0xb4, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0x54, 0x0a, 0x0d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x31, 0x0a, 0x07, 0x64, 0x65, 0x63, + 0x6f, 0x64, 0x65, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x52, 0x07, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x22, 0x96, 0x02, 0x0a, + 0x11, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x12, 0x62, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x10, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x42, 0x02, - 0x18, 0x01, 0x52, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, - 0x72, 0x69, 0x65, 0x73, 0x12, 0x4d, 0x0a, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x90, 0x03, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x73, 0x12, 0x7e, 0x0a, 0x1e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x9a, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x1e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, + 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x04, 0x08, 0x0a, 0x10, + 0x0b, 0x52, 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, + 0x62, 0x72, 0x61, 0x72, 0x79, 0x22, 0xc0, 0x0e, 0x0a, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, + 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, + 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, + 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, + 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x68, + 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x64, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x12, + 0x4a, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x18, 0x6e, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x65, 0x74, + 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x0f, 0x68, + 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0xa4, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x54, 0x0a, 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0xae, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0c, + 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x80, 0x01, 0x0a, + 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x78, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, + 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x30, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, + 0x29, 0x28, 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x11, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x52, 0x13, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, + 0x94, 0x01, 0x0a, 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x82, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x42, 0x3a, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x32, 0x0a, 0x18, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x18, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x6a, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x25, 0xa0, 0xda, + 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1d, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x21, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x90, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x21, + 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x73, 0x12, 0x7e, 0x0a, 0x1e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0xf4, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1c, 0x65, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x74, 0x0a, 0x19, 0x65, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0xfe, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, + 0x63, 0x65, 0x73, 0x18, 0x9a, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x1e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x12, 0x43, 0x0a, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0xf4, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1c, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, + 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x74, 0x0a, 0x19, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, + 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x18, 0xfe, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x19, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x0a, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x0f, 0xa0, 0xda, 0x29, 0x01, + 0x9a, 0xe3, 0x29, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x15, 0x74, 0x63, + 0x70, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x63, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x42, 0x1b, 0xa0, 0xda, 0x29, 0x01, 0x9a, 0xe3, 0x29, 0x03, 0x74, 0x63, + 0x70, 0xfa, 0xd2, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, + 0x4c, 0x48, 0x00, 0x52, 0x13, 0x74, 0x63, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, + 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x61, 0x74, 0x74, + 0x72, 0x73, 0x4a, 0x06, 0x08, 0x96, 0x01, 0x10, 0x97, 0x01, 0x4a, 0x06, 0x08, 0xb4, 0x01, 0x10, + 0xb5, 0x01, 0x52, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, + 0x72, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x52, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, + 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x22, 0x87, 0x01, 0x0a, 0x13, 0x54, 0x63, 0x70, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x12, 0x70, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2e, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x26, 0x0a, 0x17, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x0b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x50, 0x6f, 0x72, 0x74, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, + 0x72, 0x74, 0x22, 0x26, 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xed, 0x03, 0x0a, 0x18, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, + 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x78, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0b, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x82, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, + 0x18, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x08, 0x65, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x8d, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x52, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x96, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x19, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x4b, - 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0xc8, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x0f, 0xa0, 0xda, - 0x29, 0x01, 0x9a, 0xe3, 0x29, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, - 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x15, - 0x74, 0x63, 0x70, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x63, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x42, 0x1b, 0xa0, 0xda, 0x29, 0x01, 0x9a, 0xe3, 0x29, 0x03, - 0x74, 0x63, 0x70, 0xfa, 0xd2, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x49, 0x4e, 0x54, 0x45, 0x52, - 0x4e, 0x41, 0x4c, 0x48, 0x00, 0x52, 0x13, 0x74, 0x63, 0x70, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x61, - 0x74, 0x74, 0x72, 0x73, 0x22, 0x87, 0x01, 0x0a, 0x13, 0x54, 0x63, 0x70, 0x54, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x70, 0x0a, 0x0c, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x42, 0x2e, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x26, 0x0a, 0x17, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, - 0x6f, 0x72, 0x74, 0x12, 0x0b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x26, - 0x0a, 0x0a, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xed, 0x03, 0x0a, 0x18, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x77, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0xeb, 0x03, 0x0a, 0x14, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, @@ -1417,65 +1304,35 @@ var file_controller_api_resources_targets_v1_target_proto_rawDesc = []byte{ 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x50, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x5a, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x78, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x68, - 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x8c, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, - 0x6f, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x18, 0x8d, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x12, 0x52, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x96, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x57, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0xeb, 0x03, 0x0a, 0x14, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x12, - 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, - 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, - 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x32, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, - 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x18, - 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x13, - 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, - 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x58, 0x0a, 0x0b, 0x63, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x73, 0x22, 0x50, 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x50, 0x61, 0x73, 0x73, - 0x77, 0x6f, 0x72, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1a, - 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 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, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, - 0x3b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x12, 0x20, 0x0a, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, + 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x30, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x64, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x58, + 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x6e, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x50, 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, + 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 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, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x73, 0x3b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1490,60 +1347,57 @@ func file_controller_api_resources_targets_v1_target_proto_rawDescGZIP() []byte return file_controller_api_resources_targets_v1_target_proto_rawDescData } -var file_controller_api_resources_targets_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_controller_api_resources_targets_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_controller_api_resources_targets_v1_target_proto_goTypes = []interface{}{ (*HostSource)(nil), // 0: controller.api.resources.targets.v1.HostSource (*HostSet)(nil), // 1: controller.api.resources.targets.v1.HostSet (*CredentialSource)(nil), // 2: controller.api.resources.targets.v1.CredentialSource - (*CredentialLibrary)(nil), // 3: controller.api.resources.targets.v1.CredentialLibrary - (*SessionSecret)(nil), // 4: controller.api.resources.targets.v1.SessionSecret - (*SessionCredential)(nil), // 5: controller.api.resources.targets.v1.SessionCredential - (*Target)(nil), // 6: controller.api.resources.targets.v1.Target - (*TcpTargetAttributes)(nil), // 7: controller.api.resources.targets.v1.TcpTargetAttributes - (*WorkerInfo)(nil), // 8: controller.api.resources.targets.v1.WorkerInfo - (*SessionAuthorizationData)(nil), // 9: controller.api.resources.targets.v1.SessionAuthorizationData - (*SessionAuthorization)(nil), // 10: controller.api.resources.targets.v1.SessionAuthorization - (*UserPasswordCredential)(nil), // 11: controller.api.resources.targets.v1.UserPasswordCredential - (*structpb.Struct)(nil), // 12: google.protobuf.Struct - (*scopes.ScopeInfo)(nil), // 13: controller.api.resources.scopes.v1.ScopeInfo - (*wrapperspb.StringValue)(nil), // 14: google.protobuf.StringValue - (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp - (*wrapperspb.UInt32Value)(nil), // 16: google.protobuf.UInt32Value - (*wrapperspb.Int32Value)(nil), // 17: google.protobuf.Int32Value + (*SessionSecret)(nil), // 3: controller.api.resources.targets.v1.SessionSecret + (*SessionCredential)(nil), // 4: controller.api.resources.targets.v1.SessionCredential + (*Target)(nil), // 5: controller.api.resources.targets.v1.Target + (*TcpTargetAttributes)(nil), // 6: controller.api.resources.targets.v1.TcpTargetAttributes + (*WorkerInfo)(nil), // 7: controller.api.resources.targets.v1.WorkerInfo + (*SessionAuthorizationData)(nil), // 8: controller.api.resources.targets.v1.SessionAuthorizationData + (*SessionAuthorization)(nil), // 9: controller.api.resources.targets.v1.SessionAuthorization + (*UserPasswordCredential)(nil), // 10: controller.api.resources.targets.v1.UserPasswordCredential + (*structpb.Struct)(nil), // 11: google.protobuf.Struct + (*scopes.ScopeInfo)(nil), // 12: controller.api.resources.scopes.v1.ScopeInfo + (*wrapperspb.StringValue)(nil), // 13: google.protobuf.StringValue + (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp + (*wrapperspb.UInt32Value)(nil), // 15: google.protobuf.UInt32Value + (*wrapperspb.Int32Value)(nil), // 16: google.protobuf.Int32Value } var file_controller_api_resources_targets_v1_target_proto_depIdxs = []int32{ - 12, // 0: controller.api.resources.targets.v1.SessionSecret.decoded:type_name -> google.protobuf.Struct + 11, // 0: controller.api.resources.targets.v1.SessionSecret.decoded:type_name -> google.protobuf.Struct 2, // 1: controller.api.resources.targets.v1.SessionCredential.credential_source:type_name -> controller.api.resources.targets.v1.CredentialSource - 3, // 2: controller.api.resources.targets.v1.SessionCredential.credential_library:type_name -> controller.api.resources.targets.v1.CredentialLibrary - 4, // 3: controller.api.resources.targets.v1.SessionCredential.secret:type_name -> controller.api.resources.targets.v1.SessionSecret - 12, // 4: controller.api.resources.targets.v1.SessionCredential.credential:type_name -> google.protobuf.Struct - 13, // 5: controller.api.resources.targets.v1.Target.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 14, // 6: controller.api.resources.targets.v1.Target.name:type_name -> google.protobuf.StringValue - 14, // 7: controller.api.resources.targets.v1.Target.description:type_name -> google.protobuf.StringValue - 15, // 8: controller.api.resources.targets.v1.Target.created_time:type_name -> google.protobuf.Timestamp - 15, // 9: controller.api.resources.targets.v1.Target.updated_time:type_name -> google.protobuf.Timestamp - 1, // 10: controller.api.resources.targets.v1.Target.host_sets:type_name -> controller.api.resources.targets.v1.HostSet - 0, // 11: controller.api.resources.targets.v1.Target.host_sources:type_name -> controller.api.resources.targets.v1.HostSource - 16, // 12: controller.api.resources.targets.v1.Target.session_max_seconds:type_name -> google.protobuf.UInt32Value - 17, // 13: controller.api.resources.targets.v1.Target.session_connection_limit:type_name -> google.protobuf.Int32Value - 14, // 14: controller.api.resources.targets.v1.Target.worker_filter:type_name -> google.protobuf.StringValue - 3, // 15: controller.api.resources.targets.v1.Target.application_credential_libraries:type_name -> controller.api.resources.targets.v1.CredentialLibrary - 2, // 16: controller.api.resources.targets.v1.Target.application_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource - 2, // 17: controller.api.resources.targets.v1.Target.egress_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource - 12, // 18: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct - 7, // 19: controller.api.resources.targets.v1.Target.tcp_target_attributes:type_name -> controller.api.resources.targets.v1.TcpTargetAttributes - 16, // 20: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value - 13, // 21: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 15, // 22: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp - 8, // 23: controller.api.resources.targets.v1.SessionAuthorizationData.worker_info:type_name -> controller.api.resources.targets.v1.WorkerInfo - 13, // 24: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 15, // 25: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp - 5, // 26: controller.api.resources.targets.v1.SessionAuthorization.credentials:type_name -> controller.api.resources.targets.v1.SessionCredential - 27, // [27:27] is the sub-list for method output_type - 27, // [27:27] is the sub-list for method input_type - 27, // [27:27] is the sub-list for extension type_name - 27, // [27:27] is the sub-list for extension extendee - 0, // [0:27] is the sub-list for field type_name + 3, // 2: controller.api.resources.targets.v1.SessionCredential.secret:type_name -> controller.api.resources.targets.v1.SessionSecret + 11, // 3: controller.api.resources.targets.v1.SessionCredential.credential:type_name -> google.protobuf.Struct + 12, // 4: controller.api.resources.targets.v1.Target.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 13, // 5: controller.api.resources.targets.v1.Target.name:type_name -> google.protobuf.StringValue + 13, // 6: controller.api.resources.targets.v1.Target.description:type_name -> google.protobuf.StringValue + 14, // 7: controller.api.resources.targets.v1.Target.created_time:type_name -> google.protobuf.Timestamp + 14, // 8: controller.api.resources.targets.v1.Target.updated_time:type_name -> google.protobuf.Timestamp + 1, // 9: controller.api.resources.targets.v1.Target.host_sets:type_name -> controller.api.resources.targets.v1.HostSet + 0, // 10: controller.api.resources.targets.v1.Target.host_sources:type_name -> controller.api.resources.targets.v1.HostSource + 15, // 11: controller.api.resources.targets.v1.Target.session_max_seconds:type_name -> google.protobuf.UInt32Value + 16, // 12: controller.api.resources.targets.v1.Target.session_connection_limit:type_name -> google.protobuf.Int32Value + 13, // 13: controller.api.resources.targets.v1.Target.worker_filter:type_name -> google.protobuf.StringValue + 2, // 14: controller.api.resources.targets.v1.Target.application_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource + 2, // 15: controller.api.resources.targets.v1.Target.egress_credential_sources:type_name -> controller.api.resources.targets.v1.CredentialSource + 11, // 16: controller.api.resources.targets.v1.Target.attributes:type_name -> google.protobuf.Struct + 6, // 17: controller.api.resources.targets.v1.Target.tcp_target_attributes:type_name -> controller.api.resources.targets.v1.TcpTargetAttributes + 15, // 18: controller.api.resources.targets.v1.TcpTargetAttributes.default_port:type_name -> google.protobuf.UInt32Value + 12, // 19: controller.api.resources.targets.v1.SessionAuthorizationData.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 14, // 20: controller.api.resources.targets.v1.SessionAuthorizationData.created_time:type_name -> google.protobuf.Timestamp + 7, // 21: controller.api.resources.targets.v1.SessionAuthorizationData.worker_info:type_name -> controller.api.resources.targets.v1.WorkerInfo + 12, // 22: controller.api.resources.targets.v1.SessionAuthorization.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 14, // 23: controller.api.resources.targets.v1.SessionAuthorization.created_time:type_name -> google.protobuf.Timestamp + 4, // 24: controller.api.resources.targets.v1.SessionAuthorization.credentials:type_name -> controller.api.resources.targets.v1.SessionCredential + 25, // [25:25] is the sub-list for method output_type + 25, // [25:25] is the sub-list for method input_type + 25, // [25:25] is the sub-list for extension type_name + 25, // [25:25] is the sub-list for extension extendee + 0, // [0:25] is the sub-list for field type_name } func init() { file_controller_api_resources_targets_v1_target_proto_init() } @@ -1589,18 +1443,6 @@ func file_controller_api_resources_targets_v1_target_proto_init() { } } file_controller_api_resources_targets_v1_target_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CredentialLibrary); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_resources_targets_v1_target_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SessionSecret); i { case 0: return &v.state @@ -1612,7 +1454,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() { return nil } } - file_controller_api_resources_targets_v1_target_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_resources_targets_v1_target_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SessionCredential); i { case 0: return &v.state @@ -1624,7 +1466,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() { return nil } } - file_controller_api_resources_targets_v1_target_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_resources_targets_v1_target_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Target); i { case 0: return &v.state @@ -1636,7 +1478,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() { return nil } } - file_controller_api_resources_targets_v1_target_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_resources_targets_v1_target_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TcpTargetAttributes); i { case 0: return &v.state @@ -1648,7 +1490,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() { return nil } } - file_controller_api_resources_targets_v1_target_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_resources_targets_v1_target_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkerInfo); i { case 0: return &v.state @@ -1660,7 +1502,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() { return nil } } - file_controller_api_resources_targets_v1_target_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_resources_targets_v1_target_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SessionAuthorizationData); i { case 0: return &v.state @@ -1672,7 +1514,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() { return nil } } - file_controller_api_resources_targets_v1_target_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_resources_targets_v1_target_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SessionAuthorization); i { case 0: return &v.state @@ -1684,7 +1526,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() { return nil } } - file_controller_api_resources_targets_v1_target_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_controller_api_resources_targets_v1_target_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserPasswordCredential); i { case 0: return &v.state @@ -1697,7 +1539,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() { } } } - file_controller_api_resources_targets_v1_target_proto_msgTypes[6].OneofWrappers = []interface{}{ + file_controller_api_resources_targets_v1_target_proto_msgTypes[5].OneofWrappers = []interface{}{ (*Target_Attributes)(nil), (*Target_TcpTargetAttributes)(nil), } @@ -1707,7 +1549,7 @@ func file_controller_api_resources_targets_v1_target_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_api_resources_targets_v1_target_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 11, NumExtensions: 0, NumServices: 0, }, From 5ef9496e0af46bb14dee81e0587b92467a977372 Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Wed, 15 Jun 2022 09:30:45 -0700 Subject: [PATCH 14/15] feat(cli): Support credential brokering with sshpass (#2191) --- CHANGELOG.md | 6 + internal/cmd/commands/connect/connect.go | 17 +- internal/cmd/commands/connect/credentials.go | 61 +++++ .../cmd/commands/connect/credentials_test.go | 231 ++++++++++++++++++ internal/cmd/commands/connect/postgres.go | 43 ++-- internal/cmd/commands/connect/ssh.go | 51 +++- 6 files changed, 371 insertions(+), 38 deletions(-) create mode 100644 internal/cmd/commands/connect/credentials.go create mode 100644 internal/cmd/commands/connect/credentials_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index a7b62adf7..411fba65e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,12 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. credential sources for targets, similar to credential libraries from the `vault` credential store, and thus can be brokered to users at session authorization time. [PR](https://github.com/hashicorp/boundary/pull/2174) +* `boundary connect` Credential Brokering Integration: we have extended integration + into the `boundary connect` helpers. A new `sshpass` style has been added to the + `ssh` helper, when used, if the credential contains a username/password and `sshpass` + is installed, the command will automatically pass the credentials to the `ssh` process. + Additionally, the default `ssh` helper will now use the `username` of the brokered credential. + [PR](https://github.com/hashicorp/boundary/pull/2191). * controller: Improve response time for listing sessions. This also creates a new periodic job that will delete terminated sessions after 1 hour. diff --git a/internal/cmd/commands/connect/connect.go b/internal/cmd/commands/connect/connect.go index 4c6617499..90f99977c 100644 --- a/internal/cmd/commands/connect/connect.go +++ b/internal/cmd/commands/connect/connect.go @@ -492,6 +492,15 @@ func (c *Command) Run(args []string) (retCode int) { // Credentials are brokered when connecting to the postgres db. // TODO: Figure out how to handle cases where we don't automatically know how to // broker the credentials like unrecognized or multiple credentials. + case "ssh": + if c.flagExec == "sshpass" { + // If we can broker ssh credentials using sshpass, skip displaying creds on client side. + // TODO: Figure out how to handle multiple credentials + break + } + + // We cannot broker credentials print to user + fallthrough case "connect": // "connect" indicates there is no subcommand to the connect function. // The only way a user will be able to connect to the session is by @@ -838,7 +847,13 @@ func (c *Command) handleExec(passthroughArgs []string) { args = append(args, c.rdpFlags.buildArgs(c, port, ip, addr)...) case "ssh": - args = append(args, c.sshFlags.buildArgs(c, port, ip, addr)...) + sshArgs, sshEnvs, sshErr := c.sshFlags.buildArgs(c, port, ip, addr) + if sshErr != nil { + argsErr = sshErr + break + } + args = append(args, sshArgs...) + envs = append(envs, sshEnvs...) case "kube": kubeArgs, err := c.kubeFlags.buildArgs(c, port, ip, addr) diff --git a/internal/cmd/commands/connect/credentials.go b/internal/cmd/commands/connect/credentials.go new file mode 100644 index 000000000..7ca65cf30 --- /dev/null +++ b/internal/cmd/commands/connect/credentials.go @@ -0,0 +1,61 @@ +package connect + +import ( + "errors" + + "github.com/hashicorp/boundary/api/targets" + "github.com/hashicorp/boundary/internal/credential" + "github.com/mitchellh/mapstructure" +) + +type usernamePasswordCredentials struct { + Username string `mapstructure:"username"` + Password string `mapstructure:"password"` +} + +// TODO: this should support multiple types of credentials and return an interface instead +// of only usernamepassword. Each subcommand can use the returned credentials as it needs. +func parseCredentials(creds []*targets.SessionCredential) ([]usernamePasswordCredentials, error) { + if creds == nil { + return nil, nil + } + var out []usernamePasswordCredentials + for _, cred := range creds { + if cred.CredentialSource == nil { + return nil, errors.New("missing credential source") + } + + var c usernamePasswordCredentials + if cred.CredentialSource.CredentialType == string(credential.UsernamePasswordType) { + // Decode attributes from credential struct + if err := mapstructure.Decode(cred.Credential, &c); err != nil { + return nil, err + } + + if c.Username != "" && c.Password != "" { + out = append(out, c) + continue + } + } + + // Credential type is unspecified, make a best effort attempt to parse both username + // and password from Decoded field if it exists + if cred.Secret == nil || cred.Secret.Decoded == nil { + // No secret data continue to next credential + continue + } + + switch cred.CredentialSource.Type { + case "vault", "static": + // Attempt unmarshaling into creds + if err := mapstructure.Decode(cred.Secret.Decoded, &c); err != nil { + return nil, err + } + } + + if c.Username != "" && c.Password != "" { + out = append(out, c) + } + } + return out, nil +} diff --git a/internal/cmd/commands/connect/credentials_test.go b/internal/cmd/commands/connect/credentials_test.go new file mode 100644 index 000000000..5d1f8615f --- /dev/null +++ b/internal/cmd/commands/connect/credentials_test.go @@ -0,0 +1,231 @@ +package connect + +import ( + "testing" + + "github.com/hashicorp/boundary/api/targets" + "github.com/hashicorp/boundary/internal/credential" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_parseUsernamePasswordCredentials(t *testing.T) { + tests := []struct { + name string + creds []*targets.SessionCredential + wantCreds []usernamePasswordCredentials + wantErr bool + }{ + { + name: "no-creds", + wantCreds: nil, + wantErr: false, + }, + { + name: "no-credential-source", + creds: []*targets.SessionCredential{ + { + Credential: map[string]interface{}{ + "username": "user", + "password": "pass", + }, + }, + }, + wantCreds: nil, + wantErr: true, + }, + { + name: "valid-typed", + creds: []*targets.SessionCredential{ + { + CredentialSource: &targets.CredentialSource{ + CredentialType: string(credential.UsernamePasswordType), + }, + Credential: map[string]interface{}{ + "username": "user", + "password": "pass", + }, + }, + }, + wantCreds: []usernamePasswordCredentials{ + { + Username: "user", + Password: "pass", + }, + }, + wantErr: false, + }, + { + name: "valid-typed-instead-of-decoded", + creds: []*targets.SessionCredential{ + { + CredentialSource: &targets.CredentialSource{ + CredentialType: string(credential.UsernamePasswordType), + }, + Credential: map[string]interface{}{ + "username": "user", + "password": "pass", + }, + Secret: &targets.SessionSecret{ + Decoded: map[string]interface{}{ + "username": "secret-user", + "password": "secret-pass", + }, + }, + }, + }, + wantCreds: []usernamePasswordCredentials{ + { + Username: "user", + Password: "pass", + }, + }, + wantErr: false, + }, + { + name: "valid-vault-not-typed", + creds: []*targets.SessionCredential{ + { + CredentialSource: &targets.CredentialSource{ + Type: "vault", + }, + Secret: &targets.SessionSecret{ + Decoded: map[string]interface{}{ + "username": "user", + "password": "pass", + }, + }, + }, + }, + wantCreds: []usernamePasswordCredentials{ + { + Username: "user", + Password: "pass", + }, + }, + wantErr: false, + }, + { + name: "valid-static-not-typed", + creds: []*targets.SessionCredential{ + { + CredentialSource: &targets.CredentialSource{ + Type: "static", + }, + Secret: &targets.SessionSecret{ + Decoded: map[string]interface{}{ + "username": "user", + "password": "pass", + }, + }, + }, + }, + wantCreds: []usernamePasswordCredentials{ + { + Username: "user", + Password: "pass", + }, + }, + wantErr: false, + }, + { + name: "valid-multiple", + creds: []*targets.SessionCredential{ + { + CredentialSource: &targets.CredentialSource{ + CredentialType: string(credential.UsernamePasswordType), + }, + Credential: map[string]interface{}{ + "username": "user", + "password": "pass", + }, + }, + { + CredentialSource: &targets.CredentialSource{ + CredentialType: string(credential.UsernamePasswordType), + }, + Credential: map[string]interface{}{ + "username": "user1", + "password": "pass1", + }, + }, + }, + wantCreds: []usernamePasswordCredentials{ + { + Username: "user", + Password: "pass", + }, + { + Username: "user1", + Password: "pass1", + }, + }, + wantErr: false, + }, + { + name: "valid-multiple-mixed", + creds: []*targets.SessionCredential{ + { + CredentialSource: &targets.CredentialSource{ + CredentialType: string(credential.UsernamePasswordType), + }, + Credential: map[string]interface{}{ + "username": "user", + "password": "pass", + }, + }, + { + CredentialSource: &targets.CredentialSource{ + Type: "vault", + }, + Secret: &targets.SessionSecret{ + Decoded: map[string]interface{}{ + "username": "user1", + "password": "pass1", + }, + }, + }, + { + CredentialSource: &targets.CredentialSource{ + Type: "static", + }, + Secret: &targets.SessionSecret{ + Decoded: map[string]interface{}{ + "username": "user2", + "password": "pass2", + }, + }, + }, + }, + wantCreds: []usernamePasswordCredentials{ + { + Username: "user", + Password: "pass", + }, + { + Username: "user1", + Password: "pass1", + }, + { + Username: "user2", + Password: "pass2", + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + creds, err := parseCredentials(tt.creds) + if tt.wantErr { + require.Error(err) + assert.Nil(creds) + return + } + require.NoError(err) + assert.ElementsMatch(tt.wantCreds, creds) + }) + } +} diff --git a/internal/cmd/commands/connect/postgres.go b/internal/cmd/commands/connect/postgres.go index 8ef73ffee..9ab0abe2a 100644 --- a/internal/cmd/commands/connect/postgres.go +++ b/internal/cmd/commands/connect/postgres.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/hashicorp/boundary/internal/cmd/base" - "github.com/mitchellh/mapstructure" "github.com/posener/complete" ) @@ -52,32 +51,18 @@ func (p *postgresFlags) defaultExec() string { return strings.ToLower(p.flagPostgresStyle) } -type postgresCredentials struct { - Username string `mapstructure:"username"` - Password string `mapstructure:"password"` -} - func (p *postgresFlags) buildArgs(c *Command, port, ip, addr string) (args, envs []string, retErr error) { - var creds postgresCredentials - if c.sessionAuthz != nil && len(c.sessionAuthz.Credentials) > 0 { - for _, cred := range c.sessionAuthz.Credentials { - if cred.Secret == nil || cred.Secret.Decoded == nil { - continue - } - switch cred.CredentialSource.Type { - case "vault": - // Attempt unmarshaling into creds - if err := mapstructure.Decode(cred.Secret.Decoded, &creds); err != nil { - return nil, nil, fmt.Errorf("Error interpreting Vault secret: %w", err) - } - } - - if creds.Username != "" && creds.Password != "" { - // In the future we can look for other types if we support other - // authentication mechanisms - break - } + var cred usernamePasswordCredentials + if c.sessionAuthz != nil { + creds, err := parseCredentials(c.sessionAuthz.Credentials) + if err != nil { + return nil, nil, fmt.Errorf("Error interpreting secret: %w", err) } + if len(creds) > 0 { + // Just use first credentials returned + cred = creds[0] + } + } switch p.flagPostgresStyle { @@ -89,13 +74,13 @@ func (p *postgresFlags) buildArgs(c *Command, port, ip, addr string) (args, envs } switch { - case creds.Username != "": - args = append(args, "-U", creds.Username) + case cred.Username != "": + args = append(args, "-U", cred.Username) case c.flagUsername != "": args = append(args, "-U", c.flagUsername) } - if creds.Password != "" { + if cred.Password != "" { passfile, err := ioutil.TempFile("", "*") if err != nil { return nil, nil, fmt.Errorf("Error saving postgres password to tmp file: %w", err) @@ -106,7 +91,7 @@ func (p *postgresFlags) buildArgs(c *Command, port, ip, addr string) (args, envs } return nil }) - _, err = passfile.WriteString(fmt.Sprintf("*:*:*:*:%s", creds.Password)) + _, err = passfile.WriteString(fmt.Sprintf("*:*:*:*:%s", cred.Password)) if err != nil { return nil, nil, fmt.Errorf("Error writing password file to %s: %w", passfile.Name(), err) } diff --git a/internal/cmd/commands/connect/ssh.go b/internal/cmd/commands/connect/ssh.go index df446e88c..38ae46496 100644 --- a/internal/cmd/commands/connect/ssh.go +++ b/internal/cmd/commands/connect/ssh.go @@ -1,6 +1,7 @@ package connect import ( + "errors" "fmt" "strings" @@ -19,7 +20,7 @@ func sshOptions(c *Command, set *base.FlagSets) { Name: "style", Target: &c.flagSshStyle, EnvVar: "BOUNDARY_CONNECT_SSH_STYLE", - Completion: complete.PredictSet("ssh", "putty"), + Completion: complete.PredictSet("ssh", "putty", "sshpass"), Default: "ssh", Usage: `Specifies how the CLI will attempt to invoke an SSH client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "ssh" and "putty".`, }) @@ -41,18 +42,52 @@ func (s *sshFlags) defaultExec() string { return strings.ToLower(s.flagSshStyle) } -func (s *sshFlags) buildArgs(c *Command, port, ip, addr string) []string { - // Might want -t for ssh or -tt but seems fine without it for now... - var args []string - switch s.flagSshStyle { +func (s *sshFlags) buildArgs(c *Command, port, ip, addr string) (args, envs []string, retErr error) { + var cred usernamePasswordCredentials + if c.sessionAuthz != nil { + creds, err := parseCredentials(c.sessionAuthz.Credentials) + if err != nil { + return nil, nil, fmt.Errorf("Error interpreting secret: %w", err) + } + if len(creds) > 0 { + // Just use first credentials returned + cred = creds[0] + } + } + + switch strings.ToLower(s.flagSshStyle) { case "ssh": + // Might want -t for ssh or -tt but seems fine without it for now... args = append(args, "-p", port, ip) - args = append(args, "-o", fmt.Sprintf("HostKeyAlias=%s", c.sessionAuthzData.HostId)) + + // SSH detects a host key change when localhost proxy port changes, disable localhost + // host key verification to avoid 'WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED...' + args = append(args, "-o", "NoHostAuthenticationForLocalhost=yes") + + case "sshpass": + if cred.Password == "" { + return nil, nil, errors.New("Password is required when using sshpass") + } + // sshpass requires that the password is passed as env-var "SSHPASS" + // when the using env-vars. + envs = append(envs, fmt.Sprintf("SSHPASS=%s", cred.Password)) + args = append(args, "-e", "ssh") + args = append(args, "-p", port, ip) + + // sshpass cannot handle host key checking, disable localhost key verification + // to avoid error: 'SSHPASS detected host authentication prompt. Exiting.' + args = append(args, "-o", "NoHostAuthenticationForLocalhost=yes") + case "putty": args = append(args, "-P", port, ip) } - if c.flagUsername != "" { + + switch { + case cred.Username != "": + args = append(args, "-l", cred.Username) + case c.flagUsername != "": args = append(args, "-l", c.flagUsername) } - return args + + return args, envs, nil } From d06ac0d37024bc0c2ac5de7b7a26c74e15418a9b Mon Sep 17 00:00:00 2001 From: Johan Brandhorst-Satzkorn Date: Wed, 15 Jun 2022 16:16:18 -0400 Subject: [PATCH 15/15] Propagate plugin execution dir to CreatePlugin (#2183) * fix: Propagate plugin options to CreatePlugin Plugin options were previously only applied to the BuildPluginMap call, resulting in the plugin execution_dir variable being ineffective. Also add a test to verify functionality * fix: Support plugin execution_dir for all plugins Previously, only external host plugins would respect the plugin execution dir. Now, all uses of the embedded plugins respect the configuration. --- CHANGELOG.md | 5 +++ internal/cmd/base/servers.go | 1 + internal/daemon/controller/controller_test.go | 41 ++++++++++++++++++ sdk/plugins/host/load.go | 2 +- sdk/wrapper/wrapper.go | 43 +++++++++++++++++++ 5 files changed, 91 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 411fba65e..8ec95ec34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,11 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. See Deprecations/Changes for some additional details. [PR](https://github.com/hashicorp/boundary/pull/2160). +### Bug Fixes + +* The plugin execution_dir configuration parameter is now respected. + [PR](https://github.com/hashicorp/boundary/pull/2183). + ### Deprecations/Changes * Targets: Removes support for `credential libraries` with respect to Target resources. diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index 92a314ba6..eebce3f25 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -555,6 +555,7 @@ func (b *Server) SetupKMSes(ctx context.Context, ui cli.Ui, config *config.Confi configutil.WithPluginOptions( pluginutil.WithPluginsMap(kms_plugin_assets.BuiltinKmsPlugins()), pluginutil.WithPluginsFilesystem(kms_plugin_assets.KmsPluginPrefix, kms_plugin_assets.FileSystem()), + pluginutil.WithPluginExecutionDirectory(config.Plugins.ExecutionDir), ), configutil.WithLogger(pluginLogger.Named(kms.Type).With("purpose", purpose)), ) diff --git a/internal/daemon/controller/controller_test.go b/internal/daemon/controller/controller_test.go index 160886595..6ec8d6138 100644 --- a/internal/daemon/controller/controller_test.go +++ b/internal/daemon/controller/controller_test.go @@ -2,12 +2,17 @@ package controller import ( "context" + "os" + "path/filepath" "testing" + "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/types/scope" + host_plugin_assets "github.com/hashicorp/boundary/plugins/host" "github.com/hashicorp/go-secure-stdlib/listenerutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -223,3 +228,39 @@ func TestControllerNewListenerConfig(t *testing.T) { }) } } + +func TestController_NewPluginsConfig(t *testing.T) { + require := require.New(t) + testCtx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + tc := &TestController{ + t: t, + ctx: ctx, + cancel: cancel, + opts: nil, + } + + initialConfig, err := config.DevController() + require.NoError(err) + tmpDir := t.TempDir() + initialConfig.Plugins.ExecutionDir = tmpDir + conf := TestControllerConfig(t, ctx, tc, &TestControllerOpts{Config: initialConfig}) + conf.EnabledPlugins = []base.EnabledPlugin{ + base.EnabledPluginHostAws, + base.EnabledPluginHostAzure, + } + + _, err = New(testCtx, conf) + require.NoError(err) + + // Check that both plugins were written to the temp dir + files, err := os.ReadDir(tmpDir) + require.NoError(err) + require.Len(files, 2) + var pluginNames []string + for _, file := range files { + pluginNames = append(pluginNames, filepath.Base(file.Name())) + } + expectedPluginNames := []string{host_plugin_assets.HostPluginPrefix + "aws.gz", host_plugin_assets.HostPluginPrefix + "azure.gz"} + require.Empty(cmp.Diff(expectedPluginNames, pluginNames)) +} diff --git a/sdk/plugins/host/load.go b/sdk/plugins/host/load.go index d9d23c559..ac74473ef 100644 --- a/sdk/plugins/host/load.go +++ b/sdk/plugins/host/load.go @@ -60,7 +60,7 @@ func CreateHostPlugin( } // Create the plugin and cleanup func - plugClient, cleanup, err := pluginutil.CreatePlugin(pluginMap[pluginType]) + plugClient, cleanup, err := pluginutil.CreatePlugin(pluginMap[pluginType], opts.withPluginOptions...) if err != nil { return nil, cleanup, err } diff --git a/sdk/wrapper/wrapper.go b/sdk/wrapper/wrapper.go index 5945868c6..fbd13fc32 100644 --- a/sdk/wrapper/wrapper.go +++ b/sdk/wrapper/wrapper.go @@ -3,17 +3,43 @@ package wrapper import ( "context" "fmt" + "os" wrapping "github.com/hashicorp/go-kms-wrapping/v2" configutil "github.com/hashicorp/go-secure-stdlib/configutil/v2" + "github.com/hashicorp/go-secure-stdlib/pluginutil/v2" "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/hashicorp/hcl" ) +// pluginsConfig is used to pre-parse any plugins stanza +// in the configuration file, so that we can use the correct +// configuration when creating the KMS plugin for reading the +// rest of the config. +type pluginsConfig struct { + Plugins struct { + ExecutionDir string `hcl:"execution_dir"` + } `hcl:"plugins"` +} + func GetWrapperFromPath(ctx context.Context, path, purpose string, opt ...configutil.Option) (wrapping.Wrapper, func() error, error) { kmses, err := configutil.LoadConfigKMSes(path) if err != nil { return nil, nil, fmt.Errorf("Error parsing config file: %w", err) } + hclBytes, err := os.ReadFile(path) + if err != nil { + return nil, nil, fmt.Errorf("Error reading config file: %w", err) + } + pluginsConfig, err := parsePluginsConfig(string(hclBytes)) + if err != nil { + return nil, nil, fmt.Errorf("Error parsing plugins stanza in config file: %w", err) + } + if pluginsConfig.Plugins.ExecutionDir != "" { + // Note, this is safe to use because configutil.WithPluginOptions invocations + // are additive with each other. + opt = append(opt, configutil.WithPluginOptions(pluginutil.WithPluginExecutionDirectory(pluginsConfig.Plugins.ExecutionDir))) + } return getWrapper(ctx, kmses, purpose, opt...) } @@ -23,6 +49,15 @@ func GetWrapperFromHcl(ctx context.Context, inHcl, purpose string, opt ...config if err != nil { return nil, nil, fmt.Errorf("Error parsing KMS HCL: %w", err) } + pluginsConfig, err := parsePluginsConfig(inHcl) + if err != nil { + return nil, nil, fmt.Errorf("Error parsing plugins stanza in config file: %w", err) + } + if pluginsConfig.Plugins.ExecutionDir != "" { + // Note, this is safe to use because configutil.WithPluginOptions invocations + // are additive with each other. + opt = append(opt, configutil.WithPluginOptions(pluginutil.WithPluginExecutionDirectory(pluginsConfig.Plugins.ExecutionDir))) + } return getWrapper(ctx, kmses, purpose, opt...) } @@ -54,3 +89,11 @@ func getWrapper(ctx context.Context, kmses []*configutil.KMS, purpose string, op return wrapper, cleanup, nil } + +func parsePluginsConfig(inHcl string) (*pluginsConfig, error) { + var conf pluginsConfig + if err := hcl.Decode(&conf, inHcl); err != nil { + return nil, err + } + return &conf, nil +}