From 4f32443722c59563e6f8f4aeeb1a458a72d44593 Mon Sep 17 00:00:00 2001 From: miagilepner Date: Tue, 7 Jan 2025 13:56:07 +0100 Subject: [PATCH 01/25] fixes for flakes in raft removed tests (#29270) * fixes for flakes in raft removed tests * one more fix --- helper/testhelpers/testhelpers.go | 12 +++++++++ physical/raft/testing.go | 5 ++++ vault/external_tests/raft/raft_test.go | 28 ++++++++++++++++----- vault/external_tests/raftha/raft_ha_test.go | 2 +- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/helper/testhelpers/testhelpers.go b/helper/testhelpers/testhelpers.go index acecc084f8e5..5b96e1abe747 100644 --- a/helper/testhelpers/testhelpers.go +++ b/helper/testhelpers/testhelpers.go @@ -1003,3 +1003,15 @@ func WaitForNodesExcludingSelectedStandbys(t testing.TB, cluster *vault.TestClus func IsLocalOrRegressionTests() bool { return os.Getenv("CI") == "" || os.Getenv("VAULT_REGRESSION_TESTS") == "true" } + +func RaftDataDir(t testing.TB, core *vault.TestClusterCore) string { + t.Helper() + r, ok := core.UnderlyingStorage.(*raft.RaftBackend) + if !ok { + r, ok = core.UnderlyingHAStorage.(*raft.RaftBackend) + if !ok { + t.Fatal("no raft backend") + } + } + return r.DataDir(t) +} diff --git a/physical/raft/testing.go b/physical/raft/testing.go index 0a72e3f13cc6..c2e95320963f 100644 --- a/physical/raft/testing.go +++ b/physical/raft/testing.go @@ -13,6 +13,11 @@ import ( "github.com/hashicorp/go-uuid" ) +func (b *RaftBackend) DataDir(t testing.TB) string { + t.Helper() + return b.dataDir +} + func GetRaft(t testing.TB, bootstrap bool, noStoreState bool) (*RaftBackend, string) { return getRaftInternal(t, bootstrap, defaultRaftConfig(t, bootstrap, noStoreState), nil, nil, nil) } diff --git a/vault/external_tests/raft/raft_test.go b/vault/external_tests/raft/raft_test.go index b1a28c9c2047..32de80f8c684 100644 --- a/vault/external_tests/raft/raft_test.go +++ b/vault/external_tests/raft/raft_test.go @@ -1378,14 +1378,26 @@ func TestRaftCluster_Removed(t *testing.T) { }) require.NoError(t, err) - _, err = cluster.Cores[0].Client.Logical().Write("/sys/storage/raft/remove-peer", map[string]interface{}{ + leaderClient := cluster.Cores[0].Client + _, err = leaderClient.Logical().Write("/sys/storage/raft/remove-peer", map[string]interface{}{ "server_id": follower.NodeID, }) + require.NoError(t, err) followerClient.SetCheckRedirect(func(request *http.Request, requests []*http.Request) error { require.Fail(t, "request caused a redirect", request.URL.Path) return fmt.Errorf("no redirects allowed") }) - require.NoError(t, err) + configChanged := func() bool { + config, err := leaderClient.Logical().Read("sys/storage/raft/configuration") + require.NoError(t, err) + cfg := config.Data["config"].(map[string]interface{}) + servers := cfg["servers"].([]interface{}) + return len(servers) == 2 + } + // raft config changes happen async, so block until the config change is + // applied + require.Eventually(t, configChanged, 3*time.Second, 50*time.Millisecond) + _, err = followerClient.Logical().Write("secret/foo", map[string]interface{}{ "test": "other_data", }) @@ -1514,8 +1526,7 @@ func TestSysHealth_Raft(t *testing.T) { var erroredResponse *api.Response // now that the node can connect again, it will start getting the removed - // error when trying to connect. The code should be removed, and the ha - // connection will be nil because there is no ha connection + // error when trying to connect. The code should be removed testhelpers.RetryUntil(t, 10*time.Second, func() error { resp, err := followerClient.Logical().ReadRawWithData("sys/health", map[string][]string{ "perfstandbyok": {"true"}, @@ -1536,7 +1547,12 @@ func TestSysHealth_Raft(t *testing.T) { }) r := parseHealthBody(t, erroredResponse) require.True(t, true, *r.RemovedFromCluster) - require.Nil(t, r.HAConnectionHealthy) + // The HA connection health should either be nil or false. It's possible + // for it to be false if we got the response in between the node marking + // itself removed and sealing + if r.HAConnectionHealthy != nil { + require.False(t, *r.HAConnectionHealthy) + } }) } @@ -1555,7 +1571,7 @@ func TestRaftCluster_Removed_ReAdd(t *testing.T) { "server_id": follower.NodeID, }) require.NoError(t, err) - require.Eventually(t, follower.Sealed, 3*time.Second, 250*time.Millisecond) + require.Eventually(t, follower.Sealed, 10*time.Second, 250*time.Millisecond) joinReq := &api.RaftJoinRequest{LeaderAPIAddr: leader.Address.String()} _, err = follower.Client.Sys().RaftJoin(joinReq) diff --git a/vault/external_tests/raftha/raft_ha_test.go b/vault/external_tests/raftha/raft_ha_test.go index 82529859e19a..bd163566157a 100644 --- a/vault/external_tests/raftha/raft_ha_test.go +++ b/vault/external_tests/raftha/raft_ha_test.go @@ -371,7 +371,7 @@ func TestRaftHACluster_Removed_ReAdd(t *testing.T) { "server_id": follower.NodeID, }) require.NoError(t, err) - require.Eventually(t, follower.Sealed, 3*time.Second, 250*time.Millisecond) + require.Eventually(t, follower.Sealed, 10*time.Second, 250*time.Millisecond) _, err = follower.Client.Sys().RaftJoin(joinReq) require.Error(t, err) From e1538468c950ba036a45b23d7f5b94a6fb6ed9bf Mon Sep 17 00:00:00 2001 From: Steven Clark Date: Tue, 7 Jan 2025 09:34:17 -0500 Subject: [PATCH 02/25] PKI: Add management APIs for ACME accounts (#29173) * Allow a Vault operator to list, read and update PKI ACME accounts - This allows an operator to list the ACME account key ids, read the ACME account getting all the various information along with the account's associated orders and update the ACME account's status to either valid or revoked * Add tests for new ACME management APIs * Update PKI api-docs * Add cl * Add missing error handling and a few more test assertions * PR feedback * Fix Note tags within the website * Apply suggestions from docscode review Co-authored-by: Sarah Chavis <62406755+schavis@users.noreply.github.com> * Update website/content/api-docs/secret/pki/issuance.mdx * Update website/content/api-docs/secret/pki/issuance.mdx * Update website/content/api-docs/secret/pki/issuance.mdx --------- Co-authored-by: Sarah Chavis <62406755+schavis@users.noreply.github.com> --- builtin/logical/pki/acme_state.go | 72 ++++-- builtin/logical/pki/backend.go | 2 + builtin/logical/pki/backend_test.go | 8 +- builtin/logical/pki/path_acme_account_mgmt.go | 226 ++++++++++++++++++ builtin/logical/pki/path_acme_eab.go | 2 +- builtin/logical/pki/path_acme_test.go | 101 +++++++- builtin/logical/pki/storage.go | 12 + changelog/29173.txt | 3 + .../content/api-docs/secret/pki/issuance.mdx | 155 +++++++++++- 9 files changed, 549 insertions(+), 32 deletions(-) create mode 100644 builtin/logical/pki/path_acme_account_mgmt.go create mode 100644 changelog/29173.txt diff --git a/builtin/logical/pki/acme_state.go b/builtin/logical/pki/acme_state.go index 63962d933b99..996639af1d9d 100644 --- a/builtin/logical/pki/acme_state.go +++ b/builtin/logical/pki/acme_state.go @@ -11,7 +11,6 @@ import ( "io" "net" "path" - "strings" "sync" "sync/atomic" "time" @@ -311,7 +310,22 @@ func (a *acmeState) UpdateAccount(sc *storageContext, acct *acmeAccount) error { // LoadAccount will load the account object based on the passed in keyId field value // otherwise will return an error if the account does not exist. func (a *acmeState) LoadAccount(ac *acmeContext, keyId string) (*acmeAccount, error) { - entry, err := ac.sc.Storage.Get(ac.sc.Context, acmeAccountPrefix+keyId) + acct, err := a.LoadAccountWithoutDirEnforcement(ac.sc, keyId) + if err != nil { + return acct, err + } + + if acct.AcmeDirectory != ac.acmeDirectory { + return nil, fmt.Errorf("%w: account part of different ACME directory path", ErrMalformed) + } + + return acct, nil +} + +// LoadAccountWithoutDirEnforcement will load the account object based on the passed in keyId field value, +// but does not enforce the ACME directory path, normally this is used by non ACME specific APIs. +func (a *acmeState) LoadAccountWithoutDirEnforcement(sc *storageContext, keyId string) (*acmeAccount, error) { + entry, err := sc.Storage.Get(sc.Context, acmeAccountPrefix+keyId) if err != nil { return nil, fmt.Errorf("error loading account: %w", err) } @@ -324,13 +338,7 @@ func (a *acmeState) LoadAccount(ac *acmeContext, keyId string) (*acmeAccount, er if err != nil { return nil, fmt.Errorf("error decoding account: %w", err) } - - if acct.AcmeDirectory != ac.acmeDirectory { - return nil, fmt.Errorf("%w: account part of different ACME directory path", ErrMalformed) - } - acct.KeyId = keyId - return &acct, nil } @@ -536,6 +544,27 @@ func (a *acmeState) LoadOrder(ac *acmeContext, userCtx *jwsCtx, orderId string) return &order, nil } +// LoadAccountOrders will load all orders for a given account ID, this should be used by the +// management interface only, not through any of the ACME APIs. +func (a *acmeState) LoadAccountOrders(sc *storageContext, accountId string) ([]*acmeOrder, error) { + orderIds, err := a.ListOrderIds(sc, accountId) + if err != nil { + return nil, fmt.Errorf("failed listing order ids for account id %s: %w", accountId, err) + } + + var orders []*acmeOrder + for _, orderId := range orderIds { + order, err := a.LoadOrder(&acmeContext{sc: sc}, &jwsCtx{Kid: accountId}, orderId) + if err != nil { + return nil, err + } + + orders = append(orders, order) + } + + return orders, nil +} + func (a *acmeState) SaveOrder(ac *acmeContext, order *acmeOrder) error { if order.OrderId == "" { return fmt.Errorf("invalid order, missing order id") @@ -565,15 +594,7 @@ func (a *acmeState) ListOrderIds(sc *storageContext, accountId string) ([]string return nil, fmt.Errorf("failed listing order ids for account %s: %w", accountId, err) } - orderIds := []string{} - for _, order := range rawOrderIds { - if strings.HasSuffix(order, "/") { - // skip any folders we might have for some reason - continue - } - orderIds = append(orderIds, order) - } - return orderIds, nil + return filterDirEntries(rawOrderIds), nil } type acmeCertEntry struct { @@ -672,17 +693,20 @@ func (a *acmeState) ListEabIds(sc *storageContext) ([]string, error) { if err != nil { return nil, err } - var ids []string - for _, entry := range entries { - if strings.HasSuffix(entry, "/") { - continue - } - ids = append(ids, entry) - } + ids := filterDirEntries(entries) return ids, nil } +func (a *acmeState) ListAccountIds(sc *storageContext) ([]string, error) { + entries, err := sc.Storage.List(sc.Context, acmeAccountPrefix) + if err != nil { + return nil, fmt.Errorf("failed listing ACME account prefix directory %s: %w", acmeAccountPrefix, err) + } + + return filterDirEntries(entries), nil +} + func getAcmeSerialToAccountTrackerPath(accountId string, serial string) string { return acmeAccountPrefix + accountId + "/certs/" + normalizeSerial(serial) } diff --git a/builtin/logical/pki/backend.go b/builtin/logical/pki/backend.go index 615380a826fa..de4f8c3f77de 100644 --- a/builtin/logical/pki/backend.go +++ b/builtin/logical/pki/backend.go @@ -237,6 +237,8 @@ func Backend(conf *logical.BackendConfig) *backend { pathAcmeConfig(&b), pathAcmeEabList(&b), pathAcmeEabDelete(&b), + pathAcmeMgmtAccountList(&b), + pathAcmeMgmtAccountRead(&b), }, Secrets: []*framework.Secret{ diff --git a/builtin/logical/pki/backend_test.go b/builtin/logical/pki/backend_test.go index 5c38989f8f85..2fcd946286ed 100644 --- a/builtin/logical/pki/backend_test.go +++ b/builtin/logical/pki/backend_test.go @@ -6831,6 +6831,7 @@ func TestProperAuthing(t *testing.T) { } serial := resp.Data["serial_number"].(string) eabKid := "13b80844-e60d-42d2-b7e9-152a8e834b90" + acmeKeyId := "hrKmDYTvicHoHGVN2-3uzZV_BPGdE0W_dNaqYTtYqeo=" paths := map[string]pathAuthChecker{ "ca_chain": shouldBeUnauthedReadList, "cert/ca_chain": shouldBeUnauthedReadList, @@ -6950,6 +6951,8 @@ func TestProperAuthing(t *testing.T) { "unified-ocsp/dGVzdAo=": shouldBeUnauthedReadList, "eab/": shouldBeAuthed, "eab/" + eabKid: shouldBeAuthed, + "acme/mgmt/account/keyid/": shouldBeAuthed, + "acme/mgmt/account/keyid/" + acmeKeyId: shouldBeAuthed, } entPaths := getEntProperAuthingPaths(serial) @@ -7020,7 +7023,10 @@ func TestProperAuthing(t *testing.T) { raw_path = strings.ReplaceAll(raw_path, "{serial}", serial) } if strings.Contains(raw_path, "acme/account/") && strings.Contains(raw_path, "{kid}") { - raw_path = strings.ReplaceAll(raw_path, "{kid}", "hrKmDYTvicHoHGVN2-3uzZV_BPGdE0W_dNaqYTtYqeo=") + raw_path = strings.ReplaceAll(raw_path, "{kid}", acmeKeyId) + } + if strings.Contains(raw_path, "acme/mgmt/account/") && strings.Contains(raw_path, "{keyid}") { + raw_path = strings.ReplaceAll(raw_path, "{keyid}", acmeKeyId) } if strings.Contains(raw_path, "acme/") && strings.Contains(raw_path, "{auth_id}") { raw_path = strings.ReplaceAll(raw_path, "{auth_id}", "29da8c38-7a09-465e-b9a6-3d76802b1afd") diff --git a/builtin/logical/pki/path_acme_account_mgmt.go b/builtin/logical/pki/path_acme_account_mgmt.go new file mode 100644 index 000000000000..ef85aa5fdaab --- /dev/null +++ b/builtin/logical/pki/path_acme_account_mgmt.go @@ -0,0 +1,226 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package pki + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + "github.com/hashicorp/vault/sdk/framework" + "github.com/hashicorp/vault/sdk/logical" +) + +func pathAcmeMgmtAccountList(b *backend) *framework.Path { + return &framework.Path{ + Pattern: "acme/mgmt/account/keyid/?$", + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ListOperation: &framework.PathOperation{ + Callback: b.pathAcmeMgmtListAccounts, + DisplayAttrs: &framework.DisplayAttributes{ + OperationPrefix: operationPrefixPKI, + OperationVerb: "list-acme-account-keys", + Description: "List all ACME account key identifiers.", + }, + }, + }, + + HelpSynopsis: "List all ACME account key identifiers.", + HelpDescription: `Allows an operator to list all ACME account key identifiers.`, + } +} + +func pathAcmeMgmtAccountRead(b *backend) *framework.Path { + return &framework.Path{ + Pattern: "acme/mgmt/account/keyid/" + framework.GenericNameRegex("keyid"), + Fields: map[string]*framework.FieldSchema{ + "keyid": { + Type: framework.TypeString, + Description: "The key identifier of the account.", + Required: true, + }, + "status": { + Type: framework.TypeString, + Description: "The status of the account.", + Required: true, + AllowedValues: []interface{}{AccountStatusValid.String(), AccountStatusRevoked.String()}, + }, + }, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.pathAcmeMgmtReadAccount, + DisplayAttrs: &framework.DisplayAttributes{ + OperationPrefix: operationPrefixPKI, + OperationSuffix: "acme-key-id", + }, + }, + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.pathAcmeMgmtUpdateAccount, + DisplayAttrs: &framework.DisplayAttributes{ + OperationPrefix: operationPrefixPKI, + OperationSuffix: "acme-key-id", + }, + }, + }, + + HelpSynopsis: "Fetch the details or update the status of an ACME account by key identifier.", + HelpDescription: `Allows an operator to retrieve details of an ACME account and to update the account status.`, + } +} + +func (b *backend) pathAcmeMgmtListAccounts(ctx context.Context, r *logical.Request, d *framework.FieldData) (*logical.Response, error) { + sc := b.makeStorageContext(ctx, r.Storage) + + accountIds, err := b.GetAcmeState().ListAccountIds(sc) + if err != nil { + return nil, err + } + + return logical.ListResponse(accountIds), nil +} + +func (b *backend) pathAcmeMgmtReadAccount(ctx context.Context, r *logical.Request, d *framework.FieldData) (*logical.Response, error) { + keyId := d.Get("keyid").(string) + if len(keyId) == 0 { + return logical.ErrorResponse("keyid is required"), logical.ErrInvalidRequest + } + + sc := b.makeStorageContext(ctx, r.Storage) + as := b.GetAcmeState() + + accountEntry, err := as.LoadAccountWithoutDirEnforcement(sc, keyId) + if err != nil { + if errors.Is(err, ErrAccountDoesNotExist) { + return logical.ErrorResponse("ACME key id %s did not exist", keyId), logical.ErrNotFound + } + return nil, fmt.Errorf("failed loading ACME account id %q: %w", keyId, err) + } + + orders, err := as.LoadAccountOrders(sc, accountEntry.KeyId) + if err != nil { + return nil, fmt.Errorf("failed loading orders for account %q: %w", accountEntry.KeyId, err) + } + + orderData := make([]map[string]interface{}, 0, len(orders)) + for _, order := range orders { + orderData = append(orderData, acmeOrderToDataMap(order)) + } + + dataMap := acmeAccountToDataMap(accountEntry) + dataMap["orders"] = orderData + return &logical.Response{Data: dataMap}, nil +} + +func (b *backend) pathAcmeMgmtUpdateAccount(ctx context.Context, r *logical.Request, d *framework.FieldData) (*logical.Response, error) { + keyId := d.Get("keyid").(string) + if len(keyId) == 0 { + return logical.ErrorResponse("keyid is required"), logical.ErrInvalidRequest + } + + status, err := convertToAccountStatus(d.Get("status")) + if err != nil { + return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest + } + if status != AccountStatusValid && status != AccountStatusRevoked { + return logical.ErrorResponse("invalid status %q", status), logical.ErrInvalidRequest + } + + sc := b.makeStorageContext(ctx, r.Storage) + as := b.GetAcmeState() + + accountEntry, err := as.LoadAccountWithoutDirEnforcement(sc, keyId) + if err != nil { + if errors.Is(err, ErrAccountDoesNotExist) { + return logical.ErrorResponse("ACME key id %q did not exist", keyId), logical.ErrNotFound + } + return nil, fmt.Errorf("failed loading ACME account id %q: %w", keyId, err) + } + + if accountEntry.Status != status { + accountEntry.Status = status + + switch status { + case AccountStatusRevoked: + accountEntry.AccountRevokedDate = time.Now() + case AccountStatusValid: + accountEntry.AccountRevokedDate = time.Time{} + } + + if err := as.UpdateAccount(sc, accountEntry); err != nil { + return nil, fmt.Errorf("failed saving account %q: %w", keyId, err) + } + } + + dataMap := acmeAccountToDataMap(accountEntry) + return &logical.Response{Data: dataMap}, nil +} + +func convertToAccountStatus(status any) (ACMEAccountStatus, error) { + if status == nil { + return "", fmt.Errorf("status is required") + } + + statusStr, ok := status.(string) + if !ok { + return "", fmt.Errorf("status must be a string") + } + + switch strings.ToLower(strings.TrimSpace(statusStr)) { + case AccountStatusValid.String(): + return AccountStatusValid, nil + case AccountStatusRevoked.String(): + return AccountStatusRevoked, nil + case AccountStatusDeactivated.String(): + return AccountStatusDeactivated, nil + default: + return "", fmt.Errorf("invalid status %q", statusStr) + } +} + +func acmeAccountToDataMap(accountEntry *acmeAccount) map[string]interface{} { + revokedDate := "" + if !accountEntry.AccountRevokedDate.IsZero() { + revokedDate = accountEntry.AccountRevokedDate.Format(time.RFC3339) + } + + eab := map[string]string{} + if accountEntry.Eab != nil { + eab["eab_id"] = accountEntry.Eab.KeyID + eab["directory"] = accountEntry.Eab.AcmeDirectory + eab["created_time"] = accountEntry.Eab.CreatedOn.Format(time.RFC3339) + eab["key_type"] = accountEntry.Eab.KeyType + } + + return map[string]interface{}{ + "key_id": accountEntry.KeyId, + "status": accountEntry.Status, + "contacts": accountEntry.Contact, + "created_time": accountEntry.AccountCreatedDate.Format(time.RFC3339), + "revoked_time": revokedDate, + "directory": accountEntry.AcmeDirectory, + "eab": eab, + } +} + +func acmeOrderToDataMap(order *acmeOrder) map[string]interface{} { + identifiers := make([]string, 0, len(order.Identifiers)) + for _, identifier := range order.Identifiers { + identifiers = append(identifiers, identifier.Value) + } + var certExpiry string + if !order.CertificateExpiry.IsZero() { + certExpiry = order.CertificateExpiry.Format(time.RFC3339) + } + return map[string]interface{}{ + "order_id": order.OrderId, + "status": string(order.Status), + "identifiers": identifiers, + "cert_serial_number": strings.ReplaceAll(order.CertificateSerialNumber, "-", ":"), + "cert_expiry": certExpiry, + "order_expiry": order.Expires.Format(time.RFC3339), + } +} diff --git a/builtin/logical/pki/path_acme_eab.go b/builtin/logical/pki/path_acme_eab.go index fa026a1c1892..00b1cdaa13d8 100644 --- a/builtin/logical/pki/path_acme_eab.go +++ b/builtin/logical/pki/path_acme_eab.go @@ -173,7 +173,7 @@ a warning that it did not exist.`, } type eabType struct { - KeyID string `json:"-"` + KeyID string `json:"key-id"` KeyType string `json:"key-type"` PrivateBytes []byte `json:"private-bytes"` AcmeDirectory string `json:"acme-directory"` diff --git a/builtin/logical/pki/path_acme_test.go b/builtin/logical/pki/path_acme_test.go index ac16c36cc871..599e7d92b63a 100644 --- a/builtin/logical/pki/path_acme_test.go +++ b/builtin/logical/pki/path_acme_test.go @@ -1625,7 +1625,7 @@ func TestAcmeRevocationAcrossAccounts(t *testing.T) { acmeClient1 := getAcmeClientForCluster(t, cluster, baseAcmeURL, accountKey1) - leafKey, certs := doACMEWorkflow(t, vaultClient, acmeClient1) + _, leafKey, certs := doACMEWorkflow(t, vaultClient, acmeClient1) acmeCert, err := x509.ParseCertificate(certs[0]) require.NoError(t, err, "failed parsing acme cert bytes") @@ -1675,7 +1675,7 @@ func TestAcmeRevocationAcrossAccounts(t *testing.T) { "revocation time was not greater than 0, cert was not revoked: %v", revocationTimeInt) // Make sure we can revoke a certificate without a registered ACME account - leafKey2, certs2 := doACMEWorkflow(t, vaultClient, acmeClient1) + _, leafKey2, certs2 := doACMEWorkflow(t, vaultClient, acmeClient1) acmeClient3 := getAcmeClientForCluster(t, cluster, baseAcmeURL, nil) err = acmeClient3.RevokeCert(ctx, leafKey2, certs2[0], acme.CRLReasonUnspecified) @@ -1781,7 +1781,94 @@ func TestAcmeMaxTTL(t *testing.T) { require.Less(t, acmeCertNotAfter, maxTTL.Add(buffer), "ACME cert: %v should have been less than max TTL was %v", acmeCert.NotAfter, maxTTL) } -func doACMEWorkflow(t *testing.T, vaultClient *api.Client, acmeClient *acme.Client) (*ecdsa.PrivateKey, [][]byte) { +// TestVaultOperatorACMEDisableWorkflow validates that the Vault management API for ACME accounts works as expected. +func TestVaultOperatorACMEDisableWorkflow(t *testing.T) { + t.Parallel() + cluster, vaultClient, _ := setupAcmeBackend(t) + defer cluster.Cleanup() + testCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + // Make sure we can call list on an empty ACME mount + resp, err := vaultClient.Logical().ListWithContext(testCtx, "pki/acme/mgmt/account/keyid") + require.NoError(t, err, "failed listing acme accounts") + require.Nil(t, resp, "expected nil, nil response on list of an empty mount") + + // Make sure we get nil, nil response when trying to read a non-existent key (the API returns this for a 404) + resp, err = vaultClient.Logical().ReadWithContext(testCtx, "pki/acme/mgmt/account/keyid/doesnotexist") + require.NoError(t, err, "failed reading non-existent ACME key") + require.Nil(t, resp, "expected nil, nil response on a non-existent ACME key") + + // Make sure we get an error response when trying to write to a non-existent key, unlike read the write call returns an error. + _, err = vaultClient.Logical().WriteWithContext(testCtx, "pki/acme/mgmt/account/keyid/doesnotexist", map[string]interface{}{"status": "valid"}) + require.ErrorContains(t, err, "did not exist", "failed writing non-existent ACME key") + + baseAcmeURL := "/v1/pki/acme/" + accountKey1, err := cryptoutil.GenerateRSAKey(rand.Reader, 2048) + require.NoError(t, err, "failed creating rsa key") + + acmeClient := getAcmeClientForCluster(t, cluster, baseAcmeURL, accountKey1) + acct, _, _ := doACMEWorkflow(t, vaultClient, acmeClient) + + // ACME client KID is formatted as https://127.0.0.1:60777/v1/pki/acme/account/45f52b66-a3ed-4080-dec7-cdcff1ef189f + acmeClientKid := acmeClient.KID + + // Make sure we can call list on an empty ACME mount + resp, err = vaultClient.Logical().ListWithContext(testCtx, "pki/acme/mgmt/account/keyid") + require.NoError(t, err, "failed listing acme accounts") + require.NotNil(t, resp, "expected non-nil response on list on ACME keyid") + keysFromList := resp.Data["keys"].([]interface{}) + require.Len(t, keysFromList, 1, "expected one key in the list") + kid := keysFromList[0].(string) + require.True(t, strings.HasSuffix(string(acmeClientKid), kid), "expected key to match the one the ACME client has") + + // Make sure we can read the key we just listed + resp, err = vaultClient.Logical().ReadWithContext(testCtx, "pki/acme/mgmt/account/keyid/"+kid) + require.NoError(t, err, "failed reading ACME with account key") + require.NotNil(t, resp, "read response was nil on ACME keyid") + require.Equal(t, "acme/", resp.Data["directory"], "expected directory field in response") + require.Equal(t, kid, resp.Data["key_id"], "expected key_id field in response") + require.Equal(t, "valid", resp.Data["status"], "expected status field in response") + require.NotEmpty(t, resp.Data["created_time"], "expected created_time field in response") + require.NotEmpty(t, resp.Data["orders"], "expected orders field in response") + require.Empty(t, resp.Data["revoked_time"], "expected revoked_time field in response") + require.Empty(t, resp.Data["eab"], "expected eab field in response to be empty") + orders := resp.Data["orders"].([]interface{}) + require.Len(t, orders, 1, "expected one order in the list") + order := orders[0].(map[string]interface{}) + require.NotEmpty(t, order["order_id"], "expected order_id field in response") + require.NotEmpty(t, order["cert_expiry"], "expected cert_expiry field in response") + require.NotEmpty(t, order["cert_serial_number"], "expected cert_serial_number field in response") + + // Make sure we can update the status of the account to revoked and the revoked_time field is set + resp, err = vaultClient.Logical().WriteWithContext(testCtx, "pki/acme/mgmt/account/keyid/"+kid, map[string]interface{}{"status": "revoked"}) + require.NoError(t, err, "failed updating writing ACME with account key") + require.NotNil(t, resp, "expected non-nil response on write to ACME keyid") + require.Equal(t, "acme/", resp.Data["directory"], "expected directory field in response") + require.Equal(t, kid, resp.Data["key_id"], "expected key_id field in response") + require.Equal(t, "revoked", resp.Data["status"], "expected status field in response") + require.NotEmpty(t, resp.Data["created_time"], "expected created_time field in response") + require.NotEmpty(t, resp.Data["revoked_time"], "expected revoked_time field in response") + require.Empty(t, resp.Data["eab"], "expected eab field in response to be empty") + require.Empty(t, resp.Data["orders"], "write response should not contain orders") + + // Now make sure that we can't use the ACME account anymore + identifiers := []string{"*.localdomain"} + _, err = acmeClient.AuthorizeOrder(testCtx, []acme.AuthzID{ + {Type: "dns", Value: identifiers[0]}, + }) + require.ErrorContains(t, err, "account in status: revoked", "Requesting an order with a revoked account should have failed") + + // Switch the account back to valid and make sure we can use it again + resp, err = vaultClient.Logical().WriteWithContext(testCtx, "pki/acme/mgmt/account/keyid/"+kid, map[string]interface{}{"status": "valid"}) + require.NoError(t, err, "failed updating writing ACME with account key") + require.Empty(t, resp.Data["revoked_time"], "revoked_time should have been reset") + require.Equal(t, "valid", resp.Data["status"], "status should have been reset to valid") + + doACMEOrderWorkflow(t, vaultClient, acmeClient, acct) +} + +func doACMEWorkflow(t *testing.T, vaultClient *api.Client, acmeClient *acme.Client) (*acme.Account, *ecdsa.PrivateKey, [][]byte) { testCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() @@ -1796,6 +1883,14 @@ func doACMEWorkflow(t *testing.T, vaultClient *api.Client, acmeClient *acme.Clie } } + csrKey, certs := doACMEOrderWorkflow(t, vaultClient, acmeClient, acct) + return acct, csrKey, certs +} + +func doACMEOrderWorkflow(t *testing.T, vaultClient *api.Client, acmeClient *acme.Client, acct *acme.Account) (*ecdsa.PrivateKey, [][]byte) { + testCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + // Create an order identifiers := []string{"*.localdomain"} order, err := acmeClient.AuthorizeOrder(testCtx, []acme.AuthzID{ diff --git a/builtin/logical/pki/storage.go b/builtin/logical/pki/storage.go index 43c4853ba34c..8b21d3e7992e 100644 --- a/builtin/logical/pki/storage.go +++ b/builtin/logical/pki/storage.go @@ -829,3 +829,15 @@ func fetchRevocationInfo(sc pki_backend.StorageContext, serial string) (*revocat return revInfo, nil } + +// filterDirEntries filters out directory entries from a list of entries normally from a List operation. +func filterDirEntries(entries []string) []string { + ids := make([]string, 0, len(entries)) + for _, entry := range entries { + if strings.HasSuffix(entry, "/") { + continue + } + ids = append(ids, entry) + } + return ids +} diff --git a/changelog/29173.txt b/changelog/29173.txt new file mode 100644 index 000000000000..7ccdfbdda39b --- /dev/null +++ b/changelog/29173.txt @@ -0,0 +1,3 @@ +```release-note:improvement +secrets/pki: Add a new set of APIs that allow listing ACME account key ids, retrieving ACME account information along with the associated order and certificate information and updating an ACME account's status +``` diff --git a/website/content/api-docs/secret/pki/issuance.mdx b/website/content/api-docs/secret/pki/issuance.mdx index c0cf65bdc458..80972618c804 100644 --- a/website/content/api-docs/secret/pki/issuance.mdx +++ b/website/content/api-docs/secret/pki/issuance.mdx @@ -13,6 +13,9 @@ description: This is the API documentation for the issuance protocol support in - [Delete Unused ACME EAB Binding Tokens](#delete-unused-acme-eab-binding-tokens) - [Get ACME Configuration](#get-acme-configuration) - [Set ACME Configuration](#set-acme-configuration) + - [List ACME Account Keys](#list-acme-account-keys) + - [Get ACME Account Info](#get-acme-account-info) + - [Update ACME Account Info](#update-acme-account-info) - [EST - Certificate Issuance ](#est-certificate-issuance) - [EST Protocol Paths ](#est-protocol-paths) - [Read EST Configuration ](#read-est-configuration) @@ -109,9 +112,13 @@ fetch an EAB token and pass it to the ACME client for use on the initial registration: this binds the ACME client's registration to an authenticated Vault endpoint, but not further to the client's entity or other information. -~> Note: Enabling EAB is strongly recommended for public-facing Vault - deployments. Use of the `VAULT_DISABLE_PUBLIC_ACME` environment variable - can be used to enforce all ACME instances have EAB enabled. + + +We strongly recommend enabling EAB for public-facing Vault +deployments. Use the `VAULT_DISABLE_PUBLIC_ACME` environment +variable to force-enable EAB for all ACME instances. + + #### ACME accounts @@ -367,6 +374,148 @@ $ curl \ } ``` +### List ACME account keys + +The `ListAcmeAccountKeys` endpoint returns a list of ACME account key +identifiers. + +| Method | Path | +|:-------|:-------------------------------| +| `LIST` | `/pki/acme/mgmt/account/keyid` | + +#### Sample request + +``` +$ curl \ + --header "X-Vault-Token: ..." \ + --request LIST \ + http://127.0.0.1:8200/v1/pki/acme/mgmt/account/keyid +``` + +#### Sample response + +``` +{ + "data": { + "keys": [ + "2ea9859a-eba8-ff24-cd03-2a51639fc7d5" + ] + } +} +``` + +### Get ACME account info + +The `GetAcmeAccountInfo` endpoint returns account information, +including orders and certificate details, for the provided ACME account +key. + +| Method | Path | +|:-------|:---------------------------------------| +| `GET` | `/pki/acme/mgmt/account/keyid/:key_id` | + +#### Path parameters + +- `key_id` `(string: )` - ID of the target ACME account. + +#### Sample request + +``` +$ curl \ + --header "X-Vault-Token: ..." \ + http://127.0.0.1:8200/v1/pki/acme/mgmt/account/keyid/2ea9859a-eba8-ff24-cd03-2a51639fc7d5 +``` + +#### Sample response + +``` +{ + "data": { + "contacts": null, + "created_time": "2024-12-12T12:55:50-05:00", + "directory": "acme/", + "eab": { + "created_time": "2024-12-12T12:55:29-05:00", + "directory": "acme/", + "eab_id": "24c0673a-df53-0671-a628-e7b9c995485c", + "key_type": "hs" + }, + "key_id": "2ea9859a-eba8-ff24-cd03-2a51639fc7d5", + "orders": [ + { + "cert_expiry": "2024-12-13T17:55:28Z", + "cert_serial_number": "4a:6f:d0:f7:13:55:f7:c9:19:82:fc:34:69:67:77:2e:58:27:02:8b", + "identifiers": [ + "testing.dadgarcorp.com" + ], + "order_expiry": "2024-12-13T12:56:04-05:00", + "order_id": "90699994-8863-571c-26b0-46755e0db351", + "status": "valid" + } + ], + "revoked_time": "", + "status": "valid" + }, +} +``` + +### Update ACME account info + +The `UpdateAcmeAccountInfo` endpoint revokes or re-enables an ACME +account and returns the account details excluding order or certificate +details. + +| Method | Path | +|:-------|:---------------------------------------| +| `POST` | `/pki/acme/mgmt/account/keyid/:key_id` | + +#### Path parameters + +- `key_id` `(string: )` - ID of the target ACME account. + + +### Parameters + +- `status` `(string: )` - The new account status. Must be one of: + `revoked`, `valid`. + + + +Revoking an ACME account forbids further operations on the account +without revoking existing certificates. You must revoke any existing +certificates manually. + + + +#### Sample request + +``` +$ curl \ + --header "X-Vault-Token: ..." \ + http://127.0.0.1:8200/v1/pki/acme/mgmt/account/keyid/2ea9859a-eba8-ff24-cd03-2a51639fc7d5 +``` + +#### Sample response + +``` +{ + "data": { + "contacts": null, + "created_time": "2024-12-12T12:55:50-05:00", + "directory": "acme/", + "eab": { + "created_time": "2024-12-12T12:55:29-05:00", + "directory": "acme/", + "eab_id": "24c0673a-df53-0671-a628-e7b9c995485c", + "key_type": "hs" + }, + "key_id": "2ea9859a-eba8-ff24-cd03-2a51639fc7d5", + "revoked_time": "2024-12-12T12:59:02-05:00", + "status": "revoked" + }, +} +``` + ## EST Certificate issuance Within Vault Enterprise, support can be enabled for the From c39aa519166529f7c3b5a9aaf69fc0044039d74a Mon Sep 17 00:00:00 2001 From: John-Michael Faircloth Date: Tue, 7 Jan 2025 11:19:32 -0600 Subject: [PATCH 03/25] test: fix ce/ent diff (#29307) --- builtin/logical/database/rotation_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/logical/database/rotation_test.go b/builtin/logical/database/rotation_test.go index 84a4df9078cd..6ef586f3679a 100644 --- a/builtin/logical/database/rotation_test.go +++ b/builtin/logical/database/rotation_test.go @@ -1029,7 +1029,7 @@ func TestBackend_StaticRole_Rotation_MongoDBAtlas(t *testing.T) { // does not break on invalid values. func TestQueueTickIntervalKeyConfig(t *testing.T) { t.Parallel() - cluster, sys := getClusterPostgresDB(t) + cluster, sys := getCluster(t) defer cluster.Cleanup() values := []string{"1", "0", "-1"} From 67663c85a3e1ea71e78ea3ccb23063f1e599298e Mon Sep 17 00:00:00 2001 From: Kuba Wieczorek Date: Tue, 7 Jan 2025 17:35:46 +0000 Subject: [PATCH 04/25] [VAULT-21282] Bump raft-autopilot dependency to v0.3.0 (#29306) This pulls in a logic change in the autopilot library that helps us to avoid increasing the quorum when reconciling in presence of an unhealthy voter, when the overall number of voters is odd. --- go.mod | 2 +- go.sum | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index ec0d650ae010..3a4d2ec0d32f 100644 --- a/go.mod +++ b/go.mod @@ -122,7 +122,7 @@ require ( github.com/hashicorp/hcp-sdk-go v0.101.0 github.com/hashicorp/nomad/api v0.0.0-20240213164230-c364cb57298d github.com/hashicorp/raft v1.7.1 - github.com/hashicorp/raft-autopilot v0.2.0 + github.com/hashicorp/raft-autopilot v0.3.0 github.com/hashicorp/raft-boltdb/v2 v2.3.0 github.com/hashicorp/raft-snapshot v1.0.4 github.com/hashicorp/raft-wal v0.4.0 diff --git a/go.sum b/go.sum index e95bc9ea35bc..4c040190e612 100644 --- a/go.sum +++ b/go.sum @@ -1410,7 +1410,6 @@ github.com/hashicorp/go-gcp-common v0.9.1 h1:ZzAJNAz6OwpNUutnnUVnFERtR2fI1oZT5Z2 github.com/hashicorp/go-gcp-common v0.9.1/go.mod h1:JJ5Zvnsmrn1GkBg64+oDfSK/gJtnGyX5x2nFuSdulLw= github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= @@ -1538,11 +1537,10 @@ github.com/hashicorp/nomad/api v0.0.0-20240213164230-c364cb57298d h1:nvfutImOr3G github.com/hashicorp/nomad/api v0.0.0-20240213164230-c364cb57298d/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw= github.com/hashicorp/raft v1.0.1/go.mod h1:DVSAWItjLjTOkVbSpWQ0j0kUADIvDaCtBxIcbNAQLkI= github.com/hashicorp/raft v1.1.2-0.20191002163536-9c6bd3e3eb17/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= -github.com/hashicorp/raft v1.2.0/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= github.com/hashicorp/raft v1.7.1 h1:ytxsNx4baHsRZrhUcbt3+79zc4ly8qm7pi0393pSchY= github.com/hashicorp/raft v1.7.1/go.mod h1:hUeiEwQQR/Nk2iKDD0dkEhklSsu3jcAcqvPzPoZSAEM= -github.com/hashicorp/raft-autopilot v0.2.0 h1:2/R2RPgamgRKgNWGQioULZvjeKXQZmDuw5Ty+6c+H7Y= -github.com/hashicorp/raft-autopilot v0.2.0/go.mod h1:q6tZ8UAZ5xio2gv2JvjgmtOlh80M6ic8xQYBe2Egkg8= +github.com/hashicorp/raft-autopilot v0.3.0 h1:KhXCecBFqAMpC0i77qVfuYd937cl2dNatSA/sSNs+2s= +github.com/hashicorp/raft-autopilot v0.3.0/go.mod h1:pUBzcE8bXIm/NcFZ/xKz7O3aNOU/4T4Zkv11YqdxpUc= github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= github.com/hashicorp/raft-boltdb v0.0.0-20230125174641-2a8082862702 h1:RLKEcCuKcZ+qp2VlaaZsYZfLOmIiuJNpEi48Rl8u9cQ= github.com/hashicorp/raft-boltdb v0.0.0-20230125174641-2a8082862702/go.mod h1:nTakvJ4XYq45UXtn0DbwR4aU9ZdjlnIenpbs6Cd+FM0= @@ -1819,7 +1817,6 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -2560,7 +2557,6 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From 6e3ae793f557aa5a541a41d6e1871c32523931fa Mon Sep 17 00:00:00 2001 From: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:54:36 -0600 Subject: [PATCH 05/25] UI: LDAP Hierarchical Library names (#29293) * refactor crumbs * add subdirectory library route and hierarchical nav * update library breadcrumbs; * fix role popup menus * add getter to library model for full path * cleanup model getters * add changelog * add bug fix note * add transition after deleting * fix function definition * update adapter test * add test coverage * fix crumb typo --- changelog/29293.txt | 6 + ui/app/adapters/ldap/library.js | 27 +- ui/app/adapters/ldap/role.js | 4 +- ui/app/models/ldap/library.js | 7 + ui/app/models/ldap/role.js | 12 +- .../ldap/addon/components/page/libraries.hbs | 48 ++- .../ldap/addon/components/page/libraries.ts | 12 +- ui/lib/ldap/addon/components/page/roles.hbs | 10 +- ui/lib/ldap/addon/components/page/roles.ts | 9 +- ui/lib/ldap/addon/routes.js | 2 + .../routes/libraries/library/check-out.ts | 7 +- .../addon/routes/libraries/library/details.ts | 7 +- .../addon/routes/libraries/library/edit.ts | 6 +- .../addon/routes/libraries/subdirectory.ts | 60 +++ .../addon/routes/roles/role/credentials.ts | 7 +- .../ldap/addon/routes/roles/role/details.ts | 8 +- ui/lib/ldap/addon/routes/roles/role/edit.ts | 8 +- .../ldap/addon/routes/roles/subdirectory.ts | 9 +- .../templates/libraries/subdirectory.hbs | 11 + ui/lib/ldap/addon/utils/ldap-breadcrumbs.ts | 15 +- ui/mirage/handlers/ldap.js | 18 +- ui/mirage/scenarios/ldap.js | 2 + .../secrets/backend/ldap/libraries-test.js | 30 +- ui/tests/helpers/ldap/ldap-selectors.ts | 2 + .../components/ldap/page/libraries-test.js | 18 +- ui/tests/unit/adapters/ldap/role-test.js | 404 +++++++++++------- ui/tests/unit/utils/ldap-breadcrumbs-test.js | 7 +- ui/types/vault/models/ldap/library.d.ts | 2 + ui/types/vault/models/ldap/role.d.ts | 1 + 29 files changed, 518 insertions(+), 241 deletions(-) create mode 100644 changelog/29293.txt create mode 100644 ui/lib/ldap/addon/routes/libraries/subdirectory.ts create mode 100644 ui/lib/ldap/addon/templates/libraries/subdirectory.hbs diff --git a/changelog/29293.txt b/changelog/29293.txt new file mode 100644 index 000000000000..056b7cf96892 --- /dev/null +++ b/changelog/29293.txt @@ -0,0 +1,6 @@ +```release-note:improvement +ui: Adds navigation for LDAP hierarchical libraries +``` +```release-note:bug +ui: Fixes navigation for quick actions in LDAP roles' popup menu +``` \ No newline at end of file diff --git a/ui/app/adapters/ldap/library.js b/ui/app/adapters/ldap/library.js index 66dca68e4f81..3120b505a74c 100644 --- a/ui/app/adapters/ldap/library.js +++ b/ui/app/adapters/ldap/library.js @@ -7,23 +7,28 @@ import NamedPathAdapter from 'vault/adapters/named-path'; import { encodePath } from 'vault/utils/path-encoding-helpers'; export default class LdapLibraryAdapter extends NamedPathAdapter { - getURL(backend, name) { + // path could be the library name (full path) or just part of the path i.e. west-account/ + _getURL(backend, path) { const base = `${this.buildURL()}/${encodePath(backend)}/library`; - return name ? `${base}/${name}` : base; + return path ? `${base}/${path}` : base; } urlForUpdateRecord(name, modelName, snapshot) { - return this.getURL(snapshot.attr('backend'), name); + // when editing the name IS the full path so we can use "name" instead of "completeLibraryName" here + return this._getURL(snapshot.attr('backend'), name); } urlForDeleteRecord(name, modelName, snapshot) { - return this.getURL(snapshot.attr('backend'), name); + const { backend, completeLibraryName } = snapshot.record; + return this._getURL(backend, completeLibraryName); } query(store, type, query) { - const { backend } = query; - return this.ajax(this.getURL(backend), 'GET', { data: { list: true } }) + const { backend, path_to_library } = query; + // if we have a path_to_library then we're listing subdirectories at a hierarchical library path (i.e west-account/my-library) + const url = this._getURL(backend, path_to_library); + return this.ajax(url, 'GET', { data: { list: true } }) .then((resp) => { - return resp.data.keys.map((name) => ({ name, backend })); + return resp.data.keys.map((name) => ({ name, backend, path_to_library })); }) .catch((error) => { if (error.httpStatus === 404) { @@ -34,11 +39,11 @@ export default class LdapLibraryAdapter extends NamedPathAdapter { } queryRecord(store, type, query) { const { backend, name } = query; - return this.ajax(this.getURL(backend, name), 'GET').then((resp) => ({ ...resp.data, backend, name })); + return this.ajax(this._getURL(backend, name), 'GET').then((resp) => ({ ...resp.data, backend, name })); } fetchStatus(backend, name) { - const url = `${this.getURL(backend, name)}/status`; + const url = `${this._getURL(backend, name)}/status`; return this.ajax(url, 'GET').then((resp) => { const statuses = []; for (const key in resp.data) { @@ -53,7 +58,7 @@ export default class LdapLibraryAdapter extends NamedPathAdapter { }); } checkOutAccount(backend, name, ttl) { - const url = `${this.getURL(backend, name)}/check-out`; + const url = `${this._getURL(backend, name)}/check-out`; return this.ajax(url, 'POST', { data: { ttl } }).then((resp) => { const { lease_id, lease_duration, renewable } = resp; const { service_account_name: account, password } = resp.data; @@ -61,7 +66,7 @@ export default class LdapLibraryAdapter extends NamedPathAdapter { }); } checkInAccount(backend, name, service_account_names) { - const url = `${this.getURL(backend, name)}/check-in`; + const url = `${this._getURL(backend, name)}/check-in`; return this.ajax(url, 'POST', { data: { service_account_names } }).then((resp) => resp.data); } } diff --git a/ui/app/adapters/ldap/role.js b/ui/app/adapters/ldap/role.js index 93bb67db371a..f02d3b79112d 100644 --- a/ui/app/adapters/ldap/role.js +++ b/ui/app/adapters/ldap/role.js @@ -56,8 +56,8 @@ export default class LdapRoleAdapter extends ApplicationAdapter { } urlForDeleteRecord(id, modelName, snapshot) { - const { backend, type, name } = snapshot.record; - return this._getURL(backend, this._pathForRoleType(type), name); + const { backend, type, completeRoleName } = snapshot.record; + return this._getURL(backend, this._pathForRoleType(type), completeRoleName); } /* diff --git a/ui/app/models/ldap/library.js b/ui/app/models/ldap/library.js index e66789a6eb1f..972d91569bfe 100644 --- a/ui/app/models/ldap/library.js +++ b/ui/app/models/ldap/library.js @@ -18,6 +18,7 @@ const formFields = ['name', 'service_account_names', 'ttl', 'max_ttl', 'disable_ @withFormFields(formFields) export default class LdapLibraryModel extends Model { @attr('string') backend; // dynamic path of secret -- set on response from value passed to queryRecord + @attr('string') path_to_library; // ancestral path to the library added in the adapter (only exists for nested libraries) @attr('string', { label: 'Library name', @@ -64,6 +65,12 @@ export default class LdapLibraryModel extends Model { }) disable_check_in_enforcement; + get completeLibraryName() { + // if there is a path_to_library then the name is hierarchical + // and we must concat the ancestors with the leaf name to get the full library path + return this.path_to_library ? this.path_to_library + this.name : this.name; + } + get displayFields() { return this.formFields.filter((field) => field.name !== 'service_account_names'); } diff --git a/ui/app/models/ldap/role.js b/ui/app/models/ldap/role.js index 4c2dc68c2f1f..74d0aed000ab 100644 --- a/ui/app/models/ldap/role.js +++ b/ui/app/models/ldap/role.js @@ -163,6 +163,12 @@ export default class LdapRoleModel extends Model { }) rollback_ldif; + get completeRoleName() { + // if there is a path_to_role then the name is hierarchical + // and we must concat the ancestors with the leaf name to get the full role path + return this.path_to_role ? this.path_to_role + this.name : this.name; + } + get isStatic() { return this.type === 'static'; } @@ -224,9 +230,11 @@ export default class LdapRoleModel extends Model { } fetchCredentials() { - return this.store.adapterFor('ldap/role').fetchCredentials(this.backend, this.type, this.name); + return this.store + .adapterFor('ldap/role') + .fetchCredentials(this.backend, this.type, this.completeRoleName); } rotateStaticPassword() { - return this.store.adapterFor('ldap/role').rotateStaticPassword(this.backend, this.name); + return this.store.adapterFor('ldap/role').rotateStaticPassword(this.backend, this.completeRoleName); } } diff --git a/ui/lib/ldap/addon/components/page/libraries.hbs b/ui/lib/ldap/addon/components/page/libraries.hbs index b881dff58a4a..55f31e266038 100644 --- a/ui/lib/ldap/addon/components/page/libraries.hbs +++ b/ui/lib/ldap/addon/components/page/libraries.hbs @@ -43,10 +43,10 @@ {{else}}
{{#each this.filteredLibraries as |library|}} - + - {{library.name}} + {{library.name}} {{#if (or library.canRead library.canEdit library.canDelete)}} @@ -55,24 +55,36 @@ @icon="more-horizontal" @text="More options" @hasChevron={{false}} - data-test-popup-menu-trigger + data-test-popup-menu-trigger={{library.completeLibraryName}} /> - {{#if library.canEdit}} - Edit - {{/if}} - {{#if library.canRead}} - Details - {{/if}} - {{#if library.canDelete}} + {{#if (this.isHierarchical library.name)}} Delete + data-test-subdirectory + @route="libraries.subdirectory" + @model={{library.completeLibraryName}} + >Content + {{else}} + {{#if library.canEdit}} + Edit + {{/if}} + {{#if library.canRead}} + Details + {{/if}} + {{#if library.canDelete}} + Delete + {{/if}} {{/if}} {{/if}} diff --git a/ui/lib/ldap/addon/components/page/libraries.ts b/ui/lib/ldap/addon/components/page/libraries.ts index 06d7bb35b320..28a4fc688d24 100644 --- a/ui/lib/ldap/addon/components/page/libraries.ts +++ b/ui/lib/ldap/addon/components/page/libraries.ts @@ -14,6 +14,7 @@ import type LdapLibraryModel from 'vault/models/ldap/library'; import type SecretEngineModel from 'vault/models/secret-engine'; import type FlashMessageService from 'vault/services/flash-messages'; import type { Breadcrumb, EngineOwner } from 'vault/vault/app-types'; +import type RouterService from '@ember/routing/router-service'; interface Args { libraries: Array; @@ -24,10 +25,18 @@ interface Args { export default class LdapLibrariesPageComponent extends Component { @service declare readonly flashMessages: FlashMessageService; + @service('app-router') declare readonly router: RouterService; @tracked filterValue = ''; @tracked libraryToDelete: LdapLibraryModel | null = null; + isHierarchical = (name: string) => name.endsWith('/'); + + linkParams = (library: LdapLibraryModel) => { + const route = this.isHierarchical(library.name) ? 'libraries.subdirectory' : 'libraries.library.details'; + return [route, library.completeLibraryName]; + }; + get mountPoint(): string { const owner = getOwner(this) as EngineOwner; return owner.mountPoint; @@ -43,8 +52,9 @@ export default class LdapLibrariesPageComponent extends Component { @action async onDelete(model: LdapLibraryModel) { try { - const message = `Successfully deleted library ${model.name}.`; + const message = `Successfully deleted library ${model.completeLibraryName}.`; await model.destroyRecord(); + this.router.transitionTo('vault.cluster.secrets.backend.ldap.libraries'); this.flashMessages.success(message); } catch (error) { this.flashMessages.danger(`Error deleting library \n ${errorMessage(error)}`); diff --git a/ui/lib/ldap/addon/components/page/roles.hbs b/ui/lib/ldap/addon/components/page/roles.hbs index cf9747a5dff4..946adeb39c42 100644 --- a/ui/lib/ldap/addon/components/page/roles.hbs +++ b/ui/lib/ldap/addon/components/page/roles.hbs @@ -61,7 +61,7 @@ Content {{else}} {{#if role.canEdit}} @@ -72,7 +72,11 @@ >Edit {{/if}} {{#if role.canReadCreds}} - + Get credentials {{/if}} @@ -87,7 +91,7 @@ data-test-details @route="roles.role.details" {{! this will force the roles.role model hook to fire since we may only have a partial model loaded in the list view }} - @models={{array role.type role.name}} + @models={{array role.type role.completeRoleName}} >Details {{#if role.canDelete}} { linkParams = (role: LdapRoleModel) => { const route = this.isHierarchical(role.name) ? 'roles.subdirectory' : 'roles.role.details'; - // if there is a path_to_role we're in a subdirectory - // and must concat the ancestors with the leaf name to get the full role path - const roleName = role.path_to_role ? role.path_to_role + role.name : role.name; - return [route, role.type, roleName]; + return [route, role.type, role.completeRoleName]; }; get mountPoint(): string { @@ -61,7 +58,7 @@ export default class LdapRolesPageComponent extends Component { @action async onRotate(model: LdapRoleModel) { try { - const message = `Successfully rotated credentials for ${model.name}.`; + const message = `Successfully rotated credentials for ${model.completeRoleName}.`; await model.rotateStaticPassword(); this.flashMessages.success(message); } catch (error) { @@ -74,7 +71,7 @@ export default class LdapRolesPageComponent extends Component { @action async onDelete(model: LdapRoleModel) { try { - const message = `Successfully deleted role ${model.name}.`; + const message = `Successfully deleted role ${model.completeRoleName}.`; await model.destroyRecord(); this.pagination.clearDataset('ldap/role'); this.router.transitionTo('vault.cluster.secrets.backend.ldap.roles'); diff --git a/ui/lib/ldap/addon/routes.js b/ui/lib/ldap/addon/routes.js index 593128f47abe..f11c0ceb4cc3 100644 --- a/ui/lib/ldap/addon/routes.js +++ b/ui/lib/ldap/addon/routes.js @@ -19,6 +19,8 @@ export default buildRoutes(function () { }); this.route('libraries', function () { this.route('create'); + // wildcard route so we can traverse hierarchical libraries i.e. prod/admin/my-library + this.route('subdirectory', { path: '/subdirectory/*path_to_library' }); this.route('library', { path: '/:name' }, function () { this.route('details', function () { this.route('accounts'); diff --git a/ui/lib/ldap/addon/routes/libraries/library/check-out.ts b/ui/lib/ldap/addon/routes/libraries/library/check-out.ts index 23025f96892b..c8f71936a25f 100644 --- a/ui/lib/ldap/addon/routes/libraries/library/check-out.ts +++ b/ui/lib/ldap/addon/routes/libraries/library/check-out.ts @@ -16,6 +16,7 @@ import type Transition from '@ember/routing/transition'; import type { Breadcrumb } from 'vault/vault/app-types'; import { LdapLibraryCheckOutCredentials } from 'vault/vault/adapters/ldap/library'; import type AdapterError from 'ember-data/adapter'; // eslint-disable-line ember/use-ember-data-rfc-395-imports +import { ldapBreadcrumbs, libraryRoutes } from 'ldap/utils/ldap-breadcrumbs'; interface LdapLibraryCheckOutController extends Controller { breadcrumbs: Array; @@ -45,12 +46,14 @@ export default class LdapLibraryCheckOutRoute extends Route { transition: Transition ) { super.setupController(controller, resolvedModel, transition); - const library = this.modelFor('libraries.library') as LdapLibraryModel; + const routeParams = (childResource: string) => { + return [library.backend, childResource]; + }; controller.breadcrumbs = [ { label: library.backend, route: 'overview' }, { label: 'Libraries', route: 'libraries' }, - { label: library.name, route: 'libraries.library' }, + ...ldapBreadcrumbs(library.name, routeParams, libraryRoutes), { label: 'Check-Out' }, ]; } diff --git a/ui/lib/ldap/addon/routes/libraries/library/details.ts b/ui/lib/ldap/addon/routes/libraries/library/details.ts index 233835b602c8..e39ab6e1c346 100644 --- a/ui/lib/ldap/addon/routes/libraries/library/details.ts +++ b/ui/lib/ldap/addon/routes/libraries/library/details.ts @@ -9,6 +9,7 @@ import type LdapLibraryModel from 'vault/models/ldap/library'; import type Controller from '@ember/controller'; import type Transition from '@ember/routing/transition'; import type { Breadcrumb } from 'vault/vault/app-types'; +import { ldapBreadcrumbs, libraryRoutes } from 'ldap/utils/ldap-breadcrumbs'; interface LdapLibraryDetailsController extends Controller { breadcrumbs: Array; @@ -23,10 +24,14 @@ export default class LdapLibraryDetailsRoute extends Route { ) { super.setupController(controller, resolvedModel, transition); + const routeParams = (childResource: string) => { + return [resolvedModel.backend, childResource]; + }; + controller.breadcrumbs = [ { label: resolvedModel.backend, route: 'overview' }, { label: 'Libraries', route: 'libraries' }, - { label: resolvedModel.name }, + ...ldapBreadcrumbs(resolvedModel.name, routeParams, libraryRoutes, true), ]; } } diff --git a/ui/lib/ldap/addon/routes/libraries/library/edit.ts b/ui/lib/ldap/addon/routes/libraries/library/edit.ts index 191190cee140..5a3059ab6e41 100644 --- a/ui/lib/ldap/addon/routes/libraries/library/edit.ts +++ b/ui/lib/ldap/addon/routes/libraries/library/edit.ts @@ -9,6 +9,7 @@ import type LdapLibraryModel from 'vault/models/ldap/library'; import type Controller from '@ember/controller'; import type Transition from '@ember/routing/transition'; import type { Breadcrumb } from 'vault/vault/app-types'; +import { ldapBreadcrumbs, libraryRoutes } from 'ldap/utils/ldap-breadcrumbs'; interface LdapLibraryEditController extends Controller { breadcrumbs: Array; @@ -23,10 +24,13 @@ export default class LdapLibraryEditRoute extends Route { ) { super.setupController(controller, resolvedModel, transition); + const routeParams = (childResource: string) => { + return [resolvedModel.backend, childResource]; + }; controller.breadcrumbs = [ { label: resolvedModel.backend, route: 'overview' }, { label: 'Libraries', route: 'libraries' }, - { label: resolvedModel.name, route: 'libraries.library.details' }, + ...ldapBreadcrumbs(resolvedModel.name, routeParams, libraryRoutes), { label: 'Edit' }, ]; } diff --git a/ui/lib/ldap/addon/routes/libraries/subdirectory.ts b/ui/lib/ldap/addon/routes/libraries/subdirectory.ts new file mode 100644 index 000000000000..136bb435d529 --- /dev/null +++ b/ui/lib/ldap/addon/routes/libraries/subdirectory.ts @@ -0,0 +1,60 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Route from '@ember/routing/route'; +import { service } from '@ember/service'; +import { hash } from 'rsvp'; + +import type Store from '@ember-data/store'; +import type SecretMountPath from 'vault/services/secret-mount-path'; +import type Transition from '@ember/routing/transition'; +import type LdapLibraryModel from 'vault/models/ldap/library'; +import type SecretEngineModel from 'vault/models/secret-engine'; +import type Controller from '@ember/controller'; +import type { Breadcrumb } from 'vault/vault/app-types'; +import { ldapBreadcrumbs, libraryRoutes } from 'ldap/utils/ldap-breadcrumbs'; + +interface RouteModel { + backendModel: SecretEngineModel; + path_to_library: string; + libraries: Array; +} +interface RouteController extends Controller { + breadcrumbs: Array; + model: RouteModel; +} +interface RouteParams { + path_to_library?: string; +} + +export default class LdapLibrariesSubdirectoryRoute extends Route { + @service declare readonly store: Store; + @service declare readonly secretMountPath: SecretMountPath; + + model(params: RouteParams) { + const backendModel = this.modelFor('application') as SecretEngineModel; + const { path_to_library } = params; + return hash({ + backendModel, + path_to_library, + libraries: this.store.query('ldap/library', { backend: backendModel.id, path_to_library }), + }); + } + + setupController(controller: RouteController, resolvedModel: RouteModel, transition: Transition) { + super.setupController(controller, resolvedModel, transition); + + const routeParams = (childResource: string) => { + return [resolvedModel.backendModel.id, childResource]; + }; + + controller.breadcrumbs = [ + { label: 'Secrets', route: 'secrets', linkExternal: true }, + { label: resolvedModel.backendModel.id, route: 'overview' }, + { label: 'Libraries', route: 'libraries' }, + ...ldapBreadcrumbs(resolvedModel.path_to_library, routeParams, libraryRoutes, true), + ]; + } +} diff --git a/ui/lib/ldap/addon/routes/roles/role/credentials.ts b/ui/lib/ldap/addon/routes/roles/role/credentials.ts index a3082de8431d..70c68c813568 100644 --- a/ui/lib/ldap/addon/routes/roles/role/credentials.ts +++ b/ui/lib/ldap/addon/routes/roles/role/credentials.ts @@ -5,7 +5,7 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { ldapBreadcrumbs } from 'ldap/utils/ldap-breadcrumbs'; +import { ldapBreadcrumbs, roleRoutes } from 'ldap/utils/ldap-breadcrumbs'; import type Store from '@ember-data/store'; import type LdapRoleModel from 'vault/models/ldap/role'; @@ -58,11 +58,14 @@ export default class LdapRoleCredentialsRoute extends Route { super.setupController(controller, resolvedModel, transition); const role = this.modelFor('roles.role') as LdapRoleModel; + const routeParams = (childResource: string) => { + return [role.backend, role.type, childResource]; + }; controller.breadcrumbs = [ { label: 'Secrets', route: 'secrets', linkExternal: true }, { label: role.backend, route: 'overview' }, { label: 'Roles', route: 'roles' }, - ...ldapBreadcrumbs(role.name, role.type, role.backend), + ...ldapBreadcrumbs(role.name, routeParams, roleRoutes), { label: 'Credentials' }, ]; } diff --git a/ui/lib/ldap/addon/routes/roles/role/details.ts b/ui/lib/ldap/addon/routes/roles/role/details.ts index 0b6ee9afa490..c8c7ecb65236 100644 --- a/ui/lib/ldap/addon/routes/roles/role/details.ts +++ b/ui/lib/ldap/addon/routes/roles/role/details.ts @@ -5,7 +5,7 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { ldapBreadcrumbs } from 'ldap/utils/ldap-breadcrumbs'; +import { ldapBreadcrumbs, roleRoutes } from 'ldap/utils/ldap-breadcrumbs'; import type { Breadcrumb } from 'vault/vault/app-types'; import type Controller from '@ember/controller'; @@ -24,11 +24,15 @@ export default class LdapRolesRoleDetailsRoute extends Route { setupController(controller: RouteController, resolvedModel: LdapRoleModel, transition: Transition) { super.setupController(controller, resolvedModel, transition); + const routeParams = (childResource: string) => { + return [this.secretMountPath.currentPath, resolvedModel.type, childResource]; + }; + controller.breadcrumbs = [ { label: 'Secrets', route: 'secrets', linkExternal: true }, { label: resolvedModel.backend, route: 'overview' }, { label: 'Roles', route: 'roles' }, - ...ldapBreadcrumbs(resolvedModel.name, resolvedModel.type, this.secretMountPath.currentPath, true), + ...ldapBreadcrumbs(resolvedModel.name, routeParams, roleRoutes, true), ]; } } diff --git a/ui/lib/ldap/addon/routes/roles/role/edit.ts b/ui/lib/ldap/addon/routes/roles/role/edit.ts index c12ad20e3a3f..df383ad03cad 100644 --- a/ui/lib/ldap/addon/routes/roles/role/edit.ts +++ b/ui/lib/ldap/addon/routes/roles/role/edit.ts @@ -4,7 +4,7 @@ */ import Route from '@ember/routing/route'; -import { ldapBreadcrumbs } from 'ldap/utils/ldap-breadcrumbs'; +import { ldapBreadcrumbs, roleRoutes } from 'ldap/utils/ldap-breadcrumbs'; import type LdapRoleModel from 'vault/models/ldap/role'; import type Controller from '@ember/controller'; @@ -20,11 +20,15 @@ export default class LdapRoleEditRoute extends Route { setupController(controller: RouteController, resolvedModel: LdapRoleModel, transition: Transition) { super.setupController(controller, resolvedModel, transition); + const routeParams = (childResource: string) => { + return [resolvedModel.backend, resolvedModel.type, childResource]; + }; + controller.breadcrumbs = [ { label: 'Secrets', route: 'secrets', linkExternal: true }, { label: resolvedModel.backend, route: 'overview' }, { label: 'Roles', route: 'roles' }, - ...ldapBreadcrumbs(resolvedModel.name, resolvedModel.type, resolvedModel.backend), + ...ldapBreadcrumbs(resolvedModel.name, routeParams, roleRoutes), { label: 'Edit' }, ]; } diff --git a/ui/lib/ldap/addon/routes/roles/subdirectory.ts b/ui/lib/ldap/addon/routes/roles/subdirectory.ts index cb84d232566b..5b83c72deca4 100644 --- a/ui/lib/ldap/addon/routes/roles/subdirectory.ts +++ b/ui/lib/ldap/addon/routes/roles/subdirectory.ts @@ -5,7 +5,7 @@ import LdapRolesRoute from '../roles'; import { hash } from 'rsvp'; -import { ldapBreadcrumbs } from 'ldap/utils/ldap-breadcrumbs'; +import { ldapBreadcrumbs, roleRoutes } from 'ldap/utils/ldap-breadcrumbs'; import type { Breadcrumb } from 'vault/vault/app-types'; import type Controller from '@ember/controller'; @@ -55,11 +55,16 @@ export default class LdapRolesSubdirectoryRoute extends LdapRolesRoute { setupController(controller: RouteController, resolvedModel: RouteModel, transition: Transition) { super.setupController(controller, resolvedModel, transition); const { backendModel, roleAncestry } = resolvedModel; + + const routeParams = (childResource: string) => { + return [backendModel.id, roleAncestry.type, childResource]; + }; + const crumbs = [ { label: 'Secrets', route: 'secrets', linkExternal: true }, { label: backendModel.id, route: 'overview' }, { label: 'Roles', route: 'roles' }, - ...ldapBreadcrumbs(roleAncestry.path_to_role, roleAncestry.type, backendModel.id, true), + ...ldapBreadcrumbs(roleAncestry.path_to_role, routeParams, roleRoutes, true), ]; // must call 'set' so breadcrumbs update as we navigate through directories diff --git a/ui/lib/ldap/addon/templates/libraries/subdirectory.hbs b/ui/lib/ldap/addon/templates/libraries/subdirectory.hbs new file mode 100644 index 000000000000..0cc729408812 --- /dev/null +++ b/ui/lib/ldap/addon/templates/libraries/subdirectory.hbs @@ -0,0 +1,11 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +~}} + + \ No newline at end of file diff --git a/ui/lib/ldap/addon/utils/ldap-breadcrumbs.ts b/ui/lib/ldap/addon/utils/ldap-breadcrumbs.ts index cbc21b50564b..cdeda3a12a7f 100644 --- a/ui/lib/ldap/addon/utils/ldap-breadcrumbs.ts +++ b/ui/lib/ldap/addon/utils/ldap-breadcrumbs.ts @@ -4,10 +4,16 @@ */ import type { Breadcrumb } from 'vault/vault/app-types'; +export const roleRoutes = { details: 'roles.role.details', subdirectory: 'roles.subdirectory' }; +export const libraryRoutes = { + details: 'libraries.library.details', + subdirectory: 'libraries.subdirectory', +}; + export const ldapBreadcrumbs = ( fullPath: string | undefined, // i.e. path/to/item - roleType: string, - mountPath: string, + routeParams: (childResource: string) => string[], // array of route param strings + routes: { details: string; subdirectory: string }, lastItemCurrent = false // this array of objects can be spread anywhere within the crumbs array ): Breadcrumb[] => { if (!fullPath) return []; @@ -26,11 +32,10 @@ export const ldapBreadcrumbs = ( const segment = ancestry.slice(0, idx + 1).join('/'); const itemPath = isLast && !isDirectory ? segment : `${segment}/`; - const routeParams = [mountPath, roleType, itemPath]; return { label: name, - route: isLast && !isDirectory ? 'roles.role.details' : 'roles.subdirectory', - models: routeParams, + route: isLast && !isDirectory ? routes.details : routes.subdirectory, + models: routeParams(itemPath), }; }); }; diff --git a/ui/mirage/handlers/ldap.js b/ui/mirage/handlers/ldap.js index 47fb433de204..99aed83da6e7 100644 --- a/ui/mirage/handlers/ldap.js +++ b/ui/mirage/handlers/ldap.js @@ -46,14 +46,16 @@ export default function (server) { }; const listOrGetRecord = (schema, req, type) => { - // if the param name is admin, we want to LIST admin/ roles + const dbKey = type ? 'ldapRoles' : 'ldapLibraries'; + const query = type ? { type, name: `admin/child-${type}-role` } : { name: 'admin/test-library' }; if (req.queryParams.list) { - // passing a query with specific name is not flexible - // but we only seeded the mirage db with one hierarchical role for each type - return listRecords(schema, 'ldapRoles', { type, name: `admin/child-${type}-role` }); + // the mirage database has setup all hierarchical names to be prefixed with "admin/" + // while passing a query with specific name is not flexible, for simplicity + // we only seeded the mirage db with one hierarchical resource for each role and a library + return listRecords(schema, dbKey, query); } - // otherwise we want to view details for a specific role - return getRecord(schema, req, 'ldapRoles', type); + // otherwise we want to view details for a specific resource + return getRecord(schema, req, dbKey); }; // config @@ -77,9 +79,9 @@ export default function (server) { })); // libraries server.post('/:backend/library/:name', (schema, req) => createOrUpdateRecord(schema, req, 'ldapLibraries')); - server.get('/:backend/library/:name', (schema, req) => getRecord(schema, req, 'ldapLibraries')); + server.get('/:backend/library/*name', (schema, req) => listOrGetRecord(schema, req)); server.get('/:backend/library', (schema) => listRecords(schema, 'ldapLibraries')); - server.get('/:backend/library/:name/status', (schema) => { + server.get('/:backend/library/*name/status', (schema) => { const data = schema.db['ldapAccountStatuses'].reduce((prev, curr) => { prev[curr.account] = { available: curr.available, diff --git a/ui/mirage/scenarios/ldap.js b/ui/mirage/scenarios/ldap.js index cff48bd84df1..66df01ff37ef 100644 --- a/ui/mirage/scenarios/ldap.js +++ b/ui/mirage/scenarios/ldap.js @@ -14,6 +14,8 @@ export default function (server) { server.create('ldap-role', 'static', { name: 'my-role' }); server.create('ldap-role', 'dynamic', { name: 'my-role' }); server.create('ldap-library', { name: 'test-library' }); + // mirage handler is hardcoded to accommodate hierarchical paths starting with 'admin/' + server.create('ldap-library', { name: 'admin/test-library' }); server.create('ldap-account-status', { id: 'bob.johnson', account: 'bob.johnson', diff --git a/ui/tests/acceptance/secrets/backend/ldap/libraries-test.js b/ui/tests/acceptance/secrets/backend/ldap/libraries-test.js index fee09ad7f859..d04b3718846a 100644 --- a/ui/tests/acceptance/secrets/backend/ldap/libraries-test.js +++ b/ui/tests/acceptance/secrets/backend/ldap/libraries-test.js @@ -10,9 +10,10 @@ import { v4 as uuidv4 } from 'uuid'; import ldapMirageScenario from 'vault/mirage/scenarios/ldap'; import ldapHandlers from 'vault/mirage/handlers/ldap'; import authPage from 'vault/tests/pages/auth'; -import { click } from '@ember/test-helpers'; +import { click, currentURL } from '@ember/test-helpers'; import { isURL, visitURL } from 'vault/tests/helpers/ldap/ldap-helpers'; import { deleteEngineCmd, mountEngineCmd, runCmd } from 'vault/tests/helpers/commands'; +import { LDAP_SELECTORS } from 'vault/tests/helpers/ldap/ldap-selectors'; module('Acceptance | ldap | libraries', function (hooks) { setupApplicationTest(hooks); @@ -37,7 +38,7 @@ module('Acceptance | ldap | libraries', function (hooks) { test('it should show libraries on overview page', async function (assert) { await visitURL('overview', this.backend); - assert.dom('[data-test-libraries-count]').hasText('1'); + assert.dom('[data-test-libraries-count]').hasText('2'); }); test('it should transition to create library route on toolbar link click', async function (assert) { @@ -49,15 +50,34 @@ module('Acceptance | ldap | libraries', function (hooks) { }); test('it should transition to library details route on list item click', async function (assert) { - await click('[data-test-list-item-link] a'); - assert.true( - isURL('libraries/test-library/details/accounts', this.backend), + await click(LDAP_SELECTORS.libraryItem('test-library')); + assert.strictEqual( + currentURL(), + `/vault/secrets/${this.backend}/ldap/libraries/test-library/details/accounts`, 'Transitions to library details accounts route on list item click' ); assert.dom('[data-test-account-name]').exists({ count: 2 }, 'lists the accounts'); assert.dom('[data-test-checked-out-account]').exists({ count: 1 }, 'lists the checked out accounts'); }); + test('it should transition to library details for hierarchical list items', async function (assert) { + await click(LDAP_SELECTORS.libraryItem('admin/')); + assert.strictEqual( + currentURL(), + `/vault/secrets/${this.backend}/ldap/libraries/subdirectory/admin/`, + 'Transitions to subdirectory list view' + ); + + await click(LDAP_SELECTORS.libraryItem('admin/test-library')); + assert.strictEqual( + currentURL(), + `/vault/secrets/${this.backend}/ldap/libraries/admin%2Ftest-library/details/accounts`, + 'Transitions to child library details accounts' + ); + assert.dom('[data-test-account-name]').exists({ count: 2 }, 'lists the accounts'); + assert.dom('[data-test-checked-out-account]').exists({ count: 1 }, 'lists the checked out accounts'); + }); + test('it should transition to routes from list item action menu', async function (assert) { assert.expect(2); diff --git a/ui/tests/helpers/ldap/ldap-selectors.ts b/ui/tests/helpers/ldap/ldap-selectors.ts index 73933bcaae8c..b99b77d0dd77 100644 --- a/ui/tests/helpers/ldap/ldap-selectors.ts +++ b/ui/tests/helpers/ldap/ldap-selectors.ts @@ -5,6 +5,8 @@ export const LDAP_SELECTORS = { roleItem: (type: string, name: string) => `[data-test-role="${type} ${name}"]`, + libraryItem: (name: string) => `[data-test-library="${name}"]`, roleMenu: (type: string, name: string) => `[data-test-popup-menu-trigger="${type} ${name}"]`, + libraryMenu: (name: string) => `[data-test-popup-menu-trigger="${name}"]`, action: (action: string) => `[data-test-${action}]`, }; diff --git a/ui/tests/integration/components/ldap/page/libraries-test.js b/ui/tests/integration/components/ldap/page/libraries-test.js index b9b3be13cd3c..09cd90945db6 100644 --- a/ui/tests/integration/components/ldap/page/libraries-test.js +++ b/ui/tests/integration/components/ldap/page/libraries-test.js @@ -12,6 +12,7 @@ import hbs from 'htmlbars-inline-precompile'; import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs'; import { createSecretsEngine, generateBreadcrumbs } from 'vault/tests/helpers/ldap/ldap-helpers'; import { setRunOptions } from 'ember-a11y-testing/test-support'; +import { LDAP_SELECTORS } from 'vault/tests/helpers/ldap/ldap-selectors'; module('Integration | Component | ldap | Page::Libraries', function (hooks) { setupRenderingTest(hooks); @@ -25,7 +26,7 @@ module('Integration | Component | ldap | Page::Libraries', function (hooks) { this.backend = createSecretsEngine(this.store); this.breadcrumbs = generateBreadcrumbs(this.backend.id); - for (const name of ['foo', 'bar']) { + for (const name of ['foo', 'bar', 'foo/']) { this.store.pushPayload('ldap/library', { modelName: 'ldap/library', backend: 'ldap-test', @@ -93,12 +94,19 @@ module('Integration | Component | ldap | Page::Libraries', function (hooks) { await this.renderComponent(); assert.dom('[data-test-list-item-content] svg').hasClass('hds-icon-folder', 'List item icon renders'); - assert.dom('[data-test-library]').hasText(this.libraries[0].name, 'List item name renders'); + assert.dom('[data-test-library="foo"]').hasText('foo', 'List item name renders'); - await click('[data-test-popup-menu-trigger]'); + await click(LDAP_SELECTORS.libraryMenu('foo')); + assert.dom('[data-test-subdirectory]').doesNotExist(); assert.dom('[data-test-edit]').hasText('Edit', 'Edit link renders in menu'); assert.dom('[data-test-details]').hasText('Details', 'Details link renders in menu'); assert.dom('[data-test-delete]').hasText('Delete', 'Details link renders in menu'); + + await click(LDAP_SELECTORS.libraryMenu('foo/')); + assert.dom('[data-test-subdirectory]').hasText('Content', 'Content link renders in menu'); + assert.dom('[data-test-edit]').doesNotExist(); + assert.dom('[data-test-details]').doesNotExist(); + assert.dom('[data-test-delete]').doesNotExist(); }); test('it should filter libraries', async function (assert) { @@ -110,11 +118,11 @@ module('Integration | Component | ldap | Page::Libraries', function (hooks) { .hasText('There are no libraries matching "baz"', 'Filter message renders'); await fillIn('[data-test-filter-input]', 'foo'); - assert.dom('[data-test-list-item-content]').exists({ count: 1 }, 'List is filtered with correct results'); + assert.dom('[data-test-list-item-content]').exists({ count: 2 }, 'List is filtered with correct results'); await fillIn('[data-test-filter-input]', ''); assert .dom('[data-test-list-item-content]') - .exists({ count: 2 }, 'All libraries are displayed when filter is cleared'); + .exists({ count: 3 }, 'All libraries are displayed when filter is cleared'); }); }); diff --git a/ui/tests/unit/adapters/ldap/role-test.js b/ui/tests/unit/adapters/ldap/role-test.js index 78bda11cbabb..aa61b2f3ea2e 100644 --- a/ui/tests/unit/adapters/ldap/role-test.js +++ b/ui/tests/unit/adapters/ldap/role-test.js @@ -19,8 +19,8 @@ module('Unit | Adapter | ldap/role', function (hooks) { this.adapter = this.store.adapterFor('ldap/role'); this.path = 'role'; - this.getModel = (type) => { - const name = 'test-role'; + this.getModel = (type, roleName) => { + const name = roleName || 'test-role'; this.store.pushPayload('ldap/role', { modelName: 'ldap/role', backend: 'ldap-test', @@ -32,214 +32,296 @@ module('Unit | Adapter | ldap/role', function (hooks) { }; }); - test('it should make request to correct endpoints when listing records', async function (assert) { - assert.expect(6); + module('happy paths', function () { + test('it should make request to correct endpoints when listing records', async function (assert) { + assert.expect(6); - const assertRequest = (schema, req) => { - assert.ok(req.queryParams.list, 'list query param sent when listing roles'); - const name = req.url.includes('static-role') ? 'static-test' : 'dynamic-test'; - return { data: { keys: [name] } }; - }; - - this.server.get('/ldap-test/static-role', assertRequest); - this.server.get('/ldap-test/role', assertRequest); + const assertRequest = (schema, req) => { + assert.ok(req.queryParams.list, 'list query param sent when listing roles'); + const name = req.url.includes('static-role') ? 'static-test' : 'dynamic-test'; + return { data: { keys: [name] } }; + }; - this.models = await this.store.query('ldap/role', { backend: 'ldap-test' }); + this.server.get('/ldap-test/static-role', assertRequest); + this.server.get('/ldap-test/role', assertRequest); - const model = this.models[0]; - assert.strictEqual(this.models.length, 2, 'Returns responses from both endpoints'); - assert.strictEqual(model.backend, 'ldap-test', 'Backend value is set on records returned from query'); - // sorted alphabetically by name so dynamic should be first - assert.strictEqual(model.type, 'dynamic', 'Type value is set on records returned from query'); - assert.strictEqual(model.name, 'dynamic-test', 'Name value is set on records returned from query'); - }); + this.models = await this.store.query('ldap/role', { backend: 'ldap-test' }); - test('it should conditionally trigger info level flash message for single endpoint error from query', async function (assert) { - const flashMessages = this.owner.lookup('service:flashMessages'); - const flashSpy = sinon.spy(flashMessages, 'info'); - - this.server.get('/ldap-test/static-role', () => { - return new Response(403, {}, { errors: ['permission denied'] }); + const model = this.models[0]; + assert.strictEqual(this.models.length, 2, 'Returns responses from both endpoints'); + assert.strictEqual(model.backend, 'ldap-test', 'Backend value is set on records returned from query'); + // sorted alphabetically by name so dynamic should be first + assert.strictEqual(model.type, 'dynamic', 'Type value is set on records returned from query'); + assert.strictEqual(model.name, 'dynamic-test', 'Name value is set on records returned from query'); }); - this.server.get('/ldap-test/role', () => ({ data: { keys: ['dynamic-test'] } })); - - await this.store.query('ldap/role', { backend: 'ldap-test' }); - await this.store.query( - 'ldap/role', - { backend: 'ldap-test' }, - { adapterOptions: { showPartialError: true } } - ); - - assert.true( - flashSpy.calledOnceWith('Error fetching roles from /v1/ldap-test/static-role: permission denied'), - 'Partial error info only displays when adapter option is passed' - ); - }); - test('it should throw error for query when requests to both endpoints fail', async function (assert) { - assert.expect(2); + test('it should conditionally trigger info level flash message for single endpoint error from query', async function (assert) { + const flashMessages = this.owner.lookup('service:flashMessages'); + const flashSpy = sinon.spy(flashMessages, 'info'); - this.server.get('/ldap-test/:path', (schema, req) => { - const errors = { - 'static-role': ['permission denied'], - role: ['server error'], - }[req.params.path]; - return new Response(req.params.path === 'static-role' ? 403 : 500, {}, { errors }); - }); + this.server.get('/ldap-test/static-role', () => { + return new Response(403, {}, { errors: ['permission denied'] }); + }); + this.server.get('/ldap-test/role', () => ({ data: { keys: ['dynamic-test'] } })); - try { await this.store.query('ldap/role', { backend: 'ldap-test' }); - } catch (error) { - assert.deepEqual( - error.errors, - ['/v1/ldap-test/static-role: permission denied', '/v1/ldap-test/role: server error'], - 'Error messages is thrown with correct payload from query.' + await this.store.query( + 'ldap/role', + { backend: 'ldap-test' }, + { adapterOptions: { showPartialError: true } } ); - assert.strictEqual( - error.message, - 'Error fetching roles:', - 'Error message is thrown with correct payload from query.' - ); - } - }); - - test('it should make request to correct endpoints when querying record', async function (assert) { - assert.expect(5); - this.server.get('/ldap-test/:path/test-role', (schema, req) => { - assert.strictEqual( - req.params.path, - this.path, - 'GET request made to correct endpoint when querying record' + assert.true( + flashSpy.calledOnceWith('Error fetching roles from /v1/ldap-test/static-role: permission denied'), + 'Partial error info only displays when adapter option is passed' ); }); - for (const type of ['dynamic', 'static']) { - this.model = await this.store.queryRecord('ldap/role', { - backend: 'ldap-test', - type, - name: 'test-role', + test('it should throw error for query when requests to both endpoints fail', async function (assert) { + assert.expect(2); + + this.server.get('/ldap-test/:path', (schema, req) => { + const errors = { + 'static-role': ['permission denied'], + role: ['server error'], + }[req.params.path]; + return new Response(req.params.path === 'static-role' ? 403 : 500, {}, { errors }); }); - this.path = 'static-role'; - } - assert.strictEqual( - this.model.backend, - 'ldap-test', - 'Backend value is set on records returned from query' - ); - assert.strictEqual(this.model.type, 'static', 'Type value is set on records returned from query'); - assert.strictEqual(this.model.name, 'test-role', 'Name value is set on records returned from query'); - }); + try { + await this.store.query('ldap/role', { backend: 'ldap-test' }); + } catch (error) { + assert.deepEqual( + error.errors, + ['/v1/ldap-test/static-role: permission denied', '/v1/ldap-test/role: server error'], + 'Error messages is thrown with correct payload from query.' + ); + assert.strictEqual( + error.message, + 'Error fetching roles:', + 'Error message is thrown with correct payload from query.' + ); + } + }); - test('it should make request to correct endpoints when creating new dynamic role record', async function (assert) { - assert.expect(1); + test('it should make request to correct endpoints when querying record', async function (assert) { + assert.expect(5); + + this.server.get('/ldap-test/:path/test-role', (schema, req) => { + assert.strictEqual( + req.params.path, + this.path, + 'GET request made to correct endpoint when querying record' + ); + }); + + for (const type of ['dynamic', 'static']) { + this.model = await this.store.queryRecord('ldap/role', { + backend: 'ldap-test', + type, + name: 'test-role', + }); + this.path = 'static-role'; + } - this.server.post('/ldap-test/:path/:name', (schema, req) => { assert.strictEqual( - req.params.path, - this.path, - 'POST request made to correct endpoint when creating new record for a dynamic role' + this.model.backend, + 'ldap-test', + 'Backend value is set on records returned from query' ); + assert.strictEqual(this.model.type, 'static', 'Type value is set on records returned from query'); + assert.strictEqual(this.model.name, 'test-role', 'Name value is set on records returned from query'); }); - const getModel = (type, name) => { - return this.store.createRecord('ldap/role', { - backend: 'ldap-test', - name, - type, + test('it should make request to correct endpoints when creating new dynamic role record', async function (assert) { + assert.expect(1); + + this.server.post('/ldap-test/:path/:name', (schema, req) => { + assert.strictEqual( + req.params.path, + this.path, + 'POST request made to correct endpoint when creating new record for a dynamic role' + ); }); - }; - const model = getModel('dynamic-role', 'dynamic-role-name'); - await model.save(); - }); + const getModel = (type, name) => { + return this.store.createRecord('ldap/role', { + backend: 'ldap-test', + name, + type, + }); + }; - test('it should make request to correct endpoints when creating new static role record', async function (assert) { - assert.expect(1); + const model = getModel('dynamic-role', 'dynamic-role-name'); + await model.save(); + }); - this.server.post('/ldap-test/:path/:name', (schema, req) => { - assert.strictEqual( - req.params.path, - this.path, - 'POST request made to correct endpoint when creating new record for a static role' - ); + test('it should make request to correct endpoints when creating new static role record', async function (assert) { + assert.expect(1); + + this.server.post('/ldap-test/:path/:name', (schema, req) => { + assert.strictEqual( + req.params.path, + this.path, + 'POST request made to correct endpoint when creating new record for a static role' + ); + }); + + const getModel = (type, name) => { + return this.store.createRecord('ldap/role', { + backend: 'ldap-test', + name, + type, + }); + }; + + const model = getModel('static-role', 'static-role-name'); + await model.save(); }); - const getModel = (type, name) => { - return this.store.createRecord('ldap/role', { - backend: 'ldap-test', - name, - type, + test('it should make request to correct endpoints when updating record', async function (assert) { + assert.expect(2); + + this.server.post('/ldap-test/:path/test-role', (schema, req) => { + assert.strictEqual( + req.params.path, + this.path, + 'POST request made to correct endpoint when updating record' + ); }); - }; - const model = getModel('static-role', 'static-role-name'); - await model.save(); - }); + for (const type of ['dynamic', 'static']) { + const record = this.getModel(type); + await record.save(); + this.path = 'static-role'; + } + }); - test('it should make request to correct endpoints when updating record', async function (assert) { - assert.expect(2); + test('it should make request to correct endpoints when deleting record', async function (assert) { + assert.expect(2); - this.server.post('/ldap-test/:path/test-role', (schema, req) => { - assert.strictEqual( - req.params.path, - this.path, - 'POST request made to correct endpoint when updating record' - ); + this.server.delete('/ldap-test/:path/test-role', (schema, req) => { + assert.strictEqual( + req.params.path, + this.path, + 'DELETE request made to correct endpoint when deleting record' + ); + }); + + for (const type of ['dynamic', 'static']) { + const record = this.getModel(type); + await record.destroyRecord(); + this.path = 'static-role'; + } }); - for (const type of ['dynamic', 'static']) { - const record = this.getModel(type); - await record.save(); - this.path = 'static-role'; - } - }); + test('it should make request to correct endpoints when fetching credentials', async function (assert) { + assert.expect(2); - test('it should make request to correct endpoints when deleting record', async function (assert) { - assert.expect(2); + this.path = 'creds'; - this.server.delete('/ldap-test/:path/test-role', (schema, req) => { - assert.strictEqual( - req.params.path, - this.path, - 'DELETE request made to correct endpoint when deleting record' - ); + this.server.get('/ldap-test/:path/test-role', (schema, req) => { + assert.strictEqual( + req.params.path, + this.path, + 'GET request made to correct endpoint when fetching credentials' + ); + }); + + for (const type of ['dynamic', 'static']) { + await this.adapter.fetchCredentials('ldap-test', type, 'test-role'); + this.path = 'static-cred'; + } }); - for (const type of ['dynamic', 'static']) { - const record = this.getModel(type); - await record.destroyRecord(); - this.path = 'static-role'; - } + test('it should make request to correct endpoint when rotating static role password', async function (assert) { + assert.expect(1); + + this.server.post('/ldap-test/rotate-role/test-role', () => { + assert.ok('GET request made to correct endpoint when rotating static role password'); + }); + + await this.adapter.rotateStaticPassword('ldap-test', 'test-role'); + }); }); - test('it should make request to correct endpoints when fetching credentials', async function (assert) { - assert.expect(2); + module('hierarchical paths', function () { + test('it should make request to correct endpoint when listing hierarchical records', async function (assert) { + assert.expect(2); - this.path = 'creds'; + const staticAncestry = { path_to_role: 'static-admin/', type: 'static' }; + const dynamicAncestry = { path_to_role: 'dynamic-admin/', type: 'dynamic' }; - this.server.get('/ldap-test/:path/test-role', (schema, req) => { - assert.strictEqual( - req.params.path, - this.path, - 'GET request made to correct endpoint when fetching credentials' + this.server.get(`/ldap-test/static-role/${staticAncestry.path_to_role}`, (schema, req) => { + assert.strictEqual( + req.queryParams.list, + 'true', + `query request lists roles of type: ${staticAncestry.type}` + ); + return { data: { keys: ['my-static-role'] } }; + }); + this.server.get(`/ldap-test/role/${dynamicAncestry.path_to_role}`, (schema, req) => { + assert.strictEqual( + req.queryParams.list, + 'true', + `query request lists roles of type: ${dynamicAncestry.type}` + ); + return { data: { keys: ['my-dynamic-role'] } }; + }); + + await this.store.query( + 'ldap/role', + { backend: 'ldap-test' }, + { adapterOptions: { roleAncestry: staticAncestry } } + ); + await this.store.query( + 'ldap/role', + { backend: 'ldap-test' }, + { adapterOptions: { roleAncestry: dynamicAncestry } } ); }); for (const type of ['dynamic', 'static']) { - await this.adapter.fetchCredentials('ldap-test', type, 'test-role'); - this.path = 'static-cred'; + test(`it should make request to correct endpoint when deleting a role for type: ${type}`, async function (assert) { + assert.expect(1); + + const url = + type === 'static' + ? '/ldap-test/static-role/admin/my-static-role' + : '/ldap-test/role/admin/my-dynamic-role'; + + this.server.delete(url, () => { + assert.true(true, `DELETE request made to delete hierarchical role of type: ${type}`); + }); + + const record = this.getModel(type, `admin/my-${type}-role`); + await record.destroyRecord(); + }); + + test(`it should make request to correct endpoints when fetching credentials for type: ${type}`, async function (assert) { + assert.expect(1); + + const url = + type === 'static' + ? '/ldap-test/static-cred/admin/my-static-role' + : '/ldap-test/creds/admin/my-dynamic-role'; + + this.server.get(url, () => { + assert.true(true, `request made to fetch credentials for role type: ${type}`); + }); + + await this.adapter.fetchCredentials('ldap-test', type, `admin/my-${type}-role`); + }); } - }); - test('it should make request to correct endpoint when rotating static role password', async function (assert) { - assert.expect(1); + test('it should make request to correct endpoint when rotating static role password', async function (assert) { + assert.expect(1); - this.server.post('/ldap-test/rotate-role/test-role', () => { - assert.ok('GET request made to correct endpoint when rotating static role password'); - }); + this.server.post('/ldap-test/rotate-role/admin/test-role', () => { + assert.ok('GET request made to correct endpoint when rotating static role password'); + }); - await this.adapter.rotateStaticPassword('ldap-test', 'test-role'); + await this.adapter.rotateStaticPassword('ldap-test', 'admin/test-role'); + }); }); }); diff --git a/ui/tests/unit/utils/ldap-breadcrumbs-test.js b/ui/tests/unit/utils/ldap-breadcrumbs-test.js index 8c1318b40c7c..5ad510594b2a 100644 --- a/ui/tests/unit/utils/ldap-breadcrumbs-test.js +++ b/ui/tests/unit/utils/ldap-breadcrumbs-test.js @@ -3,15 +3,18 @@ * SPDX-License-Identifier: BUSL-1.1 */ -import { ldapBreadcrumbs } from 'ldap/utils/ldap-breadcrumbs'; +import { ldapBreadcrumbs, roleRoutes } from 'ldap/utils/ldap-breadcrumbs'; import { module, test } from 'qunit'; module('Unit | Utility | ldap breadcrumbs', function (hooks) { hooks.beforeEach(async function () { this.mountPath = 'my-engine'; this.roleType = 'static'; + const routeParams = (childResource) => { + return [this.mountPath, this.roleType, childResource]; + }; this.testCrumbs = (path, { lastItemCurrent }) => { - return ldapBreadcrumbs(path, this.roleType, this.mountPath, lastItemCurrent); + return ldapBreadcrumbs(path, routeParams, roleRoutes, lastItemCurrent); }; }); diff --git a/ui/types/vault/models/ldap/library.d.ts b/ui/types/vault/models/ldap/library.d.ts index 1def4cc4f6f1..969538ba951e 100644 --- a/ui/types/vault/models/ldap/library.d.ts +++ b/ui/types/vault/models/ldap/library.d.ts @@ -13,10 +13,12 @@ import type { export default interface LdapLibraryModel extends WithFormFieldsAndValidationsModel { backend: string; name: string; + path_to_library: string; service_account_names: string; default_ttl: number; max_ttl: number; disable_check_in_enforcement: string; + get completeLibraryName(): string; get displayFields(): Array; libraryPath: CapabilitiesModel; statusPath: CapabilitiesModel; diff --git a/ui/types/vault/models/ldap/role.d.ts b/ui/types/vault/models/ldap/role.d.ts index 6464e3686496..cb7dce38e879 100644 --- a/ui/types/vault/models/ldap/role.d.ts +++ b/ui/types/vault/models/ldap/role.d.ts @@ -20,6 +20,7 @@ export default interface LdapRoleModel extends WithFormFieldsAndValidationsModel username_template: string; creation_ldif: string; rollback_ldif: string; + get completeRoleName(): string; get isStatic(): string; get isDynamic(): string; get fieldsForType(): Array; From 035b7e6d8e1ea282de1c69f534a59252fb468eff Mon Sep 17 00:00:00 2001 From: Evan Moncuso <46458931+emoncuso@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:53:06 -0800 Subject: [PATCH 06/25] VAULT-32677 - Fix missing client count card in managed clusters (#29241) * add check for admin namespace on managed clusters * add tests for client count card in managed clusters * add changelog --- changelog/29241.txt | 3 ++ ui/app/components/dashboard/overview.hbs | 9 ++-- ui/app/components/dashboard/overview.ts | 46 +++++++++++++++++++ .../components/dashboard/overview-test.js | 44 +++++++++++++++++- 4 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 changelog/29241.txt create mode 100644 ui/app/components/dashboard/overview.ts diff --git a/changelog/29241.txt b/changelog/29241.txt new file mode 100644 index 000000000000..06699fdf1271 --- /dev/null +++ b/changelog/29241.txt @@ -0,0 +1,3 @@ +```release-note:bug +UI: Fix missing Client Count card when running as a Vault Dedicated cluster +``` diff --git a/ui/app/components/dashboard/overview.hbs b/ui/app/components/dashboard/overview.hbs index bc3abf150637..0d56a1007788 100644 --- a/ui/app/components/dashboard/overview.hbs +++ b/ui/app/components/dashboard/overview.hbs @@ -7,12 +7,14 @@
- {{#if (and @version.isEnterprise @isRootNamespace)}} + {{#if @version.isEnterprise}}
- {{#if (has-permission "clients" routeParams="activity")}} + {{#if (and (has-permission "clients" routeParams="activity") this.shouldShowClientCount)}} {{/if}} - {{#if (and (has-permission "status" routeParams="replication") (not (is-empty-value @replication)))}} + {{#if + (and (has-permission "status" routeParams="replication") (not (is-empty-value @replication)) @isRootNamespace) + }}
- {{else}}
diff --git a/ui/app/components/dashboard/overview.ts b/ui/app/components/dashboard/overview.ts new file mode 100644 index 000000000000..06dffea602f4 --- /dev/null +++ b/ui/app/components/dashboard/overview.ts @@ -0,0 +1,46 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Component from '@glimmer/component'; +import { service } from '@ember/service'; + +import type flagsService from 'vault/services/flags'; +import NamespaceService from 'vault/services/namespace'; + +export type Args = { + isRootNamespace: boolean; + replication: unknown; + secretsEngines: unknown; + vaultConfiguration: unknown; + version: { isEnterprise: boolean }; +}; + +export default class OverviewComponent extends Component { + @service declare readonly flags: flagsService; + @service declare readonly namespace: NamespaceService; + + /** + * the client count card should show in the following conditions + * Self Managed clusters that are running enterprise and showing the `root` namespace + * Managed clusters that are running enterprise and show the `admin` namespace + */ + // for self managed clusters, this is the `root` namespace + // for HVD clusters, this is the `admin` namespace + get shouldShowClientCount() { + const { version, isRootNamespace } = this.args; + const { flags, namespace } = this; + + // don't show client count if this isn't an enterprise cluster + if (!version.isEnterprise) return false; + + // HVD clusters + if (flags.isHvdManaged && namespace.currentNamespace === 'admin') return true; + + // SM clusters + if (isRootNamespace) return true; + + return false; + } +} diff --git a/ui/tests/integration/components/dashboard/overview-test.js b/ui/tests/integration/components/dashboard/overview-test.js index c64ead6d4ec7..0cb9df8474c3 100644 --- a/ui/tests/integration/components/dashboard/overview-test.js +++ b/ui/tests/integration/components/dashboard/overview-test.js @@ -15,8 +15,10 @@ module('Integration | Component | dashboard/overview', function (hooks) { setupMirage(hooks); hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); + this.flags = this.owner.lookup('service:flags'); + this.namespace = this.owner.lookup('service:namespace'); this.permissions = this.owner.lookup('service:permissions'); + this.store = this.owner.lookup('service:store'); this.version = this.owner.lookup('service:version'); this.version.version = '1.13.1+ent'; this.version.type = 'enterprise'; @@ -151,6 +153,46 @@ module('Integration | Component | dashboard/overview', function (hooks) { assert.dom(DASHBOARD.cardName('replication')).exists(); }); + test('it should show client count on enterprise in admin namespace when running a managed mode', async function (assert) { + this.permissions.exactPaths = { + 'admin/sys/internal/counters/activity': { + capabilities: ['read'], + }, + 'admin/sys/replication/status': { + capabilities: ['read'], + }, + }; + + this.version.type = 'enterprise'; + this.flags.featureFlags = ['VAULT_CLOUD_ADMIN_NAMESPACE']; + this.namespace.path = 'admin'; + this.isRootNamespace = false; + + await this.renderComponent(); + + assert.dom(DASHBOARD.cardName('client-count')).exists(); + }); + + test('it should hide client count on enterprise in any other namespace when running a managed mode', async function (assert) { + this.permissions.exactPaths = { + 'sys/internal/counters/activity': { + capabilities: ['read'], + }, + 'sys/replication/status': { + capabilities: ['read'], + }, + }; + + this.version.type = 'enterprise'; + this.flags.featureFlags = ['VAULT_CLOUD_ADMIN_NAMESPACE']; + this.namespace.path = 'groceries'; + this.isRootNamespace = false; + + await this.renderComponent(); + + assert.dom(DASHBOARD.cardName('client-count')).doesNotExist(); + }); + test('it should hide cards on enterprise in root namespace but no permission', async function (assert) { await this.renderComponent(); assert.dom(DASHBOARD.cardName('client-count')).doesNotExist(); From f18801693b6c04bb7aa471a18ebb84edd117af6c Mon Sep 17 00:00:00 2001 From: Michael Blaum <96261585+hashiblaum@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:51:15 -0500 Subject: [PATCH 07/25] Vault 27392 log ldap warning - remove from warning from response (#29134) * log ldap warnings instead of returning them to end user * add cl * code review * Update changelog/29134.txt Co-authored-by: John-Michael Faircloth * Update changelog/29134.txt Co-authored-by: John-Michael Faircloth * fix test --------- Co-authored-by: John-Michael Faircloth --- builtin/credential/ldap/backend.go | 2 -- builtin/credential/ldap/backend_test.go | 4 ++-- changelog/29134.txt | 3 +++ 3 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 changelog/29134.txt diff --git a/builtin/credential/ldap/backend.go b/builtin/credential/ldap/backend.go index 6993eb06fc00..9bdb6f567311 100644 --- a/builtin/credential/ldap/backend.go +++ b/builtin/credential/ldap/backend.go @@ -121,14 +121,12 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri if b.Logger().IsDebug() { b.Logger().Debug(errString) } - ldapResponse.AddWarning(errString) } for _, warning := range c.Warnings { if b.Logger().IsDebug() { b.Logger().Debug(string(warning)) } - ldapResponse.AddWarning(string(warning)) } var allGroups []string diff --git a/builtin/credential/ldap/backend_test.go b/builtin/credential/ldap/backend_test.go index c791cb4cf795..c1b84c82a9cd 100644 --- a/builtin/credential/ldap/backend_test.go +++ b/builtin/credential/ldap/backend_test.go @@ -1183,8 +1183,8 @@ func testAccStepLoginNoGroupDN(t *testing.T, user string, pass string) logicalte // Verifies a search without defined GroupDN returns a warning rather than failing Check: func(resp *logical.Response) error { - if len(resp.Warnings) != 1 { - return fmt.Errorf("expected a warning due to no group dn, got: %#v", resp.Warnings) + if len(resp.Warnings) != 0 { + return fmt.Errorf("expected a no warnings, got: %#v", resp.Warnings) } return logicaltest.TestCheckAuth([]string{"bar", "default"})(resp) diff --git a/changelog/29134.txt b/changelog/29134.txt new file mode 100644 index 000000000000..8fd4ca62e02f --- /dev/null +++ b/changelog/29134.txt @@ -0,0 +1,3 @@ +```release-note:change +auth/ldap: No longer return authentication warnings to client. +``` From 27bd3e9535d9a847f72f6a8f23e3a987b1d43c0e Mon Sep 17 00:00:00 2001 From: vinay-gopalan <86625824+vinay-gopalan@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:22:45 -0800 Subject: [PATCH 08/25] Add SDK helpers and Core stubs for plugins to communicate with Enterprise Rotation Manager (#29273) Co-authored-by: Robert <17119716+robmonte@users.noreply.github.com> Co-authored-by: John-Michael Faircloth --- builtin/logical/aws/backend.go | 87 + builtin/logical/aws/path_config_root.go | 111 +- builtin/logical/aws/path_config_root_test.go | 137 +- changelog/29273.txt | 3 + sdk/framework/backend.go | 19 + sdk/go.mod | 99 +- sdk/go.sum | 550 ++---- sdk/helper/automatedrotationutil/fields.go | 115 ++ sdk/logical/request.go | 5 + sdk/logical/system_view.go | 19 + sdk/plugin/grpc_system.go | 39 + sdk/plugin/pb/backend.pb.go | 1588 ++++++++++-------- sdk/plugin/pb/backend.proto | 27 + sdk/plugin/pb/backend_grpc.pb.go | 80 + sdk/rotation/rotation_job.go | 105 ++ sdk/rotation/schedule.go | 112 ++ vault/activity/activity_log.pb.go | 2 +- vault/core.go | 9 + vault/core_test.go | 2 +- vault/dynamic_system_view.go | 25 + vault/rotation_stubs_oss.go | 33 + 21 files changed, 1992 insertions(+), 1175 deletions(-) create mode 100644 changelog/29273.txt create mode 100644 sdk/helper/automatedrotationutil/fields.go create mode 100644 sdk/rotation/rotation_job.go create mode 100644 sdk/rotation/schedule.go create mode 100644 vault/rotation_stubs_oss.go diff --git a/builtin/logical/aws/backend.go b/builtin/logical/aws/backend.go index 85ff7aa994bc..2313561201a2 100644 --- a/builtin/logical/aws/backend.go +++ b/builtin/logical/aws/backend.go @@ -10,6 +10,8 @@ import ( "sync" "time" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/iam/iamiface" "github.com/aws/aws-sdk-go/service/sts/stsiface" "github.com/hashicorp/vault/sdk/framework" @@ -74,6 +76,91 @@ func Backend(_ *logical.BackendConfig) *backend { } return nil }, + RotateCredential: func(ctx context.Context, req *logical.Request) error { + // the following code is a modified version of the rotate-root method + client, err := b.clientIAM(ctx, req.Storage) + if err != nil { + return err + } + if client == nil { + return fmt.Errorf("nil IAM client") + } + + b.clientMutex.Lock() + defer b.clientMutex.Unlock() + + rawRootConfig, err := req.Storage.Get(ctx, "config/root") + if err != nil { + return err + } + if rawRootConfig == nil { + return fmt.Errorf("no configuration found for config/root") + } + var config rootConfig + if err := rawRootConfig.DecodeJSON(&config); err != nil { + return fmt.Errorf("error reading root configuration: %w", err) + } + + if config.AccessKey == "" || config.SecretKey == "" { + return fmt.Errorf("cannot call config/rotate-root when either access_key or secret_key is empty") + } + + var getUserInput iam.GetUserInput // empty input means get current user + getUserRes, err := client.GetUserWithContext(ctx, &getUserInput) + if err != nil { + return fmt.Errorf("error calling GetUser: %w", err) + } + if getUserRes == nil { + return fmt.Errorf("nil response from GetUser") + } + if getUserRes.User == nil { + return fmt.Errorf("nil user returned from GetUser") + } + if getUserRes.User.UserName == nil { + return fmt.Errorf("nil UserName returned from GetUser") + } + + createAccessKeyInput := iam.CreateAccessKeyInput{ + UserName: getUserRes.User.UserName, + } + createAccessKeyRes, err := client.CreateAccessKeyWithContext(ctx, &createAccessKeyInput) + if err != nil { + return fmt.Errorf("error calling CreateAccessKey: %w", err) + } + if createAccessKeyRes.AccessKey == nil { + return fmt.Errorf("nil response from CreateAccessKey") + } + if createAccessKeyRes.AccessKey.AccessKeyId == nil || createAccessKeyRes.AccessKey.SecretAccessKey == nil { + return fmt.Errorf("nil AccessKeyId or SecretAccessKey returned from CreateAccessKey") + } + + oldAccessKey := config.AccessKey + + config.AccessKey = *createAccessKeyRes.AccessKey.AccessKeyId + config.SecretKey = *createAccessKeyRes.AccessKey.SecretAccessKey + + newEntry, err := logical.StorageEntryJSON("config/root", config) + if err != nil { + return fmt.Errorf("error generating new config/root JSON: %w", err) + } + if err := req.Storage.Put(ctx, newEntry); err != nil { + return fmt.Errorf("error saving new config/root: %w", err) + } + + b.iamClient = nil + b.stsClient = nil + + deleteAccessKeyInput := iam.DeleteAccessKeyInput{ + AccessKeyId: aws.String(oldAccessKey), + UserName: getUserRes.User.UserName, + } + _, err = client.DeleteAccessKeyWithContext(ctx, &deleteAccessKeyInput) + if err != nil { + return fmt.Errorf("error deleting old access key: %w", err) + } + + return nil + }, BackendType: logical.TypeLogical, } diff --git a/builtin/logical/aws/path_config_root.go b/builtin/logical/aws/path_config_root.go index 84b2f92fa555..96c13244304c 100644 --- a/builtin/logical/aws/path_config_root.go +++ b/builtin/logical/aws/path_config_root.go @@ -9,13 +9,18 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/hashicorp/vault/sdk/framework" + "github.com/hashicorp/vault/sdk/helper/automatedrotationutil" "github.com/hashicorp/vault/sdk/helper/pluginidentityutil" "github.com/hashicorp/vault/sdk/helper/pluginutil" "github.com/hashicorp/vault/sdk/logical" + "github.com/hashicorp/vault/sdk/rotation" ) // A single default template that supports both the different credential types (IAM/STS) that are capped at differing length limits (64 chars/32 chars respectively) -const defaultUserNameTemplate = `{{ if (eq .Type "STS") }}{{ printf "vault-%s-%s" (unix_time) (random 20) | truncate 32 }}{{ else }}{{ printf "vault-%s-%s-%s" (printf "%s-%s" (.DisplayName) (.PolicyName) | truncate 42) (unix_time) (random 20) | truncate 64 }}{{ end }}` +const ( + defaultUserNameTemplate = `{{ if (eq .Type "STS") }}{{ printf "vault-%s-%s" (unix_time) (random 20) | truncate 32 }}{{ else }}{{ printf "vault-%s-%s-%s" (printf "%s-%s" (.DisplayName) (.PolicyName) | truncate 42) (unix_time) (random 20) | truncate 64 }}{{ end }}` + rootRotationJobName = "aws-root-creds" +) func pathConfigRoot(b *backend) *framework.Path { p := &framework.Path{ @@ -95,6 +100,7 @@ func pathConfigRoot(b *backend) *framework.Path { HelpDescription: pathConfigRootHelpDesc, } pluginidentityutil.AddPluginIdentityTokenFields(p.Fields) + automatedrotationutil.AddAutomatedRotationFields(p.Fields) return p } @@ -103,20 +109,14 @@ func (b *backend) pathConfigRootRead(ctx context.Context, req *logical.Request, b.clientMutex.RLock() defer b.clientMutex.RUnlock() - entry, err := req.Storage.Get(ctx, "config/root") + config, exists, err := getConfigFromStorage(ctx, req) if err != nil { return nil, err } - if entry == nil { + if !exists { return nil, nil } - var config rootConfig - - if err := entry.DecodeJSON(&config); err != nil { - return nil, err - } - configData := map[string]interface{}{ "access_key": config.AccessKey, "region": config.Region, @@ -131,6 +131,8 @@ func (b *backend) pathConfigRootRead(ctx context.Context, req *logical.Request, } config.PopulatePluginIdentityTokenData(configData) + config.PopulateAutomatedRotationData(configData) + return &logical.Response{ Data: configData, }, nil @@ -158,6 +160,12 @@ func (b *backend) pathConfigRootWrite(ctx context.Context, req *logical.Request, b.clientMutex.Lock() defer b.clientMutex.Unlock() + // check for existing config + previousCfg, previousCfgExists, err := getConfigFromStorage(ctx, req) + if err != nil { + return nil, err + } + rc := rootConfig{ AccessKey: data.Get("access_key").(string), SecretKey: data.Get("secret_key").(string), @@ -174,6 +182,9 @@ func (b *backend) pathConfigRootWrite(ctx context.Context, req *logical.Request, if err := rc.ParsePluginIdentityTokenFields(data); err != nil { return logical.ErrorResponse(err.Error()), nil } + if err := rc.ParseAutomatedRotationFields(data); err != nil { + return logical.ErrorResponse(err.Error()), nil + } if rc.IdentityTokenAudience != "" && rc.AccessKey != "" { return logical.ErrorResponse("only one of 'access_key' or 'identity_token_audience' can be set"), nil @@ -195,12 +206,54 @@ func (b *backend) pathConfigRootWrite(ctx context.Context, req *logical.Request, } } - entry, err := logical.StorageEntryJSON("config/root", rc) - if err != nil { - return nil, err + // Save the initial config only if it does not already exist + if !previousCfgExists { + if err := putConfigToStorage(ctx, req, &rc); err != nil { + return nil, err + } } - if err := req.Storage.Put(ctx, entry); err != nil { + // Now that the root config is set up, register the rotation job if it required + if rc.ShouldRegisterRotationJob() { + cfgReq := &rotation.RotationJobConfigureRequest{ + Name: rootRotationJobName, + MountPoint: req.MountPoint, + ReqPath: req.Path, + RotationSchedule: rc.RotationSchedule, + RotationWindow: rc.RotationWindow, + RotationPeriod: rc.RotationPeriod, + } + + rotationJob, err := rotation.ConfigureRotationJob(cfgReq) + if err != nil { + return logical.ErrorResponse("error configuring rotation job: %s", err), nil + } + + b.Logger().Debug("Registering rotation job", "mount", req.MountPoint+req.Path) + rotationID, err := b.System().RegisterRotationJob(ctx, rotationJob) + if err != nil { + return logical.ErrorResponse("error registering rotation job: %s", err), nil + } + + rc.RotationID = rotationID + } + + // Disable Automated Rotation and Deregister credentials if required + if rc.DisableAutomatedRotation { + // Ensure de-registering only occurs on updates and if + // a credential has actually been registered + if previousCfgExists && previousCfg.RotationID != "" { + err := b.System().DeregisterRotationJob(ctx, previousCfg.RotationID) + if err != nil { + return logical.ErrorResponse("error de-registering rotation job: %s", err), nil + } + + rc.RotationID = "" + } + } + + // update config entry with rotation ID + if err := putConfigToStorage(ctx, req, &rc); err != nil { return nil, err } @@ -212,8 +265,40 @@ func (b *backend) pathConfigRootWrite(ctx context.Context, req *logical.Request, return nil, nil } +func getConfigFromStorage(ctx context.Context, req *logical.Request) (*rootConfig, bool, error) { + entry, err := req.Storage.Get(ctx, "config/root") + if err != nil { + return nil, false, err + } + if entry == nil { + return nil, false, nil + } + + var config rootConfig + + if err := entry.DecodeJSON(&config); err != nil { + return nil, false, err + } + + return &config, true, nil +} + +func putConfigToStorage(ctx context.Context, req *logical.Request, rc *rootConfig) error { + entry, err := logical.StorageEntryJSON("config/root", rc) + if err != nil { + return err + } + + if err := req.Storage.Put(ctx, entry); err != nil { + return err + } + + return nil +} + type rootConfig struct { pluginidentityutil.PluginIdentityTokenParams + automatedrotationutil.AutomatedRotationParams AccessKey string `json:"access_key"` SecretKey string `json:"secret_key"` diff --git a/builtin/logical/aws/path_config_root_test.go b/builtin/logical/aws/path_config_root_test.go index 1439a8b5ce21..d0d205772b3e 100644 --- a/builtin/logical/aws/path_config_root_test.go +++ b/builtin/logical/aws/path_config_root_test.go @@ -8,9 +8,12 @@ import ( "reflect" "testing" + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/sdk/helper/automatedrotationutil" "github.com/hashicorp/vault/sdk/helper/pluginidentityutil" "github.com/hashicorp/vault/sdk/helper/pluginutil" "github.com/hashicorp/vault/sdk/logical" + "github.com/hashicorp/vault/sdk/rotation" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -25,19 +28,23 @@ func TestBackend_PathConfigRoot(t *testing.T) { } configData := map[string]interface{}{ - "access_key": "AKIAEXAMPLE", - "secret_key": "RandomData", - "region": "us-west-2", - "iam_endpoint": "https://iam.amazonaws.com", - "sts_endpoint": "https://sts.us-west-2.amazonaws.com", - "sts_region": "", - "sts_fallback_endpoints": []string{}, - "sts_fallback_regions": []string{}, - "max_retries": 10, - "username_template": defaultUserNameTemplate, - "role_arn": "", - "identity_token_audience": "", - "identity_token_ttl": int64(0), + "access_key": "AKIAEXAMPLE", + "secret_key": "RandomData", + "region": "us-west-2", + "iam_endpoint": "https://iam.amazonaws.com", + "sts_endpoint": "https://sts.us-west-2.amazonaws.com", + "sts_region": "", + "sts_fallback_endpoints": []string{}, + "sts_fallback_regions": []string{}, + "max_retries": 10, + "username_template": defaultUserNameTemplate, + "role_arn": "", + "identity_token_audience": "", + "identity_token_ttl": int64(0), + "rotation_schedule": "", + "rotation_window": 0, + "rotation_id": "", + "disable_automated_rotation": false, } configReq := &logical.Request{ @@ -62,6 +69,8 @@ func TestBackend_PathConfigRoot(t *testing.T) { } delete(configData, "secret_key") + // remove rotation_period from response for comparison with original config + delete(resp.Data, "rotation_period") require.Equal(t, configData, resp.Data) if !reflect.DeepEqual(resp.Data, configData) { t.Errorf("bad: expected to read config root as %#v, got %#v instead", configData, resp.Data) @@ -80,19 +89,23 @@ func TestBackend_PathConfigRoot_STSFallback(t *testing.T) { } configData := map[string]interface{}{ - "access_key": "AKIAEXAMPLE", - "secret_key": "RandomData", - "region": "us-west-2", - "iam_endpoint": "https://iam.amazonaws.com", - "sts_endpoint": "https://sts.us-west-2.amazonaws.com", - "sts_region": "", - "sts_fallback_endpoints": []string{"192.168.1.1", "127.0.0.1"}, - "sts_fallback_regions": []string{"my-house-1", "my-house-2"}, - "max_retries": 10, - "username_template": defaultUserNameTemplate, - "role_arn": "", - "identity_token_audience": "", - "identity_token_ttl": int64(0), + "access_key": "AKIAEXAMPLE", + "secret_key": "RandomData", + "region": "us-west-2", + "iam_endpoint": "https://iam.amazonaws.com", + "sts_endpoint": "https://sts.us-west-2.amazonaws.com", + "sts_region": "", + "sts_fallback_endpoints": []string{"192.168.1.1", "127.0.0.1"}, + "sts_fallback_regions": []string{"my-house-1", "my-house-2"}, + "max_retries": 10, + "username_template": defaultUserNameTemplate, + "role_arn": "", + "identity_token_audience": "", + "identity_token_ttl": int64(0), + "rotation_schedule": "", + "rotation_window": 0, + "rotation_id": "", + "disable_automated_rotation": false, } configReq := &logical.Request{ @@ -117,6 +130,8 @@ func TestBackend_PathConfigRoot_STSFallback(t *testing.T) { } delete(configData, "secret_key") + // remove rotation_period from response for comparison with original config + delete(resp.Data, "rotation_period") require.Equal(t, configData, resp.Data) if !reflect.DeepEqual(resp.Data, configData) { t.Errorf("bad: expected to read config root as %#v, got %#v instead", configData, resp.Data) @@ -124,19 +139,23 @@ func TestBackend_PathConfigRoot_STSFallback(t *testing.T) { // test we can handle comma separated strings, per CommaStringSlice configData = map[string]interface{}{ - "access_key": "AKIAEXAMPLE", - "secret_key": "RandomData", - "region": "us-west-2", - "iam_endpoint": "https://iam.amazonaws.com", - "sts_endpoint": "https://sts.us-west-2.amazonaws.com", - "sts_region": "", - "sts_fallback_endpoints": "1.1.1.1,8.8.8.8", - "sts_fallback_regions": "zone-1,zone-2", - "max_retries": 10, - "username_template": defaultUserNameTemplate, - "role_arn": "", - "identity_token_audience": "", - "identity_token_ttl": int64(0), + "access_key": "AKIAEXAMPLE", + "secret_key": "RandomData", + "region": "us-west-2", + "iam_endpoint": "https://iam.amazonaws.com", + "sts_endpoint": "https://sts.us-west-2.amazonaws.com", + "sts_region": "", + "sts_fallback_endpoints": "1.1.1.1,8.8.8.8", + "sts_fallback_regions": "zone-1,zone-2", + "max_retries": 10, + "username_template": defaultUserNameTemplate, + "role_arn": "", + "identity_token_audience": "", + "identity_token_ttl": int64(0), + "rotation_schedule": "", + "rotation_window": 0, + "rotation_id": "", + "disable_automated_rotation": false, } configReq = &logical.Request{ @@ -161,6 +180,8 @@ func TestBackend_PathConfigRoot_STSFallback(t *testing.T) { } delete(configData, "secret_key") + // remove rotation_period from response for comparison with original config + delete(resp.Data, "rotation_period") configData["sts_fallback_endpoints"] = []string{"1.1.1.1", "8.8.8.8"} configData["sts_fallback_regions"] = []string{"zone-1", "zone-2"} require.Equal(t, configData, resp.Data) @@ -245,6 +266,40 @@ func TestBackend_PathConfigRoot_PluginIdentityToken(t *testing.T) { assert.ErrorContains(t, resp.Error(), pluginidentityutil.ErrPluginWorkloadIdentityUnsupported.Error()) } +// TestBackend_PathConfigRoot_RegisterRootRotation tests that configuration +// and registering a root credential returns an immediate error. +func TestBackend_PathConfigRoot_RegisterRootRotation(t *testing.T) { + config := logical.TestBackendConfig() + config.StorageView = &logical.InmemStorage{} + config.System = &testSystemView{} + + nsCtx := namespace.ContextWithNamespace(context.Background(), namespace.RootNamespace) + + b := Backend(config) + if err := b.Setup(nsCtx, config); err != nil { + t.Fatal(err) + } + + configData := map[string]interface{}{ + "access_key": "access-key", + "secret_key": "secret-key", + "rotation_schedule": "*/30 * * * * *", + "rotation_window": 60, + } + + configReq := &logical.Request{ + Operation: logical.UpdateOperation, + Storage: config.StorageView, + Path: "config/root", + Data: configData, + } + + resp, err := b.HandleRequest(context.Background(), configReq) + assert.NoError(t, err) + assert.NotNil(t, resp) + assert.ErrorContains(t, resp.Error(), automatedrotationutil.ErrRotationManagerUnsupported.Error()) +} + type testSystemView struct { logical.StaticSystemView } @@ -252,3 +307,7 @@ type testSystemView struct { func (d testSystemView) GenerateIdentityToken(_ context.Context, _ *pluginutil.IdentityTokenRequest) (*pluginutil.IdentityTokenResponse, error) { return nil, pluginidentityutil.ErrPluginWorkloadIdentityUnsupported } + +func (d testSystemView) RegisterRotationJob(_ context.Context, _ *rotation.RotationJob) (string, error) { + return "", automatedrotationutil.ErrRotationManagerUnsupported +} diff --git a/changelog/29273.txt b/changelog/29273.txt new file mode 100644 index 000000000000..b491294a14b8 --- /dev/null +++ b/changelog/29273.txt @@ -0,0 +1,3 @@ +```release-note:improvement +sdk: Add helpers and CE stubs for plugins to communicate with Rotation Manager (Enterprise). +``` diff --git a/sdk/framework/backend.go b/sdk/framework/backend.go index 5ebf9deb25de..7eb3dcf106be 100644 --- a/sdk/framework/backend.go +++ b/sdk/framework/backend.go @@ -108,6 +108,10 @@ type Backend struct { // RunningVersion is the optional version that will be self-reported RunningVersion string + // RotateCredential is the callback function used by the RotationManager + // to communicate with a plugin on when to rotate a credential + RotateCredential func(context.Context, *logical.Request) error + logger log.Logger system logical.SystemView events logical.EventSender @@ -216,6 +220,8 @@ func (b *Backend) HandleRequest(ctx context.Context, req *logical.Request) (*log return b.handleRevokeRenew(ctx, req) case logical.RollbackOperation: return b.handleRollback(ctx, req) + case logical.RotationOperation: + return b.handleRotation(ctx, req) } // If the path is empty and it is a help operation, handle that. @@ -665,6 +671,19 @@ func (b *Backend) handleRollback(ctx context.Context, req *logical.Request) (*lo return resp, merr.ErrorOrNil() } +// handleRotation invokes the RotateCredential func set on the backend. +func (b *Backend) handleRotation(ctx context.Context, req *logical.Request) (*logical.Response, error) { + if b.RotateCredential == nil { + return nil, logical.ErrUnsupportedOperation + } + + err := b.RotateCredential(ctx, req) + if err != nil { + return nil, err + } + return &logical.Response{}, nil +} + func (b *Backend) handleAuthRenew(ctx context.Context, req *logical.Request) (*logical.Response, error) { if b.AuthRenew == nil { return logical.ErrorResponse("this auth type doesn't support renew"), nil diff --git a/sdk/go.mod b/sdk/go.mod index 8d306d850a4e..2e243af41329 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/vault/sdk -go 1.23.0 +go 1.23.3 require ( cloud.google.com/go/cloudsqlconn v1.4.3 @@ -8,26 +8,26 @@ require ( github.com/armon/go-radix v1.0.0 github.com/cenkalti/backoff/v3 v3.2.2 github.com/docker/docker v27.2.1+incompatible - github.com/docker/go-connections v0.4.0 + github.com/docker/go-connections v0.5.0 github.com/evanphx/json-patch/v5 v5.6.0 github.com/fatih/structs v1.1.0 - github.com/go-ldap/ldap/v3 v3.4.6 - github.com/go-test/deep v1.1.0 + github.com/go-ldap/ldap/v3 v3.4.8 + github.com/go-test/deep v1.1.1 github.com/golang/protobuf v1.5.4 github.com/golang/snappy v0.0.4 - github.com/hashicorp/cap/ldap v0.0.0-20240328153749-fcfe271d0227 + github.com/hashicorp/cap/ldap v0.0.0-20240403125925-c0418810d10e github.com/hashicorp/errwrap v1.1.0 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-immutable-radix v1.3.1 - github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 + github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.1 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.16 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-plugin v1.6.1 github.com/hashicorp/go-retryablehttp v0.7.7 github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 github.com/hashicorp/go-secure-stdlib/cryptoutil v0.1.0 - github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 + github.com/hashicorp/go-secure-stdlib/mlock v0.1.3 github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 github.com/hashicorp/go-secure-stdlib/password v0.1.1 github.com/hashicorp/go-secure-stdlib/plugincontainer v0.4.1 @@ -35,54 +35,55 @@ require ( github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3 github.com/hashicorp/go-sockaddr v1.0.6 github.com/hashicorp/go-uuid v1.0.3 - github.com/hashicorp/go-version v1.6.0 - github.com/hashicorp/golang-lru v0.5.4 + github.com/hashicorp/go-version v1.7.0 + github.com/hashicorp/golang-lru v1.0.2 github.com/hashicorp/hcl v1.0.1-vault-5 - github.com/hashicorp/vault/api v1.14.0 + github.com/hashicorp/vault v1.18.3 + github.com/hashicorp/vault/api v1.15.0 github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/mapstructure v1.5.0 github.com/pierrec/lz4 v2.6.1+incompatible + github.com/robfig/cron/v3 v3.0.1 github.com/ryanuber/go-glob v1.0.0 github.com/stretchr/testify v1.9.0 github.com/tink-crypto/tink-go/v2 v2.2.0 - go.uber.org/atomic v1.9.0 - golang.org/x/crypto v0.27.0 - golang.org/x/net v0.29.0 - golang.org/x/text v0.18.0 - google.golang.org/grpc v1.66.1 - google.golang.org/protobuf v1.34.2 + go.uber.org/atomic v1.11.0 + golang.org/x/crypto v0.31.0 + golang.org/x/net v0.31.0 + golang.org/x/text v0.21.0 + google.golang.org/grpc v1.68.0 + google.golang.org/protobuf v1.35.2 ) require ( + cloud.google.com/go/auth v0.10.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.5 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/go-jose/go-jose/v4 v4.0.1 // indirect + github.com/go-jose/go-jose/v4 v4.0.4 // indirect github.com/hashicorp/go-hmac-drbg v0.0.0-20210916214228-a6e5a68489f6 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/sys/userns v0.1.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect - golang.org/x/sync v0.8.0 // indirect ) require ( - cloud.google.com/go/compute/metadata v0.3.0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + cloud.google.com/go/compute/metadata v0.5.2 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/containerd/log v0.1.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/fatih/color v1.16.0 // indirect + github.com/fatih/color v1.17.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/frankban/quicktest v1.14.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect @@ -90,10 +91,10 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/s2a-go v0.1.7 // indirect + github.com/google/s2a-go v0.1.8 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.2 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.14.0 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -101,11 +102,11 @@ require ( github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgtype v1.14.3 // indirect github.com/jackc/pgx/v4 v4.18.3 github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531 // indirect - github.com/klauspost/compress v1.16.5 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -113,30 +114,26 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/patternmatcher v0.5.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect - github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/sys/user v0.2.0 // indirect github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect - github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect - github.com/sasha-s/go-deadlock v0.2.0 + github.com/sasha-s/go-deadlock v0.3.5 github.com/sirupsen/logrus v1.9.3 // indirect github.com/stretchr/objx v0.5.2 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect go.opentelemetry.io/otel v1.30.0 // indirect go.opentelemetry.io/otel/metric v1.30.0 // indirect go.opentelemetry.io/otel/trace v1.30.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/api v0.169.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/time v0.8.0 // indirect + google.golang.org/api v0.207.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/sdk/go.sum b/sdk/go.sum index b226fc928814..94410aaaa8b7 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -1,60 +1,34 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/auth v0.10.2 h1:oKF7rgBfSHdp/kuhXtqU/tNDr0mZqhYbEh+6SiqzkKo= +cloud.google.com/go/auth v0.10.2/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= +cloud.google.com/go/auth/oauth2adapt v0.2.5 h1:2p29+dePqsCHPP1bqDJcKj4qxRyYCcbzKpFyKGt3MTk= +cloud.google.com/go/auth/oauth2adapt v0.2.5/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= cloud.google.com/go/cloudsqlconn v1.4.3 h1:/WYFbB1NtMtoMxCbqpzzTFPDkxxlLTPme390KEGaEPc= cloud.google.com/go/cloudsqlconn v1.4.3/go.mod h1:QL3tuStVOO70txb3rs4G8j5uMfo5ztZii8K3oGD3VYA= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= -github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= @@ -69,16 +43,12 @@ github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEe github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -99,8 +69,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v27.2.1+incompatible h1:fQdiLfW7VLscyoeYEBz7/J8soYFDZV1u6VW6gJEjNMI= github.com/docker/docker v27.2.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -110,8 +80,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -120,33 +90,29 @@ github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzP github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= -github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= -github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= +github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ= +github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= -github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= +github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -155,80 +121,50 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2V github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.12.2 h1:mhN09QQW1jEWeMF74zGR81R30z4VJzjZsfkUhuHF+DA= -github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o= +github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= -github.com/hashicorp/cap/ldap v0.0.0-20240328153749-fcfe271d0227 h1:R5CMNyBNZqODw2DcGaSa2X96AgtLotXsH7aOa07zTTI= -github.com/hashicorp/cap/ldap v0.0.0-20240328153749-fcfe271d0227/go.mod h1:Ofp5fMLl1ImcwjNGu9FtEwNOdxA0LYoWpcWQE2vltuI= +github.com/hashicorp/cap/ldap v0.0.0-20240403125925-c0418810d10e h1:IakB/NhT0YtMEGqAf2tViMdBABC2cMAZn3O/mVeg2j4= +github.com/hashicorp/cap/ldap v0.0.0-20240403125925-c0418810d10e/go.mod h1:Ofp5fMLl1ImcwjNGu9FtEwNOdxA0LYoWpcWQE2vltuI= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -242,10 +178,10 @@ github.com/hashicorp/go-hmac-drbg v0.0.0-20210916214228-a6e5a68489f6/go.mod h1:y github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0 h1:pSjQfW3vPtrOTcasTUKgCTQT7OGPPTTMVRrOfU6FJD8= -github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0/go.mod h1:xvb32K2keAc+R8DSFG2IwDcydK9DBQE+fGA5fsw6hSk= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 h1:9Q2lu1YbbmiAgvYZ7Pr31RdlVonUpX+mmDL7Z7qTA2U= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.8/go.mod h1:qTCjxGig/kjuj3hk1z8pOUrzbse/GxB1tGfbrq8tGJg= +github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.1 h1:KIge4FHZEDb2/xjaWgmBheCTgRL6HV4sgTfDsH876L8= +github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.1/go.mod h1:aHO1EoFD0kBYLBedqxXgalfFT8lrWfP7kpuSoaqGjH0= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.16 h1:WZeXfD26QMWYC35at25KgE021SF9L3u9UMHK8fJAdV0= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.16/go.mod h1:ZiKZctjRTLEppuRwrttWkp71VYMbTTCkazK4xT7U/NQ= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= @@ -259,8 +195,8 @@ github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= github.com/hashicorp/go-secure-stdlib/cryptoutil v0.1.0 h1:4B46+S65WqQUlp0rX2F7TX6/p0HmUZsDD+cVzFTwztw= github.com/hashicorp/go-secure-stdlib/cryptoutil v0.1.0/go.mod h1:hH8rgXHh9fPSDPerG6WzABHsHF+9ZpLhRI1LPk4JZ8c= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.3 h1:kH3Rhiht36xhAfhuHyWJDgdXXEx9IIZhDGRk24CDhzg= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.3/go.mod h1:ov1Q0oEDjC3+A4BwsG2YdKltrmEw8sf9Pau4V9JQ4Vo= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8/go.mod h1:aiJI+PIApBRQG7FZTEBx5GiiX+HbOHilUdNxUZi4eV0= github.com/hashicorp/go-secure-stdlib/password v0.1.1 h1:6JzmBqXprakgFEHwBgdchsjaA9x3GyjdI568bXKxa60= @@ -277,19 +213,19 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/vault/api v1.14.0 h1:Ah3CFLixD5jmjusOgm8grfN9M0d+Y8fVR2SW0K6pJLU= -github.com/hashicorp/vault/api v1.14.0/go.mod h1:pV9YLxBGSz+cItFDd8Ii4G17waWOQ32zVjMWHe/cOqk= +github.com/hashicorp/vault v1.18.3 h1:M5ZIM6N3qAfcmCfcxmBtFRD2KHIO7YdrbnMILMi9lto= +github.com/hashicorp/vault v1.18.3/go.mod h1:pm/2xflI/XldISU4G0qyL8P8wmoJpuXP8FzcXP3Pkcg= +github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA= +github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -320,23 +256,39 @@ github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.14.3 h1:h6W9cPuHsRWQFTWUZMAKMgG5jSwQI0Zurzdvlx3Plus= +github.com/jackc/pgtype v1.14.3/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= @@ -346,26 +298,17 @@ github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531 h1:hgVxRoDDPtQE68PT4 github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531/go.mod h1:fqTUQpVYBvhCNIsMXGl2GE9q6z94DIP6NtFKXCSTVbg= github.com/joshlf/testutil v0.0.0-20170608050642-b5d8aa79d93d h1:J8tJzRyiddAFF65YVgxli+TyWBi0f79Sld6rJP6CBcY= github.com/joshlf/testutil v0.0.0-20170608050642-b5d8aa79d93d/go.mod h1:b+Q3v8Yrg5o15d71PSUraUzYb+jWl6wQMSBXSGS/hv0= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= -github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -393,8 +336,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/microsoft/go-mssqldb v1.5.0 h1:CgENxkwtOBNj3Jg6T1X209y2blCfTTcwuOlznd2k9fk= github.com/microsoft/go-mssqldb v1.5.0/go.mod h1:lmWsjHD8XX/Txr0f8ZqgbEZSC+BZjmEQy/Ms+rLrvho= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -413,8 +354,8 @@ github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/user v0.2.0 h1:OnpapJsRp25vkhw8TFG6OLJODNh/3rEwRWtJ3kakwRM= +github.com/moby/sys/user v0.2.0/go.mod h1:RYstrcWOJpVh+6qzUqp2bU3eaRpdiQeKGlKitaH0PM8= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -423,24 +364,23 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= -github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -451,52 +391,44 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y= -github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= +github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -520,21 +452,14 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/tink-crypto/tink-go/v2 v2.2.0 h1:L2Da0F2Udh2agtKztdr69mV/KpnY3/lGTkMgLTVIXlA= github.com/tink-crypto/tink-go/v2 v2.2.0/go.mod h1:JJ6PomeNPF3cJpfWC0lgyTES6zpJILkAX0cJNwlS3xU= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= @@ -553,8 +478,8 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -566,7 +491,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -574,155 +498,78 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -730,21 +577,22 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -752,144 +600,52 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY= -google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= +google.golang.org/api v0.207.0 h1:Fvt6IGCYjf7YLcQ+GCegeAI2QSQCfIWhRkmrMPj3JRM= +google.golang.org/api v0.207.0/go.mod h1:I53S168Yr/PNDNMi5yPnDc0/LGRZO6o7PoEbl/HY3CM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 h1:g/4bk7P6TPMkAUbUhquq98xey1slwvuVJPosdBqYJlU= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto v0.0.0-20241113202542-65e8d215514f h1:zDoHYmMzMacIdjNe+P2XiTmPsLawi/pCbSPfxt6lTfw= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f h1:C1QccEa9kUwvMgEUORqQD9S17QesQijxjZ84sO82mfo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM= -google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -898,12 +654,9 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -916,20 +669,11 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/sdk/helper/automatedrotationutil/fields.go b/sdk/helper/automatedrotationutil/fields.go new file mode 100644 index 000000000000..938aef94d79c --- /dev/null +++ b/sdk/helper/automatedrotationutil/fields.go @@ -0,0 +1,115 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package automatedrotationutil + +import ( + "errors" + "fmt" + + "github.com/hashicorp/vault/sdk/framework" +) + +var ( + ErrRotationMutuallyExclusiveFields = errors.New("mutually exclusive fields rotation_schedule and rotation_period were both specified; only one of them can be provided") + ErrRotationManagerUnsupported = errors.New("rotation manager capabilities not supported in Vault community edition") +) + +// AutomatedRotationParams contains a set of common parameters that plugins +// can use for setting automated credential rotation. +type AutomatedRotationParams struct { + // RotationSchedule is the CRON-style rotation schedule. + RotationSchedule string `json:"rotation_schedule"` + // RotationWindow specifies the amount of time in which the rotation is allowed to + // occur starting from a given rotation_schedule. + RotationWindow int `json:"rotation_window"` + // RotationPeriod is an alternate choice for simple time-to-live based rotation timing. + RotationPeriod int `json:"rotation_period"` + + // RotationID is the unique ID of the registered rotation job. + // Used by the plugin to track the rotation. + RotationID string `json:"rotation_id"` + + // If set, will deregister all registered rotation jobs from the RotationManager for plugin. + DisableAutomatedRotation bool `json:"disable_automated_rotation"` +} + +// ParseAutomatedRotationFields provides common field parsing to embedding structs. +func (p *AutomatedRotationParams) ParseAutomatedRotationFields(d *framework.FieldData) error { + rotationScheduleRaw, scheduleOk := d.GetOk("rotation_schedule") + rotationWindowRaw, windowOk := d.GetOk("rotation_window") + rotationPeriodRaw, periodOk := d.GetOk("rotation_period") + + if scheduleOk { + if periodOk { + return ErrRotationMutuallyExclusiveFields + } + p.RotationSchedule = rotationScheduleRaw.(string) + } + + if windowOk { + if periodOk { + return fmt.Errorf("rotation_window does not apply to period") + } + p.RotationWindow = rotationWindowRaw.(int) + } + + if periodOk { + p.RotationPeriod = rotationPeriodRaw.(int) + } + + if (scheduleOk && !windowOk) || (windowOk && !scheduleOk) { + return fmt.Errorf("must include both schedule and window") + } + + p.DisableAutomatedRotation = d.Get("disable_automated_rotation").(bool) + + return nil +} + +// PopulateAutomatedRotationData adds PluginIdentityTokenParams info into the given map. +func (p *AutomatedRotationParams) PopulateAutomatedRotationData(m map[string]interface{}) { + m["rotation_schedule"] = p.RotationSchedule + m["rotation_window"] = p.RotationWindow + m["rotation_period"] = p.RotationPeriod + m["rotation_id"] = p.RotationID + m["disable_automated_rotation"] = p.DisableAutomatedRotation +} + +func (p *AutomatedRotationParams) ShouldRegisterRotationJob() bool { + return p.RotationSchedule != "" || p.RotationPeriod != 0 +} + +// AddAutomatedRotationFields adds plugin identity token fields to the given +// field schema map. +func AddAutomatedRotationFields(m map[string]*framework.FieldSchema) { + fields := map[string]*framework.FieldSchema{ + "rotation_schedule": { + Type: framework.TypeString, + Description: "CRON-style string that will define the schedule on which rotations should occur. Mutually exclusive with rotation_period", + }, + "rotation_window": { + Type: framework.TypeInt, + Description: "Specifies the amount of time in which the rotation is allowed to occur starting from a given rotation_schedule", + }, + "rotation_period": { + Type: framework.TypeInt, + Description: "TTL for automatic credential rotation of the given username. Mutually exclusive with rotation_schedule", + }, + "rotation_id": { + Type: framework.TypeInt, + Description: "Unique ID of the registered rotation job", + }, + "disable_automated_rotation": { + Type: framework.TypeBool, + Description: "If set to true, will deregister all registered rotation jobs from the RotationManager for the plugin.", + }, + } + + for name, schema := range fields { + if _, ok := m[name]; ok { + panic(fmt.Sprintf("adding field %q would overwrite existing field", name)) + } + m[name] = schema + } +} diff --git a/sdk/logical/request.go b/sdk/logical/request.go index 1c360d4dbc42..bc8e731d3c53 100644 --- a/sdk/logical/request.go +++ b/sdk/logical/request.go @@ -203,6 +203,10 @@ type Request struct { // X-Vault-MFA header MFACreds MFACreds `json:"mfa_creds" structs:"mfa_creds" mapstructure:"mfa_creds" sentinel:""` + // RotationEntryName is internally used by + // the RotationManager to rotate root credentials + RotationEntryName string + // Cached token entry. This avoids another lookup in request handling when // we've already looked it up at http handling time. Note that this token // has not been "used", as in it will not properly take into account use @@ -456,6 +460,7 @@ const ( RevokeOperation Operation = "revoke" RenewOperation = "renew" RollbackOperation = "rollback" + RotationOperation = "rotate" ) type MFACreds map[string][]string diff --git a/sdk/logical/system_view.go b/sdk/logical/system_view.go index cecbc261e14e..4cfb7d8c2492 100644 --- a/sdk/logical/system_view.go +++ b/sdk/logical/system_view.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/vault/sdk/helper/license" "github.com/hashicorp/vault/sdk/helper/pluginutil" "github.com/hashicorp/vault/sdk/helper/wrapping" + "github.com/hashicorp/vault/sdk/rotation" ) // SystemView exposes system configuration information in a safe way @@ -100,6 +101,16 @@ type SystemView interface { // GenerateIdentityToken returns an identity token for the requesting plugin. GenerateIdentityToken(ctx context.Context, req *pluginutil.IdentityTokenRequest) (*pluginutil.IdentityTokenResponse, error) + + // RegisterRotationJob returns a rotation ID after registering a + // rotation job for the requesting plugin. + // NOTE: This method is intended for use only by HashiCorp Vault Enterprise plugins. + RegisterRotationJob(ctx context.Context, job *rotation.RotationJob) (rotationID string, err error) + + // DeregisterRotationJob returns any errors in de-registering a + // credential from the Rotation Manager. + // NOTE: This method is intended for use only by HashiCorp Vault Enterprise plugins. + DeregisterRotationJob(ctx context.Context, rotationID string) error } type PasswordPolicy interface { @@ -285,3 +296,11 @@ func (d StaticSystemView) GenerateIdentityToken(_ context.Context, _ *pluginutil func (d StaticSystemView) APILockShouldBlockRequest() (bool, error) { return d.APILockShouldBlockRequestVal, nil } + +func (d StaticSystemView) RegisterRotationJob(_ context.Context, _ *rotation.RotationJob) (rotationID string, err error) { + return "", errors.New("RegisterRotationJob is not implemented in StaticSystemView") +} + +func (d StaticSystemView) DeregisterRotationJob(_ context.Context, _ string) (err error) { + return errors.New("DeregisterRotationJob is not implemented in StaticSystemView") +} diff --git a/sdk/plugin/grpc_system.go b/sdk/plugin/grpc_system.go index d907e60eac6f..7f51b02921b6 100644 --- a/sdk/plugin/grpc_system.go +++ b/sdk/plugin/grpc_system.go @@ -16,9 +16,11 @@ import ( "github.com/hashicorp/vault/sdk/helper/wrapping" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/sdk/plugin/pb" + "github.com/hashicorp/vault/sdk/rotation" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/structpb" ) var errMissingSystemView = errors.New("missing system view implementation: this method should not be called during plugin Setup, but only during and after Initialize") @@ -226,6 +228,43 @@ func (s *gRPCSystemViewClient) GenerateIdentityToken(ctx context.Context, req *p }, nil } +func (s *gRPCSystemViewClient) RegisterRotationJob(ctx context.Context, job *rotation.RotationJob) (id string, retErr error) { + scheduleData := map[string]interface{}{ + "schedule": job.Schedule.Schedule, + "rotation_window": job.Schedule.RotationWindow, + "rotation_schedule": job.Schedule.RotationSchedule, + "next_vault_rotation": job.Schedule.NextVaultRotation, + } + m, err := structpb.NewValue(scheduleData) + if err != nil { + return "", err + } + req := &pb.RegisterRotationJobRequest{ + Job: &pb.RotationJobInput{ + Schedule: m.GetStructValue(), + RotationID: job.RotationID, + Path: job.Path, + Name: job.Name, + }, + } + resp, err := s.client.RegisterRotationJob(ctx, req) + if err != nil { + return "", err + } + return resp.RotationID, nil +} + +func (s *gRPCSystemViewClient) DeregisterRotationJob(ctx context.Context, rotationID string) error { + _, err := s.client.DeregisterRotationJob(ctx, &pb.DeregisterRotationJobRequest{ + RotationID: rotationID, + }) + if err != nil { + return err + } + + return nil +} + type gRPCSystemViewServer struct { pb.UnimplementedSystemViewServer diff --git a/sdk/plugin/pb/backend.pb.go b/sdk/plugin/pb/backend.pb.go index c969b61bfe02..0083b4e386d9 100644 --- a/sdk/plugin/pb/backend.pb.go +++ b/sdk/plugin/pb/backend.pb.go @@ -13,6 +13,7 @@ import ( logical "github.com/hashicorp/vault/sdk/logical" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" @@ -3211,6 +3212,214 @@ func (x *GenerateIdentityTokenResponse) GetTTL() int64 { return 0 } +type RegisterRotationJobRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Job *RotationJobInput `protobuf:"bytes,1,opt,name=job,proto3" json:"job,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterRotationJobRequest) Reset() { + *x = RegisterRotationJobRequest{} + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterRotationJobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterRotationJobRequest) ProtoMessage() {} + +func (x *RegisterRotationJobRequest) ProtoReflect() protoreflect.Message { + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[49] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterRotationJobRequest.ProtoReflect.Descriptor instead. +func (*RegisterRotationJobRequest) Descriptor() ([]byte, []int) { + return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{49} +} + +func (x *RegisterRotationJobRequest) GetJob() *RotationJobInput { + if x != nil { + return x.Job + } + return nil +} + +type RegisterRotationJobResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + RotationID string `protobuf:"bytes,1,opt,name=rotation_id,json=rotationId,proto3" json:"rotation_id,omitempty"` + Err string `protobuf:"bytes,2,opt,name=err,proto3" json:"err,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterRotationJobResponse) Reset() { + *x = RegisterRotationJobResponse{} + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterRotationJobResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterRotationJobResponse) ProtoMessage() {} + +func (x *RegisterRotationJobResponse) ProtoReflect() protoreflect.Message { + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[50] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterRotationJobResponse.ProtoReflect.Descriptor instead. +func (*RegisterRotationJobResponse) Descriptor() ([]byte, []int) { + return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{50} +} + +func (x *RegisterRotationJobResponse) GetRotationID() string { + if x != nil { + return x.RotationID + } + return "" +} + +func (x *RegisterRotationJobResponse) GetErr() string { + if x != nil { + return x.Err + } + return "" +} + +type RotationJobInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Schedule *structpb.Struct `protobuf:"bytes,1,opt,name=schedule,proto3" json:"schedule,omitempty"` + RotationID string `protobuf:"bytes,2,opt,name=rotation_id,json=rotationId,proto3" json:"rotation_id,omitempty"` + Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RotationJobInput) Reset() { + *x = RotationJobInput{} + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RotationJobInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RotationJobInput) ProtoMessage() {} + +func (x *RotationJobInput) ProtoReflect() protoreflect.Message { + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[51] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RotationJobInput.ProtoReflect.Descriptor instead. +func (*RotationJobInput) Descriptor() ([]byte, []int) { + return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{51} +} + +func (x *RotationJobInput) GetSchedule() *structpb.Struct { + if x != nil { + return x.Schedule + } + return nil +} + +func (x *RotationJobInput) GetRotationID() string { + if x != nil { + return x.RotationID + } + return "" +} + +func (x *RotationJobInput) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *RotationJobInput) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type DeregisterRotationJobRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + RotationID string `protobuf:"bytes,1,opt,name=rotation_id,json=rotationId,proto3" json:"rotation_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeregisterRotationJobRequest) Reset() { + *x = DeregisterRotationJobRequest{} + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeregisterRotationJobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeregisterRotationJobRequest) ProtoMessage() {} + +func (x *DeregisterRotationJobRequest) ProtoReflect() protoreflect.Message { + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[52] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeregisterRotationJobRequest.ProtoReflect.Descriptor instead. +func (*DeregisterRotationJobRequest) Descriptor() ([]byte, []int) { + return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{52} +} + +func (x *DeregisterRotationJobRequest) GetRotationID() string { + if x != nil { + return x.RotationID + } + return "" +} + type Connection struct { state protoimpl.MessageState `protogen:"open.v1"` // RemoteAddr is the network address that sent the request. @@ -3226,7 +3435,7 @@ type Connection struct { func (x *Connection) Reset() { *x = Connection{} - mi := &file_sdk_plugin_pb_backend_proto_msgTypes[49] + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3238,7 +3447,7 @@ func (x *Connection) String() string { func (*Connection) ProtoMessage() {} func (x *Connection) ProtoReflect() protoreflect.Message { - mi := &file_sdk_plugin_pb_backend_proto_msgTypes[49] + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3251,7 +3460,7 @@ func (x *Connection) ProtoReflect() protoreflect.Message { // Deprecated: Use Connection.ProtoReflect.Descriptor instead. func (*Connection) Descriptor() ([]byte, []int) { - return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{49} + return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{53} } func (x *Connection) GetRemoteAddr() string { @@ -3295,7 +3504,7 @@ type ConnectionState struct { func (x *ConnectionState) Reset() { *x = ConnectionState{} - mi := &file_sdk_plugin_pb_backend_proto_msgTypes[50] + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3307,7 +3516,7 @@ func (x *ConnectionState) String() string { func (*ConnectionState) ProtoMessage() {} func (x *ConnectionState) ProtoReflect() protoreflect.Message { - mi := &file_sdk_plugin_pb_backend_proto_msgTypes[50] + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3320,7 +3529,7 @@ func (x *ConnectionState) ProtoReflect() protoreflect.Message { // Deprecated: Use ConnectionState.ProtoReflect.Descriptor instead. func (*ConnectionState) Descriptor() ([]byte, []int) { - return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{50} + return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{54} } func (x *ConnectionState) GetVersion() uint32 { @@ -3416,7 +3625,7 @@ type Certificate struct { func (x *Certificate) Reset() { *x = Certificate{} - mi := &file_sdk_plugin_pb_backend_proto_msgTypes[51] + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3428,7 +3637,7 @@ func (x *Certificate) String() string { func (*Certificate) ProtoMessage() {} func (x *Certificate) ProtoReflect() protoreflect.Message { - mi := &file_sdk_plugin_pb_backend_proto_msgTypes[51] + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3441,7 +3650,7 @@ func (x *Certificate) ProtoReflect() protoreflect.Message { // Deprecated: Use Certificate.ProtoReflect.Descriptor instead. func (*Certificate) Descriptor() ([]byte, []int) { - return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{51} + return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{55} } func (x *Certificate) GetAsn1Data() []byte { @@ -3460,7 +3669,7 @@ type CertificateChain struct { func (x *CertificateChain) Reset() { *x = CertificateChain{} - mi := &file_sdk_plugin_pb_backend_proto_msgTypes[52] + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3472,7 +3681,7 @@ func (x *CertificateChain) String() string { func (*CertificateChain) ProtoMessage() {} func (x *CertificateChain) ProtoReflect() protoreflect.Message { - mi := &file_sdk_plugin_pb_backend_proto_msgTypes[52] + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3485,7 +3694,7 @@ func (x *CertificateChain) ProtoReflect() protoreflect.Message { // Deprecated: Use CertificateChain.ProtoReflect.Descriptor instead. func (*CertificateChain) Descriptor() ([]byte, []int) { - return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{52} + return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{56} } func (x *CertificateChain) GetCertificates() []*Certificate { @@ -3505,7 +3714,7 @@ type SendEventRequest struct { func (x *SendEventRequest) Reset() { *x = SendEventRequest{} - mi := &file_sdk_plugin_pb_backend_proto_msgTypes[53] + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3517,7 +3726,7 @@ func (x *SendEventRequest) String() string { func (*SendEventRequest) ProtoMessage() {} func (x *SendEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_sdk_plugin_pb_backend_proto_msgTypes[53] + mi := &file_sdk_plugin_pb_backend_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3530,7 +3739,7 @@ func (x *SendEventRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SendEventRequest.ProtoReflect.Descriptor instead. func (*SendEventRequest) Descriptor() ([]byte, []int) { - return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{53} + return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{57} } func (x *SendEventRequest) GetEventType() string { @@ -3554,565 +3763,599 @@ var file_sdk_plugin_pb_backend_proto_rawDesc = []byte{ 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70, 0x62, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x17, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x73, 0x64, 0x6b, - 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, - 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x20, 0x0a, 0x06, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x5b, 0x0a, 0x0a, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x72, - 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, 0x72, - 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x72, 0x72, 0x5f, 0x6d, 0x73, 0x67, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x19, - 0x0a, 0x08, 0x65, 0x72, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x07, 0x65, 0x72, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x80, 0x02, 0x0a, 0x05, 0x50, 0x61, - 0x74, 0x68, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x75, 0x6e, 0x61, 0x75, 0x74, - 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0f, 0x75, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x53, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x61, 0x6c, 0x5f, 0x77, - 0x72, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0f, 0x73, 0x65, 0x61, 0x6c, 0x57, 0x72, 0x61, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x77, - 0x61, 0x72, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x15, 0x77, 0x72, 0x69, 0x74, 0x65, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, - 0x64, 0x65, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x69, - 0x6e, 0x61, 0x72, 0x79, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x62, 0x69, 0x6e, 0x61, - 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x22, 0xbf, 0x06, 0x0a, - 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x12, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, - 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, - 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x08, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, - 0x12, 0x32, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x64, - 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, - 0x0a, 0x0b, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, - 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, - 0x0a, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x30, 0x0a, 0x09, 0x77, 0x72, 0x61, 0x70, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x72, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x77, - 0x72, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, - 0x67, 0x5f, 0x75, 0x73, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x18, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, - 0x6e, 0x67, 0x55, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6f, 0x76, - 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x0f, - 0x75, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, - 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x46, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe6, - 0x05, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x35, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x73, 0x65, - 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, - 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x0c, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, - 0x0a, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, - 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, - 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, - 0x65, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x12, 0x19, 0x0a, - 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x07, 0x6e, 0x75, 0x6d, 0x55, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x33, 0x0a, 0x0d, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, + 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x17, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x73, 0x64, 0x6b, 0x2f, 0x6c, + 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, + 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x20, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x5b, 0x0a, 0x0a, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x72, 0x72, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, 0x72, 0x72, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x72, 0x72, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x19, 0x0a, 0x08, + 0x65, 0x72, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, + 0x65, 0x72, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x80, 0x02, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x68, + 0x73, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x75, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, + 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, + 0x75, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, + 0x23, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x61, 0x6c, 0x5f, 0x77, 0x72, 0x61, + 0x70, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0f, 0x73, 0x65, 0x61, 0x6c, 0x57, 0x72, 0x61, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x12, 0x36, 0x0a, 0x17, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x15, 0x77, 0x72, 0x69, 0x74, 0x65, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, + 0x64, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x69, 0x6e, 0x61, + 0x72, 0x79, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, + 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x07, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x22, 0xbf, 0x06, 0x0a, 0x07, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x12, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x12, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x06, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, + 0x62, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x12, 0x1c, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, + 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x12, 0x32, + 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, + 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0e, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x6f, 0x72, 0x12, 0x30, 0x0a, 0x09, 0x77, 0x72, 0x61, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x57, 0x72, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x77, 0x72, 0x61, + 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x5f, + 0x75, 0x73, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x18, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, + 0x55, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, + 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, + 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6f, 0x76, 0x65, 0x72, + 0x72, 0x69, 0x64, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x75, 0x6e, + 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x13, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x46, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe6, 0x05, 0x0a, + 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x35, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, + 0x62, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0c, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, + 0x12, 0x32, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6e, + 0x75, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6e, + 0x75, 0x6d, 0x55, 0x73, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x41, 0x6c, 0x69, - 0x61, 0x73, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, - 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x73, 0x18, - 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x69, 0x64, 0x72, - 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x69, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x0f, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x10, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, - 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0e, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x4d, 0x61, 0x78, 0x54, 0x74, 0x6c, 0x12, - 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, - 0x0a, 0x11, 0x6e, 0x6f, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x44, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xca, 0x06, 0x0a, 0x0a, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2c, 0x0a, 0x04, 0x6d, 0x65, - 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, - 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6e, - 0x75, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6e, - 0x75, 0x6d, 0x55, 0x73, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, - 0x74, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x28, 0x0a, - 0x10, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x74, - 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, - 0x74, 0x4d, 0x61, 0x78, 0x54, 0x74, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, - 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x65, 0x72, - 0x69, 0x6f, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, - 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x73, 0x18, - 0x0f, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x69, 0x64, 0x72, - 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x62, 0x62, 0x79, 0x68, 0x6f, 0x6c, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x75, 0x62, 0x62, - 0x79, 0x68, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x12, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x45, 0x0a, 0x0d, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x13, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, - 0x74, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, 0x5f, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, - 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x6e, 0x6f, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, - 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x1a, 0x3f, 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, - 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0xaf, 0x01, 0x0a, 0x0c, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x6e, 0x65, 0x77, - 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x6e, 0x65, - 0x77, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x09, 0x69, 0x73, 0x73, 0x75, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x4d, 0x61, 0x78, 0x54, 0x54, 0x4c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, - 0x4d, 0x61, 0x78, 0x54, 0x54, 0x4c, 0x22, 0x7f, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x12, 0x35, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x65, 0x61, - 0x73, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0c, 0x6c, 0x65, 0x61, 0x73, 0x65, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x12, 0x19, 0x0a, 0x08, - 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x64, 0x22, 0xe7, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x74, 0x68, - 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, - 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, - 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x73, 0x12, 0x31, 0x0a, 0x09, 0x77, 0x72, 0x61, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x77, 0x72, 0x61, - 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x33, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x1a, 0x46, 0x0a, 0x0c, 0x48, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, - 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0xc8, 0x02, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, - 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, - 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x77, - 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x2a, 0x0a, 0x11, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, - 0x64, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x12, - 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x61, 0x6c, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x61, 0x6c, 0x57, 0x72, 0x61, 0x70, 0x22, 0x58, 0x0a, 0x0f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x72, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, - 0x4c, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x61, - 0x6c, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, - 0x61, 0x6c, 0x57, 0x72, 0x61, 0x70, 0x22, 0x59, 0x0a, 0x11, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x07, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x62, - 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0x60, 0x0a, 0x12, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x28, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x20, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x03, - 0x65, 0x72, 0x72, 0x22, 0x10, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x41, 0x72, 0x67, 0x73, 0x22, 0x33, 0x0a, 0x0f, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, - 0x69, 0x7a, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x34, 0x0a, 0x11, 0x53, 0x70, - 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, - 0x1f, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, - 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, - 0x22, 0x60, 0x0a, 0x18, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, - 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x0a, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x07, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, - 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x76, 0x0a, 0x19, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, - 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, - 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x46, 0x6f, 0x75, 0x6e, 0x64, - 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0xb8, 0x01, 0x0a, 0x09, 0x53, - 0x65, 0x74, 0x75, 0x70, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x72, 0x6f, 0x6b, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x72, 0x6f, - 0x6b, 0x65, 0x72, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, - 0x41, 0x72, 0x67, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x63, 0x6b, - 0x65, 0x6e, 0x64, 0x55, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, - 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x55, 0x55, 0x49, 0x44, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x33, 0x0a, 0x0d, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1f, + 0x0a, 0x0b, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x73, 0x18, 0x0d, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x69, 0x64, 0x72, 0x73, 0x12, + 0x25, 0x0a, 0x0e, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, + 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x10, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x69, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, + 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, + 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x4d, 0x61, 0x78, 0x54, 0x74, 0x6c, 0x12, 0x1d, 0x0a, + 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x11, + 0x6e, 0x6f, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x1e, 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x75, 0x70, 0x52, 0x65, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xca, 0x06, 0x0a, 0x0a, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, + 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x69, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2c, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x75, 0x6d, + 0x5f, 0x75, 0x73, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6e, 0x75, 0x6d, + 0x55, 0x73, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x28, 0x0a, 0x10, 0x65, + 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x74, 0x6c, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x4d, + 0x61, 0x78, 0x54, 0x74, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x1f, + 0x0a, 0x0b, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x73, 0x18, 0x0f, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x69, 0x64, 0x72, 0x73, 0x12, + 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x62, 0x62, 0x79, 0x68, 0x6f, 0x6c, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x75, 0x62, 0x62, 0x79, 0x68, + 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x12, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x45, 0x0a, 0x0d, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, + 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x6f, 0x5f, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x15, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x12, 0x6e, 0x6f, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x1a, 0x3f, 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x74, + 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0xaf, 0x01, 0x0a, 0x0c, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, + 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x09, 0x69, 0x73, 0x73, 0x75, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x4d, 0x61, 0x78, 0x54, 0x54, 0x4c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, 0x61, + 0x78, 0x54, 0x54, 0x4c, 0x22, 0x7f, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x35, + 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x65, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0c, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x49, 0x64, 0x22, 0xe7, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x06, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x04, + 0x61, 0x75, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, + 0x12, 0x31, 0x0a, 0x09, 0x77, 0x72, 0x61, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x57, 0x72, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x77, 0x72, 0x61, 0x70, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x33, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x1a, 0x46, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0xc8, 0x02, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x72, 0x61, + 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x6f, 0x72, 0x12, 0x2a, 0x0a, 0x11, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, + 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1b, 0x0a, + 0x09, 0x73, 0x65, 0x61, 0x6c, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x08, 0x73, 0x65, 0x61, 0x6c, 0x57, 0x72, 0x61, 0x70, 0x22, 0x58, 0x0a, 0x0f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x72, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, + 0x03, 0x54, 0x54, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, + 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x61, 0x6c, 0x5f, + 0x77, 0x72, 0x61, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x61, 0x6c, + 0x57, 0x72, 0x61, 0x70, 0x22, 0x59, 0x0a, 0x11, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x60, 0x0a, 0x12, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x28, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x20, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, + 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x03, 0x65, 0x72, + 0x72, 0x22, 0x10, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x41, + 0x72, 0x67, 0x73, 0x22, 0x33, 0x0a, 0x0f, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x34, 0x0a, 0x11, 0x53, 0x70, 0x65, 0x63, + 0x69, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f, 0x0a, + 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x70, + 0x62, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x22, 0x60, + 0x0a, 0x18, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, + 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x07, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x62, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x76, 0x0a, 0x19, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, + 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f, 0x0a, + 0x0b, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x16, + 0x0a, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, + 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0xb8, 0x01, 0x0a, 0x09, 0x53, 0x65, 0x74, + 0x75, 0x70, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x72, 0x6f, 0x6b, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x41, 0x72, + 0x67, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x55, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x55, 0x55, 0x49, 0x44, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x1e, 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x65, 0x72, 0x72, 0x22, 0x1f, 0x0a, 0x09, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x22, 0x25, 0x0a, 0x11, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x53, 0x0a, 0x0c, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x61, 0x6c, 0x5f, 0x77, 0x72, 0x61, 0x70, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x61, 0x6c, 0x57, 0x72, 0x61, 0x70, + 0x22, 0x29, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x41, + 0x72, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x38, 0x0a, 0x10, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, + 0x65, 0x79, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x22, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x47, 0x65, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4b, 0x0a, 0x0f, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x05, + 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x62, + 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x65, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x38, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x50, 0x75, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, + 0x22, 0x23, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x1f, 0x0a, 0x09, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x25, 0x0a, 0x11, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x53, 0x0a, - 0x0c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x61, 0x6c, 0x5f, 0x77, 0x72, - 0x61, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x61, 0x6c, 0x57, 0x72, - 0x61, 0x70, 0x22, 0x29, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x38, 0x0a, - 0x10, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, - 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x22, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x47, 0x65, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4b, 0x0a, 0x0f, 0x53, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, - 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x38, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x50, 0x75, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x65, 0x6e, - 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x53, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x65, 0x6e, 0x74, - 0x72, 0x79, 0x22, 0x23, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x75, 0x74, - 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x25, 0x0a, 0x11, 0x53, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x26, - 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, - 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x1c, 0x0a, 0x08, 0x54, 0x54, 0x4c, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x03, 0x54, 0x54, 0x4c, 0x22, 0x28, 0x0a, 0x0c, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x52, - 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x22, 0x32, - 0x0a, 0x14, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x22, 0x2d, 0x0a, 0x15, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x22, 0x4e, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, - 0x70, 0x44, 0x61, 0x74, 0x61, 0x41, 0x72, 0x67, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, - 0x03, 0x54, 0x54, 0x4c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, - 0x10, 0x0a, 0x03, 0x4a, 0x57, 0x54, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x4a, 0x57, - 0x54, 0x22, 0x5c, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, - 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x09, 0x77, 0x72, - 0x61, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x77, 0x72, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, + 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x25, 0x0a, 0x11, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x26, 0x0a, 0x12, + 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x65, 0x72, 0x72, 0x22, 0x1c, 0x0a, 0x08, 0x54, 0x54, 0x4c, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, + 0x54, 0x4c, 0x22, 0x28, 0x0a, 0x0c, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x22, 0x32, 0x0a, 0x14, + 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x22, 0x2d, 0x0a, 0x15, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, + 0x4e, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44, + 0x61, 0x74, 0x61, 0x41, 0x72, 0x67, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x54, + 0x54, 0x4c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x10, 0x0a, + 0x03, 0x4a, 0x57, 0x54, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x4a, 0x57, 0x54, 0x22, + 0x5c, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44, + 0x61, 0x74, 0x61, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x09, 0x77, 0x72, 0x61, 0x70, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x62, + 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x08, 0x77, 0x72, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x65, + 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x2d, 0x0a, + 0x11, 0x4d, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x27, 0x0a, 0x0f, + 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x22, 0x2d, 0x0a, 0x0e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, + 0x6e, 0x66, 0x6f, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x49, 0x64, 0x22, 0x4c, 0x0a, 0x0f, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x27, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, + 0x6c, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, + 0x72, 0x72, 0x22, 0x50, 0x0a, 0x14, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f, 0x72, 0x45, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x06, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, + 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x65, 0x72, 0x72, 0x22, 0x6d, 0x0a, 0x0e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, + 0x76, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x49, 0x0a, 0x12, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x5f, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x50, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x11, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x65, 0x72, 0x72, 0x22, 0x44, 0x0a, 0x21, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3d, 0x0a, 0x1f, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, + 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x66, 0x0a, 0x10, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, + 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, + 0x22, 0x4c, 0x0a, 0x1c, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x74, 0x74, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0x47, + 0x0a, 0x1d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0x44, 0x0a, 0x1a, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0x50, 0x0a, + 0x1b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, - 0x2d, 0x0a, 0x11, 0x4d, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, - 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x27, - 0x0a, 0x0f, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x22, 0x2d, 0x0a, 0x0e, 0x45, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x22, 0x4c, 0x0a, 0x0f, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x27, 0x0a, 0x06, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6f, 0x67, 0x69, - 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x65, 0x72, 0x72, 0x22, 0x50, 0x0a, 0x14, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f, - 0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x06, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, - 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x6d, 0x0a, 0x0e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x45, 0x6e, 0x76, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x49, 0x0a, 0x12, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x5f, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x50, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, - 0x52, 0x11, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, - 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x44, 0x0a, 0x21, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3d, 0x0a, 0x1f, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, - 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x66, 0x0a, 0x10, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x21, - 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, - 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, - 0x72, 0x72, 0x22, 0x4c, 0x0a, 0x1c, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x49, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x74, 0x74, 0x6c, - 0x22, 0x47, 0x0a, 0x1d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0x8e, 0x01, 0x0a, 0x0a, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x10, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0xbb, 0x04, 0x0a, 0x0f, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x68, 0x61, 0x6e, 0x64, - 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x43, - 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x69, 0x64, 0x5f, 0x72, - 0x65, 0x73, 0x75, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x64, 0x69, 0x64, - 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, - 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x69, - 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6e, 0x65, 0x67, - 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, - 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x41, 0x0a, 0x1d, 0x6e, 0x65, - 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x5f, 0x69, 0x73, 0x5f, 0x6d, 0x75, 0x74, 0x75, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x1a, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x49, 0x73, 0x4d, 0x75, 0x74, 0x75, 0x61, 0x6c, 0x12, 0x1f, 0x0a, - 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x41, - 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, - 0x10, 0x70, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x73, 0x12, 0x3d, 0x0a, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x62, 0x2e, - 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x52, 0x0e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, - 0x12, 0x42, 0x0a, 0x1d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x1b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x63, 0x73, 0x70, 0x5f, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6f, 0x63, 0x73, - 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6c, 0x73, - 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, - 0x6c, 0x73, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x22, 0x2a, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x73, 0x6e, 0x31, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x61, 0x73, 0x6e, 0x31, - 0x44, 0x61, 0x74, 0x61, 0x22, 0x47, 0x0a, 0x10, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x0c, 0x63, 0x65, 0x72, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, - 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, - 0x0c, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x22, 0x5b, 0x0a, - 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x28, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x12, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x32, 0xa5, 0x03, 0x0a, 0x07, 0x42, - 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3e, 0x0a, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, - 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x0c, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, - 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61, - 0x74, 0x68, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x53, 0x0a, 0x14, 0x48, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x12, 0x1c, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, - 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x1d, - 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, - 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f, 0x0a, - 0x07, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x31, - 0x0a, 0x0d, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, - 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x12, 0x26, 0x0a, 0x05, 0x53, 0x65, 0x74, 0x75, 0x70, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, - 0x53, 0x65, 0x74, 0x75, 0x70, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x53, - 0x65, 0x74, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x0a, 0x49, 0x6e, 0x69, - 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, - 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, - 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x12, 0x20, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x32, 0xd5, 0x01, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x31, - 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x14, 0x2e, 0x70, 0x62, - 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, - 0x79, 0x12, 0x2e, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x47, 0x65, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, - 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, - 0x79, 0x12, 0x2e, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x75, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, - 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, - 0x79, 0x12, 0x37, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x70, 0x62, - 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x72, - 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0xbf, 0x06, 0x0a, 0x0a, 0x53, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x56, 0x69, 0x65, 0x77, 0x12, 0x2a, 0x0a, 0x0f, 0x44, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x54, 0x4c, 0x12, 0x09, 0x2e, 0x70, - 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x54, 0x4c, - 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x0b, 0x4d, 0x61, 0x78, 0x4c, 0x65, 0x61, 0x73, - 0x65, 0x54, 0x54, 0x4c, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, - 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x54, 0x4c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, - 0x07, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, - 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, - 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, - 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x38, 0x0a, - 0x10, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, - 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x2e, 0x70, 0x62, - 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, - 0x61, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x12, 0x30, 0x0a, 0x0c, 0x4d, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x90, 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x12, 0x33, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, + 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x3f, 0x0a, 0x1c, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x22, 0x8e, 0x01, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, + 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x6f, + 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x50, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x22, 0xbb, 0x04, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, + 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, + 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x69, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x64, 0x69, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, + 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, + 0x69, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x12, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x41, 0x0a, 0x1d, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x69, 0x73, 0x5f, 0x6d, + 0x75, 0x74, 0x75, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6e, 0x65, 0x67, + 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x49, + 0x73, 0x4d, 0x75, 0x74, 0x75, 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, + 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x10, 0x70, 0x65, 0x65, 0x72, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x0f, 0x76, + 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x09, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x0e, 0x76, 0x65, 0x72, 0x69, + 0x66, 0x69, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x1d, 0x73, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x1b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x12, 0x23, + 0x0a, 0x0d, 0x6f, 0x63, 0x73, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6c, 0x73, 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, + 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x6c, 0x73, 0x55, 0x6e, 0x69, 0x71, + 0x75, 0x65, 0x22, 0x2a, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x73, 0x6e, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x61, 0x73, 0x6e, 0x31, 0x44, 0x61, 0x74, 0x61, 0x22, 0x47, + 0x0a, 0x10, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x0c, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x63, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x22, 0x5b, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x67, 0x69, + 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x32, 0xa5, 0x03, 0x0a, 0x07, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, + 0x12, 0x3e, 0x0a, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x12, 0x30, 0x0a, 0x0c, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x70, 0x62, - 0x2e, 0x4d, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x12, 0x2c, 0x0a, 0x0a, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x70, 0x62, - 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x12, 0x35, 0x0a, 0x0a, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, - 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x72, - 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x09, 0x50, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x45, 0x6e, 0x76, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, - 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x12, 0x3f, 0x0a, 0x0f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f, 0x72, - 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, - 0x65, 0x70, 0x6c, 0x79, 0x12, 0x68, 0x0a, 0x1a, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x12, 0x25, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x62, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, - 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, - 0x0a, 0x0b, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x09, 0x2e, - 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x5c, - 0x0a, 0x15, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x20, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x62, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x36, 0x0a, 0x06, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x12, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, - 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x62, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x53, 0x0a, 0x14, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1c, 0x2e, 0x70, 0x62, 0x2e, + 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x1d, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x07, 0x43, 0x6c, 0x65, 0x61, 0x6e, + 0x75, 0x70, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x09, 0x2e, + 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x31, 0x0a, 0x0d, 0x49, 0x6e, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x49, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x72, 0x67, 0x73, + 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x53, + 0x65, 0x74, 0x75, 0x70, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x41, + 0x72, 0x67, 0x73, 0x1a, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, + 0x70, 0x62, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0xd5, 0x01, 0x0a, + 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, + 0x12, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x03, 0x47, + 0x65, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x47, + 0x65, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x03, 0x50, + 0x75, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, + 0x75, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x50, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x37, 0x0a, 0x06, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x70, + 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x32, 0xdd, 0x07, 0x0a, 0x0a, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x56, + 0x69, 0x65, 0x77, 0x12, 0x2a, 0x0a, 0x0f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4c, 0x65, + 0x61, 0x73, 0x65, 0x54, 0x54, 0x4c, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x54, 0x4c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x26, 0x0a, 0x0b, 0x4d, 0x61, 0x78, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x54, 0x4c, 0x12, 0x09, + 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, + 0x54, 0x4c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x07, 0x54, 0x61, 0x69, 0x6e, 0x74, + 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, + 0x70, 0x62, 0x2e, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x36, 0x0a, 0x0f, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x18, 0x2e, + 0x70, 0x62, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x38, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x09, 0x2e, 0x70, 0x62, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, + 0x70, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x41, 0x72, 0x67, 0x73, 0x1a, + 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, + 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x0c, 0x4d, 0x6c, + 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x6c, 0x6f, 0x63, 0x6b, + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2c, 0x0a, 0x0a, + 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, + 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x0a, 0x45, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, + 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x2a, 0x0a, 0x09, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x12, 0x09, + 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x50, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3f, 0x0a, + 0x0f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, + 0x41, 0x72, 0x67, 0x73, 0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x46, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x68, + 0x0a, 0x1a, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x25, 0x2e, 0x70, + 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x0b, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x5c, 0x0a, 0x15, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x12, 0x20, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x49, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x13, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, + 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, + 0x0a, 0x15, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x20, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, + 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x32, 0x36, 0x0a, 0x06, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2c, + 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x2e, 0x70, 0x62, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x2a, 0x5a, 0x28, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -4127,7 +4370,7 @@ func file_sdk_plugin_pb_backend_proto_rawDescGZIP() []byte { return file_sdk_plugin_pb_backend_proto_rawDescData } -var file_sdk_plugin_pb_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 60) +var file_sdk_plugin_pb_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 64) var file_sdk_plugin_pb_backend_proto_goTypes = []any{ (*Empty)(nil), // 0: pb.Empty (*Header)(nil), // 1: pb.Header @@ -4178,43 +4421,48 @@ var file_sdk_plugin_pb_backend_proto_goTypes = []any{ (*ClusterInfoReply)(nil), // 46: pb.ClusterInfoReply (*GenerateIdentityTokenRequest)(nil), // 47: pb.GenerateIdentityTokenRequest (*GenerateIdentityTokenResponse)(nil), // 48: pb.GenerateIdentityTokenResponse - (*Connection)(nil), // 49: pb.Connection - (*ConnectionState)(nil), // 50: pb.ConnectionState - (*Certificate)(nil), // 51: pb.Certificate - (*CertificateChain)(nil), // 52: pb.CertificateChain - (*SendEventRequest)(nil), // 53: pb.SendEventRequest - nil, // 54: pb.Request.HeadersEntry - nil, // 55: pb.Auth.MetadataEntry - nil, // 56: pb.TokenEntry.MetaEntry - nil, // 57: pb.TokenEntry.InternalMetaEntry - nil, // 58: pb.Response.HeadersEntry - nil, // 59: pb.SetupArgs.ConfigEntry - (*logical.Alias)(nil), // 60: logical.Alias - (*timestamppb.Timestamp)(nil), // 61: google.protobuf.Timestamp - (*logical.Entity)(nil), // 62: logical.Entity - (*logical.Group)(nil), // 63: logical.Group - (*logical.PluginEnvironment)(nil), // 64: logical.PluginEnvironment - (*logical.EventData)(nil), // 65: logical.EventData + (*RegisterRotationJobRequest)(nil), // 49: pb.RegisterRotationJobRequest + (*RegisterRotationJobResponse)(nil), // 50: pb.RegisterRotationJobResponse + (*RotationJobInput)(nil), // 51: pb.RotationJobInput + (*DeregisterRotationJobRequest)(nil), // 52: pb.DeregisterRotationJobRequest + (*Connection)(nil), // 53: pb.Connection + (*ConnectionState)(nil), // 54: pb.ConnectionState + (*Certificate)(nil), // 55: pb.Certificate + (*CertificateChain)(nil), // 56: pb.CertificateChain + (*SendEventRequest)(nil), // 57: pb.SendEventRequest + nil, // 58: pb.Request.HeadersEntry + nil, // 59: pb.Auth.MetadataEntry + nil, // 60: pb.TokenEntry.MetaEntry + nil, // 61: pb.TokenEntry.InternalMetaEntry + nil, // 62: pb.Response.HeadersEntry + nil, // 63: pb.SetupArgs.ConfigEntry + (*logical.Alias)(nil), // 64: logical.Alias + (*timestamppb.Timestamp)(nil), // 65: google.protobuf.Timestamp + (*logical.Entity)(nil), // 66: logical.Entity + (*logical.Group)(nil), // 67: logical.Group + (*logical.PluginEnvironment)(nil), // 68: logical.PluginEnvironment + (*structpb.Struct)(nil), // 69: google.protobuf.Struct + (*logical.EventData)(nil), // 70: logical.EventData } var file_sdk_plugin_pb_backend_proto_depIDxs = []int32{ 8, // 0: pb.Request.secret:type_name -> pb.Secret 5, // 1: pb.Request.auth:type_name -> pb.Auth - 54, // 2: pb.Request.headers:type_name -> pb.Request.HeadersEntry + 58, // 2: pb.Request.headers:type_name -> pb.Request.HeadersEntry 11, // 3: pb.Request.wrap_info:type_name -> pb.RequestWrapInfo - 49, // 4: pb.Request.connection:type_name -> pb.Connection + 53, // 4: pb.Request.connection:type_name -> pb.Connection 7, // 5: pb.Auth.lease_options:type_name -> pb.LeaseOptions - 55, // 6: pb.Auth.metadata:type_name -> pb.Auth.MetadataEntry - 60, // 7: pb.Auth.alias:type_name -> logical.Alias - 60, // 8: pb.Auth.group_aliases:type_name -> logical.Alias - 56, // 9: pb.TokenEntry.meta:type_name -> pb.TokenEntry.MetaEntry - 57, // 10: pb.TokenEntry.internal_meta:type_name -> pb.TokenEntry.InternalMetaEntry - 61, // 11: pb.LeaseOptions.issue_time:type_name -> google.protobuf.Timestamp + 59, // 6: pb.Auth.metadata:type_name -> pb.Auth.MetadataEntry + 64, // 7: pb.Auth.alias:type_name -> logical.Alias + 64, // 8: pb.Auth.group_aliases:type_name -> logical.Alias + 60, // 9: pb.TokenEntry.meta:type_name -> pb.TokenEntry.MetaEntry + 61, // 10: pb.TokenEntry.internal_meta:type_name -> pb.TokenEntry.InternalMetaEntry + 65, // 11: pb.LeaseOptions.issue_time:type_name -> google.protobuf.Timestamp 7, // 12: pb.Secret.lease_options:type_name -> pb.LeaseOptions 8, // 13: pb.Response.secret:type_name -> pb.Secret 5, // 14: pb.Response.auth:type_name -> pb.Auth 10, // 15: pb.Response.wrap_info:type_name -> pb.ResponseWrapInfo - 58, // 16: pb.Response.headers:type_name -> pb.Response.HeadersEntry - 61, // 17: pb.ResponseWrapInfo.creation_time:type_name -> google.protobuf.Timestamp + 62, // 16: pb.Response.headers:type_name -> pb.Response.HeadersEntry + 65, // 17: pb.ResponseWrapInfo.creation_time:type_name -> google.protobuf.Timestamp 4, // 18: pb.HandleRequestArgs.request:type_name -> pb.Request 9, // 19: pb.HandleRequestReply.response:type_name -> pb.Response 2, // 20: pb.HandleRequestReply.err:type_name -> pb.ProtoError @@ -4222,79 +4470,85 @@ var file_sdk_plugin_pb_backend_proto_depIDxs = []int32{ 3, // 22: pb.SpecialPathsReply.paths:type_name -> pb.Paths 4, // 23: pb.HandleExistenceCheckArgs.request:type_name -> pb.Request 2, // 24: pb.HandleExistenceCheckReply.err:type_name -> pb.ProtoError - 59, // 25: pb.SetupArgs.Config:type_name -> pb.SetupArgs.ConfigEntry + 63, // 25: pb.SetupArgs.Config:type_name -> pb.SetupArgs.ConfigEntry 23, // 26: pb.StorageGetReply.entry:type_name -> pb.StorageEntry 23, // 27: pb.StoragePutArgs.entry:type_name -> pb.StorageEntry 10, // 28: pb.ResponseWrapDataReply.wrap_info:type_name -> pb.ResponseWrapInfo - 62, // 29: pb.EntityInfoReply.entity:type_name -> logical.Entity - 63, // 30: pb.GroupsForEntityReply.groups:type_name -> logical.Group - 64, // 31: pb.PluginEnvReply.plugin_environment:type_name -> logical.PluginEnvironment - 50, // 32: pb.Connection.connection_state:type_name -> pb.ConnectionState - 52, // 33: pb.ConnectionState.peer_certificates:type_name -> pb.CertificateChain - 52, // 34: pb.ConnectionState.verified_chains:type_name -> pb.CertificateChain - 51, // 35: pb.CertificateChain.certificates:type_name -> pb.Certificate - 65, // 36: pb.SendEventRequest.event:type_name -> logical.EventData - 1, // 37: pb.Request.HeadersEntry.value:type_name -> pb.Header - 1, // 38: pb.Response.HeadersEntry.value:type_name -> pb.Header - 12, // 39: pb.Backend.HandleRequest:input_type -> pb.HandleRequestArgs - 0, // 40: pb.Backend.SpecialPaths:input_type -> pb.Empty - 17, // 41: pb.Backend.HandleExistenceCheck:input_type -> pb.HandleExistenceCheckArgs - 0, // 42: pb.Backend.Cleanup:input_type -> pb.Empty - 22, // 43: pb.Backend.InvalidateKey:input_type -> pb.InvalidateKeyArgs - 19, // 44: pb.Backend.Setup:input_type -> pb.SetupArgs - 14, // 45: pb.Backend.Initialize:input_type -> pb.InitializeArgs - 0, // 46: pb.Backend.Type:input_type -> pb.Empty - 24, // 47: pb.Storage.List:input_type -> pb.StorageListArgs - 26, // 48: pb.Storage.Get:input_type -> pb.StorageGetArgs - 28, // 49: pb.Storage.Put:input_type -> pb.StoragePutArgs - 30, // 50: pb.Storage.Delete:input_type -> pb.StorageDeleteArgs - 0, // 51: pb.SystemView.DefaultLeaseTTL:input_type -> pb.Empty - 0, // 52: pb.SystemView.MaxLeaseTTL:input_type -> pb.Empty - 0, // 53: pb.SystemView.Tainted:input_type -> pb.Empty - 0, // 54: pb.SystemView.CachingDisabled:input_type -> pb.Empty - 0, // 55: pb.SystemView.ReplicationState:input_type -> pb.Empty - 36, // 56: pb.SystemView.ResponseWrapData:input_type -> pb.ResponseWrapDataArgs - 0, // 57: pb.SystemView.MlockEnabled:input_type -> pb.Empty - 0, // 58: pb.SystemView.LocalMount:input_type -> pb.Empty - 40, // 59: pb.SystemView.EntityInfo:input_type -> pb.EntityInfoArgs - 0, // 60: pb.SystemView.PluginEnv:input_type -> pb.Empty - 40, // 61: pb.SystemView.GroupsForEntity:input_type -> pb.EntityInfoArgs - 44, // 62: pb.SystemView.GeneratePasswordFromPolicy:input_type -> pb.GeneratePasswordFromPolicyRequest - 0, // 63: pb.SystemView.ClusterInfo:input_type -> pb.Empty - 47, // 64: pb.SystemView.GenerateIdentityToken:input_type -> pb.GenerateIdentityTokenRequest - 53, // 65: pb.Events.SendEvent:input_type -> pb.SendEventRequest - 13, // 66: pb.Backend.HandleRequest:output_type -> pb.HandleRequestReply - 16, // 67: pb.Backend.SpecialPaths:output_type -> pb.SpecialPathsReply - 18, // 68: pb.Backend.HandleExistenceCheck:output_type -> pb.HandleExistenceCheckReply - 0, // 69: pb.Backend.Cleanup:output_type -> pb.Empty - 0, // 70: pb.Backend.InvalidateKey:output_type -> pb.Empty - 20, // 71: pb.Backend.Setup:output_type -> pb.SetupReply - 15, // 72: pb.Backend.Initialize:output_type -> pb.InitializeReply - 21, // 73: pb.Backend.Type:output_type -> pb.TypeReply - 25, // 74: pb.Storage.List:output_type -> pb.StorageListReply - 27, // 75: pb.Storage.Get:output_type -> pb.StorageGetReply - 29, // 76: pb.Storage.Put:output_type -> pb.StoragePutReply - 31, // 77: pb.Storage.Delete:output_type -> pb.StorageDeleteReply - 32, // 78: pb.SystemView.DefaultLeaseTTL:output_type -> pb.TTLReply - 32, // 79: pb.SystemView.MaxLeaseTTL:output_type -> pb.TTLReply - 33, // 80: pb.SystemView.Tainted:output_type -> pb.TaintedReply - 34, // 81: pb.SystemView.CachingDisabled:output_type -> pb.CachingDisabledReply - 35, // 82: pb.SystemView.ReplicationState:output_type -> pb.ReplicationStateReply - 37, // 83: pb.SystemView.ResponseWrapData:output_type -> pb.ResponseWrapDataReply - 38, // 84: pb.SystemView.MlockEnabled:output_type -> pb.MlockEnabledReply - 39, // 85: pb.SystemView.LocalMount:output_type -> pb.LocalMountReply - 41, // 86: pb.SystemView.EntityInfo:output_type -> pb.EntityInfoReply - 43, // 87: pb.SystemView.PluginEnv:output_type -> pb.PluginEnvReply - 42, // 88: pb.SystemView.GroupsForEntity:output_type -> pb.GroupsForEntityReply - 45, // 89: pb.SystemView.GeneratePasswordFromPolicy:output_type -> pb.GeneratePasswordFromPolicyReply - 46, // 90: pb.SystemView.ClusterInfo:output_type -> pb.ClusterInfoReply - 48, // 91: pb.SystemView.GenerateIdentityToken:output_type -> pb.GenerateIdentityTokenResponse - 0, // 92: pb.Events.SendEvent:output_type -> pb.Empty - 66, // [66:93] is the sub-list for method output_type - 39, // [39:66] is the sub-list for method input_type - 39, // [39:39] is the sub-list for extension type_name - 39, // [39:39] is the sub-list for extension extendee - 0, // [0:39] is the sub-list for field type_name + 66, // 29: pb.EntityInfoReply.entity:type_name -> logical.Entity + 67, // 30: pb.GroupsForEntityReply.groups:type_name -> logical.Group + 68, // 31: pb.PluginEnvReply.plugin_environment:type_name -> logical.PluginEnvironment + 51, // 32: pb.RegisterRotationJobRequest.job:type_name -> pb.RotationJobInput + 69, // 33: pb.RotationJobInput.schedule:type_name -> google.protobuf.Struct + 54, // 34: pb.Connection.connection_state:type_name -> pb.ConnectionState + 56, // 35: pb.ConnectionState.peer_certificates:type_name -> pb.CertificateChain + 56, // 36: pb.ConnectionState.verified_chains:type_name -> pb.CertificateChain + 55, // 37: pb.CertificateChain.certificates:type_name -> pb.Certificate + 70, // 38: pb.SendEventRequest.event:type_name -> logical.EventData + 1, // 39: pb.Request.HeadersEntry.value:type_name -> pb.Header + 1, // 40: pb.Response.HeadersEntry.value:type_name -> pb.Header + 12, // 41: pb.Backend.HandleRequest:input_type -> pb.HandleRequestArgs + 0, // 42: pb.Backend.SpecialPaths:input_type -> pb.Empty + 17, // 43: pb.Backend.HandleExistenceCheck:input_type -> pb.HandleExistenceCheckArgs + 0, // 44: pb.Backend.Cleanup:input_type -> pb.Empty + 22, // 45: pb.Backend.InvalidateKey:input_type -> pb.InvalidateKeyArgs + 19, // 46: pb.Backend.Setup:input_type -> pb.SetupArgs + 14, // 47: pb.Backend.Initialize:input_type -> pb.InitializeArgs + 0, // 48: pb.Backend.Type:input_type -> pb.Empty + 24, // 49: pb.Storage.List:input_type -> pb.StorageListArgs + 26, // 50: pb.Storage.Get:input_type -> pb.StorageGetArgs + 28, // 51: pb.Storage.Put:input_type -> pb.StoragePutArgs + 30, // 52: pb.Storage.Delete:input_type -> pb.StorageDeleteArgs + 0, // 53: pb.SystemView.DefaultLeaseTTL:input_type -> pb.Empty + 0, // 54: pb.SystemView.MaxLeaseTTL:input_type -> pb.Empty + 0, // 55: pb.SystemView.Tainted:input_type -> pb.Empty + 0, // 56: pb.SystemView.CachingDisabled:input_type -> pb.Empty + 0, // 57: pb.SystemView.ReplicationState:input_type -> pb.Empty + 36, // 58: pb.SystemView.ResponseWrapData:input_type -> pb.ResponseWrapDataArgs + 0, // 59: pb.SystemView.MlockEnabled:input_type -> pb.Empty + 0, // 60: pb.SystemView.LocalMount:input_type -> pb.Empty + 40, // 61: pb.SystemView.EntityInfo:input_type -> pb.EntityInfoArgs + 0, // 62: pb.SystemView.PluginEnv:input_type -> pb.Empty + 40, // 63: pb.SystemView.GroupsForEntity:input_type -> pb.EntityInfoArgs + 44, // 64: pb.SystemView.GeneratePasswordFromPolicy:input_type -> pb.GeneratePasswordFromPolicyRequest + 0, // 65: pb.SystemView.ClusterInfo:input_type -> pb.Empty + 47, // 66: pb.SystemView.GenerateIdentityToken:input_type -> pb.GenerateIdentityTokenRequest + 49, // 67: pb.SystemView.RegisterRotationJob:input_type -> pb.RegisterRotationJobRequest + 52, // 68: pb.SystemView.DeregisterRotationJob:input_type -> pb.DeregisterRotationJobRequest + 57, // 69: pb.Events.SendEvent:input_type -> pb.SendEventRequest + 13, // 70: pb.Backend.HandleRequest:output_type -> pb.HandleRequestReply + 16, // 71: pb.Backend.SpecialPaths:output_type -> pb.SpecialPathsReply + 18, // 72: pb.Backend.HandleExistenceCheck:output_type -> pb.HandleExistenceCheckReply + 0, // 73: pb.Backend.Cleanup:output_type -> pb.Empty + 0, // 74: pb.Backend.InvalidateKey:output_type -> pb.Empty + 20, // 75: pb.Backend.Setup:output_type -> pb.SetupReply + 15, // 76: pb.Backend.Initialize:output_type -> pb.InitializeReply + 21, // 77: pb.Backend.Type:output_type -> pb.TypeReply + 25, // 78: pb.Storage.List:output_type -> pb.StorageListReply + 27, // 79: pb.Storage.Get:output_type -> pb.StorageGetReply + 29, // 80: pb.Storage.Put:output_type -> pb.StoragePutReply + 31, // 81: pb.Storage.Delete:output_type -> pb.StorageDeleteReply + 32, // 82: pb.SystemView.DefaultLeaseTTL:output_type -> pb.TTLReply + 32, // 83: pb.SystemView.MaxLeaseTTL:output_type -> pb.TTLReply + 33, // 84: pb.SystemView.Tainted:output_type -> pb.TaintedReply + 34, // 85: pb.SystemView.CachingDisabled:output_type -> pb.CachingDisabledReply + 35, // 86: pb.SystemView.ReplicationState:output_type -> pb.ReplicationStateReply + 37, // 87: pb.SystemView.ResponseWrapData:output_type -> pb.ResponseWrapDataReply + 38, // 88: pb.SystemView.MlockEnabled:output_type -> pb.MlockEnabledReply + 39, // 89: pb.SystemView.LocalMount:output_type -> pb.LocalMountReply + 41, // 90: pb.SystemView.EntityInfo:output_type -> pb.EntityInfoReply + 43, // 91: pb.SystemView.PluginEnv:output_type -> pb.PluginEnvReply + 42, // 92: pb.SystemView.GroupsForEntity:output_type -> pb.GroupsForEntityReply + 45, // 93: pb.SystemView.GeneratePasswordFromPolicy:output_type -> pb.GeneratePasswordFromPolicyReply + 46, // 94: pb.SystemView.ClusterInfo:output_type -> pb.ClusterInfoReply + 48, // 95: pb.SystemView.GenerateIdentityToken:output_type -> pb.GenerateIdentityTokenResponse + 50, // 96: pb.SystemView.RegisterRotationJob:output_type -> pb.RegisterRotationJobResponse + 0, // 97: pb.SystemView.DeregisterRotationJob:output_type -> pb.Empty + 0, // 98: pb.Events.SendEvent:output_type -> pb.Empty + 70, // [70:99] is the sub-list for method output_type + 41, // [41:70] is the sub-list for method input_type + 41, // [41:41] is the sub-list for extension type_name + 41, // [41:41] is the sub-list for extension extendee + 0, // [0:41] is the sub-list for field type_name } func init() { file_sdk_plugin_pb_backend_proto_init() } @@ -4308,7 +4562,7 @@ func file_sdk_plugin_pb_backend_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_sdk_plugin_pb_backend_proto_rawDesc, NumEnums: 0, - NumMessages: 60, + NumMessages: 64, NumExtensions: 0, NumServices: 4, }, diff --git a/sdk/plugin/pb/backend.proto b/sdk/plugin/pb/backend.proto index cf0f47d8dbe7..1bf023726505 100644 --- a/sdk/plugin/pb/backend.proto +++ b/sdk/plugin/pb/backend.proto @@ -5,6 +5,7 @@ syntax = "proto3"; package pb; import "google/protobuf/timestamp.proto"; +import "google/protobuf/struct.proto"; import "sdk/logical/event.proto"; import "sdk/logical/identity.proto"; import "sdk/logical/plugin.proto"; @@ -607,6 +608,26 @@ message GenerateIdentityTokenResponse { int64 ttl = 2; } +message RegisterRotationJobRequest { + RotationJobInput job = 1; +} + +message RegisterRotationJobResponse { + string rotation_id = 1; + string err = 2; +} + +message RotationJobInput { + google.protobuf.Struct schedule = 1; + string rotation_id = 2; + string path = 3; + string name = 4; +} + +message DeregisterRotationJobRequest { + string rotation_id = 1; +} + // SystemView exposes system configuration information in a safe way for plugins // to consume. Plugins should implement the client for this service. service SystemView { @@ -665,6 +686,12 @@ service SystemView { // GenerateIdentityToken returns an identity token for the requesting plugin. rpc GenerateIdentityToken(GenerateIdentityTokenRequest) returns (GenerateIdentityTokenResponse); + + // RegisterRotationJob returns a rotation ID for the requested plugin credential. + rpc RegisterRotationJob(RegisterRotationJobRequest) returns (RegisterRotationJobResponse); + + // DeregisterRotationJob returns any errors in de-registering a credential from the Rotation Manager. + rpc DeregisterRotationJob(DeregisterRotationJobRequest) returns (Empty); } message Connection { diff --git a/sdk/plugin/pb/backend_grpc.pb.go b/sdk/plugin/pb/backend_grpc.pb.go index 65d59ae77abc..8c15395f7d5b 100644 --- a/sdk/plugin/pb/backend_grpc.pb.go +++ b/sdk/plugin/pb/backend_grpc.pb.go @@ -688,6 +688,8 @@ const ( SystemView_GeneratePasswordFromPolicy_FullMethodName = "/pb.SystemView/GeneratePasswordFromPolicy" SystemView_ClusterInfo_FullMethodName = "/pb.SystemView/ClusterInfo" SystemView_GenerateIdentityToken_FullMethodName = "/pb.SystemView/GenerateIdentityToken" + SystemView_RegisterRotationJob_FullMethodName = "/pb.SystemView/RegisterRotationJob" + SystemView_DeregisterRotationJob_FullMethodName = "/pb.SystemView/DeregisterRotationJob" ) // SystemViewClient is the client API for SystemView service. @@ -739,6 +741,10 @@ type SystemViewClient interface { ClusterInfo(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ClusterInfoReply, error) // GenerateIdentityToken returns an identity token for the requesting plugin. GenerateIdentityToken(ctx context.Context, in *GenerateIdentityTokenRequest, opts ...grpc.CallOption) (*GenerateIdentityTokenResponse, error) + // RegisterRotationJob returns a rotation ID for the requested plugin credential. + RegisterRotationJob(ctx context.Context, in *RegisterRotationJobRequest, opts ...grpc.CallOption) (*RegisterRotationJobResponse, error) + // DeregisterRotationJob returns any errors in de-registering a credential from the Rotation Manager. + DeregisterRotationJob(ctx context.Context, in *DeregisterRotationJobRequest, opts ...grpc.CallOption) (*Empty, error) } type systemViewClient struct { @@ -889,6 +895,26 @@ func (c *systemViewClient) GenerateIdentityToken(ctx context.Context, in *Genera return out, nil } +func (c *systemViewClient) RegisterRotationJob(ctx context.Context, in *RegisterRotationJobRequest, opts ...grpc.CallOption) (*RegisterRotationJobResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RegisterRotationJobResponse) + err := c.cc.Invoke(ctx, SystemView_RegisterRotationJob_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *systemViewClient) DeregisterRotationJob(ctx context.Context, in *DeregisterRotationJobRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, SystemView_DeregisterRotationJob_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // SystemViewServer is the server API for SystemView service. // All implementations must embed UnimplementedSystemViewServer // for forward compatibility. @@ -938,6 +964,10 @@ type SystemViewServer interface { ClusterInfo(context.Context, *Empty) (*ClusterInfoReply, error) // GenerateIdentityToken returns an identity token for the requesting plugin. GenerateIdentityToken(context.Context, *GenerateIdentityTokenRequest) (*GenerateIdentityTokenResponse, error) + // RegisterRotationJob returns a rotation ID for the requested plugin credential. + RegisterRotationJob(context.Context, *RegisterRotationJobRequest) (*RegisterRotationJobResponse, error) + // DeregisterRotationJob returns any errors in de-registering a credential from the Rotation Manager. + DeregisterRotationJob(context.Context, *DeregisterRotationJobRequest) (*Empty, error) mustEmbedUnimplementedSystemViewServer() } @@ -990,6 +1020,12 @@ func (UnimplementedSystemViewServer) ClusterInfo(context.Context, *Empty) (*Clus func (UnimplementedSystemViewServer) GenerateIdentityToken(context.Context, *GenerateIdentityTokenRequest) (*GenerateIdentityTokenResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GenerateIdentityToken not implemented") } +func (UnimplementedSystemViewServer) RegisterRotationJob(context.Context, *RegisterRotationJobRequest) (*RegisterRotationJobResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RegisterRotationJob not implemented") +} +func (UnimplementedSystemViewServer) DeregisterRotationJob(context.Context, *DeregisterRotationJobRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeregisterRotationJob not implemented") +} func (UnimplementedSystemViewServer) mustEmbedUnimplementedSystemViewServer() {} func (UnimplementedSystemViewServer) testEmbeddedByValue() {} @@ -1263,6 +1299,42 @@ func _SystemView_GenerateIdentityToken_Handler(srv interface{}, ctx context.Cont return interceptor(ctx, in, info, handler) } +func _SystemView_RegisterRotationJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterRotationJobRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SystemViewServer).RegisterRotationJob(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SystemView_RegisterRotationJob_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SystemViewServer).RegisterRotationJob(ctx, req.(*RegisterRotationJobRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SystemView_DeregisterRotationJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeregisterRotationJobRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SystemViewServer).DeregisterRotationJob(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SystemView_DeregisterRotationJob_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SystemViewServer).DeregisterRotationJob(ctx, req.(*DeregisterRotationJobRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SystemView_ServiceDesc is the grpc.ServiceDesc for SystemView service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -1326,6 +1398,14 @@ var SystemView_ServiceDesc = grpc.ServiceDesc{ MethodName: "GenerateIdentityToken", Handler: _SystemView_GenerateIdentityToken_Handler, }, + { + MethodName: "RegisterRotationJob", + Handler: _SystemView_RegisterRotationJob_Handler, + }, + { + MethodName: "DeregisterRotationJob", + Handler: _SystemView_DeregisterRotationJob_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "sdk/plugin/pb/backend.proto", diff --git a/sdk/rotation/rotation_job.go b/sdk/rotation/rotation_job.go new file mode 100644 index 000000000000..ec395d06c203 --- /dev/null +++ b/sdk/rotation/rotation_job.go @@ -0,0 +1,105 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package rotation + +import ( + "fmt" + "time" + + "github.com/robfig/cron/v3" +) + +// RotationOptions is an embeddable struct to capture common rotation +// settings between a Secret and Auth +type RotationOptions struct { + // Schedule holds the info for the framework.Schedule + Schedule *RotationSchedule +} + +// RotationJob represents the secret part of a response. +type RotationJob struct { + RotationOptions + + // RotationID is the ID returned to the user to manage this secret. + // This is generated by Vault core. Any set value will be ignored. + // For requests, this will always be blank. + RotationID string `sentinel:""` + Path string + Name string +} + +type RotationJobConfigureRequest struct { + Name string + MountPoint string + ReqPath string + RotationSchedule string + RotationWindow int + RotationPeriod int +} + +func (s *RotationJob) Validate() error { + // TODO: validation? + return nil +} + +func newRotationJob(rotationSchedule, path, rotationJobName string, rotationWindow, ttl int) (*RotationJob, error) { + var cronSc *cron.SpecSchedule + if rotationSchedule != "" { + var err error + cronSc, err = DefaultScheduler.Parse(rotationSchedule) + if err != nil { + return nil, err + } + } + + rs := &RotationSchedule{ + Schedule: cronSc, + RotationSchedule: rotationSchedule, + RotationWindow: time.Duration(rotationWindow) * time.Second, + RotationPeriod: time.Duration(ttl) * time.Second, + // TODO + // decide if next rotation should be set here + // or when we actually push item into queue + NextVaultRotation: time.Time{}, + LastVaultRotation: time.Time{}, + } + + return &RotationJob{ + RotationOptions: RotationOptions{ + Schedule: rs, + }, + // Figure out how to get mount info + Path: path, + Name: rotationJobName, + }, nil +} + +// ConfigureRotationJob builds and returns a configured RotationJob for the mount and request with the given schedule. +func ConfigureRotationJob(configRequest *RotationJobConfigureRequest) (*RotationJob, error) { + mount := configRequest.MountPoint + configRequest.ReqPath + + var rotationJob *RotationJob + if configRequest.RotationSchedule != "" && configRequest.RotationWindow != 0 { + var err error + rotationJob, err = newRotationJob(configRequest.RotationSchedule, mount, configRequest.Name, configRequest.RotationWindow, 0) + if err != nil { + return nil, err + } + } + + if configRequest.RotationPeriod != 0 { + var err error + rotationJob, err = newRotationJob("", mount, configRequest.Name, 0, configRequest.RotationPeriod) + if err != nil { + return nil, err + } + } + + // Expect rotation job to exist here + if rotationJob == nil { + return nil, fmt.Errorf("rotation credential was nil; expected non-nil value") + } + + return rotationJob, nil +} diff --git a/sdk/rotation/schedule.go b/sdk/rotation/schedule.go new file mode 100644 index 000000000000..7c35f3615e62 --- /dev/null +++ b/sdk/rotation/schedule.go @@ -0,0 +1,112 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package rotation + +import ( + "fmt" + "time" + + "github.com/robfig/cron/v3" +) + +const ( + // Minimum allowed value for rotation_window + minRotationWindowSeconds = 3600 + parseOptions = cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow +) + +// RotationSchedule holds the parsed and unparsed versions of the schedule, along with the projected next rotation time. +type RotationSchedule struct { + Schedule *cron.SpecSchedule `json:"schedule"` + RotationWindow time.Duration `json:"rotation_window"` // seconds of window + RotationSchedule string `json:"rotation_schedule"` + RotationPeriod time.Duration `json:"rotation_period"` + NextVaultRotation time.Time `json:"next_vault_rotation"` + LastVaultRotation time.Time `json:"last_vault_rotation"` +} + +type Scheduler interface { + Parse(rotationSchedule string) (*cron.SpecSchedule, error) + ValidateRotationWindow(s int) error + NextRotationTimeFromInput(rs *RotationSchedule, input time.Time) time.Time + IsInsideRotationWindow(rs *RotationSchedule, t time.Time) bool + ShouldRotate(rs *RotationSchedule, priority int64, t time.Time) bool + NextRotationTime(rs *RotationSchedule) time.Time + SetNextVaultRotation(rs *RotationSchedule, t time.Time) + UsesTTL(rs *RotationSchedule) bool + UsesRotationSchedule(rs *RotationSchedule) bool +} + +var DefaultScheduler Scheduler = &DefaultSchedule{} + +type DefaultSchedule struct{} + +func (d *DefaultSchedule) Parse(rotationSchedule string) (*cron.SpecSchedule, error) { + parser := cron.NewParser(parseOptions) + schedule, err := parser.Parse(rotationSchedule) + if err != nil { + return nil, err + } + sched, ok := schedule.(*cron.SpecSchedule) + if !ok { + return nil, fmt.Errorf("invalid rotation schedule") + } + return sched, nil +} + +func (d *DefaultSchedule) ValidateRotationWindow(s int) error { + if s < minRotationWindowSeconds { + return fmt.Errorf("rotation_window must be %d seconds or more", minRotationWindowSeconds) + } + return nil +} + +func (d *DefaultSchedule) UsesRotationSchedule(rs *RotationSchedule) bool { + return rs.RotationSchedule != "" && rs.RotationPeriod == 0 +} + +func (d *DefaultSchedule) UsesTTL(rs *RotationSchedule) bool { + return rs.RotationPeriod != 0 && rs.RotationSchedule == "" +} + +// NextRotationTime calculates the next scheduled rotation +func (d *DefaultSchedule) NextRotationTime(rs *RotationSchedule) time.Time { + if d.UsesTTL(rs) { + return rs.LastVaultRotation.Add(rs.RotationPeriod) + } + return rs.Schedule.Next(time.Now()) +} + +// NextRotationTimeFromInput calculates and returns the next rotation time based on the provided schedule and input time +func (d *DefaultSchedule) NextRotationTimeFromInput(rs *RotationSchedule, input time.Time) time.Time { + if d.UsesTTL(rs) { + return input.Add(rs.RotationPeriod) + } + return rs.Schedule.Next(input) +} + +// IsInsideRotationWindow checks if the current time is before the calculated end of the rotation window, +// to make sure that t time is within the specified rotation window +// It returns true if rotation window is not specified +func (d *DefaultSchedule) IsInsideRotationWindow(rs *RotationSchedule, t time.Time) bool { + if rs.RotationWindow != 0 { + return t.Before(rs.NextVaultRotation.Add(rs.RotationWindow)) + } + return true +} + +// ShouldRotate checks if the rotation should occur based on priority, current time, and rotation window +// It returns true if the priority is less than or equal to the current time and the current time is within the rotation window +func (d *DefaultSchedule) ShouldRotate(rs *RotationSchedule, priority int64, t time.Time) bool { + return priority <= t.Unix() && d.IsInsideRotationWindow(rs, t) +} + +// SetNextVaultRotation calculates the next rotation time of a given schedule based on the time. +func (d *DefaultSchedule) SetNextVaultRotation(rs *RotationSchedule, t time.Time) { + if d.UsesTTL(rs) { + rs.NextVaultRotation = t.Add(rs.RotationPeriod) + } else { + rs.NextVaultRotation = rs.Schedule.Next(t) + } +} diff --git a/vault/activity/activity_log.pb.go b/vault/activity/activity_log.pb.go index 88a8bc08f7d9..6f4056c170ef 100644 --- a/vault/activity/activity_log.pb.go +++ b/vault/activity/activity_log.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.1 +// protoc-gen-go v1.36.0 // protoc (unknown) // source: vault/activity/activity_log.proto diff --git a/vault/core.go b/vault/core.go index 967e235552e9..bdae94d8238f 100644 --- a/vault/core.go +++ b/vault/core.go @@ -422,6 +422,9 @@ type Core struct { // renewal, expiration and revocation expiration *ExpirationManager + // the rotation manager handles periodic rotation of credentials + rotationManager *RotationManager + // rollback manager is used to run rollbacks periodically rollback *RollbackManager @@ -2612,6 +2615,9 @@ func buildUnsealSetupFunctionSlice(c *Core) []func(context.Context) error { setupFunctions = append(setupFunctions, func(_ context.Context) error { return c.setupExpiration(expireLeaseStrategyFairsharing) }) + setupFunctions = append(setupFunctions, func(_ context.Context) error { + return c.startRotation() + }) setupFunctions = append(setupFunctions, c.loadAudits) setupFunctions = append(setupFunctions, c.setupAuditedHeadersConfig) setupFunctions = append(setupFunctions, c.setupAudits) @@ -2910,6 +2916,9 @@ func (c *Core) preSeal() error { if err := c.stopExpiration(); err != nil { result = multierror.Append(result, fmt.Errorf("error stopping expiration: %w", err)) } + if err := c.stopRotation(); err != nil { + result = multierror.Append(result, fmt.Errorf("error stopping rotation: %w", err)) + } if err := c.teardownCredentials(context.Background()); err != nil { result = multierror.Append(result, fmt.Errorf("error tearing down credentials: %w", err)) } diff --git a/vault/core_test.go b/vault/core_test.go index 1681d2077510..d14052c6375c 100644 --- a/vault/core_test.go +++ b/vault/core_test.go @@ -3664,7 +3664,7 @@ func TestBuildUnsealSetupFunctionSlice(t *testing.T) { core: &Core{ replicationState: uint32Ptr(uint32(0)), }, - expectedLength: 25, + expectedLength: 26, }, { name: "dr secondary core", diff --git a/vault/dynamic_system_view.go b/vault/dynamic_system_view.go index 3c161ec5abc8..d24c0310ba6e 100644 --- a/vault/dynamic_system_view.go +++ b/vault/dynamic_system_view.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/vault/sdk/helper/pluginutil" "github.com/hashicorp/vault/sdk/helper/wrapping" "github.com/hashicorp/vault/sdk/logical" + "github.com/hashicorp/vault/sdk/rotation" "github.com/hashicorp/vault/vault/plugincatalog" "github.com/hashicorp/vault/version" "google.golang.org/protobuf/types/known/timestamppb" @@ -487,3 +488,27 @@ func (d dynamicSystemView) GenerateIdentityToken(ctx context.Context, req *plugi TTL: ttl, }, nil } + +func (d dynamicSystemView) RegisterRotationJob(ctx context.Context, job *rotation.RotationJob) (string, error) { + mountEntry := d.mountEntry + if mountEntry == nil { + return "", fmt.Errorf("no mount entry") + } + ns := mountEntry.Namespace() + path := job.Path + if ns != namespace.RootNamespace { + path = ns.Path + "/" + path + } + nsCtx := namespace.ContextWithNamespace(ctx, mountEntry.Namespace()) + id, err := d.core.RegisterRotationJob(nsCtx, path, job) + if err != nil { + return "", fmt.Errorf("error registering rotation job: %s", err) + } + + job.RotationID = id + return id, nil +} + +func (d dynamicSystemView) DeregisterRotationJob(_ context.Context, rotationID string) (err error) { + return d.core.DeregisterRotationJob(rotationID) +} diff --git a/vault/rotation_stubs_oss.go b/vault/rotation_stubs_oss.go new file mode 100644 index 000000000000..4798e9375c38 --- /dev/null +++ b/vault/rotation_stubs_oss.go @@ -0,0 +1,33 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1package vault + +//go:build !enterprise + +package vault + +//go:generate go run github.com/hashicorp/vault/tools/stubmaker + +import ( + "context" + + "github.com/hashicorp/vault/sdk/helper/automatedrotationutil" + "github.com/hashicorp/vault/sdk/rotation" +) + +type RotationManager struct{} + +func (c *Core) startRotation() error { + return nil +} + +func (c *Core) stopRotation() error { + return nil +} + +func (c *Core) RegisterRotationJob(_ context.Context, _ string, _ *rotation.RotationJob) (string, error) { + return "", automatedrotationutil.ErrRotationManagerUnsupported +} + +func (c *Core) DeregisterRotationJob(_ string) error { + return automatedrotationutil.ErrRotationManagerUnsupported +} From cb32dd03c0589d2950173270ff8e93048e6fa77e Mon Sep 17 00:00:00 2001 From: "Luis (LT) Carbonell" Date: Tue, 7 Jan 2025 20:18:11 -0500 Subject: [PATCH 09/25] Update docs for auth-cf (#29311) --- website/content/api-docs/auth/cf.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/content/api-docs/auth/cf.mdx b/website/content/api-docs/auth/cf.mdx index c5b9d95cc29e..f3b2c5f3a666 100644 --- a/website/content/api-docs/auth/cf.mdx +++ b/website/content/api-docs/auth/cf.mdx @@ -46,6 +46,8 @@ documentation](/vault/docs/auth/cf). - `login_max_seconds_not_after` `(int: 60)`: In case of clock drift, the maximum number of seconds in the future when a signature could have been created. The lower the value, the lower the risk of replay attacks. +- `cf_timeout` `(duration: 0s)`: The timeout for the CF API. If not set, the default + timeout is 0, which means no timeout. ### Sample payload From b74e2e798c86a5f3d99b5186168aa1aa9a0112b0 Mon Sep 17 00:00:00 2001 From: helenfufu <25168806+helenfufu@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:27:45 -0600 Subject: [PATCH 10/25] Vault 27421 update cap/ldap dep (#29302) * go get github.com/hashicorp/cap/ldap@main && go mod tidy * add 1.19 upgrade note * changelog * cd sdk && go get github.com/hashicorp/cap/ldap@main && go mod tidy * add more detail in changelog * update changelog * go mod tidy after resolving merge conflicts --- changelog/29302.txt | 3 +++ go.mod | 3 +-- go.sum | 4 ++-- sdk/go.mod | 10 ++++++++-- sdk/go.sum | 8 +++----- website/content/docs/upgrading/upgrade-to-1.19.x.mdx | 11 +++++++++++ 6 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 changelog/29302.txt diff --git a/changelog/29302.txt b/changelog/29302.txt new file mode 100644 index 000000000000..0caf8896c841 --- /dev/null +++ b/changelog/29302.txt @@ -0,0 +1,3 @@ +```release-note:change +auth/ldap: An error will now be returned on login if the number of entries returned from the user DN LDAP search is more than one. +``` diff --git a/go.mod b/go.mod index 3a4d2ec0d32f..972575dfc142 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/google/go-github v17.0.0+incompatible github.com/google/go-metrics-stackdriver v0.2.0 github.com/hashicorp/cap v0.7.0 - github.com/hashicorp/cap/ldap v0.0.0-20240403125925-c0418810d10e + github.com/hashicorp/cap/ldap v0.0.0-20250106213447-9047b8b3240f github.com/hashicorp/cli v1.1.6 github.com/hashicorp/consul-template v0.39.1 github.com/hashicorp/consul/api v1.29.1 @@ -388,7 +388,6 @@ require ( github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect - github.com/gofrs/uuid v4.3.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect diff --git a/go.sum b/go.sum index 4c040190e612..dd03ab8b7c4d 100644 --- a/go.sum +++ b/go.sum @@ -1376,8 +1376,8 @@ github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/cap v0.7.0 h1:atLIEU5lJslYXo1qsv7RtUL1HrJVVxnfkErIT3uxLp0= github.com/hashicorp/cap v0.7.0/go.mod h1:UynhCoGX3pxL0OfVrfMzPWAyjMYp96bk11BNTf2zt8o= -github.com/hashicorp/cap/ldap v0.0.0-20240403125925-c0418810d10e h1:IakB/NhT0YtMEGqAf2tViMdBABC2cMAZn3O/mVeg2j4= -github.com/hashicorp/cap/ldap v0.0.0-20240403125925-c0418810d10e/go.mod h1:Ofp5fMLl1ImcwjNGu9FtEwNOdxA0LYoWpcWQE2vltuI= +github.com/hashicorp/cap/ldap v0.0.0-20250106213447-9047b8b3240f h1:iixO0KNqHfSMImUgaHnMHTzmu0FVLwk7VzIZf6++wak= +github.com/hashicorp/cap/ldap v0.0.0-20250106213447-9047b8b3240f/go.mod h1:vGqAhHKOR5gadKWjwhoWp3RKto/tmhVOtH8gcD0c8ss= github.com/hashicorp/cli v1.1.6 h1:CMOV+/LJfL1tXCOKrgAX0uRKnzjj/mpmqNXloRSy2K8= github.com/hashicorp/cli v1.1.6/go.mod h1:MPon5QYlgjjo0BSoAiN0ESeT5fRzDjVRp+uioJ0piz4= github.com/hashicorp/consul-template v0.39.1 h1:MfhPoNENzCVSEXtE7CnIm3JkCzM9K0I7rcJYofm1BYY= diff --git a/sdk/go.mod b/sdk/go.mod index 2e243af41329..ad5623d16437 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -15,7 +15,7 @@ require ( github.com/go-test/deep v1.1.1 github.com/golang/protobuf v1.5.4 github.com/golang/snappy v0.0.4 - github.com/hashicorp/cap/ldap v0.0.0-20240403125925-c0418810d10e + github.com/hashicorp/cap/ldap v0.0.0-20250106213447-9047b8b3240f github.com/hashicorp/errwrap v1.1.0 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-hclog v1.6.3 @@ -38,7 +38,6 @@ require ( github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/golang-lru v1.0.2 github.com/hashicorp/hcl v1.0.1-vault-5 - github.com/hashicorp/vault v1.18.3 github.com/hashicorp/vault/api v1.15.0 github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/mapstructure v1.5.0 @@ -59,10 +58,14 @@ require ( cloud.google.com/go/auth v0.10.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.5 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/go-jose/go-jose/v4 v4.0.4 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/gofrs/uuid v4.3.0+incompatible // indirect github.com/hashicorp/go-hmac-drbg v0.0.0-20210916214228-a6e5a68489f6 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/sys/userns v0.1.0 // indirect @@ -71,8 +74,11 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect ) require ( diff --git a/sdk/go.sum b/sdk/go.sum index 94410aaaa8b7..5563affc1468 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -17,7 +17,6 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= @@ -163,8 +162,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= -github.com/hashicorp/cap/ldap v0.0.0-20240403125925-c0418810d10e h1:IakB/NhT0YtMEGqAf2tViMdBABC2cMAZn3O/mVeg2j4= -github.com/hashicorp/cap/ldap v0.0.0-20240403125925-c0418810d10e/go.mod h1:Ofp5fMLl1ImcwjNGu9FtEwNOdxA0LYoWpcWQE2vltuI= +github.com/hashicorp/cap/ldap v0.0.0-20250106213447-9047b8b3240f h1:iixO0KNqHfSMImUgaHnMHTzmu0FVLwk7VzIZf6++wak= +github.com/hashicorp/cap/ldap v0.0.0-20250106213447-9047b8b3240f/go.mod h1:vGqAhHKOR5gadKWjwhoWp3RKto/tmhVOtH8gcD0c8ss= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -220,8 +219,6 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/vault v1.18.3 h1:M5ZIM6N3qAfcmCfcxmBtFRD2KHIO7YdrbnMILMi9lto= -github.com/hashicorp/vault v1.18.3/go.mod h1:pm/2xflI/XldISU4G0qyL8P8wmoJpuXP8FzcXP3Pkcg= github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA= github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= @@ -568,6 +565,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/website/content/docs/upgrading/upgrade-to-1.19.x.mdx b/website/content/docs/upgrading/upgrade-to-1.19.x.mdx index 313d4e993ffc..294a00a0a58b 100644 --- a/website/content/docs/upgrading/upgrade-to-1.19.x.mdx +++ b/website/content/docs/upgrading/upgrade-to-1.19.x.mdx @@ -42,6 +42,17 @@ based on the table below. | CE | true | any value other than sha2-512 | An error is returned | Pure Ed25519 | | CE | true | sha2-512 | An error is returned (not supported on CE) | Pure Ed25519 | +### LDAP user DN search with `upndomain` + +The github.com/hashicorp/cap/ldap dependency has been upgraded to include a security improvement +which may be a breaking change for users. The enhancement ensures that user DN searches with +`upndomain` configured will now check that exactly one user is returned and error otherwise. +For more details, see https://github.com/hashicorp/cap/pull/151. + +In previous versions of Vault, multiple users could be returned when searching for the user DN +with `upndomain` configured, and the last user would be selected. As of 1.19.x, such searches will +error if multiple users are returned. + ## Known issues and workarounds @include 'known-issues/duplicate-hsm-key.mdx' From 2f95a73d1da6f2913f2a4e69a59d4108b6063342 Mon Sep 17 00:00:00 2001 From: Alexandr Hacicheant Date: Wed, 8 Jan 2025 22:32:41 +0200 Subject: [PATCH 11/25] Updated description of the field (#29146) Co-authored-by: akshya96 <87045294+akshya96@users.noreply.github.com> --- ui/app/models/database/connection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/models/database/connection.js b/ui/app/models/database/connection.js index 1ca2038cde5e..eebcd9616262 100644 --- a/ui/app/models/database/connection.js +++ b/ui/app/models/database/connection.js @@ -186,7 +186,7 @@ export default Model.extend({ tls_ca: attr('string', { label: 'TLS CA', helpText: - 'x509 CA file for validating the certificate presented by the MongoDB server. Must be PEM encoded.', + 'x509 CA file for validating the certificate presented by the database server. Must be PEM encoded.', editType: 'file', }), tls_server_name: attr('string', { From ad35129486d52ca8db22120b1df8666309f3c25b Mon Sep 17 00:00:00 2001 From: Thy Ton Date: Wed, 8 Jan 2025 12:39:28 -0800 Subject: [PATCH 12/25] update copywrite exceptions to exclude *_ent.go (#29315) --- scripts/copywrite-exceptions.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/copywrite-exceptions.sh b/scripts/copywrite-exceptions.sh index 0e55acb400d1..0c3c56d14642 100755 --- a/scripts/copywrite-exceptions.sh +++ b/scripts/copywrite-exceptions.sh @@ -1,10 +1,11 @@ #!/bin/sh # Used as a stopgap for copywrite bot in MPL-licensed subdirs, detects BUSL licensed -# headers and deletes them, then runs the copywrite bot to utilize local subdir config +# headers in only files intended for public users, and deletes them, +# then runs the copywrite bot to utilize local subdir config # to inject correct headers. -find . -type f -name '*.go' | while read line; do +find . -type f -name '*.go' -not -name '*_ent.go' | while read line; do if grep "SPDX-License-Identifier: BUSL-1.1" $line; then sed -i '/SPDX-License-Identifier: BUSL-1.1/d' $line sed -i '/Copyright (c) HashiCorp, Inc./d' $line From 607991e5519d406162b27a34ae5f9a08570706b0 Mon Sep 17 00:00:00 2001 From: Thy Ton Date: Wed, 8 Jan 2025 13:21:19 -0800 Subject: [PATCH 13/25] exclude *_ent_test.go from copywrite-exceptions.sh (#29319) --- scripts/copywrite-exceptions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/copywrite-exceptions.sh b/scripts/copywrite-exceptions.sh index 0c3c56d14642..4399b897422e 100755 --- a/scripts/copywrite-exceptions.sh +++ b/scripts/copywrite-exceptions.sh @@ -5,7 +5,7 @@ # then runs the copywrite bot to utilize local subdir config # to inject correct headers. -find . -type f -name '*.go' -not -name '*_ent.go' | while read line; do +find . -type f -name '*.go' -not -name '*_ent.go' -not -name '*_ent_test.go' | while read line; do if grep "SPDX-License-Identifier: BUSL-1.1" $line; then sed -i '/SPDX-License-Identifier: BUSL-1.1/d' $line sed -i '/Copyright (c) HashiCorp, Inc./d' $line From 357b2949e35bbd230ddb7496aa346e772d8f0017 Mon Sep 17 00:00:00 2001 From: Ryan Cragun Date: Wed, 8 Jan 2025 14:35:04 -0700 Subject: [PATCH 14/25] protobuf: rebuild protos with protobuf 1.36.2 (#29318) * protobuf: rebuild protos with protobuf 1.36.2 * format: please buf formatter Signed-off-by: Ryan Cragun --- builtin/logical/pki/metadata.pb.go | 2 +- helper/forwarding/types.pb.go | 2 +- helper/identity/mfa/types.pb.go | 2 +- helper/identity/types.pb.go | 2 +- helper/storagepacker/types.pb.go | 2 +- physical/raft/types.pb.go | 2 +- sdk/database/dbplugin/database.pb.go | 2 +- sdk/database/dbplugin/v5/proto/database.pb.go | 2 +- sdk/helper/clientcountutil/generation/generate_data.pb.go | 2 +- sdk/helper/pluginutil/multiplexing.pb.go | 2 +- sdk/logical/event.pb.go | 2 +- sdk/logical/identity.pb.go | 2 +- sdk/logical/plugin.pb.go | 2 +- sdk/logical/version.pb.go | 2 +- sdk/plugin/pb/backend.pb.go | 2 +- sdk/plugin/pb/backend.proto | 2 +- vault/activity/activity_log.pb.go | 2 +- vault/hcp_link/proto/link_control/link_control.pb.go | 2 +- vault/hcp_link/proto/meta/meta.pb.go | 2 +- vault/hcp_link/proto/node_status/status.pb.go | 2 +- vault/request_forwarding_service.pb.go | 2 +- vault/seal/multi_wrap_value.pb.go | 2 +- vault/tokens/token.pb.go | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/builtin/logical/pki/metadata.pb.go b/builtin/logical/pki/metadata.pb.go index 53574ddc5ebb..1339857d7694 100644 --- a/builtin/logical/pki/metadata.pb.go +++ b/builtin/logical/pki/metadata.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: builtin/logical/pki/metadata.proto diff --git a/helper/forwarding/types.pb.go b/helper/forwarding/types.pb.go index 3f262195d674..2053d19b7a86 100644 --- a/helper/forwarding/types.pb.go +++ b/helper/forwarding/types.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: helper/forwarding/types.proto diff --git a/helper/identity/mfa/types.pb.go b/helper/identity/mfa/types.pb.go index 730f6d77f70b..cc65ca61a080 100644 --- a/helper/identity/mfa/types.pb.go +++ b/helper/identity/mfa/types.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: helper/identity/mfa/types.proto diff --git a/helper/identity/types.pb.go b/helper/identity/types.pb.go index 6483f082a8c8..f1397ec1d1a2 100644 --- a/helper/identity/types.pb.go +++ b/helper/identity/types.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: helper/identity/types.proto diff --git a/helper/storagepacker/types.pb.go b/helper/storagepacker/types.pb.go index 3bb573ce07ac..e9cbab657a93 100644 --- a/helper/storagepacker/types.pb.go +++ b/helper/storagepacker/types.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: helper/storagepacker/types.proto diff --git a/physical/raft/types.pb.go b/physical/raft/types.pb.go index 2538564d816a..603b064c2418 100644 --- a/physical/raft/types.pb.go +++ b/physical/raft/types.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: physical/raft/types.proto diff --git a/sdk/database/dbplugin/database.pb.go b/sdk/database/dbplugin/database.pb.go index 7a4f43bca536..266683830f3f 100644 --- a/sdk/database/dbplugin/database.pb.go +++ b/sdk/database/dbplugin/database.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: sdk/database/dbplugin/database.proto diff --git a/sdk/database/dbplugin/v5/proto/database.pb.go b/sdk/database/dbplugin/v5/proto/database.pb.go index 3ca3ddb13cd8..d9c6d3599089 100644 --- a/sdk/database/dbplugin/v5/proto/database.pb.go +++ b/sdk/database/dbplugin/v5/proto/database.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: sdk/database/dbplugin/v5/proto/database.proto diff --git a/sdk/helper/clientcountutil/generation/generate_data.pb.go b/sdk/helper/clientcountutil/generation/generate_data.pb.go index 13062203a82d..cfa565235bf7 100644 --- a/sdk/helper/clientcountutil/generation/generate_data.pb.go +++ b/sdk/helper/clientcountutil/generation/generate_data.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: sdk/helper/clientcountutil/generation/generate_data.proto diff --git a/sdk/helper/pluginutil/multiplexing.pb.go b/sdk/helper/pluginutil/multiplexing.pb.go index 5c96a16a470b..6f3fb2c6d1e1 100644 --- a/sdk/helper/pluginutil/multiplexing.pb.go +++ b/sdk/helper/pluginutil/multiplexing.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: sdk/helper/pluginutil/multiplexing.proto diff --git a/sdk/logical/event.pb.go b/sdk/logical/event.pb.go index 14128d08c9a7..96989a7d420a 100644 --- a/sdk/logical/event.pb.go +++ b/sdk/logical/event.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: sdk/logical/event.proto diff --git a/sdk/logical/identity.pb.go b/sdk/logical/identity.pb.go index 4e4fec68e1d3..a511fe61b683 100644 --- a/sdk/logical/identity.pb.go +++ b/sdk/logical/identity.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: sdk/logical/identity.proto diff --git a/sdk/logical/plugin.pb.go b/sdk/logical/plugin.pb.go index 641e55425074..faa321d020d2 100644 --- a/sdk/logical/plugin.pb.go +++ b/sdk/logical/plugin.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: sdk/logical/plugin.proto diff --git a/sdk/logical/version.pb.go b/sdk/logical/version.pb.go index 7ffdb0e3cae9..ddf5a3be6bf4 100644 --- a/sdk/logical/version.pb.go +++ b/sdk/logical/version.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: sdk/logical/version.proto diff --git a/sdk/plugin/pb/backend.pb.go b/sdk/plugin/pb/backend.pb.go index 0083b4e386d9..346163880087 100644 --- a/sdk/plugin/pb/backend.pb.go +++ b/sdk/plugin/pb/backend.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: sdk/plugin/pb/backend.proto diff --git a/sdk/plugin/pb/backend.proto b/sdk/plugin/pb/backend.proto index 1bf023726505..f572b92a8a23 100644 --- a/sdk/plugin/pb/backend.proto +++ b/sdk/plugin/pb/backend.proto @@ -4,8 +4,8 @@ syntax = "proto3"; package pb; -import "google/protobuf/timestamp.proto"; import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; import "sdk/logical/event.proto"; import "sdk/logical/identity.proto"; import "sdk/logical/plugin.proto"; diff --git a/vault/activity/activity_log.pb.go b/vault/activity/activity_log.pb.go index 6f4056c170ef..68d6e4ab9602 100644 --- a/vault/activity/activity_log.pb.go +++ b/vault/activity/activity_log.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: vault/activity/activity_log.proto diff --git a/vault/hcp_link/proto/link_control/link_control.pb.go b/vault/hcp_link/proto/link_control/link_control.pb.go index 179d5ad87bba..a11ed1c4f3cc 100644 --- a/vault/hcp_link/proto/link_control/link_control.pb.go +++ b/vault/hcp_link/proto/link_control/link_control.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: vault/hcp_link/proto/link_control/link_control.proto diff --git a/vault/hcp_link/proto/meta/meta.pb.go b/vault/hcp_link/proto/meta/meta.pb.go index d28ad9546c05..7966137e4b6d 100644 --- a/vault/hcp_link/proto/meta/meta.pb.go +++ b/vault/hcp_link/proto/meta/meta.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: vault/hcp_link/proto/meta/meta.proto diff --git a/vault/hcp_link/proto/node_status/status.pb.go b/vault/hcp_link/proto/node_status/status.pb.go index 58929533da06..b7ac02833d4a 100644 --- a/vault/hcp_link/proto/node_status/status.pb.go +++ b/vault/hcp_link/proto/node_status/status.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: vault/hcp_link/proto/node_status/status.proto diff --git a/vault/request_forwarding_service.pb.go b/vault/request_forwarding_service.pb.go index 48f1d04ab361..113a31f910d8 100644 --- a/vault/request_forwarding_service.pb.go +++ b/vault/request_forwarding_service.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: vault/request_forwarding_service.proto diff --git a/vault/seal/multi_wrap_value.pb.go b/vault/seal/multi_wrap_value.pb.go index acd94cc1a062..f5f71d8dfc06 100644 --- a/vault/seal/multi_wrap_value.pb.go +++ b/vault/seal/multi_wrap_value.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: vault/seal/multi_wrap_value.proto diff --git a/vault/tokens/token.pb.go b/vault/tokens/token.pb.go index f3c3cd7963a1..93695f8cdb69 100644 --- a/vault/tokens/token.pb.go +++ b/vault/tokens/token.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.0 +// protoc-gen-go v1.36.2 // protoc (unknown) // source: vault/tokens/token.proto From ab4e8da6971aea487cbed92f4c736b4bbf0fd04d Mon Sep 17 00:00:00 2001 From: Bianca <48203644+biazmoreira@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:27:58 -0300 Subject: [PATCH 15/25] Port activation flags with dynamic registration (#29237) --- changelog/29237.txt | 3 + helper/activationflags/activation_flags.go | 142 ++++++++++++++++++ vault/core.go | 7 + vault/core_util.go | 3 + vault/logical_system.go | 1 + vault/logical_system_activation_flags.go | 140 +++++++++++++++++ vault/logical_system_activation_flags_test.go | 87 +++++++++++ vault/logical_system_helpers.go | 14 +- 8 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 changelog/29237.txt create mode 100644 helper/activationflags/activation_flags.go create mode 100644 vault/logical_system_activation_flags.go create mode 100644 vault/logical_system_activation_flags_test.go diff --git a/changelog/29237.txt b/changelog/29237.txt new file mode 100644 index 000000000000..796a8a5bc4a0 --- /dev/null +++ b/changelog/29237.txt @@ -0,0 +1,3 @@ +```release-note:improvement +core: Add activation flags. A mechanism for users to opt in to new functionality at a convenient time. Previously used only in Enterprise for SecretSync, activation flags are now available in CE for future features to use. +``` diff --git a/helper/activationflags/activation_flags.go b/helper/activationflags/activation_flags.go new file mode 100644 index 000000000000..fb031cae8399 --- /dev/null +++ b/helper/activationflags/activation_flags.go @@ -0,0 +1,142 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package activationflags + +import ( + "context" + "fmt" + "maps" + "sync" + + "github.com/hashicorp/vault/sdk/logical" +) + +const ( + storagePathActivationFlags = "activation-flags" +) + +type FeatureActivationFlags struct { + activationFlagsLock sync.RWMutex + storage logical.Storage + activationFlags map[string]bool +} + +func NewFeatureActivationFlags() *FeatureActivationFlags { + return &FeatureActivationFlags{ + activationFlags: map[string]bool{}, + } +} + +func (f *FeatureActivationFlags) Initialize(ctx context.Context, storage logical.Storage) error { + f.activationFlagsLock.Lock() + defer f.activationFlagsLock.Unlock() + + if storage == nil { + return fmt.Errorf("unable to access storage") + } + + f.storage = storage + + entry, err := f.storage.Get(ctx, storagePathActivationFlags) + if err != nil { + return fmt.Errorf("failed to get activation flags from storage: %w", err) + } + if entry == nil { + f.activationFlags = map[string]bool{} + return nil + } + + var activationFlags map[string]bool + if err := entry.DecodeJSON(&activationFlags); err != nil { + return fmt.Errorf("failed to decode activation flags from storage: %w", err) + } + + f.activationFlags = activationFlags + + return nil +} + +// Get is the helper function called by the activation-flags API read endpoint. This reads the +// actual values from storage, then updates the in-memory cache of the activation-flags. It +// returns a slice of the feature names which have already been activated. +func (f *FeatureActivationFlags) Get(ctx context.Context) ([]string, error) { + f.activationFlagsLock.Lock() + defer f.activationFlagsLock.Unlock() + + // Don't use nil slice declaration, we want the JSON to show "[]" instead of null + activated := []string{} + + if f.storage == nil { + return activated, nil + } + + entry, err := f.storage.Get(ctx, storagePathActivationFlags) + if err != nil { + return nil, fmt.Errorf("failed to get activation flags from storage: %w", err) + } + if entry == nil { + return activated, nil + } + + var activationFlags map[string]bool + if err := entry.DecodeJSON(&activationFlags); err != nil { + return nil, fmt.Errorf("failed to decode activation flags from storage: %w", err) + } + + // Update the in-memory flags after loading the latest values from storage + f.activationFlags = activationFlags + + for flag, set := range activationFlags { + if set { + activated = append(activated, flag) + } + } + + return activated, nil +} + +// Write is the helper function called by the activation-flags API write endpoint. This stores +// the boolean value for the activation-flag feature name into Vault storage across the cluster +// and updates the in-memory cache upon success. +func (f *FeatureActivationFlags) Write(ctx context.Context, featureName string, activate bool) (err error) { + f.activationFlagsLock.Lock() + defer f.activationFlagsLock.Unlock() + + if f.storage == nil { + return fmt.Errorf("unable to access storage") + } + + activationFlags := f.activationFlags + + clonedFlags := maps.Clone(f.activationFlags) + clonedFlags[featureName] = activate + // The cloned flags are updated but the in-memory state is only updated on success of the storage update. + defer func() { + if err == nil { + activationFlags[featureName] = activate + } + }() + + entry, err := logical.StorageEntryJSON(storagePathActivationFlags, clonedFlags) + if err != nil { + return fmt.Errorf("failed to marshal object to JSON: %w", err) + } + + err = f.storage.Put(ctx, entry) + if err != nil { + return fmt.Errorf("failed to save object in storage: %w", err) + } + + return nil +} + +// IsActivationFlagEnabled is true if the specified flag is enabled in the core. +func (f *FeatureActivationFlags) IsActivationFlagEnabled(featureName string) bool { + f.activationFlagsLock.RLock() + defer f.activationFlagsLock.RUnlock() + + activated, ok := f.activationFlags[featureName] + + return ok && activated +} diff --git a/vault/core.go b/vault/core.go index bdae94d8238f..56c267ea259d 100644 --- a/vault/core.go +++ b/vault/core.go @@ -45,6 +45,7 @@ import ( "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/command/server" + "github.com/hashicorp/vault/helper/activationflags" "github.com/hashicorp/vault/helper/identity/mfa" "github.com/hashicorp/vault/helper/locking" "github.com/hashicorp/vault/helper/metricsutil" @@ -739,6 +740,9 @@ type Core struct { clusterAddrBridge *raft.ClusterAddrBridge censusManager *CensusManager + + // Activation flags for enterprise features that require a one-time activation + FeatureActivationFlags *activationflags.FeatureActivationFlags } func (c *Core) ActiveNodeClockSkewMillis() int64 { @@ -1448,11 +1452,14 @@ func (c *Core) configureLogicalBackends(backends map[string]logical.Factory, log // System logicalBackends[mountTypeSystem] = func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) { sysBackendLogger := logger.Named("system") + c.AddLogger(sysBackendLogger) b := NewSystemBackend(c, sysBackendLogger, config) + if err := b.Setup(ctx, config); err != nil { return nil, err } + return b, nil } diff --git a/vault/core_util.go b/vault/core_util.go index aba50d415b91..549971b9e49e 100644 --- a/vault/core_util.go +++ b/vault/core_util.go @@ -10,6 +10,7 @@ import ( "fmt" "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/helper/activationflags" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/limits" "github.com/hashicorp/vault/sdk/helper/license" @@ -59,6 +60,8 @@ func coreInit(c *Core, conf *CoreConfig) error { c.physical = physical.NewStorageEncoding(c.physical) } + c.FeatureActivationFlags = activationflags.NewFeatureActivationFlags() + return nil } diff --git a/vault/logical_system.go b/vault/logical_system.go index 3e328fd1dc26..49dfc0c6864d 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -231,6 +231,7 @@ func NewSystemBackend(core *Core, logger log.Logger, config *logical.BackendConf b.Backend.Paths = append(b.Backend.Paths, b.experimentPaths()...) b.Backend.Paths = append(b.Backend.Paths, b.introspectionPaths()...) b.Backend.Paths = append(b.Backend.Paths, b.wellKnownPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.activationFlagsPaths()...) if core.rawEnabled { b.Backend.Paths = append(b.Backend.Paths, b.rawPaths()...) diff --git a/vault/logical_system_activation_flags.go b/vault/logical_system_activation_flags.go new file mode 100644 index 000000000000..596f9e6f128c --- /dev/null +++ b/vault/logical_system_activation_flags.go @@ -0,0 +1,140 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package vault + +import ( + "context" + "fmt" + "slices" + "strings" + + "github.com/hashicorp/vault/sdk/framework" + "github.com/hashicorp/vault/sdk/logical" +) + +const ( + paramFeatureName = "feature_name" + descFeatureName = "The name of the feature to be activated." + summaryList = "Returns the available and activated activation-flagged features." + summaryUpdate = "Activate a flagged feature." + + prefixActivationFlags = "activation-flags" + verbActivationFlagsActivate = "activate" + verbActivationFlagsDeactivate = "deactivate" + + fieldActivated = "activated" + fieldUnactivated = "unactivated" + + helpSynopsis = "Returns information about Vault's features that require a one-time activation step." + helpDescription = ` +This path responds to the following HTTP methods. + GET / + Returns the available and activated activation-flags. + + PUT|POST //activate + Activates the specified feature. Cannot be undone.` +) + +// Register CRUD functions dynamically. +// These variables should only be mutated during initialization or server construction. +// It is unsafe to modify them once the Vault core is running. +var ( + readActivationFlag = func(ctx context.Context, b *SystemBackend, req *logical.Request, fd *framework.FieldData) (*logical.Response, error) { + return b.readActivationFlag(ctx, req, fd) + } + + writeActivationFlag = func(ctx context.Context, b *SystemBackend, req *logical.Request, fd *framework.FieldData, isActivate bool) (*logical.Response, error) { + return b.writeActivationFlagWrite(ctx, req, fd, isActivate) + } +) + +func (b *SystemBackend) activationFlagsPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: fmt.Sprintf("%s$", prefixActivationFlags), + DisplayAttrs: &framework.DisplayAttributes{ + OperationVerb: "read", + OperationSuffix: prefixActivationFlags, + }, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handleActivationFlagRead, + Summary: summaryList, + }, + }, + HelpSynopsis: helpSynopsis, + HelpDescription: helpDescription, + }, + { + Pattern: fmt.Sprintf("%s/%s/%s", prefixActivationFlags, "activation-test", verbActivationFlagsActivate), + DisplayAttrs: &framework.DisplayAttributes{ + OperationPrefix: prefixActivationFlags, + OperationVerb: verbActivationFlagsActivate, + }, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleActivationFlagsActivate, + ForwardPerformanceSecondary: true, + ForwardPerformanceStandby: true, + Summary: summaryUpdate, + }, + }, + HelpSynopsis: helpSynopsis, + HelpDescription: helpDescription, + }, + } +} + +func (b *SystemBackend) handleActivationFlagRead(ctx context.Context, req *logical.Request, fd *framework.FieldData) (*logical.Response, error) { + return readActivationFlag(ctx, b, req, fd) +} + +func (b *SystemBackend) handleActivationFlagsActivate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + return writeActivationFlag(ctx, b, req, data, true) +} + +func (b *SystemBackend) readActivationFlag(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) { + activationFlags, err := b.Core.FeatureActivationFlags.Get(ctx) + if err != nil { + return nil, err + } + + return b.activationFlagsToResponse(activationFlags), nil +} + +func (b *SystemBackend) writeActivationFlagWrite(ctx context.Context, req *logical.Request, _ *framework.FieldData, isActivate bool) (*logical.Response, error) { + // We need to manually parse out the feature_name from the path because we can't use FieldSchema parameters + // in the path to make generic endpoints. We need each activation-flag path to be a separate endpoint. + // Path starts out as activation-flags//verb + // Removes activation-flags/ from the path + trimPrefix := strings.TrimPrefix(req.Path, prefixActivationFlags+"/") + // Removes /verb from the path + featureName := trimPrefix[:strings.LastIndex(trimPrefix, "/")] + + err := b.Core.FeatureActivationFlags.Write(ctx, featureName, isActivate) + if err != nil { + return nil, fmt.Errorf("failed to write new activation flags: %w", err) + } + + // We read back the value after writing it to storage so that we can try forcing a cache update right away. + // If this fails, it's still okay to proceed as the write has been successful and the cache will get updated + // at the time of an endpoint getting called. However, we can only return the one feature name we just activated + // in the response since the read to retrieve any others did not succeed. + activationFlags, err := b.Core.FeatureActivationFlags.Get(ctx) + if err != nil { + resp := b.activationFlagsToResponse([]string{featureName}) + return resp, fmt.Errorf("failed to read activation-flags back after write: %w", err) + } + + return b.activationFlagsToResponse(activationFlags), nil +} + +func (b *SystemBackend) activationFlagsToResponse(activationFlags []string) *logical.Response { + slices.Sort(activationFlags) + return &logical.Response{ + Data: map[string]interface{}{ + fieldActivated: activationFlags, + }, + } +} diff --git a/vault/logical_system_activation_flags_test.go b/vault/logical_system_activation_flags_test.go new file mode 100644 index 000000000000..aa2e17293f9d --- /dev/null +++ b/vault/logical_system_activation_flags_test.go @@ -0,0 +1,87 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package vault + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/sdk/logical" + "github.com/stretchr/testify/require" +) + +// TestActivationFlags_Read tests the read operation for the activation flags. +func TestActivationFlags_Read(t *testing.T) { + t.Run("given an initial state then read flags and expect all to be unactivated", func(t *testing.T) { + core, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{}) + + resp, err := core.systemBackend.HandleRequest( + context.Background(), + &logical.Request{ + Operation: logical.ReadOperation, + Path: prefixActivationFlags, + Storage: core.systemBarrierView, + }, + ) + + require.NoError(t, err) + require.Equal(t, resp.Data, map[string]interface{}{ + "activated": []string{}, + }) + }) +} + +// TestActivationFlags_BadFeatureName tests a nonexistent feature name or a missing feature name +// in the activation-flags path API call. +func TestActivationFlags_BadFeatureName(t *testing.T) { + core, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{}) + + tests := map[string]struct { + featureName string + }{ + "if no feature name is provided then expect unsupported path": { + featureName: "", + }, + "if an invalid feature name is provided then expect unsupported path": { + featureName: "fake-feature", + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + resp, err := core.router.Route( + namespace.ContextWithNamespace(context.Background(), namespace.RootNamespace), + &logical.Request{ + Operation: logical.UpdateOperation, + Path: fmt.Sprintf("sys/%s/%s/%s", prefixActivationFlags, tt.featureName, verbActivationFlagsActivate), + Storage: core.systemBarrierView, + }, + ) + + require.Error(t, err) + require.Nil(t, resp) + require.Equal(t, err, logical.ErrUnsupportedPath) + }) + } +} + +// TestActivationFlags_Write tests the write operations for the activation flags +func TestActivationFlags_Write(t *testing.T) { + t.Run("given an initial state then read flags and expect all to be unactivated", func(t *testing.T) { + core, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{}) + + _, err := core.systemBackend.HandleRequest( + context.Background(), + &logical.Request{ + Operation: logical.UpdateOperation, + Path: fmt.Sprintf("%s/%s/%s", prefixActivationFlags, "activation-test", verbActivationFlagsActivate), + Storage: core.systemBarrierView, + }, + ) + + require.NoError(t, err) + }) +} diff --git a/vault/logical_system_helpers.go b/vault/logical_system_helpers.go index 9deea02b5a02..79c30bad7f3e 100644 --- a/vault/logical_system_helpers.go +++ b/vault/logical_system_helpers.go @@ -31,9 +31,7 @@ var ( return nil } - sysInitialize = func(b *SystemBackend) func(context.Context, *logical.InitializationRequest) error { - return nil - } + sysInitialize = ceSysInitialize sysClean = func(b *SystemBackend) func(context.Context) { return nil @@ -280,6 +278,16 @@ var ( checkRaw = func(b *SystemBackend, path string) error { return nil } ) +func ceSysInitialize(b *SystemBackend) func(context.Context, *logical.InitializationRequest) error { + return func(ctx context.Context, req *logical.InitializationRequest) error { + err := b.Core.FeatureActivationFlags.Initialize(ctx, b.Core.systemBarrierView) + if err != nil { + return fmt.Errorf("failed to initialize activation flags: %w", err) + } + return nil + } +} + // Contains the config for a global plugin reload type pluginReloadRequest struct { Type string `json:"type"` // Either 'plugins' or 'mounts' From ed894b34252bab0b2b7cdbf6dcddf5024ae11c39 Mon Sep 17 00:00:00 2001 From: Paul Banks Date: Thu, 9 Jan 2025 15:49:28 +0000 Subject: [PATCH 16/25] Identity: add duplicate reporting to logs (#29325) * Identity: add duplicate reporting to logs * Add changelog * Fix breaking Ent change * Revert changes to existing ent test helper arguments as they will break on merge * Update changelog/29325.txt Co-authored-by: Bianca <48203644+biazmoreira@users.noreply.github.com> --------- Co-authored-by: Bianca <48203644+biazmoreira@users.noreply.github.com> --- changelog/29325.txt | 4 + vault/identity_store_conflicts.go | 278 +++++++++++++++++++++++++ vault/identity_store_conflicts_test.go | 180 ++++++++++++++++ vault/identity_store_test.go | 184 +++++++++++----- vault/identity_store_test_stubs_oss.go | 17 +- vault/identity_store_util.go | 146 ++++++++++++- vault/testing.go | 12 +- 7 files changed, 757 insertions(+), 64 deletions(-) create mode 100644 changelog/29325.txt create mode 100644 vault/identity_store_conflicts_test.go diff --git a/changelog/29325.txt b/changelog/29325.txt new file mode 100644 index 000000000000..5c213fea0a47 --- /dev/null +++ b/changelog/29325.txt @@ -0,0 +1,4 @@ +```release-note:improvement +identity: Added reporting in Vault logs during unseal to help identify any +duplicate identify resources in storage. +``` diff --git a/vault/identity_store_conflicts.go b/vault/identity_store_conflicts.go index 8495af1a25f4..23cabcf10ed0 100644 --- a/vault/identity_store_conflicts.go +++ b/vault/identity_store_conflicts.go @@ -6,7 +6,11 @@ package vault import ( "context" "errors" + "fmt" + "sort" + "strings" + "github.com/hashicorp/go-hclog" log "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/helper/identity" ) @@ -83,3 +87,277 @@ func (r *errorResolver) ResolveAliases(ctx context.Context, parent *identity.Ent return errDuplicateIdentityName } + +// duplicateReportingErrorResolver collects duplicate information and optionally +// logs a report on all the duplicates. We don't embed an errorResolver here +// because we _don't_ want it's side effect of warning on just some duplicates +// as we go as that's confusing when we have a more comprehensive report. The +// only other behavior it has is to return a constant error which we can just do +// ourselves. +type duplicateReportingErrorResolver struct { + // seen* track the unique factors for each identity artifact, so + // that we can report on any duplication including different-case duplicates + // when in case-sensitive mode. + // + // Since this is only ever called from `load*` methods on IdentityStore during + // an unseal we can assume that it's all from a single goroutine and does'nt + // need locking. + seenEntities map[string][]*identity.Entity + seenGroups map[string][]*identity.Group + seenAliases map[string][]*identity.Alias + seenLocalAliases map[string][]*identity.Alias + logger hclog.Logger +} + +func newDuplicateReportingErrorResolver(logger hclog.Logger) *duplicateReportingErrorResolver { + return &duplicateReportingErrorResolver{ + seenEntities: make(map[string][]*identity.Entity), + seenGroups: make(map[string][]*identity.Group), + seenAliases: make(map[string][]*identity.Alias), + seenLocalAliases: make(map[string][]*identity.Alias), + logger: logger, + } +} + +func (r *duplicateReportingErrorResolver) ResolveEntities(ctx context.Context, existing, duplicate *identity.Entity) error { + entityKey := fmt.Sprintf("%s/%s", duplicate.NamespaceID, strings.ToLower(duplicate.Name)) + r.seenEntities[entityKey] = append(r.seenEntities[entityKey], duplicate) + return errDuplicateIdentityName +} + +func (r *duplicateReportingErrorResolver) ResolveGroups(ctx context.Context, existing, duplicate *identity.Group) error { + groupKey := fmt.Sprintf("%s/%s", duplicate.NamespaceID, strings.ToLower(duplicate.Name)) + r.seenGroups[groupKey] = append(r.seenGroups[groupKey], duplicate) + return errDuplicateIdentityName +} + +func (r *duplicateReportingErrorResolver) ResolveAliases(ctx context.Context, parent *identity.Entity, existing, duplicate *identity.Alias) error { + aliasKey := fmt.Sprintf("%s/%s", duplicate.MountAccessor, strings.ToLower(duplicate.Name)) + if duplicate.Local { + r.seenLocalAliases[aliasKey] = append(r.seenLocalAliases[aliasKey], duplicate) + } else { + r.seenAliases[aliasKey] = append(r.seenAliases[aliasKey], duplicate) + } + return errDuplicateIdentityName +} + +type identityDuplicateReportEntry struct { + artifactType string + scope string + name string + id string + canonicalID string + resolutionHint string + index int // we care about preserving load order in reporting + numOthers int +} + +type identityDuplicateReport struct { + entities []identityDuplicateReportEntry + groups []identityDuplicateReportEntry + aliases []identityDuplicateReportEntry + localAliases []identityDuplicateReportEntry + numEntityDuplicates int + numGroupDuplicates int + numAliasDuplicates int + numLocalAliasDuplicates int +} + +func (r *identityDuplicateReportEntry) Description() string { + scopeField := "namespace ID" + if r.artifactType == "entity-alias" || r.artifactType == "local entity-alias" { + scopeField = "mount accessor" + } + return fmt.Sprintf("%s %q with %s %q duplicates %d others", + r.artifactType, r.name, scopeField, r.scope, r.numOthers) +} + +// Labels returns metadata pairs suitable for passing to a logger each slice +// element corresponds alternately to a key and then a value. +func (r *identityDuplicateReportEntry) Labels() []interface{} { + args := []interface{}{"id", r.id} + if r.canonicalID != "" { + args = append(args, "canonical_id") + args = append(args, r.canonicalID) + } + if r.resolutionHint != "" { + args = append(args, "force_deduplication") + args = append(args, r.resolutionHint) + } + return args +} + +func (r *duplicateReportingErrorResolver) Report() identityDuplicateReport { + var report identityDuplicateReport + + for _, entities := range r.seenEntities { + if len(entities) <= 1 { + // Fast path, skip non-duplicates + continue + } + report.numEntityDuplicates++ + // We don't care if it's an exact match or not for entities since we'll + // rename in either case when we force a de-dupe. + for idx, entity := range entities { + r := identityDuplicateReportEntry{ + artifactType: "entity", + scope: entity.NamespaceID, + name: entity.Name, + id: entity.ID, + index: idx, + numOthers: len(entities) - 1, + } + if idx < len(entities)-1 { + r.resolutionHint = fmt.Sprintf("would rename to %s-%s", entity.Name, entity.ID) + } else { + r.resolutionHint = "would not rename" + } + report.entities = append(report.entities, r) + } + } + sortReportEntries(report.entities) + + for _, groups := range r.seenGroups { + if len(groups) <= 1 { + // Fast path, skip non-duplicates + continue + } + report.numGroupDuplicates++ + // We don't care if it's an exact match or not for groups since we'll + // rename in either case when we force a de-dupe. + for idx, group := range groups { + r := identityDuplicateReportEntry{ + artifactType: "group", + scope: group.NamespaceID, + name: group.Name, + id: group.ID, + index: idx, + numOthers: len(groups) - 1, + } + if idx < len(groups)-1 { + r.resolutionHint = fmt.Sprintf("would rename to %s-%s", group.Name, group.ID) + } else { + r.resolutionHint = "would not rename" + } + report.groups = append(report.groups, r) + } + } + sortReportEntries(report.groups) + + reportAliases(&report, r.seenAliases, false) + reportAliases(&report, r.seenLocalAliases, true) + + return report +} + +func reportAliases(report *identityDuplicateReport, seen map[string][]*identity.Alias, local bool) { + artType := "entity-alias" + if local { + artType = "local entity-alias" + } + for _, aliases := range seen { + if len(aliases) <= 1 { + // Fast path, skip non-duplicates + continue + } + if local { + report.numLocalAliasDuplicates++ + } else { + report.numAliasDuplicates++ + } + // We can't have exact match duplicated for aliases at this point because + // the would have been merged during load. These are different-case + // duplicates that must be handled. + for idx, alias := range aliases { + r := identityDuplicateReportEntry{ + artifactType: artType, + scope: alias.MountAccessor, + name: alias.Name, + id: alias.ID, + canonicalID: alias.CanonicalID, + index: idx, + numOthers: len(aliases) - 1, + } + if idx < len(aliases)-1 { + r.resolutionHint = fmt.Sprintf("would merge into entity %s", aliases[len(aliases)-1].CanonicalID) + } else { + r.resolutionHint = "would merge others into this entity" + } + if local { + report.localAliases = append(report.localAliases, r) + } else { + report.aliases = append(report.aliases, r) + } + } + } + sortReportEntries(report.aliases) +} + +func sortReportEntries(es []identityDuplicateReportEntry) { + sort.Slice(es, func(i, j int) bool { + a, b := es[i], es[j] + if a.scope != b.scope { + return a.scope < b.scope + } + aName, bName := strings.ToLower(a.name), strings.ToLower(b.name) + if aName != bName { + return aName < bName + } + return a.index < b.index + }) +} + +// Warner is a subset of hclog.Logger that only has the Warn method to make +// testing simpler. +type Warner interface { + Warn(msg string, args ...interface{}) +} + +// TODO set this correctly. +const identityDuplicateReportUrl = "https://developer.hashicorp.com/vault/docs/upgrading/identity-deduplication" + +func (r *duplicateReportingErrorResolver) LogReport(log Warner) { + report := r.Report() + + if report.numEntityDuplicates == 0 && report.numGroupDuplicates == 0 && report.numAliasDuplicates == 0 { + return + } + + log.Warn("DUPLICATES DETECTED, see following logs for details and refer to " + + identityDuplicateReportUrl + " for resolution.") + + // Aliases first since they are most critical to resolve. Local first because + // all the rest can be ignored on a perf secondary. + if len(report.localAliases) > 0 { + log.Warn(fmt.Sprintf("%d different-case local entity alias duplicates found (potential security risk)", report.numLocalAliasDuplicates)) + for _, e := range report.localAliases { + log.Warn(e.Description(), e.Labels()...) + } + log.Warn("end of different-case local entity-alias duplicates") + } + if len(report.aliases) > 0 { + log.Warn(fmt.Sprintf("%d different-case entity alias duplicates found (potential security risk)", report.numAliasDuplicates)) + for _, e := range report.aliases { + log.Warn(e.Description(), e.Labels()...) + } + log.Warn("end of different-case entity-alias duplicates") + } + + if len(report.entities) > 0 { + log.Warn(fmt.Sprintf("%d entity duplicates found", report.numEntityDuplicates)) + for _, e := range report.entities { + log.Warn(e.Description(), e.Labels()...) + } + log.Warn("end of entity duplicates") + } + + if len(report.groups) > 0 { + log.Warn(fmt.Sprintf("%d group duplicates found", report.numGroupDuplicates)) + for _, e := range report.groups { + log.Warn(e.Description(), e.Labels()...) + } + log.Warn("end of group duplicates") + } + log.Warn("end of identity duplicate report, refer to " + + identityDuplicateReportUrl + " for resolution.") +} diff --git a/vault/identity_store_conflicts_test.go b/vault/identity_store_conflicts_test.go new file mode 100644 index 000000000000..ac757ec95069 --- /dev/null +++ b/vault/identity_store_conflicts_test.go @@ -0,0 +1,180 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package vault + +import ( + "bytes" + "context" + "fmt" + "strings" + "testing" + + log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/helper/identity" + "github.com/stretchr/testify/require" +) + +// TestDuplicateReportingErrorResolver tests that the reporting error resolver +// correctly records, identifies, sorts and outputs information about duplicate +// entities. +func TestDuplicateReportingErrorResolver(t *testing.T) { + t.Parallel() + + entities := [][]string{ + // Some unduplicated entities in a different namespaces + {"root", "foo"}, + {"root", "bar"}, + {"admin", "foo"}, + {"developers", "BAR"}, + + // Some exact-match duplicates in different namespaces + {"root", "exact-dupe-1"}, + {"root", "exact-dupe-1"}, + {"admin", "exact-dupe-1"}, + {"admin", "exact-dupe-1"}, + + // Some different-case duplicates in different namespaces + {"root", "different-case-dupe-1"}, + {"root", "DIFFERENT-CASE-DUPE-1"}, + {"admin", "different-case-dupe-1"}, + {"admin", "DIFFERENT-case-DUPE-1"}, + {"admin", "different-case-DUPE-1"}, + } + + // Note that `local-` prefix here is used to define a mount as local as well + // as used in it's name. + aliases := [][]string{ + // Unduplicated aliases on different mounts + {"mount1", "alias1"}, + {"mount2", "alias1"}, + {"mount2", "alias2"}, + {"local-mount", "alias1"}, + + // We don't bother testing exact-match aliases since they will have been + // merged by the time they are reported) + + // Different-case aliases on different mounts (some local) + {"mount1", "different-case-alias-1"}, + {"mount1", "DIFFERENT-CASE-ALIAS-1"}, + {"mount2", "different-case-alias-1"}, + {"mount2", "DIFFERENT-CASE-ALIAS-1"}, + {"mount2", "different-CASE-ALIAS-1"}, + {"local-mount", "DIFFERENT-CASE-ALIAS-1"}, + {"local-mount", "different-CASE-ALIAS-1"}, + } + + expectReport := ` +DUPLICATES DETECTED, see following logs for details and refer to https://developer.hashicorp.com/vault/docs/upgrading/identity-deduplication for resolution.: +1 different-case local entity alias duplicates found (potential security risk): +local entity-alias "DIFFERENT-CASE-ALIAS-1" with mount accessor "local-mount" duplicates 1 others: id="00000000-0000-0000-0000-000000000009" canonical_id="11111111-0000-0000-0000-000000000009" force_deduplication="would merge into entity 11111111-0000-0000-0000-000000000010" +local entity-alias "different-CASE-ALIAS-1" with mount accessor "local-mount" duplicates 1 others: id="00000000-0000-0000-0000-000000000010" canonical_id="11111111-0000-0000-0000-000000000010" force_deduplication="would merge others into this entity" +end of different-case local entity-alias duplicates: +2 different-case entity alias duplicates found (potential security risk): +entity-alias "different-case-alias-1" with mount accessor "mount1" duplicates 1 others: id="00000000-0000-0000-0000-000000000004" canonical_id="11111111-0000-0000-0000-000000000004" force_deduplication="would merge into entity 11111111-0000-0000-0000-000000000005" +entity-alias "DIFFERENT-CASE-ALIAS-1" with mount accessor "mount1" duplicates 1 others: id="00000000-0000-0000-0000-000000000005" canonical_id="11111111-0000-0000-0000-000000000005" force_deduplication="would merge others into this entity" +entity-alias "different-case-alias-1" with mount accessor "mount2" duplicates 2 others: id="00000000-0000-0000-0000-000000000006" canonical_id="11111111-0000-0000-0000-000000000006" force_deduplication="would merge into entity 11111111-0000-0000-0000-000000000008" +entity-alias "DIFFERENT-CASE-ALIAS-1" with mount accessor "mount2" duplicates 2 others: id="00000000-0000-0000-0000-000000000007" canonical_id="11111111-0000-0000-0000-000000000007" force_deduplication="would merge into entity 11111111-0000-0000-0000-000000000008" +entity-alias "different-CASE-ALIAS-1" with mount accessor "mount2" duplicates 2 others: id="00000000-0000-0000-0000-000000000008" canonical_id="11111111-0000-0000-0000-000000000008" force_deduplication="would merge others into this entity" +end of different-case entity-alias duplicates: +4 entity duplicates found: +entity "different-case-dupe-1" with namespace ID "admin" duplicates 2 others: id="00000000-0000-0000-0000-000000000010" force_deduplication="would rename to different-case-dupe-1-00000000-0000-0000-0000-000000000010" +entity "DIFFERENT-case-DUPE-1" with namespace ID "admin" duplicates 2 others: id="00000000-0000-0000-0000-000000000011" force_deduplication="would rename to DIFFERENT-case-DUPE-1-00000000-0000-0000-0000-000000000011" +entity "different-case-DUPE-1" with namespace ID "admin" duplicates 2 others: id="00000000-0000-0000-0000-000000000012" force_deduplication="would not rename" +entity "exact-dupe-1" with namespace ID "admin" duplicates 1 others: id="00000000-0000-0000-0000-000000000006" force_deduplication="would rename to exact-dupe-1-00000000-0000-0000-0000-000000000006" +entity "exact-dupe-1" with namespace ID "admin" duplicates 1 others: id="00000000-0000-0000-0000-000000000007" force_deduplication="would not rename" +entity "different-case-dupe-1" with namespace ID "root" duplicates 1 others: id="00000000-0000-0000-0000-000000000008" force_deduplication="would rename to different-case-dupe-1-00000000-0000-0000-0000-000000000008" +entity "DIFFERENT-CASE-DUPE-1" with namespace ID "root" duplicates 1 others: id="00000000-0000-0000-0000-000000000009" force_deduplication="would not rename" +entity "exact-dupe-1" with namespace ID "root" duplicates 1 others: id="00000000-0000-0000-0000-000000000004" force_deduplication="would rename to exact-dupe-1-00000000-0000-0000-0000-000000000004" +entity "exact-dupe-1" with namespace ID "root" duplicates 1 others: id="00000000-0000-0000-0000-000000000005" force_deduplication="would not rename" +end of entity duplicates: +4 group duplicates found: +group "different-case-dupe-1" with namespace ID "admin" duplicates 2 others: id="00000000-0000-0000-0000-000000000010" force_deduplication="would rename to different-case-dupe-1-00000000-0000-0000-0000-000000000010" +group "DIFFERENT-case-DUPE-1" with namespace ID "admin" duplicates 2 others: id="00000000-0000-0000-0000-000000000011" force_deduplication="would rename to DIFFERENT-case-DUPE-1-00000000-0000-0000-0000-000000000011" +group "different-case-DUPE-1" with namespace ID "admin" duplicates 2 others: id="00000000-0000-0000-0000-000000000012" force_deduplication="would not rename" +group "exact-dupe-1" with namespace ID "admin" duplicates 1 others: id="00000000-0000-0000-0000-000000000006" force_deduplication="would rename to exact-dupe-1-00000000-0000-0000-0000-000000000006" +group "exact-dupe-1" with namespace ID "admin" duplicates 1 others: id="00000000-0000-0000-0000-000000000007" force_deduplication="would not rename" +group "different-case-dupe-1" with namespace ID "root" duplicates 1 others: id="00000000-0000-0000-0000-000000000008" force_deduplication="would rename to different-case-dupe-1-00000000-0000-0000-0000-000000000008" +group "DIFFERENT-CASE-DUPE-1" with namespace ID "root" duplicates 1 others: id="00000000-0000-0000-0000-000000000009" force_deduplication="would not rename" +group "exact-dupe-1" with namespace ID "root" duplicates 1 others: id="00000000-0000-0000-0000-000000000004" force_deduplication="would rename to exact-dupe-1-00000000-0000-0000-0000-000000000004" +group "exact-dupe-1" with namespace ID "root" duplicates 1 others: id="00000000-0000-0000-0000-000000000005" force_deduplication="would not rename" +end of group duplicates: +end of identity duplicate report, refer to https://developer.hashicorp.com/vault/docs/upgrading/identity-deduplication for resolution.: +` + + // Create a new errorResolver + r := newDuplicateReportingErrorResolver(log.NewNullLogger()) + + for i, pair := range entities { + // Create a fake UUID based on the index this makes sure sort order is + // preserved when eyeballing the expected report. + id := fmt.Sprintf("00000000-0000-0000-0000-%012d", i) + // Create a new entity with the pair + entity := &identity.Entity{ + ID: id, + Name: pair[1], + NamespaceID: pair[0], + } + + // Call ResolveEntities, assume existing is nil for now. In real life we + // should be passed the existing entity for the exact match dupes but we + // don't depend on that so it's fine to omit. + _ = r.ResolveEntities(context.Background(), nil, entity) + // Don't care about the actual error here since it would be ignored in + // case-sensitive mode anyway. + + // Also, since the data model is the same, pretend these are groups too + group := &identity.Group{ + ID: id, + Name: pair[1], + NamespaceID: pair[0], + } + _ = r.ResolveGroups(context.Background(), nil, group) + } + + // Load aliases second because that is realistic and yet we want to report on + // them first. + for i, pair := range aliases { + entity := &identity.Entity{ + ID: fmt.Sprintf("11111111-0000-0000-0000-%012d", i), + Name: pair[1] + "-entity", + NamespaceID: "root", + } + alias := &identity.Alias{ + ID: fmt.Sprintf("00000000-0000-0000-0000-%012d", i), + CanonicalID: entity.ID, + Name: pair[1], + MountAccessor: pair[0], + // Parse our hacky DSL to define some alias mounts as local + Local: strings.HasPrefix(pair[0], "local-"), + } + _ = r.ResolveAliases(context.Background(), entity, nil, alias) + } + + // "log" the report and check it matches expected report below. + var testLog identityTestWarnLogger + r.LogReport(&testLog) + + // Dump the raw report to make it easier to copy paste/read + t.Log("\n\n" + testLog.buf.String()) + + require.Equal(t, + strings.TrimSpace(expectReport), + strings.TrimSpace(testLog.buf.String()), + ) +} + +type identityTestWarnLogger struct { + buf bytes.Buffer +} + +func (l *identityTestWarnLogger) Warn(msg string, args ...interface{}) { + l.buf.WriteString(msg + ":") + if len(args)%2 != 0 { + panic("args must be key-value pairs") + } + for i := 0; i < len(args); i += 2 { + l.buf.WriteString(fmt.Sprintf(" %s=%q", args[i], args[i+1])) + } + l.buf.WriteString("\n") +} diff --git a/vault/identity_store_test.go b/vault/identity_store_test.go index cce4da413cd3..7ad41261ed13 100644 --- a/vault/identity_store_test.go +++ b/vault/identity_store_test.go @@ -7,12 +7,16 @@ import ( "context" "fmt" "math/rand" + "regexp" + "slices" + "strconv" "strings" "testing" "time" "github.com/armon/go-metrics" "github.com/go-test/deep" + "github.com/hashicorp/go-hclog" uuid "github.com/hashicorp/go-uuid" credGithub "github.com/hashicorp/vault/builtin/credential/github" "github.com/hashicorp/vault/builtin/credential/userpass" @@ -1457,10 +1461,10 @@ func TestEntityStoreLoadingIsDeterministic(t *testing.T) { // probability that is unrealistic but ensures we have duplicates on every // test run with high probability and more than 1 duplicate often. for i := 0; i <= 100; i++ { - id := fmt.Sprintf("entity-%d", i) + name := fmt.Sprintf("entity-%d", i) alias := fmt.Sprintf("alias-%d", i) localAlias := fmt.Sprintf("localalias-%d", i) - e := makeEntityForPacker(t, id, c.identityStore.entityPacker) + e := makeEntityForPacker(t, name, c.identityStore.entityPacker) attachAlias(t, e, alias, upme) attachAlias(t, e, localAlias, localMe) err = TestHelperWriteToStoragePacker(ctx, c.identityStore.entityPacker, e.ID, e) @@ -1493,6 +1497,14 @@ func TestEntityStoreLoadingIsDeterministic(t *testing.T) { rnd = rand.Float64() dupeNum++ } + // See if we should add entity _name_ duplicates too (with no aliases) + rnd = rand.Float64() + for rnd < pDup { + e := makeEntityForPacker(t, name, c.identityStore.entityPacker) + err = TestHelperWriteToStoragePacker(ctx, c.identityStore.entityPacker, e.ID, e) + require.NoError(t, err) + rnd = rand.Float64() + } // One more edge case is that it's currently possible as of the time of // writing for a failure during entity invalidation to result in a permanent // "cached" entity in the local alias packer even though we do have the @@ -1511,23 +1523,27 @@ func TestEntityStoreLoadingIsDeterministic(t *testing.T) { // Create some groups for i := 0; i <= 100; i++ { - id := fmt.Sprintf("group-%d", i) - bucketKey := c.identityStore.groupPacker.BucketKey(id) + name := fmt.Sprintf("group-%d", i) // Add an alias to every other group alias := "" if i%2 == 0 { alias = fmt.Sprintf("groupalias-%d", i) } - e := makeGroupWithIDAndAlias(t, id, alias, bucketKey, upme) + e := makeGroupWithNameAndAlias(t, name, alias, c.identityStore.groupPacker, upme) err = TestHelperWriteToStoragePacker(ctx, c.identityStore.groupPacker, e.ID, e) require.NoError(t, err) } // Now add 10 groups with the same alias to ensure duplicates don't cause // non-deterministic behavior. for i := 0; i <= 10; i++ { - id := fmt.Sprintf("group-dup-%d", i) - bucketKey := c.identityStore.groupPacker.BucketKey(id) - e := makeGroupWithIDAndAlias(t, id, "groupalias-dup", bucketKey, upme) + name := fmt.Sprintf("group-dup-%d", i) + e := makeGroupWithNameAndAlias(t, name, "groupalias-dup", c.identityStore.groupPacker, upme) + err = TestHelperWriteToStoragePacker(ctx, c.identityStore.groupPacker, e.ID, e) + require.NoError(t, err) + } + // Add a second and third groups with duplicate names too. + for _, name := range []string{"group-0", "group-1", "group-1"} { + e := makeGroupWithNameAndAlias(t, name, "", c.identityStore.groupPacker, upme) err = TestHelperWriteToStoragePacker(ctx, c.identityStore.groupPacker, e.ID, e) require.NoError(t, err) } @@ -1539,14 +1555,14 @@ func TestEntityStoreLoadingIsDeterministic(t *testing.T) { // To test that this is deterministic we need to load from storage a bunch of // times and make sure we get the same result. For easier debugging we'll // build a list of human readable ids that we can compare. - lastIDs := []string{} + prevLoadedNames := []string{} for i := 0; i < 10; i++ { // Seal and unseal to reload the identity store require.NoError(t, c.Seal(rootToken)) require.True(t, c.Sealed()) for _, key := range sealKeys { unsealed, err := c.Unseal(key) - require.NoError(t, err) + require.NoError(t, err, "failed unseal on attempt %d", i) if unsealed { break } @@ -1554,7 +1570,7 @@ func TestEntityStoreLoadingIsDeterministic(t *testing.T) { require.False(t, c.Sealed()) // Identity store should be loaded now. Check it's contents. - loadedIDs := []string{} + loadedNames := []string{} tx := c.identityStore.db.Txn(false) @@ -1562,73 +1578,143 @@ func TestEntityStoreLoadingIsDeterministic(t *testing.T) { iter, err := tx.LowerBound(entitiesTable, "id", "") require.NoError(t, err) for item := iter.Next(); item != nil; item = iter.Next() { - // We already added "type" prefixes to the IDs when creating them so just - // append here. e := item.(*identity.Entity) - loadedIDs = append(loadedIDs, e.ID) + loadedNames = append(loadedNames, e.Name) for _, a := range e.Aliases { - loadedIDs = append(loadedIDs, a.ID) + loadedNames = append(loadedNames, a.Name) } } // This is a non-triviality check to make sure we actually loaded stuff and // are not just passing because of a bug in the test. - numLoaded := len(loadedIDs) + numLoaded := len(loadedNames) require.Greater(t, numLoaded, 300, "not enough entities and aliases loaded on attempt %d", i) + // Standalone alias query + iter, err = tx.LowerBound(entityAliasesTable, "id", "") + require.NoError(t, err) + for item := iter.Next(); item != nil; item = iter.Next() { + a := item.(*identity.Alias) + loadedNames = append(loadedNames, a.Name) + } + // Groups iter, err = tx.LowerBound(groupsTable, "id", "") require.NoError(t, err) for item := iter.Next(); item != nil; item = iter.Next() { g := item.(*identity.Group) - loadedIDs = append(loadedIDs, g.ID) + loadedNames = append(loadedNames, g.Name) if g.Alias != nil { - loadedIDs = append(loadedIDs, g.Alias.ID) + loadedNames = append(loadedNames, g.Alias.Name) } } // This is a non-triviality check to make sure we actually loaded stuff and // are not just passing because of a bug in the test. - groupsLoaded := len(loadedIDs) - numLoaded + groupsLoaded := len(loadedNames) - numLoaded require.Greater(t, groupsLoaded, 140, "not enough groups and aliases loaded on attempt %d", i) - entIdentityStoreDeterminismAssert(t, i, loadedIDs, lastIDs) + // note `lastIDs` argument is not needed any more but we can't change the + // signature without breaking enterprise. It's simpler to keep it unused for + // now until both parts of this merge. + entIdentityStoreDeterminismAssert(t, i, loadedNames, nil) if i > 0 { // Should be in the same order if we are deterministic since MemDB has strong ordering. - require.Equal(t, lastIDs, loadedIDs, "different result on attempt %d", i) + require.Equal(t, prevLoadedNames, loadedNames, "different result on attempt %d", i) } - lastIDs = loadedIDs + + prevLoadedNames = loadedNames } } -func makeGroupWithIDAndAlias(t *testing.T, id, alias, bucketKey string, me *MountEntry) *identity.Group { - g := &identity.Group{ - ID: id, - Name: id, - NamespaceID: namespace.RootNamespaceID, - BucketKey: bucketKey, - } - if alias != "" { - g.Alias = &identity.Alias{ - ID: id, - Name: alias, - CanonicalID: id, - MountType: me.Type, - MountAccessor: me.Accessor, - } +// TestEntityStoreLoadingDuplicateReporting tests the reporting of different +// types of duplicates during unseal when in case-sensitive mode. +func TestEntityStoreLoadingDuplicateReporting(t *testing.T) { + logger := corehelpers.NewTestLogger(t) + ims, err := inmem.NewTransactionalInmemHA(nil, logger) + require.NoError(t, err) + + cfg := &CoreConfig{ + Physical: ims, + HAPhysical: ims.(physical.HABackend), + Logger: logger, + BuiltinRegistry: corehelpers.NewMockBuiltinRegistry(), + CredentialBackends: map[string]logical.Factory{ + "userpass": userpass.Factory, + }, } - return g -} -func makeLocalAliasWithID(t *testing.T, id, entityID string, bucketKey string, me *MountEntry) *identity.LocalAliases { - return &identity.LocalAliases{ - Aliases: []*identity.Alias{ - { - ID: id, - Name: id, - CanonicalID: entityID, - MountType: me.Type, - MountAccessor: me.Accessor, - }, + c, sealKeys, rootToken := TestCoreUnsealedWithConfig(t, cfg) + + // Inject values into storage + upme, err := TestUserpassMount(c, false) + require.NoError(t, err) + localMe, err := TestUserpassMount(c, true) + require.NoError(t, err) + + ctx := namespace.RootContext(nil) + + identityCreateCaseDuplicates(t, ctx, c, upme, localMe) + + entIdentityStoreDuplicateReportTestSetup(t, ctx, c, rootToken) + + // Storage is now primed for the test. + + // Seal and unseal to reload the identity store + require.NoError(t, c.Seal(rootToken)) + require.True(t, c.Sealed()) + + // Setup a logger we can use to capture unseal logs + var unsealLogs []string + unsealLogger := &logFn{ + fn: func(msg string, args []interface{}) { + pairs := make([]string, 0, len(args)/2) + for pair := range slices.Chunk(args, 2) { + // Yes this will panic if we didn't log an even number of args but thats + // OK because that's a bug! + pairs = append(pairs, fmt.Sprintf("%s=%s", pair[0], pair[1])) + } + unsealLogs = append(unsealLogs, fmt.Sprintf("%s: %s", msg, strings.Join(pairs, " "))) }, } + logger.RegisterSink(unsealLogger) + + for _, key := range sealKeys { + unsealed, err := c.Unseal(key) + require.NoError(t, err) + if unsealed { + break + } + } + require.False(t, c.Sealed()) + logger.DeregisterSink(unsealLogger) + + // Identity store should be loaded now. Check it's contents. + + // We don't expect any actual behavior change just logs reporting duplicates. + // We could assert the current "expected" behavior but it's actually broken in + // many of these cases and seems strange to encode in a test that we want + // broken behavior! + numDupes := make(map[string]int) + duplicateCountRe := regexp.MustCompile(`(\d+) (different-case( local)? entity alias|entity|group) duplicates found`) + for _, log := range unsealLogs { + if matches := duplicateCountRe.FindStringSubmatch(log); len(matches) >= 3 { + num, _ := strconv.Atoi(matches[1]) + numDupes[matches[2]] = num + } + } + t.Logf("numDupes: %v", numDupes) + wantAliases, wantLocalAliases, wantEntities, wantGroups := identityStoreDuplicateReportTestWantDuplicateCounts() + require.Equal(t, wantLocalAliases, numDupes["different-case local entity alias"]) + require.Equal(t, wantAliases, numDupes["different-case entity alias"]) + require.Equal(t, wantEntities, numDupes["entity"]) + require.Equal(t, wantGroups, numDupes["group"]) +} + +type logFn struct { + fn func(msg string, args []interface{}) +} + +// Accept implements hclog.SinkAdapter +func (f *logFn) Accept(name string, level hclog.Level, msg string, args ...interface{}) { + f.fn(msg, args) } diff --git a/vault/identity_store_test_stubs_oss.go b/vault/identity_store_test_stubs_oss.go index e3a570388522..821b62428ce3 100644 --- a/vault/identity_store_test_stubs_oss.go +++ b/vault/identity_store_test_stubs_oss.go @@ -12,10 +12,25 @@ import ( //go:generate go run github.com/hashicorp/vault/tools/stubmaker -func entIdentityStoreDeterminismTestSetup(t *testing.T, ctx context.Context, c *Core, upme, localme *MountEntry) { +func entIdentityStoreDeterminismTestSetup(t *testing.T, ctx context.Context, c *Core, me, localme *MountEntry) { // no op } func entIdentityStoreDeterminismAssert(t *testing.T, i int, loadedIDs, lastIDs []string) { // no op } + +func entIdentityStoreDuplicateReportTestSetup(t *testing.T, ctx context.Context, c *Core, rootToken string) { + // no op +} + +func identityStoreDuplicateReportTestWantDuplicateCounts() (int, int, int, int) { + // Note that the second count is for local aliases. CE Vault doesn't really + // distinguish between local and non-local aliases because it doesn't have any + // support for Performance Replication. But it's possible in code at least to + // set the local flag on a mount or alias during creation so we might as well + // test it behaves as expected in the CE code. It's maybe just about possible + // that this could happen in real life too because of a downgrade from + // Enterprise. + return 1, 1, 1, 1 +} diff --git a/vault/identity_store_util.go b/vault/identity_store_util.go index 845b0672d1b4..602f34016e3e 100644 --- a/vault/identity_store_util.go +++ b/vault/identity_store_util.go @@ -23,6 +23,7 @@ import ( "github.com/hashicorp/vault/helper/storagepacker" "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/sdk/logical" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -83,9 +84,19 @@ func (c *Core) loadIdentityStoreArtifacts(ctx context.Context) error { return err } + // Also reset the conflict resolver so that we report potential duplicates to + // be resolved before it's safe to return to case-insensitive mode. + reporterResolver := newDuplicateReportingErrorResolver(c.identityStore.logger) + c.identityStore.conflictResolver = reporterResolver + // Attempt to load identity artifacts once more after memdb is reset to // accept case sensitive names - return loadFunc(ctx) + err = loadFunc(ctx) + + // Log reported duplicates if any found whether or not we end up erroring. + reporterResolver.LogReport(c.identityStore.logger) + + return err } func (i *IdentityStore) sanitizeName(name string) string { @@ -623,8 +634,8 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e default: // Though this is technically a conflict that should be resolved by the // ConflictResolver implementation, the behavior here is a bit nuanced. - // Rather than introduce a behavior change, handle this case directly as - // before by merging. + // Rather than introduce a behavior change, we handle this case directly + // as before by merging. i.logger.Warn("alias is already tied to a different entity; these entities are being merged", "alias_id", alias.ID, "other_entity_id", aliasByFactors.CanonicalID, @@ -644,10 +655,34 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e return nil } - if strutil.StrListContains(aliasFactors, i.sanitizeName(alias.Name)+alias.MountAccessor) { - if err := i.conflictResolver.ResolveAliases(ctx, entity, aliasByFactors, alias); err != nil && !i.disableLowerCasedNames { - return err - } + // This is subtle. We want to call `ResolveAliases` so that the resolver can + // get full insight into all the aliases loaded and generate useful reports + // about duplicates. However, we don't want to actually change the error + // handling behavior from before which would only return an error in a very + // specific case (when the alias being added is a duplicate of one for the + // same entity and we are not in case-sensitive mode). So we call the method + // here unconditionally, but then only handle the resultant error in the + // specific case we care about. Note that we choose not to call it `err` to + // avoid it being left non-nil in some cases and tripping up later error + // handling code, and to signal something different is happening here. Note + // that we explicitly _want_ this to be here an not before we merge + // duplicates above, because duplicates that have always merged are not a + // problem to the user and are already logged. We care about different-case + // duplicates that are not being considered duplicates right now because we + // are in case-sensitive mode so we can report these to the operator ahead + // of them disabling case-sensitive mode. + conflictErr := i.conflictResolver.ResolveAliases(ctx, entity, aliasByFactors, alias) + + // This appears to be accounting for any duplicate aliases for the same + // Entity. In that case we would have skipped over the merge above in the + // `aliasByFactors.CanonicalID == entity.ID` case and made it here. Now we + // are here, duplicates are reported and may cause an insert error but only + // if we are in default case-insensitive mode. Once we are in case-sensitive + // mode we'll happily ignore duplicates of any case! This doesn't seem + // especially desirable to me, but we'd rather not change behavior for now. + if strutil.StrListContains(aliasFactors, i.sanitizeName(alias.Name)+alias.MountAccessor) && + conflictErr != nil && !i.disableLowerCasedNames { + return conflictErr } // Insert or update alias in MemDB using the transaction created above @@ -2636,24 +2671,113 @@ func (i *IdentityStore) countEntitiesByMountAccessor(ctx context.Context) (map[s return byMountAccessor, nil } -func makeEntityForPacker(_t *testing.T, id string, p *storagepacker.StoragePacker) *identity.Entity { +func makeEntityForPacker(t *testing.T, name string, p *storagepacker.StoragePacker) *identity.Entity { + t.Helper() + return makeEntityForPackerWithNamespace(t, namespace.RootNamespaceID, name, p) +} + +func makeEntityForPackerWithNamespace(t *testing.T, namespaceID, name string, p *storagepacker.StoragePacker) *identity.Entity { + t.Helper() + id, err := uuid.GenerateUUID() + require.NoError(t, err) return &identity.Entity{ ID: id, - Name: id, - NamespaceID: namespace.RootNamespaceID, + Name: name, + NamespaceID: namespaceID, BucketKey: p.BucketKey(id), } } func attachAlias(t *testing.T, e *identity.Entity, name string, me *MountEntry) *identity.Alias { t.Helper() + id, err := uuid.GenerateUUID() + require.NoError(t, err) + if e.NamespaceID != me.NamespaceID { + panic("mount and entity in different namespaces") + } a := &identity.Alias{ - ID: name, + ID: id, Name: name, + NamespaceID: me.NamespaceID, CanonicalID: e.ID, MountType: me.Type, MountAccessor: me.Accessor, + Local: me.Local, } e.UpsertAlias(a) return a } + +func identityCreateCaseDuplicates(t *testing.T, ctx context.Context, c *Core, upme, localme *MountEntry) { + t.Helper() + + if upme.NamespaceID != localme.NamespaceID { + panic("both replicated and local auth mounts must be in the same namespace") + } + + // Create entities with both case-sensitive and case-insensitive duplicate + // suffixes. + for i, suffix := range []string{"-case", "-case", "-cAsE"} { + // Entity duplicated by name + e := makeEntityForPackerWithNamespace(t, upme.NamespaceID, "entity"+suffix, c.identityStore.entityPacker) + err := TestHelperWriteToStoragePacker(ctx, c.identityStore.entityPacker, e.ID, e) + require.NoError(t, err) + + // Entity that isn't a dupe itself but has duplicated aliases + e2 := makeEntityForPackerWithNamespace(t, upme.NamespaceID, fmt.Sprintf("entity-%d", i), c.identityStore.entityPacker) + // Add local and non-local aliases for this entity (which will also be + // duplicated) + attachAlias(t, e2, "alias"+suffix, upme) + attachAlias(t, e2, "local-alias"+suffix, localme) + err = TestHelperWriteToStoragePacker(ctx, c.identityStore.entityPacker, e2.ID, e2) + require.NoError(t, err) + + // Group duplicated by name + g := makeGroupWithNameAndAlias(t, "group"+suffix, "", c.identityStore.groupPacker, upme) + err = TestHelperWriteToStoragePacker(ctx, c.identityStore.groupPacker, g.ID, g) + require.NoError(t, err) + } +} + +func makeGroupWithNameAndAlias(t *testing.T, name, alias string, p *storagepacker.StoragePacker, me *MountEntry) *identity.Group { + t.Helper() + id, err := uuid.GenerateUUID() + require.NoError(t, err) + id2, err := uuid.GenerateUUID() + require.NoError(t, err) + g := &identity.Group{ + ID: id, + Name: name, + NamespaceID: me.NamespaceID, + BucketKey: p.BucketKey(id), + } + if alias != "" { + g.Alias = &identity.Alias{ + ID: id2, + Name: alias, + CanonicalID: id, + MountType: me.Type, + MountAccessor: me.Accessor, + NamespaceID: me.NamespaceID, + } + } + return g +} + +func makeLocalAliasWithName(t *testing.T, name, entityID string, bucketKey string, me *MountEntry) *identity.LocalAliases { + t.Helper() + id, err := uuid.GenerateUUID() + require.NoError(t, err) + return &identity.LocalAliases{ + Aliases: []*identity.Alias{ + { + ID: id, + Name: name, + CanonicalID: entityID, + MountType: me.Type, + MountAccessor: me.Accessor, + NamespaceID: me.NamespaceID, + }, + }, + } +} diff --git a/vault/testing.go b/vault/testing.go index bd5c6d0fcae0..30575029cc21 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -2225,6 +2225,10 @@ var ( ) func TestUserpassMount(c *Core, local bool) (*MountEntry, error) { + return TestUserpassMountContext(namespace.RootContext(nil), c, local) +} + +func TestUserpassMountContext(ctx context.Context, c *Core, local bool) (*MountEntry, error) { name := "userpass" if local { name += "-local" @@ -2234,10 +2238,12 @@ func TestUserpassMount(c *Core, local bool) (*MountEntry, error) { Path: name + "/", Type: "userpass", Description: name, - Accessor: name, - Local: local, + // Don't specify an accessor so we use a random one otherwise we will cause + // horrible issues when we try to create a new mount on a different + // namespace but they have the same accessor! + Local: local, } - if err := c.enableCredential(namespace.RootContext(nil), userpassMe); err != nil { + if err := c.enableCredential(ctx, userpassMe); err != nil { return nil, err } return userpassMe, nil From 80fe86a3521e2abdf52bb0bc73aa89547b949abb Mon Sep 17 00:00:00 2001 From: Bianca <48203644+biazmoreira@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:51:38 -0300 Subject: [PATCH 17/25] Add identity cleanup activation flag (#29301) * Port activation flags with dynamic registration * Activation flag tests * Add identity cleanup activation flag * Fix flag name --- vault/logical_system_activation_flags.go | 23 +++++++++++++++++-- vault/logical_system_activation_flags_test.go | 23 +++++++++++++++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/vault/logical_system_activation_flags.go b/vault/logical_system_activation_flags.go index 596f9e6f128c..09dd2fbb3556 100644 --- a/vault/logical_system_activation_flags.go +++ b/vault/logical_system_activation_flags.go @@ -34,9 +34,11 @@ This path responds to the following HTTP methods. PUT|POST //activate Activates the specified feature. Cannot be undone.` + + activationFlagIdentityCleanup = "force-identity-deduplication" + activationFlagTest = "activation-test" ) -// Register CRUD functions dynamically. // These variables should only be mutated during initialization or server construction. // It is unsafe to modify them once the Vault core is running. var ( @@ -67,7 +69,24 @@ func (b *SystemBackend) activationFlagsPaths() []*framework.Path { HelpDescription: helpDescription, }, { - Pattern: fmt.Sprintf("%s/%s/%s", prefixActivationFlags, "activation-test", verbActivationFlagsActivate), + Pattern: fmt.Sprintf("%s/%s/%s", prefixActivationFlags, activationFlagTest, verbActivationFlagsActivate), + DisplayAttrs: &framework.DisplayAttributes{ + OperationPrefix: prefixActivationFlags, + OperationVerb: verbActivationFlagsActivate, + }, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handleActivationFlagsActivate, + ForwardPerformanceSecondary: true, + ForwardPerformanceStandby: true, + Summary: summaryUpdate, + }, + }, + HelpSynopsis: helpSynopsis, + HelpDescription: helpDescription, + }, + { + Pattern: fmt.Sprintf("%s/%s/%s", prefixActivationFlags, activationFlagIdentityCleanup, verbActivationFlagsActivate), DisplayAttrs: &framework.DisplayAttributes{ OperationPrefix: prefixActivationFlags, OperationVerb: verbActivationFlagsActivate, diff --git a/vault/logical_system_activation_flags_test.go b/vault/logical_system_activation_flags_test.go index aa2e17293f9d..c6065e9c5c93 100644 --- a/vault/logical_system_activation_flags_test.go +++ b/vault/logical_system_activation_flags_test.go @@ -70,18 +70,37 @@ func TestActivationFlags_BadFeatureName(t *testing.T) { // TestActivationFlags_Write tests the write operations for the activation flags func TestActivationFlags_Write(t *testing.T) { - t.Run("given an initial state then read flags and expect all to be unactivated", func(t *testing.T) { + t.Run("given an initial state then write an activation test flag and expect no errors", func(t *testing.T) { core, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{}) _, err := core.systemBackend.HandleRequest( context.Background(), &logical.Request{ Operation: logical.UpdateOperation, - Path: fmt.Sprintf("%s/%s/%s", prefixActivationFlags, "activation-test", verbActivationFlagsActivate), + Path: fmt.Sprintf("%s/%s/%s", prefixActivationFlags, activationFlagTest, verbActivationFlagsActivate), + Storage: core.systemBarrierView, + }, + ) + + require.NoError(t, err) + }) + + t.Run("activate identity cleanup flag", func(t *testing.T) { + core, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{}) + + resp, err := core.systemBackend.HandleRequest( + context.Background(), + &logical.Request{ + Operation: logical.UpdateOperation, + Path: fmt.Sprintf("%s/%s/%s", prefixActivationFlags, activationFlagIdentityCleanup, verbActivationFlagsActivate), Storage: core.systemBarrierView, }, ) require.NoError(t, err) + require.NotNil(t, resp) + require.NotEmpty(t, resp.Data) + require.NotNil(t, resp.Data["activated"]) + require.Contains(t, resp.Data["activated"], activationFlagIdentityCleanup) }) } From 4f14f7bfec975fa7caf93c0b3dd716d12b1913cc Mon Sep 17 00:00:00 2001 From: Thy Ton Date: Thu, 9 Jan 2025 08:20:09 -0800 Subject: [PATCH 18/25] plugin register with artifact stubs VAULT-32686 (#29113) * add plugin catalog's entValidate() and setInternal() oss stubs * create plugin register command constructor oss stub * create EntPluginRunner oss stub * add validateSHA256() oss stub to validate plugin catalog update input --- command/commands.go | 4 +- command/plugin_register_stubs_oss.go | 14 ++ sdk/helper/pluginutil/runner.go | 2 + sdk/helper/pluginutil/runner_stubs_oss.go | 8 + vault/logical_system.go | 4 +- vault/logical_system_plugins_stubs_oss.go | 17 +++ vault/plugincatalog/plugin_catalog.go | 120 +-------------- .../plugincatalog/plugin_catalog_stubs_oss.go | 138 ++++++++++++++++++ 8 files changed, 189 insertions(+), 118 deletions(-) create mode 100644 command/plugin_register_stubs_oss.go create mode 100644 sdk/helper/pluginutil/runner_stubs_oss.go create mode 100644 vault/logical_system_plugins_stubs_oss.go create mode 100644 vault/plugincatalog/plugin_catalog_stubs_oss.go diff --git a/command/commands.go b/command/commands.go index daa40ead48a9..90287478ef85 100644 --- a/command/commands.go +++ b/command/commands.go @@ -551,9 +551,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) map[string]cli.Co }, nil }, "plugin register": func() (cli.Command, error) { - return &PluginRegisterCommand{ - BaseCommand: getBaseCommand(), - }, nil + return NewPluginRegisterCommand(getBaseCommand()), nil }, "plugin reload": func() (cli.Command, error) { return &PluginReloadCommand{ diff --git a/command/plugin_register_stubs_oss.go b/command/plugin_register_stubs_oss.go new file mode 100644 index 000000000000..dbcfb4a44776 --- /dev/null +++ b/command/plugin_register_stubs_oss.go @@ -0,0 +1,14 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +//go:build !enterprise + +package command + +import "github.com/hashicorp/cli" + +func NewPluginRegisterCommand(baseCommand *BaseCommand) cli.Command { + return &PluginRegisterCommand{ + BaseCommand: baseCommand, + } +} diff --git a/sdk/helper/pluginutil/runner.go b/sdk/helper/pluginutil/runner.go index ebbe110c3474..ecae61459b5c 100644 --- a/sdk/helper/pluginutil/runner.go +++ b/sdk/helper/pluginutil/runner.go @@ -57,6 +57,8 @@ const MultiplexingCtxKey string = "multiplex_id" // PluginRunner defines the metadata needed to run a plugin securely with // go-plugin. type PluginRunner struct { + EntPluginRunner + Name string `json:"name" structs:"name"` Type consts.PluginType `json:"type" structs:"type"` Version string `json:"version" structs:"version"` diff --git a/sdk/helper/pluginutil/runner_stubs_oss.go b/sdk/helper/pluginutil/runner_stubs_oss.go new file mode 100644 index 000000000000..b5d390a44d24 --- /dev/null +++ b/sdk/helper/pluginutil/runner_stubs_oss.go @@ -0,0 +1,8 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:build !enterprise + +package pluginutil + +type EntPluginRunner struct{} diff --git a/vault/logical_system.go b/vault/logical_system.go index 49dfc0c6864d..1a00b10adb76 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -538,8 +538,8 @@ func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, _ *logica sha256 := d.Get("sha256").(string) if sha256 == "" { sha256 = d.Get("sha_256").(string) - if sha256 == "" { - return logical.ErrorResponse("missing SHA-256 value"), nil + if resp := validateSHA256(sha256); resp.IsError() { + return resp, nil } } diff --git a/vault/logical_system_plugins_stubs_oss.go b/vault/logical_system_plugins_stubs_oss.go new file mode 100644 index 000000000000..30ccdbc5e985 --- /dev/null +++ b/vault/logical_system_plugins_stubs_oss.go @@ -0,0 +1,17 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +//go:build !enterprise + +package vault + +import ( + "github.com/hashicorp/vault/sdk/logical" +) + +func validateSHA256(sha256 string) *logical.Response { + if sha256 == "" { + return logical.ErrorResponse("missing SHA-256 value") + } + return nil +} diff --git a/vault/plugincatalog/plugin_catalog.go b/vault/plugincatalog/plugin_catalog.go index b2b61f54bb7a..d38cf167611f 100644 --- a/vault/plugincatalog/plugin_catalog.go +++ b/vault/plugincatalog/plugin_catalog.go @@ -173,6 +173,13 @@ func SetupPluginCatalog(ctx context.Context, in *PluginCatalogInput) (*PluginCat return nil, err } + // Sanitize the plugin catalog + err = catalog.entValidate(ctx) + if err != nil { + logger.Error("error while sanitizing plugin storage", "error", err) + return nil, err + } + if legacy, _ := strconv.ParseBool(os.Getenv(pluginutil.PluginUseLegacyEnvLayering)); legacy { conflicts := false osKeys := envKeys(os.Environ()) @@ -959,119 +966,6 @@ func (c *PluginCatalog) Set(ctx context.Context, plugin pluginutil.SetPluginInpu return err } -func (c *PluginCatalog) setInternal(ctx context.Context, plugin pluginutil.SetPluginInput) (*pluginutil.PluginRunner, error) { - command := plugin.Command - if plugin.OCIImage == "" { - // Best effort check to make sure the command isn't breaking out of the - // configured plugin directory. - command = filepath.Join(c.directory, plugin.Command) - sym, err := filepath.EvalSymlinks(command) - if err != nil { - return nil, fmt.Errorf("error while validating the command path: %w", err) - } - symAbs, err := filepath.Abs(filepath.Dir(sym)) - if err != nil { - return nil, fmt.Errorf("error while validating the command path: %w", err) - } - - if symAbs != c.directory { - return nil, errors.New("cannot execute files outside of configured plugin directory") - } - } - - // entryTmp should only be used for the below type and version checks. It uses the - // full command instead of the relative command because get() normally prepends - // the plugin directory to the command, but we can't use get() here. - entryTmp := &pluginutil.PluginRunner{ - Name: plugin.Name, - Command: command, - OCIImage: plugin.OCIImage, - Runtime: plugin.Runtime, - Args: plugin.Args, - Env: plugin.Env, - Sha256: plugin.Sha256, - Builtin: false, - } - if entryTmp.OCIImage != "" && entryTmp.Runtime != "" { - var err error - entryTmp.RuntimeConfig, err = c.runtimeCatalog.Get(ctx, entryTmp.Runtime, consts.PluginRuntimeTypeContainer) - if err != nil { - return nil, fmt.Errorf("failed to get configured runtime for plugin %q: %w", plugin.Name, err) - } - } - // If the plugin type is unknown, we want to attempt to determine the type - if plugin.Type == consts.PluginTypeUnknown { - var err error - plugin.Type, err = c.getPluginTypeFromUnknown(ctx, entryTmp) - if err != nil { - return nil, err - } - if plugin.Type == consts.PluginTypeUnknown { - return nil, ErrPluginBadType - } - } - - // getting the plugin version is best-effort, so errors are not fatal - runningVersion := logical.EmptyPluginVersion - var versionErr error - switch plugin.Type { - case consts.PluginTypeSecrets, consts.PluginTypeCredential: - runningVersion, versionErr = c.getBackendRunningVersion(ctx, entryTmp) - case consts.PluginTypeDatabase: - runningVersion, versionErr = c.getDatabaseRunningVersion(ctx, entryTmp) - default: - return nil, fmt.Errorf("unknown plugin type: %v", plugin.Type) - } - if versionErr != nil { - c.logger.Warn("Error determining plugin version", "error", versionErr) - if errors.Is(versionErr, ErrPluginUnableToRun) { - return nil, versionErr - } - } else if plugin.Version != "" && runningVersion.Version != "" && plugin.Version != runningVersion.Version { - c.logger.Error("Plugin self-reported version did not match requested version", - "plugin", plugin.Name, "requestedVersion", plugin.Version, "reportedVersion", runningVersion.Version) - return nil, fmt.Errorf("%w: %s reported version (%s) did not match requested version (%s)", - ErrPluginVersionMismatch, plugin.Name, runningVersion.Version, plugin.Version) - } else if plugin.Version == "" && runningVersion.Version != "" { - plugin.Version = runningVersion.Version - _, err := semver.NewVersion(plugin.Version) - if err != nil { - return nil, fmt.Errorf("plugin self-reported version %q is not a valid semantic version: %w", plugin.Version, err) - } - } - - entry := &pluginutil.PluginRunner{ - Name: plugin.Name, - Type: plugin.Type, - Version: plugin.Version, - Command: plugin.Command, - OCIImage: plugin.OCIImage, - Runtime: plugin.Runtime, - Args: plugin.Args, - Env: plugin.Env, - Sha256: plugin.Sha256, - Builtin: false, - } - - buf, err := json.Marshal(entry) - if err != nil { - return nil, fmt.Errorf("failed to encode plugin entry: %w", err) - } - - storageKey := path.Join(plugin.Type.String(), plugin.Name) - if plugin.Version != "" { - storageKey = path.Join(storageKey, plugin.Version) - } - logicalEntry := logical.StorageEntry{ - Key: storageKey, - Value: buf, - } - if err := c.catalogView.Put(ctx, &logicalEntry); err != nil { - return nil, fmt.Errorf("failed to persist plugin entry: %w", err) - } - return entry, nil -} - // Delete is used to remove an external plugin from the catalog. Builtin plugins // can not be deleted. func (c *PluginCatalog) Delete(ctx context.Context, name string, pluginType consts.PluginType, pluginVersion string) error { diff --git a/vault/plugincatalog/plugin_catalog_stubs_oss.go b/vault/plugincatalog/plugin_catalog_stubs_oss.go new file mode 100644 index 000000000000..12673393f7e5 --- /dev/null +++ b/vault/plugincatalog/plugin_catalog_stubs_oss.go @@ -0,0 +1,138 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +//go:build !enterprise + +package plugincatalog + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "path" + "path/filepath" + + semver "github.com/hashicorp/go-version" + "github.com/hashicorp/vault/sdk/helper/consts" + "github.com/hashicorp/vault/sdk/helper/pluginutil" + "github.com/hashicorp/vault/sdk/logical" +) + +// setInternal creates a new plugin entry in the catalog and persists it to storage +func (c *PluginCatalog) setInternal(ctx context.Context, plugin pluginutil.SetPluginInput) (*pluginutil.PluginRunner, error) { + command := plugin.Command + if plugin.OCIImage == "" { + // Best effort check to make sure the command isn't breaking out of the + // configured plugin directory. + command = filepath.Join(c.directory, plugin.Command) + sym, err := filepath.EvalSymlinks(command) + if err != nil { + return nil, fmt.Errorf("error while validating the command path: %w", err) + } + symAbs, err := filepath.Abs(filepath.Dir(sym)) + if err != nil { + return nil, fmt.Errorf("error while validating the command path: %w", err) + } + + if symAbs != c.directory { + return nil, errors.New("cannot execute files outside of configured plugin directory") + } + } + + // entryTmp should only be used for the below type and version checks. It uses the + // full command instead of the relative command because get() normally prepends + // the plugin directory to the command, but we can't use get() here. + entryTmp := &pluginutil.PluginRunner{ + Name: plugin.Name, + Command: command, + OCIImage: plugin.OCIImage, + Runtime: plugin.Runtime, + Args: plugin.Args, + Env: plugin.Env, + Sha256: plugin.Sha256, + Builtin: false, + } + if entryTmp.OCIImage != "" && entryTmp.Runtime != "" { + var err error + entryTmp.RuntimeConfig, err = c.runtimeCatalog.Get(ctx, entryTmp.Runtime, consts.PluginRuntimeTypeContainer) + if err != nil { + return nil, fmt.Errorf("failed to get configured runtime for plugin %q: %w", plugin.Name, err) + } + } + // If the plugin type is unknown, we want to attempt to determine the type + if plugin.Type == consts.PluginTypeUnknown { + var err error + plugin.Type, err = c.getPluginTypeFromUnknown(ctx, entryTmp) + if err != nil { + return nil, err + } + if plugin.Type == consts.PluginTypeUnknown { + return nil, ErrPluginBadType + } + } + + // getting the plugin version is best-effort, so errors are not fatal + runningVersion := logical.EmptyPluginVersion + var versionErr error + switch plugin.Type { + case consts.PluginTypeSecrets, consts.PluginTypeCredential: + runningVersion, versionErr = c.getBackendRunningVersion(ctx, entryTmp) + case consts.PluginTypeDatabase: + runningVersion, versionErr = c.getDatabaseRunningVersion(ctx, entryTmp) + default: + return nil, fmt.Errorf("unknown plugin type: %v", plugin.Type) + } + if versionErr != nil { + c.logger.Warn("Error determining plugin version", "error", versionErr) + if errors.Is(versionErr, ErrPluginUnableToRun) { + return nil, versionErr + } + } else if plugin.Version != "" && runningVersion.Version != "" && plugin.Version != runningVersion.Version { + c.logger.Error("Plugin self-reported version did not match requested version", + "plugin", plugin.Name, "requestedVersion", plugin.Version, "reportedVersion", runningVersion.Version) + return nil, fmt.Errorf("%w: %s reported version (%s) did not match requested version (%s)", + ErrPluginVersionMismatch, plugin.Name, runningVersion.Version, plugin.Version) + } else if plugin.Version == "" && runningVersion.Version != "" { + plugin.Version = runningVersion.Version + _, err := semver.NewVersion(plugin.Version) + if err != nil { + return nil, fmt.Errorf("plugin self-reported version %q is not a valid semantic version: %w", plugin.Version, err) + } + } + + entry := &pluginutil.PluginRunner{ + Name: plugin.Name, + Type: plugin.Type, + Version: plugin.Version, + Command: plugin.Command, + OCIImage: plugin.OCIImage, + Runtime: plugin.Runtime, + Args: plugin.Args, + Env: plugin.Env, + Sha256: plugin.Sha256, + Builtin: false, + } + + buf, err := json.Marshal(entry) + if err != nil { + return nil, fmt.Errorf("failed to encode plugin entry: %w", err) + } + + storageKey := path.Join(plugin.Type.String(), plugin.Name) + if plugin.Version != "" { + storageKey = path.Join(storageKey, plugin.Version) + } + logicalEntry := logical.StorageEntry{ + Key: storageKey, + Value: buf, + } + if err := c.catalogView.Put(ctx, &logicalEntry); err != nil { + return nil, fmt.Errorf("failed to persist plugin entry: %w", err) + } + return entry, nil +} + +func (c *PluginCatalog) entValidate(context.Context) error { + return nil +} From 55ca52f3fdc8a9b7af20b70c14bb2ca99734220c Mon Sep 17 00:00:00 2001 From: Kit Haines Date: Thu, 9 Jan 2025 11:30:29 -0500 Subject: [PATCH 19/25] =?UTF-8?q?Add=20information=20about=20an=20enterpri?= =?UTF-8?q?se=20feature=20related=20to=20validating=20iss=E2=80=A6=20(#293?= =?UTF-8?q?00)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add information about an enterprise feature related to validating issued certificates to the PKI API docs. * Update website/content/api-docs/secret/pki/index.mdx Update RFC name and link, as suggested by Steve. Co-authored-by: Steven Clark * Update website/content/api-docs/secret/pki/index.mdx Update RFC name and link, as suggested by Steve. Co-authored-by: Steven Clark * Update website/content/api-docs/secret/pki/index.mdx Update RFC name and link, as suggested by Steve. Co-authored-by: Steven Clark * Update website/content/api-docs/secret/pki/index.mdx Update RFC name and link, as suggested by Steve. Co-authored-by: Steven Clark * Update enterprise tag to be on the same line for vercel reasons. --------- Co-authored-by: Steven Clark --- website/content/api-docs/secret/pki/index.mdx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/website/content/api-docs/secret/pki/index.mdx b/website/content/api-docs/secret/pki/index.mdx index abe7c34af202..889438116e08 100644 --- a/website/content/api-docs/secret/pki/index.mdx +++ b/website/content/api-docs/secret/pki/index.mdx @@ -2758,6 +2758,29 @@ do so, import a new issuer and a new `issuer_id` will be assigned. ~> **Note**: If no cluster-local address is present and templating is used, issuance will fail. +- `disable_critical_extension_checks` `(bool: false)` - This determines whether this issuer is able + to issue certificates where the chain of trust (including the issued + certificate) contain critical extensions not processed by vault, breaking the + behavior required by [RFC 5280 Section 6.1](https://www.rfc-editor.org/rfc/rfc5280#section-6.1). + +- `disable_path_length_checks` `(bool: false)` - This determines whether this issuer is able + to issue certificates where the chain of trust (including the final issued + certificate) is longer than allowed by a certificate authority in that chain, + breaking the behavior required by + [RFC 5280 Section 4.2.1.9](https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.9). + +- `disable_name_checks` `(bool: false)` - This determines whether this issuer is able + to issue certificates where the chain of trust (including the final issued + certificate) contains a link in which the subject of the issuing certificate + does not match the named issuer of the certificate it signed, breaking the + behavior required by [RFC 5280 Section 4.1.2.4](https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.4). + +- `disable_name_constraint_checks` `(bool: false)` - This determines whether this issuer is able + to issue certificates where the chain of trust (including the final issued + certificate) violates the name constraints critical extension of one of the + issuer certificates in the chain, breaking the behavior required by + [RFC 5280 Section 4.2.1.10](https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.10). + #### Sample payload ```json From 36d7e0c6bd13dc3818255b33b2cd9e3e1cd513b0 Mon Sep 17 00:00:00 2001 From: John-Michael Faircloth Date: Thu, 9 Jan 2025 11:33:23 -0600 Subject: [PATCH 20/25] sdk/db: do not hold the lock on Close (#29097) * sdk/db: do not hold the lock on Close * fix missing locks on return; ensure we don't overrite instance * add type and close timeout env vars * changelog --- changelog/29097.txt | 3 ++ sdk/database/dbplugin/v5/grpc_client.go | 14 +++++++-- sdk/database/dbplugin/v5/grpc_server.go | 33 +++++++++++++++----- sdk/database/dbplugin/v5/grpc_server_test.go | 13 ++++++++ sdk/helper/pluginutil/env.go | 8 +++++ 5 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 changelog/29097.txt diff --git a/changelog/29097.txt b/changelog/29097.txt new file mode 100644 index 000000000000..15e8c75a71b1 --- /dev/null +++ b/changelog/29097.txt @@ -0,0 +1,3 @@ +```release-note:bug +sdk/database: Fix a bug where slow database connections can cause goroutines to be blocked. +``` diff --git a/sdk/database/dbplugin/v5/grpc_client.go b/sdk/database/dbplugin/v5/grpc_client.go index 4e1ef57492a9..e26095191a37 100644 --- a/sdk/database/dbplugin/v5/grpc_client.go +++ b/sdk/database/dbplugin/v5/grpc_client.go @@ -7,6 +7,8 @@ import ( "context" "errors" "fmt" + "os" + "strconv" "time" "github.com/golang/protobuf/ptypes" @@ -270,7 +272,7 @@ func deleteUserRespFromProto(rpcResp *proto.DeleteUserResponse) (DeleteUserRespo } func (c gRPCClient) Type() (string, error) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + ctx, cancel := getContextWithTimeout(pluginutil.PluginGRPCTimeoutType) defer cancel() typeResp, err := c.client.Type(ctx, &proto.Empty{}) @@ -284,7 +286,7 @@ func (c gRPCClient) Type() (string, error) { } func (c gRPCClient) Close() error { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + ctx, cancel := getContextWithTimeout(pluginutil.PluginGRPCTimeoutClose) defer cancel() _, err := c.client.Close(ctx, &proto.Empty{}) @@ -296,3 +298,11 @@ func (c gRPCClient) Close() error { } return nil } + +func getContextWithTimeout(env string) (context.Context, context.CancelFunc) { + timeout := 1 // default timeout + if envTimeout, err := strconv.Atoi(os.Getenv(env)); err == nil && envTimeout > 0 { + timeout = envTimeout + } + return context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) +} diff --git a/sdk/database/dbplugin/v5/grpc_server.go b/sdk/database/dbplugin/v5/grpc_server.go index 691de2d0a89e..04416ee52ca5 100644 --- a/sdk/database/dbplugin/v5/grpc_server.go +++ b/sdk/database/dbplugin/v5/grpc_server.go @@ -290,27 +290,46 @@ func (g *gRPCServer) Type(ctx context.Context, _ *proto.Empty) (*proto.TypeRespo func (g *gRPCServer) Close(ctx context.Context, _ *proto.Empty) (*proto.Empty, error) { g.Lock() - defer g.Unlock() impl, err := g.getDatabaseInternal(ctx) if err != nil { + g.Unlock() return nil, err } - err = impl.Close() - if err != nil { - return &proto.Empty{}, status.Errorf(codes.Internal, "unable to close database plugin: %s", err) - } - + var id string if g.singleImpl == nil { // only cleanup instances map when multiplexing is supported - id, err := pluginutil.GetMultiplexIDFromContext(ctx) + id, err = pluginutil.GetMultiplexIDFromContext(ctx) if err != nil { + g.Unlock() return nil, err } delete(g.instances, id) } + // unlock here so that the subsequent call to Close() does not hold the + // lock in case the DB is slow to respond + g.Unlock() + + err = impl.Close() + if err != nil { + // The call to Close failed, so we will put the DB instance back in the + // map. This might not be necessary, but we do this in case anything + // relies on being able to retry Close. + g.Lock() + defer g.Unlock() + if g.singleImpl == nil { + // There is a chance that while we were calling Close another DB + // config was created for the old ID. So we only put it back if + // it's not set. + if _, ok := g.instances[id]; !ok { + g.instances[id] = impl + } + } + return &proto.Empty{}, status.Errorf(codes.Internal, "unable to close database plugin: %s", err) + } + return &proto.Empty{}, nil } diff --git a/sdk/database/dbplugin/v5/grpc_server_test.go b/sdk/database/dbplugin/v5/grpc_server_test.go index 847a9e770df0..f0419a907769 100644 --- a/sdk/database/dbplugin/v5/grpc_server_test.go +++ b/sdk/database/dbplugin/v5/grpc_server_test.go @@ -551,6 +551,19 @@ func TestGRPCServer_Close(t *testing.T) { } }, }, + "error path for multiplexed plugin": { + db: fakeDatabase{ + closeErr: errors.New("close error"), + }, + expectErr: true, + expectCode: codes.Internal, + grpcSetupFunc: testGrpcServer, + assertFunc: func(t *testing.T, g gRPCServer) { + if len(g.instances) != 1 { + t.Fatalf("err expected instances map to contain exactly 1 element") + } + }, + }, "happy path for non-multiplexed plugin": { db: fakeDatabase{}, expectErr: false, diff --git a/sdk/helper/pluginutil/env.go b/sdk/helper/pluginutil/env.go index ea05e8462c70..c4f0122ce529 100644 --- a/sdk/helper/pluginutil/env.go +++ b/sdk/helper/pluginutil/env.go @@ -49,6 +49,14 @@ const ( // configuration as a shim to the pgx posgtres library. // Deprecated: VAULT_PLUGIN_USE_POSTGRES_SSLINLINE will be removed in a future version of the Vault SDK. PluginUsePostgresSSLInline = "VAULT_PLUGIN_USE_POSTGRES_SSLINLINE" + + // PluginGRPCTimeoutType is an ENV name used to set the timeout for Vault's + // call to the plugin Type() GRPC method + PluginGRPCTimeoutType = "VAULT_PLUGIN_GRPC_TIMEOUT_TYPE" + + // PluginGRPCTimeoutClose is an ENV name used to set the timeout for Vault's + // call to the plugin Close() GRPC method + PluginGRPCTimeoutClose = "VAULT_PLUGIN_GRPC_TIMEOUT_CLOSE" ) // OptionallyEnableMlock determines if mlock should be called, and if so enables From f625f506edcfc0bf072c2b4f3c6bab8380506d09 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 9 Jan 2025 11:58:29 -0800 Subject: [PATCH 21/25] CE changes for vault-31750 (#29303) * ce changes for vault-31750 * add changelog * make proto * refactor naming * clarify error message * update changelog * one more time * make proto AGAIN --- changelog/29303.txt | 3 +++ physical/raft/raft.go | 22 ++++++++++++++++++++-- physical/raft/raft_autopilot.go | 4 ++++ sdk/plugin/pb/backend.pb.go | 8 ++++---- vault/logical_system_raft.go | 7 +++++++ 5 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 changelog/29303.txt diff --git a/changelog/29303.txt b/changelog/29303.txt new file mode 100644 index 000000000000..c71765c2f432 --- /dev/null +++ b/changelog/29303.txt @@ -0,0 +1,3 @@ +```release-note:change +core (enterprise): Add tracking of performance standbys by their HA node ID so that RPC connections can be more easily cleaned up when nodes are removed. +``` diff --git a/physical/raft/raft.go b/physical/raft/raft.go index 9010270c3f55..b68cc852ef5e 100644 --- a/physical/raft/raft.go +++ b/physical/raft/raft.go @@ -256,8 +256,9 @@ type RaftBackend struct { // limits. specialPathLimits map[string]uint64 - removed *atomic.Bool - removedCallback func() + removed *atomic.Bool + removedCallback func() + removedServerCleanup func(context.Context, string) (bool, error) } func (b *RaftBackend) IsNodeRemoved(ctx context.Context, nodeID string) (bool, error) { @@ -284,6 +285,23 @@ func (b *RaftBackend) RemoveSelf() error { return b.stableStore.SetUint64(removedKey, 1) } +func (b *RaftBackend) SetRemovedServerCleanupFunc(f func(context.Context, string) (bool, error)) { + b.l.Lock() + b.removedServerCleanup = f + b.l.Unlock() +} + +func (b *RaftBackend) RemovedServerCleanup(ctx context.Context, nodeID string) (bool, error) { + b.l.RLock() + defer b.l.RUnlock() + + if b.removedServerCleanup != nil { + return b.removedServerCleanup(ctx, nodeID) + } + + return false, nil +} + // LeaderJoinInfo contains information required by a node to join itself as a // follower to an existing raft cluster type LeaderJoinInfo struct { diff --git a/physical/raft/raft_autopilot.go b/physical/raft/raft_autopilot.go index fb17283e663e..ba2744bd8020 100644 --- a/physical/raft/raft_autopilot.go +++ b/physical/raft/raft_autopilot.go @@ -666,6 +666,10 @@ func (d *Delegate) RemoveFailedServer(server *autopilot.Server) { } d.followerStates.Delete(string(server.ID)) + _, err := d.RemovedServerCleanup(context.Background(), string(server.ID)) + if err != nil { + d.logger.Error("failed to run cleanup", "error", err) + } }() } diff --git a/sdk/plugin/pb/backend.pb.go b/sdk/plugin/pb/backend.pb.go index 346163880087..1216138d3ba0 100644 --- a/sdk/plugin/pb/backend.pb.go +++ b/sdk/plugin/pb/backend.pb.go @@ -3761,10 +3761,10 @@ var File_sdk_plugin_pb_backend_proto protoreflect.FileDescriptor var file_sdk_plugin_pb_backend_proto_rawDesc = []byte{ 0x0a, 0x1b, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70, - 0x62, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, diff --git a/vault/logical_system_raft.go b/vault/logical_system_raft.go index 32576cbb6ae6..3576c47f0dbc 100644 --- a/vault/logical_system_raft.go +++ b/vault/logical_system_raft.go @@ -268,6 +268,13 @@ func (b *SystemBackend) handleRaftRemovePeerUpdate() framework.OperationFunc { } b.Core.raftFollowerStates.Delete(serverID) + _, err := raftBackend.RemovedServerCleanup(ctx, serverID) + if err != nil { + // log the error but don't return it - we might get an error if we can't find the node in the cache, which + // is not an error condition in this instance. + b.logger.Info("attempted to remove node from perf standby cache but it failed, which might be fine", "server ID", serverID, "error", err) + return nil, nil + } return nil, nil } From 110575532173db4f5cd6f4b514346b4b8db0074c Mon Sep 17 00:00:00 2001 From: Ryan Cragun Date: Thu, 9 Jan 2025 13:07:15 -0700 Subject: [PATCH 22/25] protobuf: rebuild sdk backend protos with 1.36.2 (#29332) Signed-off-by: Ryan Cragun From 75ddf6e4edc3e7feb87039b1ac3ef2f6008a0e0e Mon Sep 17 00:00:00 2001 From: Rachel Culpepper <84159930+rculpepper@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:57:29 -0600 Subject: [PATCH 23/25] Add docs for ML-DSA and hybrid keys (#29246) * add api docs for pqc key types * add pqc key types to docs * remove slh-dsa and add hybrid --- website/content/api-docs/secret/transit.mdx | 18 ++++++++++++++++++ website/content/docs/secrets/transit/index.mdx | 5 +++++ 2 files changed, 23 insertions(+) diff --git a/website/content/api-docs/secret/transit.mdx b/website/content/api-docs/secret/transit.mdx index 140cad0d72c6..fe147bd3a8d7 100644 --- a/website/content/api-docs/secret/transit.mdx +++ b/website/content/api-docs/secret/transit.mdx @@ -67,6 +67,8 @@ values set here cannot be changed after key creation. - `managed_key` - External key configured via the [Managed Keys](/vault/docs/enterprise/managed-keys) feature (enterprise only) - `aes128-cmac` - AES-128 CMAC (CMAC generation, verification) - `aes256-cmac` - AES-256 CMAC (CMAC generation, verification) + - `ml-dsa` - ML-DSA (asymmetric) (experimental) + - `hybrid` - hybrid signatures combining a post-quantum algorithm and an elliptic curve algorithm (asymmetric) (experimental) ~> **Note**: In FIPS 140-2 mode, the following algorithms are not certified and thus should not be used: `chacha20-poly1305` and `ed25519`. @@ -79,6 +81,15 @@ values set here cannot be changed after key creation. ~> **Note**: When key type is `managed_key`, either the `managed_key_name` or `managed_key_id` parameter must be specified. + + ~> **Note**: When key type is `ml-dsa`, `parameter_set` must be specified. + + ~> **Note**: When key type is `hybrid`, `parameter_set`, `hybrid_key_type_pqc`, + and `hybrid_key_type_ec` are required. + + ~> **Warning**: ML-DSA and hybrid schemes are considered experimental functionality and + should not be used in production environments. + - `key_size` `(int: "0", optional)` - The key size in bytes for algorithms that allow variable key sizes. Currently only applicable to HMAC, where it must be between 32 and 512 bytes. @@ -88,6 +99,13 @@ values set here cannot be changed after key creation. hour. Uses [duration format strings](/vault/docs/concepts/duration-format). - `managed_key_name` `(string: "")` - The name of the managed key to use for this transit key. - `managed_key_id` `(string: "")` - The UUID of the managed key to use for this transit key. +- `parameter_set` `(string:"")` - The parameter set to use for ML-DSA. Required for +ML-DSA and hybrid keys. Valid values are `44`, `65`, and `87`. +- `hybrid_key_type_pqc` `(string: "")` - The post-quantum algorithm to use for hybrid signatures. +Currently, ML-DSA is the only supported key type. +- `hybrid_key_type_ec` `(string: "")` - The elliptic curve algorithm to use for hybrid signatures. +Supported key types are `ecdsa-p256`, `ecdsa-p384`, `ecdsa-p521`, and `ed25519`. + ### Sample payload ```json diff --git a/website/content/docs/secrets/transit/index.mdx b/website/content/docs/secrets/transit/index.mdx index f6373fb11382..c6b7c821c441 100644 --- a/website/content/docs/secrets/transit/index.mdx +++ b/website/content/docs/secrets/transit/index.mdx @@ -90,6 +90,8 @@ types also generate separate HMAC keys): for more information. - `aes128-cmac`: CMAC with a 128-bit AES key; supporting CMAC generation and verification. - `aes256-cmac`: CMAC with a 256-bit AES key; supporting CMAC generation and verification. +- `ml-dsa` - ML-DSA; supports signing and signature verification (experimental) +- `hybrid` - combination of two signature algorithms; supports signing and signature verification (experimental) ~> **Note**: In FIPS 140-2 mode, the following algorithms are not certified and thus should not be used: `chacha20-poly1305` and `ed25519`. @@ -100,6 +102,9 @@ supports HMAC, and behaves identically to other algorithms with respect to the HMAC operations but supports key import. By default, the HMAC key type uses a 256-bit key. +~> **Warning**: ML-DSA and hybrid schemes are considered experimental functionality and +should not be used in production environments. + RSA operations use one of the following methods: - OAEP (encrypt, decrypt), with SHA-256 hash function and MGF, From 49d8fb2b640569918c3d65f978fcbecc8c59a991 Mon Sep 17 00:00:00 2001 From: John-Michael Faircloth Date: Thu, 9 Jan 2025 16:00:41 -0600 Subject: [PATCH 24/25] replace keyring dependency to address zombie dbus-daemons processes (#29334) * replace keyring dependency to address zombie dbus-daemons processes * changelog --- changelog/29334.txt | 3 +++ go.mod | 9 +++++++-- go.sum | 8 ++------ 3 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 changelog/29334.txt diff --git a/changelog/29334.txt b/changelog/29334.txt new file mode 100644 index 000000000000..c4078739680b --- /dev/null +++ b/changelog/29334.txt @@ -0,0 +1,3 @@ +```release-note:bug +plugins: Fix a bug that causes zombie dbus-daemon processes on certain systems. +``` diff --git a/go.mod b/go.mod index 972575dfc142..cf5baed98784 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,13 @@ replace github.com/hashicorp/vault/api/auth/userpass => ./api/auth/userpass replace github.com/hashicorp/vault/sdk => ./sdk +// The keyring library has an outstanding bug that causes zombie dbus-daemon +// processes on each execution. Vault has an indirect dependency on keyring via +// gosnowflake. This replace is a stopgap measure until either gosnowflake or +// keyring addresses the issue. +// See https://github.com/99designs/keyring/issues/103 and https://github.com/snowflakedb/gosnowflake/issues/1183 +replace github.com/99designs/keyring => github.com/Jeffail/keyring v1.2.3 + require ( cloud.google.com/go/cloudsqlconn v1.4.3 cloud.google.com/go/monitoring v1.21.2 @@ -387,7 +394,6 @@ require ( github.com/go-openapi/validate v0.24.0 // indirect github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect @@ -406,7 +412,6 @@ require ( github.com/gophercloud/gophercloud v0.1.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect - github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/hashicorp/cronexpr v1.1.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect diff --git a/go.sum b/go.sum index dd03ab8b7c4d..be58714db23e 100644 --- a/go.sum +++ b/go.sum @@ -626,8 +626,6 @@ gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zum git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= -github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= -github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= @@ -730,6 +728,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1 github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Jeffail/gabs/v2 v2.1.0 h1:6dV9GGOjoQgzWTQEltZPXlJdFloxvIq7DwqgxMCbq30= github.com/Jeffail/gabs/v2 v2.1.0/go.mod h1:xCn81vdHKxFUuWWAaD5jCTQDNPBMh5pPs9IJ+NcziBI= +github.com/Jeffail/keyring v1.2.3 h1:WRmYdGPmHoJqX66KjGXQBALp6mUN00tD0ds5C4pqEsQ= +github.com/Jeffail/keyring v1.2.3/go.mod h1:xIg4RDmDwDuUFoU4IzDIT3b+HV24JUYlzo6ILZUH3Sc= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -1174,8 +1174,6 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gocql/gocql v1.0.0 h1:UnbTERpP72VZ/viKE1Q1gPtmLvyTZTvuAstvSRydw/c= github.com/gocql/gocql v1.0.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= -github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= -github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= @@ -1370,8 +1368,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4Zs github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= -github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= -github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/cap v0.7.0 h1:atLIEU5lJslYXo1qsv7RtUL1HrJVVxnfkErIT3uxLp0= From ff2d7ecd344ab979e686866911d847eb1a39d24e Mon Sep 17 00:00:00 2001 From: Mike Palmiotto Date: Thu, 9 Jan 2025 17:03:15 -0500 Subject: [PATCH 25/25] Add build flag for CE-specific activation flag tests (#29335) --- vault/logical_system_activation_flags_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vault/logical_system_activation_flags_test.go b/vault/logical_system_activation_flags_test.go index c6065e9c5c93..95834a88e001 100644 --- a/vault/logical_system_activation_flags_test.go +++ b/vault/logical_system_activation_flags_test.go @@ -1,6 +1,8 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 +//go:build !enterprise + package vault import (