internal/handlers: add credential pagination support

pull/4202/head
Johan Brandhorst-Satzkorn 2 years ago
parent fa898fe87b
commit 9f6ac602ee

@ -722,8 +722,8 @@ func (r *Repository) UpdateJsonCredential(ctx context.Context,
// ListCredentials returns a slice of static credentials
// for the storeId. Supports the following options:
// - WithLimit
// - WithStartPageAfterItem
// - credential.WithLimit
// - credential.WithStartPageAfterItem
func (r *Repository) ListCredentials(ctx context.Context, storeId string, opt ...credential.Option) ([]credential.Static, time.Time, error) {
const op = "static.(Repository).ListCredentials"
if storeId == "" {
@ -764,8 +764,8 @@ func (r *Repository) ListCredentials(ctx context.Context, storeId string, opt ..
// ListCredentialRefresh returns a slice of static credentials
// for the storeId. Supports the following options:
// - WithLimit
// - WithStartPageAfterItem
// - credential.WithLimit
// - credential.WithStartPageAfterItem
func (r *Repository) ListCredentialsRefresh(ctx context.Context, storeId string, updatedAfter time.Time, opt ...credential.Option) ([]credential.Static, time.Time, error) {
const op = "static.(Repository).ListCredentials"
switch {

@ -288,7 +288,12 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error {
services.RegisterWorkerServiceServer(s, ws)
}
if _, ok := currentServices[services.CredentialService_ServiceDesc.ServiceName]; !ok {
c, err := credentials.NewService(c.baseContext, c.StaticCredentialRepoFn, c.IamRepoFn)
c, err := credentials.NewService(
c.baseContext,
c.IamRepoFn,
c.StaticCredentialRepoFn,
c.conf.RawConfig.Controller.MaxPageSize,
)
if err != nil {
return fmt.Errorf("failed to create credential handler service: %w", err)
}

@ -19,6 +19,8 @@ import (
"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/listtoken"
"github.com/hashicorp/boundary/internal/pagination"
"github.com/hashicorp/boundary/internal/perms"
"github.com/hashicorp/boundary/internal/requests"
"github.com/hashicorp/boundary/internal/types/action"
@ -95,14 +97,20 @@ func init() {
type Service struct {
pbs.UnsafeCredentialServiceServer
iamRepoFn common.IamRepoFactory
repoFn common.StaticCredentialRepoFactory
iamRepoFn common.IamRepoFactory
repoFn common.StaticCredentialRepoFactory
maxPageSize uint
}
var _ pbs.CredentialServiceServer = (*Service)(nil)
// NewService returns a credential service which handles credential related requests to boundary.
func NewService(ctx context.Context, repo common.StaticCredentialRepoFactory, iamRepo common.IamRepoFactory) (Service, error) {
func NewService(
ctx context.Context,
iamRepo common.IamRepoFactory,
repo common.StaticCredentialRepoFactory,
maxPageSize uint,
) (Service, error) {
const op = "credentials.NewService"
if iamRepo == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing iam repository")
@ -110,68 +118,139 @@ func NewService(ctx context.Context, repo common.StaticCredentialRepoFactory, ia
if repo == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static credential repository")
}
return Service{iamRepoFn: iamRepo, repoFn: repo}, nil
if maxPageSize == 0 {
maxPageSize = uint(globals.DefaultMaxPageSize)
}
return Service{iamRepoFn: iamRepo, repoFn: repo, maxPageSize: maxPageSize}, nil
}
// ListCredentials implements the interface pbs.CredentialServiceServer
func (s Service) ListCredentials(ctx context.Context, req *pbs.ListCredentialsRequest) (*pbs.ListCredentialsResponse, error) {
const op = "credentials.(Service).ListCredentials"
if err := validateListRequest(ctx, req); err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
authResults := s.authResult(ctx, req.GetCredentialStoreId(), action.List)
if authResults.Error != nil {
return nil, authResults.Error
}
pageSize := int(s.maxPageSize)
// Use the requested page size only if it is smaller than
// the configured max.
if req.GetPageSize() != 0 && uint(req.GetPageSize()) < s.maxPageSize {
pageSize = int(req.GetPageSize())
}
var filterItemFn func(ctx context.Context, item credential.Static) (bool, error)
switch {
case req.GetFilter() != "":
// Only use a filter if we need to
filter, err := handlers.NewFilter(ctx, req.GetFilter())
if err != nil {
return nil, err
}
// TODO: replace the need for this function with some way to convert the `filter`
// to a domain type. This would allow filtering to happen in the domain, and we could
// remove this callback altogether.
filterItemFn = func(ctx context.Context, item credential.Static) (bool, error) {
outputOpts, ok := newOutputOpts(ctx, item, req.CredentialStoreId, authResults)
if !ok {
return ok, nil
}
pbItem, err := toProto(item, outputOpts...)
if err != nil {
return false, err
}
creds, err := s.listFromRepo(ctx, req.GetCredentialStoreId())
if err != nil {
return nil, err
}
if len(creds) == 0 {
return &pbs.ListCredentialsResponse{}, nil
filterable, err := subtypes.Filterable(ctx, pbItem)
if err != nil {
return false, err
}
return filter.Match(filterable), nil
}
default:
filterItemFn = func(ctx context.Context, item credential.Static) (bool, error) {
return true, nil
}
}
filter, err := handlers.NewFilter(ctx, req.GetFilter())
repo, err := s.repoFn()
if err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
finalItems := make([]*pb.Credential, 0, len(creds))
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.Credential,
Pin: req.GetCredentialStoreId(),
grantsHash, err := authResults.GrantsHash(ctx)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
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))
var listResp *pagination.ListResponse[credential.Static]
var sortBy string
if req.GetListToken() == "" {
sortBy = "created_time"
listResp, err = credential.List(ctx, grantsHash, pageSize, filterItemFn, repo, req.GetCredentialStoreId())
if err != nil {
return nil, err
}
} else {
listToken, err := handlers.ParseListToken(ctx, req.GetListToken(), resource.Credential, grantsHash)
if err != nil {
return nil, err
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
switch st := listToken.Subtype.(type) {
case *listtoken.PaginationToken:
sortBy = "created_time"
listResp, err = credential.ListPage(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, req.GetCredentialStoreId())
if err != nil {
return nil, err
}
case *listtoken.StartRefreshToken:
sortBy = "updated_time"
listResp, err = credential.ListRefresh(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, req.GetCredentialStoreId())
if err != nil {
return nil, err
}
case *listtoken.RefreshToken:
sortBy = "updated_time"
listResp, err = credential.ListRefreshPage(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, req.GetCredentialStoreId())
if err != nil {
return nil, err
}
default:
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "unexpected list token subtype: %T", st)
}
}
item, err := toProto(item, outputOpts...)
finalItems := make([]*pb.Credential, 0, len(listResp.Items))
for _, item := range listResp.Items {
outputOpts, ok := newOutputOpts(ctx, item, req.CredentialStoreId, authResults)
if !ok {
continue
}
pbItem, err := toProto(item, outputOpts...)
if err != nil {
return nil, err
}
finalItems = append(finalItems, pbItem)
}
respType := "delta"
if listResp.CompleteListing {
respType = "complete"
}
resp := &pbs.ListCredentialsResponse{
Items: finalItems,
EstItemCount: uint32(listResp.EstimatedItemCount),
RemovedIds: listResp.DeletedIds,
ResponseType: respType,
SortBy: sortBy,
SortDir: "desc",
}
filterable, err := subtypes.Filterable(ctx, item)
if listResp.ListToken != nil {
resp.ListToken, err = handlers.MarshalListToken(ctx, listResp.ListToken, pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL)
if err != nil {
return nil, err
}
if filter.Match(filterable) {
finalItems = append(finalItems, item)
}
}
return &pbs.ListCredentialsResponse{Items: finalItems}, nil
return resp, nil
}
// GetCredential implements the interface pbs.CredentialServiceServer.
@ -313,23 +392,6 @@ func (s Service) DeleteCredential(ctx context.Context, req *pbs.DeleteCredential
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, static.WithLimit(-1))
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
creds := make([]credential.Static, 0, len(up))
creds = append(creds, up...)
return creds, nil
}
func (s Service) getFromRepo(ctx context.Context, id string) (credential.Static, error) {
const op = "credentials.(Service).getFromRepo"
repo, err := s.repoFn()
@ -571,6 +633,35 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
return auth.Verify(ctx, opts...)
}
func newOutputOpts(
ctx context.Context,
item credential.Static,
credentialStoreId string,
authResults auth.VerifyResults,
) ([]handlers.Option, bool) {
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.Credential,
Pin: credentialStoreId,
}
res.Id = item.GetPublicId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
return nil, false
}
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))
}
return outputOpts, true
}
func toProto(in credential.Static, opt ...handlers.Option) (*pb.Credential, error) {
opts := handlers.GetOpts(opt...)
if opts.WithOutputFields == nil {

@ -8,24 +8,31 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"slices"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/authtoken"
"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/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"
authpb "github.com/hashicorp/boundary/internal/gen/controller/auth"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
"github.com/hashicorp/boundary/internal/requests"
"github.com/hashicorp/boundary/internal/scheduler"
"github.com/hashicorp/boundary/internal/server"
"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"
@ -43,6 +50,62 @@ import (
var testAuthorizedActions = []string{"no-op", "read", "update", "delete"}
func staticJsonCredentialToProto(cred *static.JsonCredential, prj *iam.Scope, hmac string) *pb.Credential {
return &pb.Credential{
Id: cred.GetPublicId(),
CredentialStoreId: cred.GetStoreId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
CreatedTime: cred.GetCreateTime().GetTimestamp(),
UpdatedTime: cred.GetUpdateTime().GetTimestamp(),
Version: cred.GetVersion(),
Type: credential.JsonSubtype.String(),
AuthorizedActions: testAuthorizedActions,
Attrs: &pb.Credential_JsonAttributes{
JsonAttributes: &pb.JsonAttributes{
ObjectHmac: base64.RawURLEncoding.EncodeToString([]byte(hmac)),
},
},
}
}
func staticPasswordCredentialToProto(cred *static.UsernamePasswordCredential, prj *iam.Scope, hmac string) *pb.Credential {
return &pb.Credential{
Id: cred.GetPublicId(),
CredentialStoreId: cred.GetStoreId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
CreatedTime: cred.GetCreateTime().GetTimestamp(),
UpdatedTime: cred.GetUpdateTime().GetTimestamp(),
Version: cred.GetVersion(),
Type: credential.UsernamePasswordSubtype.String(),
AuthorizedActions: testAuthorizedActions,
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String(cred.GetUsername()),
PasswordHmac: base64.RawURLEncoding.EncodeToString([]byte(hmac)),
},
},
}
}
func staticSshCredentialToProto(cred *static.SshPrivateKeyCredential, prj *iam.Scope, hmac string) *pb.Credential {
return &pb.Credential{
Id: cred.GetPublicId(),
CredentialStoreId: cred.GetStoreId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
CreatedTime: cred.GetCreateTime().GetTimestamp(),
UpdatedTime: cred.GetUpdateTime().GetTimestamp(),
Version: cred.GetVersion(),
Type: credential.SshPrivateKeySubtype.String(),
AuthorizedActions: testAuthorizedActions,
Attrs: &pb.Credential_SshPrivateKeyAttributes{
SshPrivateKeyAttributes: &pb.SshPrivateKeyAttributes{
Username: wrapperspb.String(cred.GetUsername()),
PrivateKeyHmac: base64.RawURLEncoding.EncodeToString([]byte(hmac)),
},
},
}
}
func TestList(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
@ -72,63 +135,19 @@ func TestList(t *testing.T) {
c := static.TestUsernamePasswordCredential(t, conn, wrapper, user, pass, store.GetPublicId(), prj.GetPublicId())
hm, err := crypto.HmacSha256(ctx, []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: credential.UsernamePasswordSubtype.String(),
AuthorizedActions: testAuthorizedActions,
Attrs: &pb.Credential_UsernamePasswordAttributes{
UsernamePasswordAttributes: &pb.UsernamePasswordAttributes{
Username: wrapperspb.String(c.GetUsername()),
PasswordHmac: base64.RawURLEncoding.EncodeToString([]byte(hm)),
},
},
})
wantCreds = append(wantCreds, staticPasswordCredentialToProto(c, prj, hm))
spk := static.TestSshPrivateKeyCredential(t, conn, wrapper, user, static.TestSshPrivateKeyPem, store.GetPublicId(), prj.GetPublicId())
hm, err = crypto.HmacSha256(ctx, []byte(static.TestSshPrivateKeyPem), databaseWrapper, []byte(store.GetPublicId()), nil)
require.NoError(t, err)
wantCreds = append(wantCreds, &pb.Credential{
Id: spk.GetPublicId(),
CredentialStoreId: store.GetPublicId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
CreatedTime: spk.GetCreateTime().GetTimestamp(),
UpdatedTime: spk.GetUpdateTime().GetTimestamp(),
Version: spk.GetVersion(),
Type: credential.SshPrivateKeySubtype.String(),
AuthorizedActions: testAuthorizedActions,
Attrs: &pb.Credential_SshPrivateKeyAttributes{
SshPrivateKeyAttributes: &pb.SshPrivateKeyAttributes{
Username: wrapperspb.String(c.GetUsername()),
PrivateKeyHmac: base64.RawURLEncoding.EncodeToString([]byte(hm)),
},
},
})
wantCreds = append(wantCreds, staticSshCredentialToProto(spk, prj, hm))
obj, objBytes := static.TestJsonObject(t)
credJson := static.TestJsonCredential(t, conn, wrapper, store.GetPublicId(), prj.GetPublicId(), obj)
hm, err = crypto.HmacSha256(ctx, objBytes, databaseWrapper, []byte(store.GetPublicId()), nil)
require.NoError(t, err)
wantCreds = append(wantCreds, &pb.Credential{
Id: credJson.GetPublicId(),
CredentialStoreId: store.GetPublicId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
CreatedTime: credJson.GetCreateTime().GetTimestamp(),
UpdatedTime: credJson.GetUpdateTime().GetTimestamp(),
Version: credJson.GetVersion(),
Type: credential.JsonSubtype.String(),
AuthorizedActions: testAuthorizedActions,
Attrs: &pb.Credential_JsonAttributes{
JsonAttributes: &pb.JsonAttributes{
ObjectHmac: base64.RawURLEncoding.EncodeToString([]byte(hm)),
},
},
})
wantCreds = append(wantCreds, staticJsonCredentialToProto(credJson, prj, hm))
}
cases := []struct {
@ -139,34 +158,104 @@ func TestList(t *testing.T) {
err error
}{
{
name: "List many credentials",
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId()},
res: &pbs.ListCredentialsResponse{Items: wantCreds},
anonRes: &pbs.ListCredentialsResponse{Items: wantCreds},
name: "List many credentials",
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId()},
res: &pbs.ListCredentialsResponse{
Items: wantCreds,
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 30,
},
anonRes: &pbs.ListCredentialsResponse{
Items: wantCreds,
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 30,
},
},
{
name: "List no credentials",
req: &pbs.ListCredentialsRequest{CredentialStoreId: storeNoCreds.GetPublicId()},
res: &pbs.ListCredentialsResponse{},
anonRes: &pbs.ListCredentialsResponse{},
name: "List no credentials",
req: &pbs.ListCredentialsRequest{CredentialStoreId: storeNoCreds.GetPublicId()},
res: &pbs.ListCredentialsResponse{
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
},
anonRes: &pbs.ListCredentialsResponse{
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
},
},
{
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 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],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 1,
},
anonRes: &pbs.ListCredentialsResponse{
Items: wantCreds[1:2],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 1,
},
},
{
name: "Filter on Attribute",
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/username"==%q`, wantCreds[3].GetUsernamePasswordAttributes().GetUsername().Value)},
res: &pbs.ListCredentialsResponse{Items: wantCreds[3:5]},
anonRes: &pbs.ListCredentialsResponse{}, // anonymous user does not have access to attributes
name: "Filter on Attribute",
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/username"==%q`, wantCreds[3].GetUsernamePasswordAttributes().GetUsername().Value)},
res: &pbs.ListCredentialsResponse{
Items: wantCreds[3:5],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 2,
},
anonRes: &pbs.ListCredentialsResponse{
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
}, // 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 to no credential",
req: &pbs.ListCredentialsRequest{CredentialStoreId: store.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
res: &pbs.ListCredentialsResponse{
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
},
anonRes: &pbs.ListCredentialsResponse{
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
},
},
{
name: "Filter Bad Format",
@ -176,7 +265,7 @@ func TestList(t *testing.T) {
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
s, err := NewService(ctx, staticRepoFn, iamRepoFn)
s, err := NewService(ctx, iamRepoFn, staticRepoFn, 1000)
require.NoError(t, err, "Couldn't create new host set service.")
// Test non-anonymous listing
@ -187,17 +276,21 @@ func TestList(t *testing.T) {
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
}),
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
))
assert.Empty(
t,
cmp.Diff(
got,
tc.res,
protocmp.Transform(),
protocmp.SortRepeated(func(x, y *pb.Credential) bool {
return x.Id < y.Id
}),
cmpopts.SortSlices(func(a, b string) bool {
return a < b
}),
protocmp.IgnoreFields(&pbs.ListCredentialsResponse{}, "list_token"),
),
)
// Test anonymous listing
got, gErr = s.ListCredentials(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId(), auth.WithUserId(globals.AnonymousUserId)), tc.req)
@ -232,7 +325,7 @@ func TestGet(t *testing.T) {
require.NoError(t, err)
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
s, err := NewService(ctx, staticRepoFn, iamRepoFn)
s, err := NewService(ctx, iamRepoFn, staticRepoFn, 1000)
require.NoError(t, err)
upCred := static.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId())
@ -251,9 +344,9 @@ func TestGet(t *testing.T) {
spkWithPassHm, err := crypto.HmacSha256(context.Background(), []byte(testdata.PEMEncryptedKeys[0].PEMBytes), databaseWrapper, []byte(store.GetPublicId()), nil)
require.NoError(t, err)
passHm, err := crypto.HmacSha256(context.Background(), []byte(testdata.PEMEncryptedKeys[0].EncryptionKey), databaseWrapper, []byte(store.GetPublicId()), nil)
require.NoError(t, err)
obj, objBytes := static.TestJsonObject(t)
assert.NoError(t, err)
jsonCred := static.TestJsonCredential(t, conn, wrapper, store.GetPublicId(), prj.GetPublicId(), obj)
objectHmac, err := crypto.HmacSha256(context.Background(), objBytes, databaseWrapper, []byte(store.GetPublicId()), nil)
@ -437,14 +530,13 @@ func TestDelete(t *testing.T) {
_, prj := iam.TestScopes(t, iamRepo)
store := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
s, err := NewService(ctx, staticRepoFn, iamRepoFn)
s, err := NewService(ctx, iamRepoFn, staticRepoFn, 1000)
require.NoError(t, err)
upCred := static.TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", store.GetPublicId(), prj.GetPublicId())
spkCred := static.TestSshPrivateKeyCredential(t, conn, wrapper, "user", static.TestSshPrivateKeyPem, store.GetPublicId(), prj.GetPublicId())
obj, _ := static.TestJsonObject(t)
assert.NoError(t, err)
jsonCred := static.TestJsonCredential(t, conn, wrapper, store.GetPublicId(), prj.GetPublicId(), obj)
@ -765,7 +857,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(ctx, repoFn, iamRepoFn)
s, err := NewService(ctx, iamRepoFn, repoFn, 1000)
require.NoError(err, "Error when getting new credential store service.")
got, gErr := s.CreateCredential(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), tc.req)
@ -884,8 +976,7 @@ func TestUpdate(t *testing.T) {
_, prj := iam.TestScopes(t, iamRepo)
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
s, err := NewService(ctx, staticRepoFn, iamRepoFn)
s, err := NewService(ctx, iamRepoFn, staticRepoFn, 1000)
require.NoError(t, err)
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
@ -1387,3 +1478,332 @@ func TestUpdate(t *testing.T) {
})
}
}
func TestListPagination(t *testing.T) {
// Set database read timeout to avoid duplicates in response
oldReadTimeout := globals.RefreshReadLookbackDuration
globals.RefreshReadLookbackDuration = 0
t.Cleanup(func() {
globals.RefreshReadLookbackDuration = oldReadTimeout
})
assert, require := assert.New(t), require.New(t)
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
sqlDB, err := conn.SqlDB(ctx)
require.NoError(err)
wrapper := db.TestWrapper(t)
kmsRepo := 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(ctx, rw, rw, kmsRepo)
}
tokenRepoFn := func() (*authtoken.Repository, error) {
return authtoken.NewRepository(ctx, rw, rw, kmsRepo)
}
serversRepoFn := func() (*server.Repository, error) {
return server.NewRepository(ctx, rw, rw, kmsRepo)
}
repo, err := staticRepoFn()
require.NoError(err)
tokenRepo, err := tokenRepoFn()
require.NoError(err)
_, prjNoStores := iam.TestScopes(t, iamRepo)
o, prj := iam.TestScopes(t, iamRepo)
credStore := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
emptyStore := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
databaseWrapper, err := kmsRepo.GetWrapper(ctx, prj.GetPublicId(), kms.KeyPurposeDatabase)
require.NoError(err)
var allCredentials []*pb.Credential
testObj, testObjBytes := static.TestJsonObject(t)
for _, l := range static.TestJsonCredentials(t, conn, wrapper, credStore.GetPublicId(), prj.PublicId, testObj, 5) {
hm, err := crypto.HmacSha256(ctx, []byte(testObjBytes), databaseWrapper, []byte(credStore.GetPublicId()), nil)
require.NoError(err)
allCredentials = append(allCredentials, staticJsonCredentialToProto(l, prj, hm))
}
for _, l := range static.TestSshPrivateKeyCredentials(t, conn, wrapper, "username", static.TestSshPrivateKeyPem, credStore.GetPublicId(), prj.PublicId, 5) {
hm, err := crypto.HmacSha256(ctx, []byte(static.TestSshPrivateKeyPem), databaseWrapper, []byte(credStore.GetPublicId()), nil)
require.NoError(err)
allCredentials = append(allCredentials, staticSshCredentialToProto(l, prj, hm))
}
for _, l := range static.TestUsernamePasswordCredentials(t, conn, wrapper, "username", "password", credStore.GetPublicId(), prj.PublicId, 5) {
hm, err := crypto.HmacSha256(ctx, []byte("password"), databaseWrapper, []byte(credStore.GetPublicId()), nil, crypto.WithEd25519())
require.NoError(err)
allCredentials = append(allCredentials, staticPasswordCredentialToProto(l, prj, hm))
}
// Reverse slices since response is ordered by created_time descending (newest first)
slices.Reverse(allCredentials)
// Run analyze to update postgres meta tables
_, err = sqlDB.ExecContext(ctx, "analyze")
require.NoError(err)
authMethod := password.TestAuthMethods(t, conn, o.GetPublicId(), 1)[0]
// auth account is only used to join auth method to user.
// We don't do anything else with the auth account in the test setup.
acct := password.TestAccount(t, conn, authMethod.GetPublicId(), "test_user")
u := iam.TestUser(t, iamRepo, o.GetPublicId(), iam.WithAccountIds(acct.PublicId))
role1 := iam.TestRole(t, conn, prj.GetPublicId())
iam.TestRoleGrant(t, conn, role1.GetPublicId(), "id=*;type=*;actions=*")
iam.TestUserRole(t, conn, role1.GetPublicId(), u.GetPublicId())
role2 := iam.TestRole(t, conn, prjNoStores.GetPublicId())
iam.TestRoleGrant(t, conn, role2.GetPublicId(), "id=*;type=*;actions=*")
iam.TestUserRole(t, conn, role2.GetPublicId(), u.GetPublicId())
at, err := tokenRepo.CreateAuthToken(ctx, u, acct.GetPublicId())
require.NoError(err)
// Test without anon user
requestInfo := authpb.RequestInfo{
TokenFormat: uint32(auth.AuthTokenTypeBearer),
PublicId: at.GetPublicId(),
Token: at.GetToken(),
}
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kmsRepo, &requestInfo)
s, err := NewService(ctx, iamRepoFn, staticRepoFn, 1000)
require.NoError(err)
// Start paginating, recursively
req := &pbs.ListCredentialsRequest{
CredentialStoreId: credStore.PublicId,
Filter: "",
ListToken: "",
PageSize: 2,
}
got, err := s.ListCredentials(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 2)
// Compare without comparing the refresh token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialsResponse{
Items: allCredentials[0:2],
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 15,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialsResponse{}, "list_token"),
),
)
// Request second page
req.ListToken = got.ListToken
got, err = s.ListCredentials(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 2)
// Compare without comparing the refresh token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialsResponse{
Items: allCredentials[2:4],
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 15,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialsResponse{}, "list_token"),
),
)
// Request rest of results
req.ListToken = got.ListToken
req.PageSize = 15
got, err = s.ListCredentials(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 11)
// Compare without comparing the refresh token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialsResponse{
Items: allCredentials[4:],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 15,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialsResponse{}, "list_token"),
),
)
// Create another credential
newCred := static.TestJsonCredential(t, conn, wrapper, credStore.GetPublicId(), prj.GetPublicId(), testObj)
hm, err := crypto.HmacSha256(ctx, []byte(testObjBytes), databaseWrapper, []byte(credStore.GetPublicId()), nil)
require.NoError(err)
pbNewCred := staticJsonCredentialToProto(newCred, prj, hm)
// Add to the front since it's most recently updated
allCredentials = append([]*pb.Credential{pbNewCred}, allCredentials...)
// Delete one of the other credentials
_, err = repo.DeleteCredential(ctx, prj.GetPublicId(), allCredentials[len(allCredentials)-1].GetId())
require.NoError(err)
deletedCred := allCredentials[len(allCredentials)-1]
allCredentials = allCredentials[:len(allCredentials)-1]
// Update one of the other credentials
allCredentials[1].Name = wrapperspb.String("new-name")
allCredentials[1].Version = 2
updatedCredential := &static.UsernamePasswordCredential{
UsernamePasswordCredential: &store.UsernamePasswordCredential{
PublicId: allCredentials[1].GetId(),
Name: allCredentials[1].GetName().GetValue(),
StoreId: allCredentials[1].GetCredentialStoreId(),
},
}
cred, _, err := repo.UpdateUsernamePasswordCredential(ctx, prj.GetPublicId(), updatedCredential, 1, []string{"name"})
require.NoError(err)
allCredentials[1].UpdatedTime = cred.UpdateTime.GetTimestamp()
allCredentials[1].Version = cred.GetVersion()
// Add to the front since it's most recently updated
allCredentials = append(
[]*pb.Credential{allCredentials[1]},
append(
[]*pb.Credential{allCredentials[0]},
allCredentials[2:]...,
)...,
)
// Run analyze to update postgres meta tables
_, err = sqlDB.ExecContext(ctx, "analyze")
require.NoError(err)
// Request updated results
req.ListToken = got.ListToken
req.PageSize = 1
got, err = s.ListCredentials(ctx, req)
require.NoError(err)
assert.Len(got.GetItems(), 1)
// Compare without comparing the refresh token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialsResponse{
Items: []*pb.Credential{allCredentials[0]},
ResponseType: "delta",
ListToken: "",
SortBy: "updated_time",
SortDir: "desc",
// Should contain the deleted credential
RemovedIds: []string{deletedCred.Id},
EstItemCount: 15,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialsResponse{}, "list_token"),
),
)
// Get next page
req.ListToken = got.ListToken
got, err = s.ListCredentials(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 1)
// Compare without comparing the list token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialsResponse{
Items: []*pb.Credential{allCredentials[1]},
ResponseType: "complete",
SortBy: "updated_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 15,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialsResponse{}, "list_token"),
),
)
// Request new page with filter requiring looping
// to fill the page.
req.ListToken = ""
req.PageSize = 1
req.Filter = fmt.Sprintf(`"/item/id"==%q or "/item/id"==%q`, allCredentials[len(allCredentials)-2].Id, allCredentials[len(allCredentials)-1].Id)
got, err = s.ListCredentials(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 1)
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialsResponse{
Items: []*pb.Credential{allCredentials[len(allCredentials)-2]},
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
// Should be empty again
RemovedIds: nil,
EstItemCount: 15,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialsResponse{}, "list_token"),
),
)
req.ListToken = got.ListToken
// Get the second page
got, err = s.ListCredentials(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 1)
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialsResponse{
Items: []*pb.Credential{allCredentials[len(allCredentials)-1]},
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 15,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialsResponse{}, "list_token"),
),
)
req.ListToken = got.ListToken
// List items in the empty store
req = &pbs.ListCredentialsRequest{
CredentialStoreId: emptyStore.PublicId,
Filter: "",
ListToken: "",
PageSize: 2,
}
got, err = s.ListCredentials(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 0)
// Compare without comparing the refresh token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialsResponse{
Items: nil,
ResponseType: "complete",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialsResponse{}, "list_token"),
),
)
}

@ -3227,7 +3227,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
require.NoError(t, err)
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, proj.GetPublicId())
credService, err := credentials.NewService(ctx, staticCredRepoFn, iamRepoFn)
credService, err := credentials.NewService(ctx, iamRepoFn, staticCredRepoFn, 1000)
require.NoError(t, err)
upCredResp, err := credService.CreateCredential(ctx, &pbs.CreateCredentialRequest{Item: &credpb.Credential{
CredentialStoreId: staticStore.GetPublicId(),

@ -955,6 +955,21 @@
"in": "query",
"required": false,
"type": "string"
},
{
"name": "list_token",
"description": "An opaque token used to continue an existing iteration or\nrequest updated items. If not specified, pagination\nwill start from the beginning.",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "page_size",
"description": "The maximum size of a page in this iteration.\nIf unset, the default page size configured will be used.\nIf the page_size is greater than the max page size configured,\nthe page size will be truncated to this number.",
"in": "query",
"required": false,
"type": "integer",
"format": "int64"
}
],
"tags": [
@ -7556,7 +7571,36 @@
"items": {
"type": "object",
"$ref": "#/definitions/controller.api.resources.credentials.v1.Credential"
}
},
"description": "The items returned in this page."
},
"response_type": {
"type": "string",
"description": "The type of response, either \"delta\" or \"complete\".\nDelta signifies that this is part of a paginated result\nor an update to a previously completed pagination.\nComplete signifies that it is the last page."
},
"list_token": {
"type": "string",
"description": "An opaque token used to continue an existing pagination or\nrequest updated items. Use this token in the next list request\nto request the next page."
},
"sort_by": {
"type": "string",
"description": "The name of the field which the items are sorted by."
},
"sort_dir": {
"type": "string",
"description": "The direction of the sort, either \"asc\" or \"desc\"."
},
"removed_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "A list of item IDs that have been removed since they were returned\nas part of a pagination. They should be dropped from any client cache.\nThis may contain items that are not known to the cache, if they were\ncreated and deleted between listings."
},
"est_item_count": {
"type": "integer",
"format": "int64",
"description": "An estimate at the total items available. This may change during pagination."
}
}
},

@ -129,6 +129,15 @@ type ListCredentialsRequest struct {
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"`
// An opaque token used to continue an existing iteration or
// request updated items. If not specified, pagination
// will start from the beginning.
ListToken string `protobuf:"bytes,40,opt,name=list_token,proto3" json:"list_token,omitempty" class:"public"` // @gotags: `class:"public"`
// The maximum size of a page in this iteration.
// If unset, the default page size configured will be used.
// If the page_size is greater than the max page size configured,
// the page size will be truncated to this number.
PageSize uint32 `protobuf:"varint,50,opt,name=page_size,proto3" json:"page_size,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *ListCredentialsRequest) Reset() {
@ -177,12 +186,47 @@ func (x *ListCredentialsRequest) GetFilter() string {
return ""
}
func (x *ListCredentialsRequest) GetListToken() string {
if x != nil {
return x.ListToken
}
return ""
}
func (x *ListCredentialsRequest) GetPageSize() uint32 {
if x != nil {
return x.PageSize
}
return 0
}
type ListCredentialsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The items returned in this page.
Items []*credentials.Credential `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
// The type of response, either "delta" or "complete".
// Delta signifies that this is part of a paginated result
// or an update to a previously completed pagination.
// Complete signifies that it is the last page.
ResponseType string `protobuf:"bytes,2,opt,name=response_type,proto3" json:"response_type,omitempty" class:"public"` // @gotags: `class:"public"`
// An opaque token used to continue an existing pagination or
// request updated items. Use this token in the next list request
// to request the next page.
ListToken string `protobuf:"bytes,3,opt,name=list_token,proto3" json:"list_token,omitempty" class:"public"` // @gotags: `class:"public"`
// The name of the field which the items are sorted by.
SortBy string `protobuf:"bytes,4,opt,name=sort_by,proto3" json:"sort_by,omitempty" class:"public"` // @gotags: `class:"public"`
// The direction of the sort, either "asc" or "desc".
SortDir string `protobuf:"bytes,5,opt,name=sort_dir,proto3" json:"sort_dir,omitempty" class:"public"` // @gotags: `class:"public"`
// A list of item IDs that have been removed since they were returned
// as part of a pagination. They should be dropped from any client cache.
// This may contain items that are not known to the cache, if they were
// created and deleted between listings.
RemovedIds []string `protobuf:"bytes,6,rep,name=removed_ids,proto3" json:"removed_ids,omitempty" class:"public"` // @gotags: `class:"public"`
// An estimate at the total items available. This may change during pagination.
EstItemCount uint32 `protobuf:"varint,7,opt,name=est_item_count,proto3" json:"est_item_count,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *ListCredentialsResponse) Reset() {
@ -224,6 +268,48 @@ func (x *ListCredentialsResponse) GetItems() []*credentials.Credential {
return nil
}
func (x *ListCredentialsResponse) GetResponseType() string {
if x != nil {
return x.ResponseType
}
return ""
}
func (x *ListCredentialsResponse) GetListToken() string {
if x != nil {
return x.ListToken
}
return ""
}
func (x *ListCredentialsResponse) GetSortBy() string {
if x != nil {
return x.SortBy
}
return ""
}
func (x *ListCredentialsResponse) GetSortDir() string {
if x != nil {
return x.SortDir
}
return ""
}
func (x *ListCredentialsResponse) GetRemovedIds() []string {
if x != nil {
return x.RemovedIds
}
return nil
}
func (x *ListCredentialsResponse) GetEstItemCount() uint32 {
if x != nil {
return x.EstItemCount
}
return 0
}
type CreateCredentialRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -551,120 +637,137 @@ var file_controller_api_services_v1_credential_service_proto_rawDesc = []byte{
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,
0x6d, 0x22, 0x95, 0x01, 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, 0x12, 0x1e, 0x0a, 0x0a, 0x6c,
0x69, 0x73, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0a, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x70,
0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09,
0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xaa, 0x02, 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, 0x62, 0x04, 0x69,
0x74, 0x65, 0x6d, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 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,
0x12, 0x24, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x69, 0x73, 0x74,
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62,
0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79,
0x12, 0x1a, 0x0a, 0x08, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x12, 0x20, 0x0a, 0x0b,
0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28,
0x09, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x26,
0x0a, 0x0e, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x74, 0x65, 0x6d,
0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 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, 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,
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, 0x1d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22,
0x93, 0x02, 0x1c, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x63,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 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, 0xc1, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65,
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, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
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, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72,
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, 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, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x14,
0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f,
0x7b, 0x69, 0x64, 0x7d, 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, 0xa2, 0xe3, 0x29,
0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 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, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
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, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 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, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x32, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 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, 0xa2, 0xe3, 0x29, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 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, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

@ -84,10 +84,40 @@ message GetCredentialResponse {
message ListCredentialsRequest {
string credential_store_id = 1 [json_name = "scope_id"]; // @gotags: `class:"public"`
string filter = 30 [json_name = "filter"]; // @gotags: `class:"public"`
// An opaque token used to continue an existing iteration or
// request updated items. If not specified, pagination
// will start from the beginning.
string list_token = 40 [json_name = "list_token"]; // @gotags: `class:"public"`
// The maximum size of a page in this iteration.
// If unset, the default page size configured will be used.
// If the page_size is greater than the max page size configured,
// the page size will be truncated to this number.
uint32 page_size = 50 [json_name = "page_size"]; // @gotags: `class:"public"`
}
message ListCredentialsResponse {
// The items returned in this page.
repeated resources.credentials.v1.Credential items = 1;
// The type of response, either "delta" or "complete".
// Delta signifies that this is part of a paginated result
// or an update to a previously completed pagination.
// Complete signifies that it is the last page.
string response_type = 2 [json_name = "response_type"]; // @gotags: `class:"public"`
// An opaque token used to continue an existing pagination or
// request updated items. Use this token in the next list request
// to request the next page.
string list_token = 3 [json_name = "list_token"]; // @gotags: `class:"public"`
// The name of the field which the items are sorted by.
string sort_by = 4 [json_name = "sort_by"]; // @gotags: `class:"public"`
// The direction of the sort, either "asc" or "desc".
string sort_dir = 5 [json_name = "sort_dir"]; // @gotags: `class:"public"`
// A list of item IDs that have been removed since they were returned
// as part of a pagination. They should be dropped from any client cache.
// This may contain items that are not known to the cache, if they were
// created and deleted between listings.
repeated string removed_ids = 6 [json_name = "removed_ids"]; // @gotags: `class:"public"`
// An estimate at the total items available. This may change during pagination.
uint32 est_item_count = 7 [json_name = "est_item_count"]; // @gotags: `class:"public"`
}
message CreateCredentialRequest {

Loading…
Cancel
Save