internal/handlers: add credential store pagination

This commit is contained in:
Johan Brandhorst-Satzkorn committed 2024-01-05 07:43:20 -08:00
1 parent a7ebb7a3ef
commit 0259f2f26b
9 files changed
+1012 -357

No files matched your search

@@ -7,6 +7,7 @@ import (
"github.com/hashicorp/boundary/internal/auth/ldap"
"github.com/hashicorp/boundary/internal/auth/oidc"
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/credential"
credstatic "github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/credential/vault"
pluginhost "github.com/hashicorp/boundary/internal/host/plugin"
@@ -22,6 +23,7 @@ type (
AuthTokenRepoFactory = oidc.AuthTokenRepoFactory
VaultCredentialRepoFactory = func() (*vault.Repository, error)
StaticCredentialRepoFactory = func() (*credstatic.Repository, error)
CredentialStoreRepoFactory func() (*credential.StoreRepository, error)
IamRepoFactory = iam.IamRepoFactory
OidcAuthRepoFactory = oidc.OidcRepoFactory
LdapAuthRepoFactory = ldap.RepoFactory
+5
View File
@@ -18,6 +18,7 @@ import (
"github.com/hashicorp/boundary/internal/census"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/config"
"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/cluster"
@@ -128,6 +129,7 @@ type Controller struct {
AuthTokenRepoFn common.AuthTokenRepoFactory
VaultCredentialRepoFn common.VaultCredentialRepoFactory
StaticCredentialRepoFn common.StaticCredentialRepoFactory
CredentialStoreRepoFn common.CredentialStoreRepoFactory
IamRepoFn common.IamRepoFactory
OidcRepoFn common.OidcAuthRepoFactory
LdapRepoFn common.LdapAuthRepoFactory
@@ -405,6 +407,9 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
c.StaticCredentialRepoFn = func() (*credstatic.Repository, error) {
return credstatic.NewRepository(ctx, dbase, dbase, c.kms)
}
c.CredentialStoreRepoFn = func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(ctx, dbase, dbase)
}
c.ServersRepoFn = func() (*server.Repository, error) {
return server.NewRepository(ctx, dbase, dbase, c.kms)
}
+8 -1
View File
@@ -266,7 +266,14 @@ 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.baseContext, c.VaultCredentialRepoFn, c.StaticCredentialRepoFn, c.IamRepoFn)
cs, err := credentialstores.NewService(
c.baseContext,
c.IamRepoFn,
c.VaultCredentialRepoFn,
c.StaticCredentialRepoFn,
c.CredentialStoreRepoFn,
c.conf.RawConfig.Controller.MaxPageSize,
)
if err != nil {
return fmt.Errorf("failed to create credential store handler service: %w", err)
}
@@ -22,6 +22,8 @@ import (
"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/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"
@@ -29,6 +31,7 @@ import (
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/hashicorp/boundary/internal/types/subtypes"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentialstores"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/wrapperspb"
@@ -98,6 +101,8 @@ type Service struct {
iamRepoFn common.IamRepoFactory
vaultRepoFn common.VaultCredentialRepoFactory
staticRepoFn common.StaticCredentialRepoFactory
storeRepoFn common.CredentialStoreRepoFactory
maxPageSize uint
}
var _ pbs.CredentialStoreServiceServer = (*Service)(nil)
@@ -105,9 +110,11 @@ var _ pbs.CredentialStoreServiceServer = (*Service)(nil)
// NewService returns a credential store service which handles credential store related requests to boundary.
func NewService(
ctx context.Context,
iamRepo common.IamRepoFactory,
vaultRepo common.VaultCredentialRepoFactory,
staticRepo common.StaticCredentialRepoFactory,
iamRepo common.IamRepoFactory,
storeRepoFn common.CredentialStoreRepoFactory,
maxPageSize uint,
) (Service, error) {
const op = "credentialstores.NewService"
if iamRepo == nil {
@@ -119,13 +126,27 @@ func NewService(
if staticRepo == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing static credential repository")
}
return Service{iamRepoFn: iamRepo, vaultRepoFn: vaultRepo, staticRepoFn: staticRepo}, nil
if storeRepoFn == nil {
return Service{}, errors.New(ctx, errors.InvalidParameter, op, "missing credential store repo")
}
if maxPageSize == 0 {
maxPageSize = uint(globals.DefaultMaxPageSize)
}
return Service{
iamRepoFn: iamRepo,
vaultRepoFn: vaultRepo,
staticRepoFn: staticRepo,
storeRepoFn: storeRepoFn,
maxPageSize: maxPageSize,
}, nil
}
// ListCredentialStores implements the interface pbs.CredentialStoreServiceServer
func (s Service) ListCredentialStores(ctx context.Context, req *pbs.ListCredentialStoresRequest) (*pbs.ListCredentialStoresResponse, error) {
const op = "credentialstores.(Service).ListCredentialStores"
if err := validateListRequest(ctx, req); err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
if authResults.Error != nil {
@@ -144,68 +165,132 @@ func (s Service) ListCredentialStores(ctx context.Context, req *pbs.ListCredenti
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.CredentialStore, req.GetRecursive())
if err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
// If no scopes match, return an empty response
if len(scopeIds) == 0 {
return &pbs.ListCredentialStoresResponse{}, nil
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.Store) (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.Store) (bool, error) {
outputOpts, ok, err := newOutputOpts(ctx, item, authResults, scopeInfoMap)
if err != nil {
return false, err
}
if !ok {
return false, nil
}
pbItem, err := toProto(ctx, item, outputOpts...)
if err != nil {
return false, err
}
csl, err := s.listFromRepo(ctx, scopeIds)
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.Store) (bool, error) {
return true, nil
}
}
repo, err := s.storeRepoFn()
if err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
if len(csl) == 0 {
return &pbs.ListCredentialStoresResponse{}, nil
}
filter, err := handlers.NewFilter(ctx, req.GetFilter())
grantsHash, err := authResults.GrantsHash(ctx)
if err != nil {
return nil, err
return nil, errors.Wrap(ctx, err, op)
}
finalItems := make([]*pb.CredentialStore, 0, len(csl))
res := perms.Resource{
Type: resource.CredentialStore,
}
for _, item := range csl {
res.Id = item.GetPublicId()
res.ScopeId = item.GetProjectId()
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(scopeInfoMap[item.GetProjectId()]))
var listResp *pagination.ListResponse[credential.Store]
var sortBy string
if req.GetListToken() == "" {
sortBy = "created_time"
listResp, err = credential.ListStores(ctx, grantsHash, pageSize, filterItemFn, repo, scopeIds)
if err != nil {
return nil, err
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
} else {
listToken, err := handlers.ParseListToken(ctx, req.GetListToken(), resource.CredentialStore, grantsHash)
if err != nil {
return nil, err
}
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, item.GetPublicId())
switch st := listToken.Subtype.(type) {
case *listtoken.PaginationToken:
sortBy = "created_time"
listResp, err = credential.ListStoresPage(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, scopeIds)
if err != nil {
return nil, err
}
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
}
item, err := toProto(ctx, item, outputOpts...)
if err != nil {
return nil, err
}
filterable, err := subtypes.Filterable(ctx, item)
if err != nil {
return nil, err
}
if filter.Match(filterable) {
finalItems = append(finalItems, item)
case *listtoken.StartRefreshToken:
sortBy = "updated_time"
listResp, err = credential.ListStoresRefresh(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, scopeIds)
if err != nil {
return nil, err
}
case *listtoken.RefreshToken:
sortBy = "updated_time"
listResp, err = credential.ListStoresRefreshPage(ctx, grantsHash, pageSize, filterItemFn, listToken, repo, scopeIds)
if err != nil {
return nil, err
}
default:
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "unexpected list token subtype: %T", st)
}
}
return &pbs.ListCredentialStoresResponse{Items: finalItems}, nil
finalItems := make([]*pb.CredentialStore, 0, len(listResp.Items))
for _, item := range listResp.Items {
outputOpts, ok, err := newOutputOpts(ctx, item, authResults, scopeInfoMap)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if !ok {
continue
}
pbItem, err := toProto(ctx, item, outputOpts...)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
finalItems = append(finalItems, pbItem)
}
respType := "delta"
if listResp.CompleteListing {
respType = "complete"
}
resp := &pbs.ListCredentialStoresResponse{
Items: finalItems,
EstItemCount: uint32(listResp.EstimatedItemCount),
RemovedIds: listResp.DeletedIds,
ResponseType: respType,
SortBy: sortBy,
SortDir: "desc",
}
if listResp.ListToken != nil {
resp.ListToken, err = handlers.MarshalListToken(ctx, listResp.ListToken, pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL_STORE)
if err != nil {
return nil, err
}
}
return resp, nil
}
// GetCredentialStore implements the interface pbs.CredentialStoreServiceServer.
@@ -362,38 +447,6 @@ func (s Service) DeleteCredentialStore(ctx context.Context, req *pbs.DeleteCrede
return nil, nil
}
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]credential.Store, error) {
const op = "credentialstores.(Service).listFromRepo"
vaultRepo, err := s.vaultRepoFn()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
vaultCsl, err := vaultRepo.ListCredentialStores(ctx, scopeIds, vault.WithLimit(-1))
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, static.WithLimit(-1))
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"
@@ -616,6 +669,41 @@ 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.Store,
authResults auth.VerifyResults,
authzScopes map[string]*scopes.ScopeInfo,
) ([]handlers.Option, bool, error) {
res := perms.Resource{
Type: resource.CredentialStore,
Id: item.GetPublicId(),
ScopeId: item.GetProjectId(),
}
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
return nil, false, nil
}
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(authzScopes[item.GetProjectId()]))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
}
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
collectionActions, err := calculateAuthorizedCollectionActions(ctx, authResults, item.GetPublicId())
if err != nil {
return nil, false, err
}
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
}
return outputOpts, true, nil
}
func toProto(ctx context.Context, in credential.Store, opt ...handlers.Option) (*pb.CredentialStore, error) {
const op = "credentialstores.toProto"
@@ -809,7 +897,7 @@ func validateCreateRequest(ctx context.Context, req *pbs.CreateCredentialStoreRe
if attrs.GetTokenHmac() != "" {
badFields[vaultTokenHmacField] = "This is a read only field."
}
if attrs.WorkerFilter.GetValue() != "" {
if attrs.GetWorkerFilter().GetValue() != "" {
err := validateVaultWorkerFilterFn(attrs.WorkerFilter.GetValue())
if err != nil {
badFields[vaultWorkerFilterField] = err.Error()
@@ -10,22 +10,30 @@ import (
"crypto/rand"
"encoding/base64"
"fmt"
"slices"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/boundary/globals"
credstatic "github.com/hashicorp/boundary/internal/credential/static"
"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/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/credentialstores"
scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
@@ -60,6 +68,43 @@ var (
}
)
func vaultCredentialStoreToProto(cs *vault.CredentialStore, prj *iam.Scope) *pb.CredentialStore {
return &pb.CredentialStore{
Id: cs.GetPublicId(),
ScopeId: prj.GetPublicId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
CreatedTime: cs.GetCreateTime().GetTimestamp(),
UpdatedTime: cs.GetUpdateTime().GetTimestamp(),
Version: cs.GetVersion(),
Type: vault.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedVaultCollectionActions,
Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{
VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{
Address: wrapperspb.String(cs.GetVaultAddress()),
TokenHmac: base64.RawURLEncoding.EncodeToString(cs.Token().GetTokenHmac()),
TokenStatus: cs.Token().GetStatus(),
ClientCertificate: wrapperspb.String(string(cs.ClientCertificate().GetCertificate())),
ClientCertificateKeyHmac: base64.RawURLEncoding.EncodeToString(cs.ClientCertificate().GetCertificateKeyHmac()),
},
},
}
}
func staticCredentialStoreToProto(cs *static.CredentialStore, prj *iam.Scope) *pb.CredentialStore {
return &pb.CredentialStore{
Id: cs.GetPublicId(),
ScopeId: prj.GetPublicId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
CreatedTime: cs.GetCreateTime().GetTimestamp(),
UpdatedTime: cs.GetUpdateTime().GetTimestamp(),
Version: cs.GetVersion(),
Type: static.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
}
}
func TestList(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
@@ -77,8 +122,11 @@ func TestList(t *testing.T) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(ctx, rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(ctx, rw, rw, kms)
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kms)
}
credStoreRepoFn := func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(context.Background(), rw, rw)
}
_, prjNoStores := iam.TestScopes(t, iamRepo)
@@ -86,41 +134,11 @@ func TestList(t *testing.T) {
var wantStores []*pb.CredentialStore
for _, s := range vault.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: vault.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedVaultCollectionActions,
Attrs: &pb.CredentialStore_VaultCredentialStoreAttributes{
VaultCredentialStoreAttributes: &pb.VaultCredentialStoreAttributes{
Address: wrapperspb.String(s.GetVaultAddress()),
TokenHmac: base64.RawURLEncoding.EncodeToString(s.Token().GetTokenHmac()),
TokenStatus: s.Token().GetStatus(),
ClientCertificate: wrapperspb.String(string(s.ClientCertificate().GetCertificate())),
ClientCertificateKeyHmac: base64.RawURLEncoding.EncodeToString(s.ClientCertificate().GetCertificateKeyHmac()),
// TODO: Add all fields including tls related fields, namespace, etc...
},
},
})
wantStores = append(wantStores, vaultCredentialStoreToProto(s, prj))
}
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,
})
for _, s := range static.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 10) {
wantStores = append(wantStores, staticCredentialStoreToProto(s, prj))
}
cases := []struct {
@@ -131,40 +149,126 @@ func TestList(t *testing.T) {
err error
}{
{
name: "List Many Stores",
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId()},
res: &pbs.ListCredentialStoresResponse{Items: wantStores},
anonRes: &pbs.ListCredentialStoresResponse{Items: wantStores},
name: "List Many Stores",
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId()},
res: &pbs.ListCredentialStoresResponse{
Items: wantStores,
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 20,
},
anonRes: &pbs.ListCredentialStoresResponse{
Items: wantStores,
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 20,
},
},
{
name: "List No Stores",
req: &pbs.ListCredentialStoresRequest{ScopeId: prjNoStores.GetPublicId()},
res: &pbs.ListCredentialStoresResponse{},
anonRes: &pbs.ListCredentialStoresResponse{},
name: "List No Stores",
req: &pbs.ListCredentialStoresRequest{ScopeId: prjNoStores.GetPublicId()},
res: &pbs.ListCredentialStoresResponse{
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
},
anonRes: &pbs.ListCredentialStoresResponse{
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
},
},
{
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 Vault Store",
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantStores[1].GetId())},
res: &pbs.ListCredentialStoresResponse{
Items: wantStores[1:2],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 1,
},
anonRes: &pbs.ListCredentialStoresResponse{
Items: wantStores[1:2],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 1,
},
},
{
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 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],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 1,
},
anonRes: &pbs.ListCredentialStoresResponse{
Items: wantStores[11:12],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 1,
},
},
{
name: "Filter on Attribute",
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/address"==%q`, wantStores[2].GetVaultCredentialStoreAttributes().GetAddress().Value)},
res: &pbs.ListCredentialStoresResponse{Items: wantStores[2:3]},
anonRes: &pbs.ListCredentialStoresResponse{}, // anonymous user does not have access to attributes
name: "Filter on Attribute",
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: fmt.Sprintf(`"/item/attributes/address"==%q`, wantStores[2].GetVaultCredentialStoreAttributes().GetAddress().Value)},
res: &pbs.ListCredentialStoresResponse{
Items: wantStores[2:3],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 1,
},
anonRes: &pbs.ListCredentialStoresResponse{
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
}, // anonymous user does not have access to attributes
},
{
name: "Filter to No Store",
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
res: &pbs.ListCredentialStoresResponse{},
anonRes: &pbs.ListCredentialStoresResponse{},
name: "Filter to No Store",
req: &pbs.ListCredentialStoresRequest{ScopeId: prj.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
res: &pbs.ListCredentialStoresResponse{
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
},
anonRes: &pbs.ListCredentialStoresResponse{
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
},
},
{
name: "Filter Bad Format",
@@ -174,7 +278,7 @@ func TestList(t *testing.T) {
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
s, err := NewService(ctx, vaultRepoFn, staticRepoFn, iamRepoFn)
s, err := NewService(ctx, iamRepoFn, vaultRepoFn, staticRepoFn, credStoreRepoFn, 1000)
require.NoError(t, err, "Couldn't create new host set service.")
// Test non-anonymous listing
@@ -198,6 +302,7 @@ func TestList(t *testing.T) {
cmpopts.SortSlices(func(a, b protocmp.Message) bool {
return a.String() < b.String()
}),
protocmp.IgnoreFields(&pbs.ListCredentialStoresResponse{}, "list_token"),
))
// Test anonymous listing
@@ -230,8 +335,11 @@ func TestCreateVault(t *testing.T) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(ctx, rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(ctx, rw, rw, kms)
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kms)
}
credStoreServiceFn := func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(context.Background(), rw, rw)
}
_, prj := iam.TestScopes(t, iamRepo)
@@ -411,7 +519,7 @@ func TestCreateVault(t *testing.T) {
Token: wrapperspb.String(newToken()),
CaCert: wrapperspb.String(string(v.CaCert)),
ClientCertificate: wrapperspb.String(string(v.ClientCert) + string(v.ClientKey)),
WorkerFilter: wrapperspb.String(fmt.Sprintf(`"worker" in "/tags/name"`)),
WorkerFilter: wrapperspb.String(`"worker" in "/tags/name"`),
},
},
}},
@@ -544,7 +652,7 @@ func TestCreateVault(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
s, err := NewService(ctx, vaultRepoFn, staticRepoFn, iamRepoFn)
s, err := NewService(ctx, iamRepoFn, vaultRepoFn, staticRepoFn, credStoreServiceFn, 1000)
require.NoError(err, "Error when getting new credential store service.")
defer cleanup(s)
@@ -617,12 +725,15 @@ func TestCreateStatic(t *testing.T) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(ctx, rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(ctx, rw, rw, kms)
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kms)
}
credStoreServiceFn := func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(context.Background(), rw, rw)
}
_, prj := iam.TestScopes(t, iamRepo)
defaultCreated := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
defaultCreated := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
cleanup := func(s Service) {
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
@@ -647,7 +758,7 @@ func TestCreateStatic(t *testing.T) {
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
Id: globals.StaticCredentialStorePrefix + "_notallowed",
Type: credstatic.Subtype.String(),
Type: static.Subtype.String(),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
@@ -657,7 +768,7 @@ func TestCreateStatic(t *testing.T) {
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
CreatedTime: timestamppb.Now(),
Type: credstatic.Subtype.String(),
Type: static.Subtype.String(),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
@@ -667,7 +778,7 @@ func TestCreateStatic(t *testing.T) {
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
UpdatedTime: timestamppb.Now(),
Type: credstatic.Subtype.String(),
Type: static.Subtype.String(),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
@@ -684,7 +795,7 @@ func TestCreateStatic(t *testing.T) {
name: "Create a valid static CredentialStore",
req: &pbs.CreateCredentialStoreRequest{Item: &pb.CredentialStore{
ScopeId: prj.GetPublicId(),
Type: credstatic.Subtype.String(),
Type: static.Subtype.String(),
}},
idPrefix: globals.StaticCredentialStorePrefix + "_",
res: &pbs.CreateCredentialStoreResponse{
@@ -693,7 +804,7 @@ func TestCreateStatic(t *testing.T) {
ScopeId: prj.GetPublicId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
Version: 1,
Type: credstatic.Subtype.String(),
Type: static.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
},
@@ -705,7 +816,7 @@ func TestCreateStatic(t *testing.T) {
ScopeId: prj.GetPublicId(),
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
Type: credstatic.Subtype.String(),
Type: static.Subtype.String(),
}},
idPrefix: globals.StaticCredentialStorePrefix + "_",
res: &pbs.CreateCredentialStoreResponse{
@@ -714,7 +825,7 @@ func TestCreateStatic(t *testing.T) {
ScopeId: prj.GetPublicId(),
Scope: &scopepb.ScopeInfo{Id: prj.GetPublicId(), Type: prj.GetType(), ParentScopeId: prj.GetParentId()},
Version: 1,
Type: credstatic.Subtype.String(),
Type: static.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
@@ -727,7 +838,7 @@ func TestCreateStatic(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
s, err := NewService(ctx, vaultRepoFn, staticRepoFn, iamRepoFn)
s, err := NewService(ctx, iamRepoFn, vaultRepoFn, staticRepoFn, credStoreServiceFn, 1000)
require.NoError(err, "Error when getting new credential store service.")
defer cleanup(s)
@@ -789,16 +900,19 @@ func TestGet(t *testing.T) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(ctx, rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(ctx, rw, rw, kms)
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kms)
}
credStoreServiceFn := func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(context.Background(), rw, rw)
}
_, prj := iam.TestScopes(t, iamRepo)
vaultStore := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 1)[0]
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
staticStorePrev := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId(), credstatic.WithPublicId(fmt.Sprintf("%s_1234567890", globals.StaticCredentialStorePreviousPrefix)))
s, err := NewService(ctx, vaultRepoFn, staticRepoFn, iamRepoFn)
staticStore := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
staticStorePrev := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId(), static.WithPublicId(fmt.Sprintf("%s_1234567890", globals.StaticCredentialStorePreviousPrefix)))
s, err := NewService(ctx, iamRepoFn, vaultRepoFn, staticRepoFn, credStoreServiceFn, 1000)
require.NoError(t, err)
cases := []struct {
@@ -841,7 +955,7 @@ func TestGet(t *testing.T) {
Id: staticStore.GetPublicId(),
ScopeId: staticStore.GetProjectId(),
Scope: &scopepb.ScopeInfo{Id: staticStore.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Type: credstatic.Subtype.String(),
Type: static.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
CreatedTime: staticStore.CreateTime.GetTimestamp(),
@@ -858,7 +972,7 @@ func TestGet(t *testing.T) {
Id: staticStorePrev.GetPublicId(),
ScopeId: staticStorePrev.GetProjectId(),
Scope: &scopepb.ScopeInfo{Id: staticStorePrev.GetProjectId(), Type: scope.Project.String(), ParentScopeId: prj.GetParentId()},
Type: credstatic.Subtype.String(),
Type: static.Subtype.String(),
AuthorizedActions: testAuthorizedActions,
AuthorizedCollectionActions: testAuthorizedStaticCollectionActions,
CreatedTime: staticStorePrev.CreateTime.GetTimestamp(),
@@ -933,15 +1047,18 @@ func TestDelete(t *testing.T) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(ctx, rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(ctx, rw, rw, kms)
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kms)
}
credStoreServiceFn := func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(context.Background(), rw, rw)
}
_, prj := iam.TestScopes(t, iamRepo)
vaultStore := vault.TestCredentialStores(t, conn, wrapper, prj.GetPublicId(), 2)[0]
staticStore := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
s, err := NewService(ctx, vaultRepoFn, staticRepoFn, iamRepoFn)
staticStore := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
s, err := NewService(ctx, iamRepoFn, vaultRepoFn, staticRepoFn, credStoreServiceFn, 1000)
require.NoError(t, err)
cases := []struct {
@@ -1008,14 +1125,17 @@ func TestUpdateVault(t *testing.T) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(testCtx, rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(testCtx, rw, rw, kms)
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(testCtx, rw, rw, kms)
}
credStoreServiceFn := func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(context.Background(), rw, rw)
}
_, prj := iam.TestScopes(t, iamRepo)
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
s, err := NewService(ctx, vaultRepoFn, staticRepoFn, iamRepoFn)
s, err := NewService(ctx, iamRepoFn, vaultRepoFn, staticRepoFn, credStoreServiceFn, 1000)
require.NoError(t, err)
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
@@ -1352,23 +1472,26 @@ func TestUpdateStatic(t *testing.T) {
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(testCtx, rw, rw, kms, sche)
}
staticRepoFn := func() (*credstatic.Repository, error) {
return credstatic.NewRepository(testCtx, rw, rw, kms)
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(testCtx, rw, rw, kms)
}
credStoreServiceFn := func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(context.Background(), rw, rw)
}
_, prj := iam.TestScopes(t, iamRepo)
ctx := auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId())
s, err := NewService(ctx, vaultRepoFn, staticRepoFn, iamRepoFn)
s, err := NewService(ctx, iamRepoFn, vaultRepoFn, staticRepoFn, credStoreServiceFn, 1000)
require.NoError(t, err)
fieldmask := func(paths ...string) *fieldmaskpb.FieldMask {
return &fieldmaskpb.FieldMask{Paths: paths}
}
freshStore := func() (*credstatic.CredentialStore, func()) {
freshStore := func() (*static.CredentialStore, func()) {
t.Helper()
st := credstatic.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
st := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
clean := func() {
_, err := s.DeleteCredentialStore(ctx, &pbs.DeleteCredentialStoreRequest{Id: st.GetPublicId()})
require.NoError(t, err)
@@ -1522,3 +1645,319 @@ func TestUpdateStatic(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)
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(ctx, rw, rw, kms, sche)
}
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kms)
}
tokenRepoFn := func() (*authtoken.Repository, error) {
return authtoken.NewRepository(ctx, rw, rw, kms)
}
serversRepoFn := func() (*server.Repository, error) {
return server.NewRepository(ctx, rw, rw, kms)
}
credStoreServiceFn := func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(context.Background(), rw, rw)
}
staticRepo, err := staticRepoFn()
require.NoError(err)
tokenRepo, err := tokenRepoFn()
require.NoError(err)
_, prjNoStores := iam.TestScopes(t, iamRepo)
o, prj := iam.TestScopes(t, iamRepo)
var allCredentialStores []*pb.CredentialStore
for _, l := range static.TestCredentialStores(t, conn, wrapper, prj.PublicId, 5) {
allCredentialStores = append(allCredentialStores, staticCredentialStoreToProto(l, prj))
}
for _, l := range vault.TestCredentialStores(t, conn, wrapper, prj.PublicId, 5) {
allCredentialStores = append(allCredentialStores, vaultCredentialStoreToProto(l, prj))
}
// Reverse slice since we're sorting by create time descending
slices.Reverse(allCredentialStores)
// Run analyze to update postgres meta tables
_, err = sqlDB.ExecContext(ctx, "analyze")
require.NoError(err)
authMethod := password.TestAuthMethods(t, conn, o.GetPublicId(), 1)[0]
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, kms, &requestInfo)
s, err := NewService(ctx, iamRepoFn, vaultRepoFn, staticRepoFn, credStoreServiceFn, 1000)
require.NoError(err)
// Start paginating, recursively
req := &pbs.ListCredentialStoresRequest{
ScopeId: prj.PublicId,
Filter: "",
ListToken: "",
PageSize: 2,
}
got, err := s.ListCredentialStores(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 2)
// Compare without comparing the refresh token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialStoresResponse{
Items: allCredentialStores[0:2],
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialStoresResponse{}, "list_token"),
),
)
// Request second page
req.ListToken = got.ListToken
got, err = s.ListCredentialStores(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 2)
// Compare without comparing the refresh token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialStoresResponse{
Items: allCredentialStores[2:4],
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialStoresResponse{}, "list_token"),
),
)
// Request rest of results
req.ListToken = got.ListToken
req.PageSize = 10
got, err = s.ListCredentialStores(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 6)
// Compare without comparing the refresh token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialStoresResponse{
Items: allCredentialStores[4:],
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialStoresResponse{}, "list_token"),
),
)
// Create another credential store
newCredStore := static.TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
pbNewCredStore := staticCredentialStoreToProto(newCredStore, prj)
// Add to the front since it's most recently updated
allCredentialStores = append([]*pb.CredentialStore{pbNewCredStore}, allCredentialStores...)
// Delete one of the other credential stores
_, err = staticRepo.DeleteCredentialStore(ctx, allCredentialStores[len(allCredentialStores)-1].Id)
require.NoError(err)
deletedCredStore := allCredentialStores[len(allCredentialStores)-1]
allCredentialStores = allCredentialStores[:len(allCredentialStores)-1]
// Update one of the other stores
allCredentialStores[len(allCredentialStores)-1].Name = wrapperspb.String("new-name")
allCredentialStores[len(allCredentialStores)-1].Version = 2
updatedStore := &static.CredentialStore{
CredentialStore: &store.CredentialStore{
PublicId: allCredentialStores[len(allCredentialStores)-1].GetId(),
Name: allCredentialStores[len(allCredentialStores)-1].GetName().GetValue(),
ProjectId: allCredentialStores[len(allCredentialStores)-1].GetScopeId(),
},
}
stre, _, err := staticRepo.UpdateCredentialStore(ctx, updatedStore, 1, []string{"name"})
require.NoError(err)
allCredentialStores[len(allCredentialStores)-1].UpdatedTime = stre.CredentialStore.UpdateTime.GetTimestamp()
allCredentialStores[len(allCredentialStores)-1].Version = stre.GetVersion()
// Add to the front since it's most recently updated
allCredentialStores = append(
[]*pb.CredentialStore{allCredentialStores[len(allCredentialStores)-1]},
allCredentialStores[:len(allCredentialStores)-1]...,
)
// 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.ListCredentialStores(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 1)
// Compare without comparing the refresh token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialStoresResponse{
Items: []*pb.CredentialStore{allCredentialStores[0]},
ResponseType: "delta",
ListToken: "",
SortBy: "updated_time",
SortDir: "desc",
// Should contain the deleted session
RemovedIds: []string{deletedCredStore.Id},
EstItemCount: 10,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialStoresResponse{}, "list_token"),
),
)
// Get next page
req.ListToken = got.ListToken
got, err = s.ListCredentialStores(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 1)
// Compare without comparing the list token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialStoresResponse{
Items: []*pb.CredentialStore{allCredentialStores[1]},
ResponseType: "complete",
SortBy: "updated_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialStoresResponse{}, "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`, allCredentialStores[len(allCredentialStores)-2].Id, allCredentialStores[len(allCredentialStores)-1].Id)
got, err = s.ListCredentialStores(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 1)
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialStoresResponse{
Items: []*pb.CredentialStore{allCredentialStores[len(allCredentialStores)-2]},
ResponseType: "delta",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
// Should be empty again
RemovedIds: nil,
EstItemCount: 10,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialStoresResponse{}, "list_token"),
),
)
req.ListToken = got.ListToken
// Get the second page
got, err = s.ListCredentialStores(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 1)
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialStoresResponse{
Items: []*pb.CredentialStore{allCredentialStores[len(allCredentialStores)-1]},
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
EstItemCount: 10,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialStoresResponse{}, "list_token"),
),
)
req.ListToken = got.ListToken
// List items in the empty scope
req = &pbs.ListCredentialStoresRequest{
ScopeId: prjNoStores.PublicId,
Filter: "",
ListToken: "",
PageSize: 2,
}
got, err = s.ListCredentialStores(ctx, req)
require.NoError(err)
require.Len(got.GetItems(), 0)
// Compare without comparing the refresh token
assert.Empty(
cmp.Diff(
got,
&pbs.ListCredentialStoresResponse{
Items: nil,
ResponseType: "complete",
ListToken: "",
SortBy: "created_time",
SortDir: "desc",
RemovedIds: nil,
},
protocmp.Transform(),
protocmp.IgnoreFields(&pbs.ListCredentialStoresResponse{}, "list_token"),
),
)
}
+45 -1
View File
@@ -845,6 +845,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": [
@@ -7646,7 +7661,36 @@
"items": {
"type": "object",
"$ref": "#/definitions/controller.api.resources.credentialstores.v1.CredentialStore"
}
},
"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."
}
}
},
@@ -130,6 +130,15 @@ type ListCredentialStoresRequest struct {
ScopeId string `protobuf:"bytes,1,opt,name=scope_id,proto3" json:"scope_id,omitempty" class:"public"` // @gotags: `class:"public"`
Recursive bool `protobuf:"varint,20,opt,name=recursive,proto3" json:"recursive,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 *ListCredentialStoresRequest) Reset() {
@@ -185,12 +194,47 @@ func (x *ListCredentialStoresRequest) GetFilter() string {
return ""
}
func (x *ListCredentialStoresRequest) GetListToken() string {
if x != nil {
return x.ListToken
}
return ""
}
func (x *ListCredentialStoresRequest) GetPageSize() uint32 {
if x != nil {
return x.PageSize
}
return 0
}
type ListCredentialStoresResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The items returned in this page.
Items []*credentialstores.CredentialStore `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 *ListCredentialStoresResponse) Reset() {
@@ -232,6 +276,48 @@ func (x *ListCredentialStoresResponse) GetItems() []*credentialstores.Credential
return nil
}
func (x *ListCredentialStoresResponse) GetResponseType() string {
if x != nil {
return x.ResponseType
}
return ""
}
func (x *ListCredentialStoresResponse) GetListToken() string {
if x != nil {
return x.ListToken
}
return ""
}
func (x *ListCredentialStoresResponse) GetSortBy() string {
if x != nil {
return x.SortBy
}
return ""
}
func (x *ListCredentialStoresResponse) GetSortDir() string {
if x != nil {
return x.SortDir
}
return ""
}
func (x *ListCredentialStoresResponse) GetRemovedIds() []string {
if x != nil {
return x.RemovedIds
}
return nil
}
func (x *ListCredentialStoresResponse) GetEstItemCount() uint32 {
if x != nil {
return x.EstItemCount
}
return 0
}
type CreateCredentialStoreRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -561,136 +647,153 @@ var file_controller_api_services_v1_credential_store_service_proto_rawDesc = []b
0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2e, 0x76, 0x31,
0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65,
0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x6f, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69,
0x64, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x14,
0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 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, 0x73, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x43,
0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xad, 0x01, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f,
0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18,
0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65,
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, 0xb9, 0x02, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73,
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 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, 0x74, 0x6f, 0x72,
0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x71, 0x0a, 0x1c,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x51, 0x0a, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 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, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22,
0x84, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 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, 0x51, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x3d, 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, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2e, 0x76, 0x31,
0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65,
0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xbf, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x51, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 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, 0x74, 0x6f, 0x72, 0x65,
0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53,
0x74, 0x6f, 0x72, 0x65, 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, 0x72, 0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72,
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x04, 0x69, 0x74, 0x65,
0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 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, 0x71, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x51, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x3d, 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, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e,
0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x84, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 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, 0x51, 0x0a, 0x04, 0x69, 0x74, 0x65,
0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 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, 0x74, 0x6f,
0x72, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x2e, 0x0a, 0x1c,
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1f, 0x0a, 0x1d,
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xc9, 0x08,
0x0a, 0x16, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72,
0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xd1, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74,
0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12,
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, 0x47, 0x65, 0x74,
0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 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, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4c,
0x92, 0x41, 0x21, 0x12, 0x1f, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x74,
0x6f, 0x72, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x2d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xc9, 0x01, 0x0a,
0x14, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53,
0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xbf, 0x01, 0x0a,
0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x51, 0x0a,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 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, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 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, 0x72,
0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x51, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 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, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x04, 0x69, 0x74,
0x65, 0x6d, 0x22, 0x2e, 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
0x69, 0x64, 0x22, 0x1f, 0x0a, 0x1d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x32, 0xc9, 0x08, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xd1,
0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 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, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x53, 0x74, 0x6f, 0x72, 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, 0x4c, 0x69, 0x73, 0x74,
0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x92, 0x41, 0x1e, 0x12, 0x1c, 0x4c,
0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02,
0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0xde, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x12, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 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, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4c, 0x92, 0x41, 0x21, 0x12, 0x1f, 0x47, 0x65, 0x74, 0x73, 0x20,
0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22,
0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, 0x7b, 0x69,
0x64, 0x7d, 0x12, 0xc9, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 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, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 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, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e,
0x92, 0x41, 0x1e, 0x12, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73,
0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0xde,
0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 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, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x39, 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,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 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, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x92, 0x41, 0x24, 0x12, 0x22, 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, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x2e,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74,
0x65, 0x6d, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0xdc, 0x01, 0x0a, 0x15, 0x55, 0x70,
0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74,
0x6f, 0x72, 0x65, 0x12, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x92,
0x41, 0x24, 0x12, 0x22, 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, 0x20,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x3a, 0x04, 0x69, 0x74,
0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12,
0xdc, 0x01, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 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, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x39, 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, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 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, 0x53, 0x74, 0x6f, 0x72, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4e, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x55,
0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28,
0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x1a, 0x2f, 0x76,
0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x74, 0x6f,
0x72, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xce, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x12, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4e,
0x92, 0x41, 0x1d, 0x12, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x43,
0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x2e,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74,
0x65, 0x6d, 0x32, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xce,
0x01, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 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, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x65, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x39, 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,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 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, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x92, 0x41, 0x1b, 0x12, 0x19, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x2a, 0x1a, 0x2f,
0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, 0x74,
0x6f, 0x72, 0x65, 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,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x92,
0x41, 0x1b, 0x12, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x43, 0x72,
0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x1c, 0x2a, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x65, 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 (
@@ -85,10 +85,40 @@ message ListCredentialStoresRequest {
string scope_id = 1 [json_name = "scope_id"]; // @gotags: `class:"public"`
bool recursive = 20 [json_name = "recursive"]; // @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 ListCredentialStoresResponse {
// The items returned in this page.
repeated resources.credentialstores.v1.CredentialStore 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 CreateCredentialStoreRequest {
@@ -6,8 +6,6 @@ syntax = "proto3";
// Package store provides protobufs for storing types in the credential package.
package controller.storage.credential.store.v1;
import "controller/storage/timestamp/v1/timestamp.proto";
option go_package = "github.com/hashicorp/boundary/internal/credential/store;store";
message Store {
@@ -60,64 +58,3 @@ message Dynamic {
// @inject_tag: `gorm:"not_null"`
string library_id = 2;
}
message StoreListQueryResult {
// public_id is a surrogate key suitable for use in a public API.
// @inject_tag: `gorm:"primary_key"`
string public_id = 1;
// The project_id of the owning project and must be set.
// @inject_tag: `gorm:"not_null"`
string project_id = 2;
// Optional name of the credential store.
string name = 3;
// Optional description of the credential store.
string description = 4;
// Create time of the credential store.
timestamp.v1.Timestamp create_time = 5;
// Update time of the credential store.
timestamp.v1.Timestamp update_time = 6;
// Version of the credential store.
uint32 version = 7;
// Optional delete time of the credential store.
timestamp.v1.Timestamp delete_time = 8;
// Optional Vault address of the credential store.
string vault_address = 9;
// Optional namespace of the credential store.
string namespace = 10;
// Optional CA cert of the credential store.
bytes ca_cert = 11;
// Optional TLS server name of the credential store.
string tls_server_name = 12;
// Optionally specifices whether to skip TLS verification of the credential store.
bool tls_skip_verify = 13;
// Optional worker filter of the credential store.
string worker_filter = 14;
// Optional token HMAC of the credential store.
bytes token_hmac = 15;
// Optional token status of the credential store.
string token_status = 16;
// Optional client certificate of the credential store.
bytes client_cert = 17;
// Optional client cert key HMAC of the credential store.
bytes client_cert_key_hmac = 18;
// The subtype of the credential store.
string subtype = 19;
}