From 53040690a2bb89a71b723cd888411182295abcd6 Mon Sep 17 00:00:00 2001 From: Steven Clark Date: Mon, 20 Nov 2023 10:32:05 -0500 Subject: [PATCH 01/74] PKI: Do not set NextUpdate OCSP field when ocsp_expiry is 0 (#24192) * Do not set NextUpdate OCSP field when ocsp_expiry is 0 * Add cl --- builtin/logical/pki/path_ocsp.go | 5 ++- builtin/logical/pki/path_ocsp_test.go | 60 +++++++++++++++++++------ changelog/24192.txt | 3 ++ website/content/api-docs/secret/pki.mdx | 5 ++- 4 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 changelog/24192.txt diff --git a/builtin/logical/pki/path_ocsp.go b/builtin/logical/pki/path_ocsp.go index 98adcc5fe4f2..6dac24d5cd7f 100644 --- a/builtin/logical/pki/path_ocsp.go +++ b/builtin/logical/pki/path_ocsp.go @@ -510,11 +510,14 @@ func genResponse(cfg *crlConfig, caBundle *certutil.ParsedCertBundle, info *ocsp Status: info.ocspStatus, SerialNumber: info.serialNumber, ThisUpdate: curTime, - NextUpdate: curTime.Add(duration), ExtraExtensions: []pkix.Extension{}, SignatureAlgorithm: revSigAlg, } + if duration > 0 { + template.NextUpdate = curTime.Add(duration) + } + if info.ocspStatus == ocsp.Revoked { template.RevokedAt = *info.revocationTimeUTC template.RevocationReason = ocsp.Unspecified diff --git a/builtin/logical/pki/path_ocsp_test.go b/builtin/logical/pki/path_ocsp_test.go index b62e7d4b5a17..20f706fa42f8 100644 --- a/builtin/logical/pki/path_ocsp_test.go +++ b/builtin/logical/pki/path_ocsp_test.go @@ -15,6 +15,7 @@ import ( "strconv" "strings" "testing" + "time" "github.com/hashicorp/go-secure-stdlib/parseutil" vaulthttp "github.com/hashicorp/vault/http" @@ -467,6 +468,18 @@ func TestOcsp_HigherLevel(t *testing.T) { require.Equal(t, certToRevoke.SerialNumber, ocspResp.SerialNumber) } +// TestOcsp_NextUpdate make sure that we are setting the appropriate values +// for the NextUpdate field within our responses. +func TestOcsp_NextUpdate(t *testing.T) { + // Within the runOcspRequestTest, with a ocspExpiry of 0, + // we will validate that NextUpdate was not set in the response + runOcspRequestTest(t, "POST", "ec", 0, 0, crypto.SHA256, 0) + + // Within the runOcspRequestTest, with a ocspExpiry of 24 hours, we will validate + // that NextUpdate is set and has a time 24 hours larger than ThisUpdate + runOcspRequestTest(t, "POST", "ec", 0, 0, crypto.SHA256, 24*time.Hour) +} + func TestOcsp_ValidRequests(t *testing.T) { type caKeyConf struct { keyType string @@ -506,13 +519,15 @@ func TestOcsp_ValidRequests(t *testing.T) { localTT.reqHash) t.Run(testName, func(t *testing.T) { runOcspRequestTest(t, localTT.reqType, localTT.keyConf.keyType, localTT.keyConf.keyBits, - localTT.keyConf.sigBits, localTT.reqHash) + localTT.keyConf.sigBits, localTT.reqHash, 12*time.Hour) }) } } -func runOcspRequestTest(t *testing.T, requestType string, caKeyType string, caKeyBits int, caKeySigBits int, requestHash crypto.Hash) { - b, s, testEnv := setupOcspEnvWithCaKeyConfig(t, caKeyType, caKeyBits, caKeySigBits) +func runOcspRequestTest(t *testing.T, requestType string, caKeyType string, + caKeyBits int, caKeySigBits int, requestHash crypto.Hash, ocspExpiry time.Duration, +) { + b, s, testEnv := setupOcspEnvWithCaKeyConfig(t, caKeyType, caKeyBits, caKeySigBits, ocspExpiry) // Non-revoked cert resp, err := SendOcspRequest(t, b, s, requestType, testEnv.leafCertIssuer1, testEnv.issuer1, requestHash) @@ -574,17 +589,28 @@ func runOcspRequestTest(t *testing.T, requestType string, caKeyType string, caKe require.Equal(t, testEnv.leafCertIssuer2.SerialNumber, ocspResp.SerialNumber) // Verify that our thisUpdate and nextUpdate fields are updated as expected - thisUpdate := ocspResp.ThisUpdate - nextUpdate := ocspResp.NextUpdate - require.True(t, thisUpdate.Before(nextUpdate), - fmt.Sprintf("thisUpdate %s, should have been before nextUpdate: %s", thisUpdate, nextUpdate)) - nextUpdateDiff := nextUpdate.Sub(thisUpdate) - expectedDiff, err := parseutil.ParseDurationSecond(defaultCrlConfig.OcspExpiry) + resp, err = CBRead(b, s, "config/crl") + requireSuccessNonNilResponse(t, resp, err, "failed reading from config/crl") + requireFieldsSetInResp(t, resp, "ocsp_expiry") + ocspExpiryRaw := resp.Data["ocsp_expiry"].(string) + expectedDiff, err := parseutil.ParseDurationSecond(ocspExpiryRaw) require.NoError(t, err, "failed to parse default ocsp expiry value") - require.Equal(t, expectedDiff, nextUpdateDiff, - fmt.Sprintf("the delta between thisUpdate %s and nextUpdate: %s should have been around: %s but was %s", - thisUpdate, nextUpdate, defaultCrlConfig.OcspExpiry, nextUpdateDiff)) + thisUpdate := ocspResp.ThisUpdate + require.Less(t, time.Since(thisUpdate), 10*time.Second, "expected ThisUpdate field to be within the last 10 seconds") + if expectedDiff != 0 { + nextUpdate := ocspResp.NextUpdate + require.False(t, nextUpdate.IsZero(), "nextUpdate field value should have been a non-zero time") + require.True(t, thisUpdate.Before(nextUpdate), + fmt.Sprintf("thisUpdate %s, should have been before nextUpdate: %s", thisUpdate, nextUpdate)) + nextUpdateDiff := nextUpdate.Sub(thisUpdate) + require.Equal(t, expectedDiff, nextUpdateDiff, + fmt.Sprintf("the delta between thisUpdate %s and nextUpdate: %s should have been around: %s but was %s", + thisUpdate, nextUpdate, defaultCrlConfig.OcspExpiry, nextUpdateDiff)) + } else { + // With the config value set to 0, we shouldn't have a NextUpdate field set + require.True(t, ocspResp.NextUpdate.IsZero(), "nextUpdate value was not zero as expected was: %v", ocspResp.NextUpdate) + } requireOcspSignatureAlgoForKey(t, testEnv.issuer2.SignatureAlgorithm, ocspResp.SignatureAlgorithm) requireOcspResponseSignedBy(t, ocspResp, testEnv.issuer2) } @@ -610,16 +636,22 @@ type ocspTestEnv struct { } func setupOcspEnv(t *testing.T, keyType string) (*backend, logical.Storage, *ocspTestEnv) { - return setupOcspEnvWithCaKeyConfig(t, keyType, 0, 0) + return setupOcspEnvWithCaKeyConfig(t, keyType, 0, 0, 12*time.Hour) } -func setupOcspEnvWithCaKeyConfig(t *testing.T, keyType string, caKeyBits int, caKeySigBits int) (*backend, logical.Storage, *ocspTestEnv) { +func setupOcspEnvWithCaKeyConfig(t *testing.T, keyType string, caKeyBits int, caKeySigBits int, ocspExpiry time.Duration) (*backend, logical.Storage, *ocspTestEnv) { b, s := CreateBackendWithStorage(t) var issuerCerts []*x509.Certificate var leafCerts []*x509.Certificate var issuerIds []issuerID var keyIds []keyID + resp, err := CBWrite(b, s, "config/crl", map[string]interface{}{ + "ocsp_enable": true, + "ocsp_expiry": fmt.Sprintf("%ds", int(ocspExpiry.Seconds())), + }) + requireSuccessNonNilResponse(t, resp, err, "config/crl failed") + for i := 0; i < 2; i++ { resp, err := CBWrite(b, s, "root/generate/internal", map[string]interface{}{ "key_type": keyType, diff --git a/changelog/24192.txt b/changelog/24192.txt new file mode 100644 index 000000000000..97a26746bd0f --- /dev/null +++ b/changelog/24192.txt @@ -0,0 +1,3 @@ +```release-note:bug +secrets/pki: Do not set nextUpdate field in OCSP responses when ocsp_expiry is 0 +``` diff --git a/website/content/api-docs/secret/pki.mdx b/website/content/api-docs/secret/pki.mdx index 28f7fba484e5..6a87cf51685c 100644 --- a/website/content/api-docs/secret/pki.mdx +++ b/website/content/api-docs/secret/pki.mdx @@ -4199,8 +4199,9 @@ the CRL. - `ocsp_disable` `(bool: false)` - Disables or enables the OCSP responder in Vault. - `ocsp_expiry` `(string: "12h")` - The amount of time an OCSP response can be cached for, - (controls the NextUpdate field), useful for OCSP stapling refresh durations. Setting to 0 - should effectively disable caching in third party systems. + (controls the NextUpdate field), useful for OCSP stapling refresh durations. If set to 0 + the NextUpdate field is not set, indicating newer revocation information is available + all the time. - `auto_rebuild` `(bool: false)` - Enables or disables periodic rebuilding of the CRL upon expiry. From d2afea92a111f94c0586d99a7902937f29ad750a Mon Sep 17 00:00:00 2001 From: Violet Hynes Date: Mon, 20 Nov 2023 10:45:36 -0500 Subject: [PATCH 02/74] VAULT-22030 update error message when from entity isn't found as part of automated entity merge (#24188) * VAULT-22030 update error message when from entity isn't found as part of automated entity merge * VAULT-22030 add extra info --- vault/identity_store_entities.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vault/identity_store_entities.go b/vault/identity_store_entities.go index bf890c57f2b0..f59457f95098 100644 --- a/vault/identity_store_entities.go +++ b/vault/identity_store_entities.go @@ -883,6 +883,15 @@ func (i *IdentityStore) mergeEntity(ctx context.Context, txn *memdb.Txn, toEntit } if fromEntity == nil { + // If forceMergeAliases is true, and we didn't find a fromEntity, then something + // is wrong with storage. This function was called as part of an automated + // merge from CreateOrFetchEntity or Invalidate to repatriate an alias with its 'true' + // entity. As a result, the entity _should_ exist, but we can't find it. + // MemDb may be in a bad state, because fromEntity should be non-nil in the + // automated merge case. + if forceMergeAliases { + return fmt.Errorf("fromEntity %s was not found in memdb as part of an automated entity merge into %s; storage/memdb may be in a bad state", fromEntityID, toEntity.ID), nil, nil + } return errors.New("entity id to merge from is invalid"), nil, nil } @@ -995,6 +1004,15 @@ func (i *IdentityStore) mergeEntity(ctx context.Context, txn *memdb.Txn, toEntit } if fromEntity == nil { + // If forceMergeAliases is true, and we didn't find a fromEntity, then something + // is wrong with storage. This function was called as part of an automated + // merge from CreateOrFetchEntity or Invalidate to repatriate an alias with its 'true' + // entity. As a result, the entity _should_ exist, but we can't find it. + // MemDb may be in a bad state, because fromEntity should be non-nil in the + // automated merge case. + if forceMergeAliases { + return fmt.Errorf("fromEntity %s was not found in memdb as part of an automated entity merge into %s; storage/memdb may be in a bad state", fromEntityID, toEntity.ID), nil, nil + } return errors.New("entity id to merge from is invalid"), nil, nil } From bcbd45b380d2cf776cb3cd920f03291301cee998 Mon Sep 17 00:00:00 2001 From: Steven Clark Date: Mon, 20 Nov 2023 10:51:03 -0500 Subject: [PATCH 03/74] Handle expired OCSP responses from server (#24193) * Handle expired OCSP responses from server - If a server replied with what we considered an expired OCSP response (nextUpdate is now or in the past), and it was our only response we would panic due to missing error handling logic. * Add cl --- changelog/24193.txt | 3 ++ sdk/helper/ocsp/client.go | 11 ++++- sdk/helper/ocsp/ocsp_test.go | 89 ++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 changelog/24193.txt diff --git a/changelog/24193.txt b/changelog/24193.txt new file mode 100644 index 000000000000..67ea1d0ae974 --- /dev/null +++ b/changelog/24193.txt @@ -0,0 +1,3 @@ +```release-note:bug +auth/cert: Handle errors related to expired OCSP server responses +``` diff --git a/sdk/helper/ocsp/client.go b/sdk/helper/ocsp/client.go index c1b9c2fbc37a..8bd9cea4ee8f 100644 --- a/sdk/helper/ocsp/client.go +++ b/sdk/helper/ocsp/client.go @@ -517,6 +517,11 @@ func (c *Client) GetRevocationStatus(ctx context.Context, subject, issuer *x509. } if isValidOCSPStatus(ret.code) { ocspStatuses[i] = ret + } else if ret.err != nil { + // This check needs to occur after the isValidOCSPStatus as the unknown + // status also sets an err value within ret. + errors[i] = ret.err + return ret.err } return nil } @@ -568,8 +573,12 @@ func (c *Client) GetRevocationStatus(ctx context.Context, subject, issuer *x509. if (ret == nil || ret.code == ocspStatusUnknown) && firstError != nil { return nil, firstError } - // otherwise ret should contain a response for the overall request + // An extra safety in case ret and firstError are both nil + if ret == nil { + return nil, fmt.Errorf("failed to extract a known response code or error from the OCSP server") + } + // otherwise ret should contain a response for the overall request if !isValidOCSPStatus(ret.code) { return ret, nil } diff --git a/sdk/helper/ocsp/ocsp_test.go b/sdk/helper/ocsp/ocsp_test.go index 2f3f1976d2a8..677dfa85aa5e 100644 --- a/sdk/helper/ocsp/ocsp_test.go +++ b/sdk/helper/ocsp/ocsp_test.go @@ -6,14 +6,20 @@ import ( "bytes" "context" "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" "crypto/tls" "crypto/x509" + "crypto/x509/pkix" "errors" "fmt" "io" "io/ioutil" + "math/big" "net" "net/http" + "net/http/httptest" "net/url" "testing" "time" @@ -21,6 +27,7 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-retryablehttp" lru "github.com/hashicorp/golang-lru" + "github.com/stretchr/testify/require" "golang.org/x/crypto/ocsp" ) @@ -201,6 +208,88 @@ func TestUnitCheckOCSPResponseCache(t *testing.T) { } } +func TestUnitExpiredOCSPResponse(t *testing.T) { + rootCaKey, rootCa, leafCert := createCaLeafCerts(t) + + expiredOcspResponse := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + now := time.Now() + ocspRes := ocsp.Response{ + SerialNumber: big.NewInt(2), + ThisUpdate: now.Add(-1 * time.Hour), + NextUpdate: now.Add(-30 * time.Minute), + Status: ocsp.Good, + } + response, err := ocsp.CreateResponse(rootCa, rootCa, ocspRes, rootCaKey) + if err != nil { + _, _ = w.Write(ocsp.InternalErrorErrorResponse) + t.Fatalf("failed generating OCSP response: %v", err) + } + _, _ = w.Write(response) + }) + ts := httptest.NewServer(expiredOcspResponse) + defer ts.Close() + + logFactory := func() hclog.Logger { + return hclog.NewNullLogger() + } + client := New(logFactory, 100) + + ctx := context.Background() + + config := &VerifyConfig{ + OcspEnabled: true, + OcspServersOverride: []string{ts.URL}, + OcspFailureMode: FailOpenFalse, + QueryAllServers: false, + } + + status, err := client.GetRevocationStatus(ctx, leafCert, rootCa, config) + require.ErrorContains(t, err, "invalid validity", + "Expected error got response: %v, %v", status, err) +} + +func createCaLeafCerts(t *testing.T) (*ecdsa.PrivateKey, *x509.Certificate, *x509.Certificate) { + rootCaKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err, "failed generated root key for CA") + + // Validate we reject CSRs that contain CN that aren't in the original order + cr := &x509.Certificate{ + Subject: pkix.Name{CommonName: "Root Cert"}, + SerialNumber: big.NewInt(1), + IsCA: true, + BasicConstraintsValid: true, + SignatureAlgorithm: x509.ECDSAWithSHA256, + NotBefore: time.Now().Add(-1 * time.Second), + NotAfter: time.Now().AddDate(1, 0, 0), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}, + } + rootCaBytes, err := x509.CreateCertificate(rand.Reader, cr, cr, &rootCaKey.PublicKey, rootCaKey) + require.NoError(t, err, "failed generating root ca") + + rootCa, err := x509.ParseCertificate(rootCaBytes) + require.NoError(t, err, "failed parsing root ca") + + leafKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err, "failed generated leaf key") + + cr = &x509.Certificate{ + Subject: pkix.Name{CommonName: "Leaf Cert"}, + SerialNumber: big.NewInt(2), + SignatureAlgorithm: x509.ECDSAWithSHA256, + NotBefore: time.Now().Add(-1 * time.Second), + NotAfter: time.Now().AddDate(1, 0, 0), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + } + leafCertBytes, err := x509.CreateCertificate(rand.Reader, cr, rootCa, &leafKey.PublicKey, rootCaKey) + require.NoError(t, err, "failed generating root ca") + + leafCert, err := x509.ParseCertificate(leafCertBytes) + require.NoError(t, err, "failed parsing root ca") + return rootCaKey, rootCa, leafCert +} + func TestUnitValidateOCSP(t *testing.T) { ocspRes := &ocsp.Response{} ost, err := validateOCSP(ocspRes) From 4cf837d56a7ec639d3bedf4b585d961c69f59458 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 20 Nov 2023 18:00:03 +0000 Subject: [PATCH 04/74] UI: HDS adoption replace footer element (#24191) * Replace footer with `Hds::AppFooter` * Remove unused `.footer` styles * Add changelog entry * Use `doc-link` helper for 'Documentation' link --- changelog/24191.txt | 3 +++ ui/app/styles/core.scss | 1 - ui/app/styles/core/footer.scss | 20 ----------------- ui/app/templates/vault.hbs | 39 +++++++++++++--------------------- 4 files changed, 18 insertions(+), 45 deletions(-) create mode 100644 changelog/24191.txt delete mode 100644 ui/app/styles/core/footer.scss diff --git a/changelog/24191.txt b/changelog/24191.txt new file mode 100644 index 000000000000..2fe98e926d05 --- /dev/null +++ b/changelog/24191.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Implement Helios Design System footer component +``` diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index 92d50091f681..eefc125be3cb 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -30,7 +30,6 @@ @import './core/control'; @import './core/field'; @import './core/file'; -@import './core/footer'; @import './core/inputs'; @import './core/json-diff-patch'; @import './core/label'; diff --git a/ui/app/styles/core/footer.scss b/ui/app/styles/core/footer.scss deleted file mode 100644 index c3a193c309e5..000000000000 --- a/ui/app/styles/core/footer.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -.footer { - background-color: transparent; - border-top: $base-border; - padding: $spacing-24 $spacing-20; - margin-top: auto; - - span:not(:first-child) { - display: inline-block; - padding: 0 0.5rem; - - @include until($mobile) { - display: none; - } - } -} diff --git a/ui/app/templates/vault.hbs b/ui/app/templates/vault.hbs index 0c3fd74bc253..3a72ea2d85ac 100644 --- a/ui/app/templates/vault.hbs +++ b/ui/app/templates/vault.hbs @@ -4,32 +4,23 @@ ~}} {{outlet}} -
- - - - - Vault - {{this.auth.activeCluster.leaderNode.version}} - - + + + + Vault + {{this.auth.activeCluster.leaderNode.version}} + {{#if (is-version "OSS")}} - - - Upgrade to Vault Enterprise - - + + Upgrade to Vault Enterprise + {{/if}} - - - Documentation - - -
+ + Documentation + + + + {{#if (eq this.env "development")}}
From b833b30315fe24f3249dd83422eb86be96f73de8 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:21:00 -0600 Subject: [PATCH 05/74] UI: always send capabilities-self request in user's root namespace (#24168) * Add getRelativePath helper and use to calculate relativeNamespace * Always request capabilities-self on users root ns and prefix body with relative path * Update capabilities adapter with test * add changelog * Simplify getRelativePath logic * test update --- changelog/24168.txt | 3 ++ ui/app/adapters/capabilities.js | 16 +++++++- ui/app/services/namespace.js | 8 ++++ ui/lib/core/addon/utils/sanitize-path.js | 20 ++++++++++ ui/lib/core/app/utils/sanitize-path.js | 2 +- ui/tests/unit/adapters/capabilities-test.js | 41 +++++++++++++++++++++ ui/tests/unit/utils/sanitize-path-test.js | 11 +++++- 7 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 changelog/24168.txt diff --git a/changelog/24168.txt b/changelog/24168.txt new file mode 100644 index 000000000000..09f34ce8621c --- /dev/null +++ b/changelog/24168.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: capabilities-self is always called in the user's root namespace +``` \ No newline at end of file diff --git a/ui/app/adapters/capabilities.js b/ui/app/adapters/capabilities.js index de54824bbb0f..8eee53c3e27b 100644 --- a/ui/app/adapters/capabilities.js +++ b/ui/app/adapters/capabilities.js @@ -6,14 +6,28 @@ import AdapterError from '@ember-data/adapter/error'; import { set } from '@ember/object'; import ApplicationAdapter from './application'; +import { sanitizePath } from 'core/utils/sanitize-path'; export default ApplicationAdapter.extend({ pathForType() { return 'capabilities-self'; }, + formatPaths(path) { + const { relativeNamespace } = this.namespaceService; + if (!relativeNamespace) { + return [path]; + } + // ensure original path doesn't have leading slash + return [`${relativeNamespace}/${path.replace(/^\//, '')}`]; + }, + findRecord(store, type, id) { - return this.ajax(this.buildURL(type), 'POST', { data: { paths: [id] } }).catch((e) => { + const paths = this.formatPaths(id); + return this.ajax(this.buildURL(type), 'POST', { + data: { paths }, + namespace: sanitizePath(this.namespaceService.userRootNamespace), + }).catch((e) => { if (e instanceof AdapterError) { set(e, 'policyPath', 'sys/capabilities-self'); } diff --git a/ui/app/services/namespace.js b/ui/app/services/namespace.js index 07802320a031..cd8e65e67e05 100644 --- a/ui/app/services/namespace.js +++ b/ui/app/services/namespace.js @@ -7,6 +7,7 @@ import { alias, equal } from '@ember/object/computed'; import Service, { inject as service } from '@ember/service'; import { task } from 'ember-concurrency'; import { computed } from '@ember/object'; +import { getRelativePath } from 'core/utils/sanitize-path'; const ROOT_NAMESPACE = ''; export default Service.extend({ @@ -28,6 +29,13 @@ export default Service.extend({ return parts[parts.length - 1]; }), + relativeNamespace: computed('path', 'userRootNamespace', function () { + // relative namespace is the current namespace minus the user's root. + // so if we're in app/staging/group1 but the user's root is app, the + // relative namespace is staging/group + return getRelativePath(this.path, this.userRootNamespace); + }), + setNamespace(path) { if (!path) { this.set('path', ''); diff --git a/ui/lib/core/addon/utils/sanitize-path.js b/ui/lib/core/addon/utils/sanitize-path.js index bfd200a362d5..7ca66c651de6 100644 --- a/ui/lib/core/addon/utils/sanitize-path.js +++ b/ui/lib/core/addon/utils/sanitize-path.js @@ -4,6 +4,7 @@ */ export function sanitizePath(path) { + if (!path) return ''; //remove whitespace + remove trailing and leading slashes return path.trim().replace(/^\/+|\/+$/g, ''); } @@ -11,3 +12,22 @@ export function sanitizePath(path) { export function ensureTrailingSlash(path) { return path.replace(/(\w+[^/]$)/g, '$1/'); } + +/** + * getRelativePath is for removing matching segments of a subpath from the front of a full path. + * This method assumes that the full path starts with all of the root path. + * @param {string} fullPath eg apps/prod/app_1/test + * @param {string} rootPath eg apps/prod + * @returns the leftover segment, eg app_1/test + */ +export function getRelativePath(fullPath = '', rootPath = '') { + const root = sanitizePath(rootPath); + const full = sanitizePath(fullPath); + + if (!root) { + return full; + } else if (root === full) { + return ''; + } + return sanitizePath(full.substring(root.length)); +} diff --git a/ui/lib/core/app/utils/sanitize-path.js b/ui/lib/core/app/utils/sanitize-path.js index 58a60b33d422..27a0db30e1c6 100644 --- a/ui/lib/core/app/utils/sanitize-path.js +++ b/ui/lib/core/app/utils/sanitize-path.js @@ -3,4 +3,4 @@ * SPDX-License-Identifier: BUSL-1.1 */ -export { ensureTrailingSlash, sanitizePath } from 'core/utils/sanitize-path'; +export { ensureTrailingSlash, sanitizePath, getRelativePath } from 'core/utils/sanitize-path'; diff --git a/ui/tests/unit/adapters/capabilities-test.js b/ui/tests/unit/adapters/capabilities-test.js index bcd1f055b551..2e0bb0f33092 100644 --- a/ui/tests/unit/adapters/capabilities-test.js +++ b/ui/tests/unit/adapters/capabilities-test.js @@ -24,4 +24,45 @@ module('Unit | Adapter | capabilities', function (hooks) { assert.deepEqual({ paths: ['foo'] }, options.data, 'data params OK'); assert.strictEqual(method, 'POST', 'method OK'); }); + + test('enterprise calls the correct url within namespace when userRoot = root', function (assert) { + const namespaceSvc = this.owner.lookup('service:namespace'); + namespaceSvc.setNamespace('admin'); + + let url, method, options; + const adapter = this.owner.factoryFor('adapter:capabilities').create({ + ajax: (...args) => { + [url, method, options] = args; + return resolve(); + }, + }); + + adapter.findRecord(null, 'capabilities', 'foo'); + assert.strictEqual(url, '/v1/sys/capabilities-self', 'calls the correct URL'); + assert.deepEqual({ paths: ['admin/foo'] }, options.data, 'data params prefix paths with namespace'); + assert.strictEqual(options.namespace, '', 'sent with root namespace'); + assert.strictEqual(method, 'POST', 'method OK'); + }); + + test('enterprise calls the correct url within namespace when userRoot is not root', function (assert) { + const namespaceSvc = this.owner.lookup('service:namespace'); + namespaceSvc.setNamespace('admin/bar/baz'); + namespaceSvc.reopen({ + userRootNamespace: 'admin/bar', + }); + + let url, method, options; + const adapter = this.owner.factoryFor('adapter:capabilities').create({ + ajax: (...args) => { + [url, method, options] = args; + return resolve(); + }, + }); + + adapter.findRecord(null, 'capabilities', 'foo'); + assert.strictEqual(url, '/v1/sys/capabilities-self', 'calls the correct URL'); + assert.deepEqual({ paths: ['baz/foo'] }, options.data, 'data params prefix path with relative namespace'); + assert.strictEqual(options.namespace, 'admin/bar', 'sent with root namespace'); + assert.strictEqual(method, 'POST', 'method OK'); + }); }); diff --git a/ui/tests/unit/utils/sanitize-path-test.js b/ui/tests/unit/utils/sanitize-path-test.js index cdde888100c9..7107d737f2c5 100644 --- a/ui/tests/unit/utils/sanitize-path-test.js +++ b/ui/tests/unit/utils/sanitize-path-test.js @@ -4,7 +4,7 @@ */ import { module, test } from 'qunit'; -import { ensureTrailingSlash, sanitizePath } from 'core/utils/sanitize-path'; +import { ensureTrailingSlash, getRelativePath, sanitizePath } from 'core/utils/sanitize-path'; module('Unit | Utility | sanitize-path', function () { test('it removes spaces and slashes from either side', function (assert) { @@ -14,10 +14,19 @@ module('Unit | Utility | sanitize-path', function () { 'removes spaces and slashes on either side' ); assert.strictEqual(sanitizePath('//foo/bar/baz/'), 'foo/bar/baz', 'removes more than one slash'); + assert.strictEqual(sanitizePath(undefined), '', 'handles falsey values'); }); test('#ensureTrailingSlash', function (assert) { assert.strictEqual(ensureTrailingSlash('foo/bar'), 'foo/bar/', 'adds trailing slash'); assert.strictEqual(ensureTrailingSlash('baz/'), 'baz/', 'keeps trailing slash if there is one'); }); + + test('#getRelativePath', function (assert) { + assert.strictEqual(getRelativePath('/', undefined), '', 'works with minimal inputs'); + assert.strictEqual(getRelativePath('/baz/bar/', undefined), 'baz/bar', 'sanitizes the output'); + assert.strictEqual(getRelativePath('recipes/cookies/choc-chip/', 'recipes/'), 'cookies/choc-chip'); + assert.strictEqual(getRelativePath('/recipes/cookies/choc-chip/', 'recipes/cookies'), 'choc-chip'); + assert.strictEqual(getRelativePath('/admin/bop/boop/admin_foo/baz/', 'admin'), 'bop/boop/admin_foo/baz'); + }); }); From c0014c9640fb4b13abdce9818bfa923d9e94e34c Mon Sep 17 00:00:00 2001 From: Victor Rodriguez Date: Tue, 21 Nov 2023 08:56:58 -0500 Subject: [PATCH 06/74] Augment testCore_Rekey_Update_Common to test for RekeyUpdate errors. (#24206) --- vault/rekey_test.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/vault/rekey_test.go b/vault/rekey_test.go index c0857a6f94b6..a7278b037f26 100644 --- a/vault/rekey_test.go +++ b/vault/rekey_test.go @@ -151,6 +151,10 @@ func TestCore_Rekey_Update(t *testing.T) { } func testCore_Rekey_Update_Common(t *testing.T, c *Core, keys [][]byte, root string, recovery bool) { + testCore_Rekey_Update_Common_Error(t, c, keys, root, recovery, false) +} + +func testCore_Rekey_Update_Common_Error(t *testing.T, c *Core, keys [][]byte, root string, recovery bool, wantRekeyUpdateError bool) { var err error // Start a rekey var expType string @@ -184,12 +188,19 @@ func testCore_Rekey_Update_Common(t *testing.T, c *Core, keys [][]byte, root str for _, key := range keys { result, err = c.RekeyUpdate(context.Background(), key, rkconf.Nonce, recovery) if err != nil { - t.Fatalf("err: %v", err) + if !wantRekeyUpdateError { + t.Fatalf("err: %v", err) + } else { + return + } } if result != nil { break } } + if wantRekeyUpdateError { + t.Fatal("expected and error during RekeyUpdate") + } if result == nil { t.Fatal("nil result after update") } From 1bf366ccdc3032f4451d6fead0bf58205de4c37b Mon Sep 17 00:00:00 2001 From: Nick Cabatoff Date: Tue, 21 Nov 2023 10:08:18 -0500 Subject: [PATCH 07/74] Use our fork of bbolt to improve freelist performance (#24010) --- changelog/24010.txt | 3 + .../cache/cacheboltdb/bolt.go | 2 +- .../cache/cacheboltdb/bolt_test.go | 2 +- go.mod | 3 +- go.sum | 18 +++++ physical/raft/fsm.go | 2 +- physical/raft/raft.go | 66 ++++++++++++++++++- physical/raft/raft_test.go | 2 +- physical/raft/snapshot.go | 2 +- 9 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 changelog/24010.txt diff --git a/changelog/24010.txt b/changelog/24010.txt new file mode 100644 index 000000000000..aa72bc977912 --- /dev/null +++ b/changelog/24010.txt @@ -0,0 +1,3 @@ +```release-note:improvement +storage/raft: Upgrade to bbolt 1.3.8, along with an extra patch to reduce time scanning large freelist maps. +``` diff --git a/command/agentproxyshared/cache/cacheboltdb/bolt.go b/command/agentproxyshared/cache/cacheboltdb/bolt.go index 6100ef896298..05d5ad93637a 100644 --- a/command/agentproxyshared/cache/cacheboltdb/bolt.go +++ b/command/agentproxyshared/cache/cacheboltdb/bolt.go @@ -12,10 +12,10 @@ import ( "time" "github.com/golang/protobuf/proto" + bolt "github.com/hashicorp-forge/bbolt" "github.com/hashicorp/go-hclog" wrapping "github.com/hashicorp/go-kms-wrapping/v2" "github.com/hashicorp/go-multierror" - bolt "go.etcd.io/bbolt" ) const ( diff --git a/command/agentproxyshared/cache/cacheboltdb/bolt_test.go b/command/agentproxyshared/cache/cacheboltdb/bolt_test.go index dbfafdce7bb4..06a31780b5ad 100644 --- a/command/agentproxyshared/cache/cacheboltdb/bolt_test.go +++ b/command/agentproxyshared/cache/cacheboltdb/bolt_test.go @@ -14,11 +14,11 @@ import ( "time" "github.com/golang/protobuf/proto" + bolt "github.com/hashicorp-forge/bbolt" "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/command/agentproxyshared/cache/keymanager" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - bolt "go.etcd.io/bbolt" ) func getTestKeyManager(t *testing.T) keymanager.KeyManager { diff --git a/go.mod b/go.mod index e2a005ae2182..ca8ab3deab56 100644 --- a/go.mod +++ b/go.mod @@ -75,6 +75,7 @@ require ( github.com/google/go-github v17.0.0+incompatible github.com/google/go-metrics-stackdriver v0.2.0 github.com/google/tink/go v1.7.0 + github.com/hashicorp-forge/bbolt v1.3.8-hc3 github.com/hashicorp/cap v0.3.4 github.com/hashicorp/cap/ldap v0.0.0-20230914221201-c4eecc7e31f7 github.com/hashicorp/consul-template v0.33.0 @@ -203,7 +204,6 @@ require ( github.com/sethvargo/go-limiter v0.7.1 github.com/shirou/gopsutil/v3 v3.22.6 github.com/stretchr/testify v1.8.4 - go.etcd.io/bbolt v1.3.7 go.etcd.io/etcd/client/pkg/v3 v3.5.7 go.etcd.io/etcd/client/v2 v2.305.5 go.etcd.io/etcd/client/v3 v3.5.7 @@ -507,6 +507,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/zclconf/go-cty v1.12.1 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect + go.etcd.io/bbolt v1.3.7 // indirect go.etcd.io/etcd/api/v3 v3.5.7 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/metric v1.16.0 // indirect diff --git a/go.sum b/go.sum index e575e7c7184a..f32079238fa6 100644 --- a/go.sum +++ b/go.sum @@ -954,10 +954,13 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aov github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.1.0 h1:Q707jfTFqfunSnh73YkCBDXR3GQJKno3chPRxXw//ho= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.1.0/go.mod h1:vjoxsjVnPwhjHZw4PuuhpgYlcxWl5tyNedLHUl0ulFA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.0.0 h1:nBy98uKOIfun5z6wx6jwWLrULcM0+cjBalBFZlEZ7CA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.0.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 h1:7CBQ+Ei8SP2c6ydQTGCCrS35bDxgTMfoP2miAwK++OU= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1/go.mod h1:c/wcGeGx5FUPbM/JltUYHZcKmigwyVLJlDq+4HdtXaw= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0 h1:u/LLAOFgsMv7HmNL4Qufg58y+qElGOt5qv0z1mURkRY= @@ -1125,6 +1128,7 @@ github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= @@ -1576,6 +1580,7 @@ github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQx github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= +github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= @@ -1637,6 +1642,7 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -1663,6 +1669,7 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= @@ -1681,6 +1688,7 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmS github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE= github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -1838,6 +1846,7 @@ github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= @@ -2101,6 +2110,7 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -2115,6 +2125,8 @@ github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8 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-forge/bbolt v1.3.8-hc3 h1:iTWR3RDPj0TGChAvJ8QjHFcNFWAUVgNQV73IE6gAX4E= +github.com/hashicorp-forge/bbolt v1.3.8-hc3/go.mod h1:sQBu5UIJ+rcUFU4Fo9rpTHNV935jwmGWS3dQ/MV8810= github.com/hashicorp/cap v0.3.4 h1:RoqWYqr6LaDLuvnBCpod1sZtvuEhehIhu0GncmoHW40= github.com/hashicorp/cap v0.3.4/go.mod h1:dHTmyMIVbzT981XxRoci5G//dfWmd/HhuNiCH6J5+IA= github.com/hashicorp/cap/ldap v0.0.0-20230914221201-c4eecc7e31f7 h1:jgVdtp5YMn++PxnYhAFfrURfLf+nlqzBeddbvRG+tTg= @@ -2126,6 +2138,7 @@ github.com/hashicorp/consul/api v1.23.0 h1:L6e4v1AfoumqAHq/Rrsmuulev+nd7vltM3k8H github.com/hashicorp/consul/api v1.23.0/go.mod h1:SfvUIT74b0EplDuNgAJQ/FVqSO6KyK2ia80UI39/Ye8= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.14.0 h1:Hly+BMNMssVzoWddbBnBFi3W+Fzytvm0haSkihhj3GU= +github.com/hashicorp/consul/sdk v0.14.0/go.mod h1:gHYeuDa0+0qRAD6Wwr6yznMBvBwHKoxSBoW5l73+saE= github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c= github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -2143,6 +2156,7 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S github.com/hashicorp/go-discover v0.0.0-20210818145131-c573d69da192 h1:eje2KOX8Sf7aYPiAsLnpWdAIrGRMcpFjN/Go/Exb7Zo= github.com/hashicorp/go-discover v0.0.0-20210818145131-c573d69da192/go.mod h1:3/4dzY4lR1Hzt9bBqMhBzG7lngZ0GKx/nL6G/ad62wE= github.com/hashicorp/go-gatedio v0.5.0 h1:Jm1X5yP4yCqqWj5L1TgW7iZwCVPGtVc+mro5r/XX7Tg= +github.com/hashicorp/go-gatedio v0.5.0/go.mod h1:Lr3t8L6IyxD3DAeaUxGcgl2JnRUpWMCsmBl4Omu/2t4= github.com/hashicorp/go-gcp-common v0.8.0 h1:/2vGAbCU1v+BZ3YHXTCzTvxqma9WOJHYtADTfhZixLo= github.com/hashicorp/go-gcp-common v0.8.0/go.mod h1:Q7zYRy9ue9SuaEN2s9YLIQs4SoKHdoRmKRcImY3SLgs= github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= @@ -2442,6 +2456,7 @@ github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= github.com/jarcoal/httpmock v1.0.7 h1:d1a2VFpSdm5gtjhCPWsQHSnx8+5V3ms5431YwvmkuNk= +github.com/jarcoal/httpmock v1.0.7/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= @@ -2809,6 +2824,7 @@ github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkA github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= +github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -2829,6 +2845,7 @@ github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2 github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/open-policy-agent/opa v0.42.2/go.mod h1:MrmoTi/BsKWT58kXlVayBb+rYVeaMwuBm3nYAN3923s= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -3057,6 +3074,7 @@ github.com/sethvargo/go-limiter v0.7.1/go.mod h1:C0kbSFbiriE5k2FFOe18M1YZbAR2Fiw github.com/shirou/gopsutil/v3 v3.22.6 h1:FnHOFOh+cYAM0C30P+zysPISzlknLC5Z1G4EAElznfQ= github.com/shirou/gopsutil/v3 v3.22.6/go.mod h1:EdIubSnZhbAvBS1yJ7Xi+AShB/hxwLHOMz4MCYz7yMs= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= diff --git a/physical/raft/fsm.go b/physical/raft/fsm.go index abc5e87a0fc8..3323f987df8b 100644 --- a/physical/raft/fsm.go +++ b/physical/raft/fsm.go @@ -20,6 +20,7 @@ import ( "github.com/armon/go-metrics" "github.com/golang/protobuf/proto" + bolt "github.com/hashicorp-forge/bbolt" log "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-raftchunking" @@ -28,7 +29,6 @@ import ( "github.com/hashicorp/vault/sdk/helper/jsonutil" "github.com/hashicorp/vault/sdk/physical" "github.com/hashicorp/vault/sdk/plugin/pb" - bolt "go.etcd.io/bbolt" ) const ( diff --git a/physical/raft/raft.go b/physical/raft/raft.go index ecfdb92c01d7..c6ca8a789b40 100644 --- a/physical/raft/raft.go +++ b/physical/raft/raft.go @@ -20,6 +20,7 @@ import ( "github.com/armon/go-metrics" "github.com/golang/protobuf/proto" + bolt "github.com/hashicorp-forge/bbolt" log "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-raftchunking" "github.com/hashicorp/go-secure-stdlib/parseutil" @@ -37,7 +38,7 @@ import ( "github.com/hashicorp/vault/sdk/physical" "github.com/hashicorp/vault/vault/cluster" "github.com/hashicorp/vault/version" - bolt "go.etcd.io/bbolt" + etcdbolt "go.etcd.io/bbolt" ) const ( @@ -400,7 +401,7 @@ func NewRaftBackend(conf map[string]string, logger log.Logger) (physical.Backend // Create the backend raft store for logs and stable storage. dbPath := filepath.Join(path, "raft.db") - opts := boltOptions(dbPath) + opts := etcdboltOptions(dbPath) raftOptions := raftboltdb.Options{ Path: dbPath, BoltOptions: opts, @@ -631,7 +632,7 @@ func (b *RaftBackend) CollectMetrics(sink *metricsutil.ClusterMetricSink) { stats = b.raft.Stats() } b.l.RUnlock() - b.collectMetricsWithStats(logstoreStats, sink, "logstore") + b.collectEtcdBoltMetricsWithStats(logstoreStats, sink, "logstore") b.collectMetricsWithStats(fsmStats, sink, "fsm") labels := []metrics.Label{ { @@ -672,6 +673,29 @@ func (b *RaftBackend) collectMetricsWithStats(stats bolt.Stats, sink *metricsuti sink.IncrCounterWithLabels([]string{"raft_storage", "bolt", "write", "time"}, float32(txstats.GetWriteTime().Milliseconds()), labels) } +func (b *RaftBackend) collectEtcdBoltMetricsWithStats(stats etcdbolt.Stats, sink *metricsutil.ClusterMetricSink, database string) { + txstats := stats.TxStats + labels := []metricsutil.Label{{"database", database}} + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "freelist", "free_pages"}, float32(stats.FreePageN), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "freelist", "pending_pages"}, float32(stats.PendingPageN), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "freelist", "allocated_bytes"}, float32(stats.FreeAlloc), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "freelist", "used_bytes"}, float32(stats.FreelistInuse), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "transaction", "started_read_transactions"}, float32(stats.TxN), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "transaction", "currently_open_read_transactions"}, float32(stats.OpenTxN), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "page", "count"}, float32(txstats.GetPageCount()), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "page", "bytes_allocated"}, float32(txstats.GetPageAlloc()), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "cursor", "count"}, float32(txstats.GetCursorCount()), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "node", "count"}, float32(txstats.GetNodeCount()), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "node", "dereferences"}, float32(txstats.GetNodeDeref()), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "rebalance", "count"}, float32(txstats.GetRebalance()), labels) + sink.AddSampleWithLabels([]string{"raft_storage", "bolt", "rebalance", "time"}, float32(txstats.GetRebalanceTime().Milliseconds()), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "split", "count"}, float32(txstats.GetSplit()), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "spill", "count"}, float32(txstats.GetSpill()), labels) + sink.AddSampleWithLabels([]string{"raft_storage", "bolt", "spill", "time"}, float32(txstats.GetSpillTime().Milliseconds()), labels) + sink.SetGaugeWithLabels([]string{"raft_storage", "bolt", "write", "count"}, float32(txstats.GetWrite()), labels) + sink.IncrCounterWithLabels([]string{"raft_storage", "bolt", "write", "time"}, float32(txstats.GetWriteTime().Milliseconds()), labels) +} + // RaftServer has information about a server in the Raft configuration type RaftServer struct { // NodeID is the name of the server @@ -1916,3 +1940,39 @@ func boltOptions(path string) *bolt.Options { return o } + +func etcdboltOptions(path string) *etcdbolt.Options { + o := &etcdbolt.Options{ + Timeout: 1 * time.Second, + FreelistType: etcdbolt.FreelistMapType, + NoFreelistSync: true, + MmapFlags: getMmapFlags(path), + } + + if os.Getenv("VAULT_RAFT_FREELIST_TYPE") == "array" { + o.FreelistType = etcdbolt.FreelistArrayType + } + + if os.Getenv("VAULT_RAFT_FREELIST_SYNC") != "" { + o.NoFreelistSync = false + } + + // By default, we want to set InitialMmapSize to 100GB, but only on 64bit platforms. + // Otherwise, we set it to whatever the value of VAULT_RAFT_INITIAL_MMAP_SIZE + // is, assuming it can be parsed as an int. Bolt itself sets this to 0 by default, + // so if users are wanting to turn this off, they can also set it to 0. Setting it + // to a negative value is the same as not setting it at all. + if os.Getenv("VAULT_RAFT_INITIAL_MMAP_SIZE") == "" { + o.InitialMmapSize = initialMmapSize + } else { + imms, err := strconv.Atoi(os.Getenv("VAULT_RAFT_INITIAL_MMAP_SIZE")) + + // If there's an error here, it means they passed something that's not convertible to + // a number. Rather than fail startup, just ignore it. + if err == nil && imms > 0 { + o.InitialMmapSize = imms + } + } + + return o +} diff --git a/physical/raft/raft_test.go b/physical/raft/raft_test.go index cc3594f0f12d..e7532b992a99 100644 --- a/physical/raft/raft_test.go +++ b/physical/raft/raft_test.go @@ -21,13 +21,13 @@ import ( "github.com/go-test/deep" "github.com/golang/protobuf/proto" + bolt "github.com/hashicorp-forge/bbolt" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-uuid" "github.com/hashicorp/raft" "github.com/hashicorp/vault/sdk/helper/jsonutil" "github.com/hashicorp/vault/sdk/physical" - bolt "go.etcd.io/bbolt" ) func connectPeers(nodes ...*RaftBackend) { diff --git a/physical/raft/snapshot.go b/physical/raft/snapshot.go index 3eb818574958..adcfac4e1c44 100644 --- a/physical/raft/snapshot.go +++ b/physical/raft/snapshot.go @@ -18,10 +18,10 @@ import ( "time" "github.com/golang/protobuf/proto" + bolt "github.com/hashicorp-forge/bbolt" log "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/sdk/plugin/pb" "github.com/rboyer/safeio" - bolt "go.etcd.io/bbolt" "go.uber.org/atomic" "github.com/hashicorp/raft" From 66b3e439d80c7cb991ec31cd84d652c1001aa3f6 Mon Sep 17 00:00:00 2001 From: Scott Miller Date: Tue, 21 Nov 2023 09:53:41 -0600 Subject: [PATCH 08/74] wordsmithing (#24205) --- website/content/docs/enterprise/fips/fips1402.mdx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/website/content/docs/enterprise/fips/fips1402.mdx b/website/content/docs/enterprise/fips/fips1402.mdx index ce9fa97904e2..4d46dfa46daa 100644 --- a/website/content/docs/enterprise/fips/fips1402.mdx +++ b/website/content/docs/enterprise/fips/fips1402.mdx @@ -88,8 +88,12 @@ reasons: of say, a Root CA involves a concerted, non-Vault effort to accomplish and must be done thoughtfully. -Combined, we suggest leaving the existing cluster in place, and carefully -consider migration of specific workloads to the FIPS-backed cluster. +As such Hashicorp cannot provide support for workloads that are affected +either technically or via non-compliance that results from converting +existing cluster workloads to the FIPS 140-2 Inside binary. + +Instead, we suggest leaving the existing cluster in place, and carefully +consider migration of specific workloads to the FIPS-backed cluster. #### Entropy augmentation restrictions From 68fbb17b9c252869be38b9b87447b51b240cdeb0 Mon Sep 17 00:00:00 2001 From: Steven Clark Date: Tue, 21 Nov 2023 10:58:44 -0500 Subject: [PATCH 09/74] TestTransitImport: Generate Transit wrapping key with a longer context (#24212) - Instead of relying on the initial call to import to generate the wrapping key, generate it within the test setup with a longer dedicated timeout. - This hopefully is enough of a timeout for the 32 bit nightly runner --- command/transit_import_key_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/command/transit_import_key_test.go b/command/transit_import_key_test.go index 6bcf0237df5c..21884c0799ca 100644 --- a/command/transit_import_key_test.go +++ b/command/transit_import_key_test.go @@ -5,11 +5,13 @@ package command import ( "bytes" + "context" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "testing" + "time" "github.com/hashicorp/vault/api" @@ -29,6 +31,15 @@ func TestTransitImport(t *testing.T) { t.Fatalf("transit mount error: %#v", err) } + // Force the generation of the Transit wrapping key now with a longer context + // to help the 32bit nightly tests. This creates a 4096-bit RSA key which can take + // a while on an overloaded system + genWrappingKeyCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + if _, err := client.Logical().ReadWithContext(genWrappingKeyCtx, "transit/wrapping_key"); err != nil { + t.Fatalf("transit failed generating wrapping key: %#v", err) + } + rsa1, rsa2, aes128, aes256 := generateKeys(t) type testCase struct { From f60c643aa8c15083fd7dec38416396ef79bbee27 Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Tue, 21 Nov 2023 09:06:45 -0700 Subject: [PATCH 10/74] UI: HDS adoption replace
-
-
- -
-
- -
-
+
+ + + + {{/if}} \ No newline at end of file diff --git a/ui/lib/replication/addon/templates/components/replication-summary.hbs b/ui/lib/replication/addon/templates/components/replication-summary.hbs index c32a365a53e6..3750ad2c5aea 100644 --- a/ui/lib/replication/addon/templates/components/replication-summary.hbs +++ b/ui/lib/replication/addon/templates/components/replication-summary.hbs @@ -278,11 +278,12 @@ ) }}
-
- -
+
{{/if}} @@ -315,9 +316,7 @@ {{#if this.submit.isRunning}} {{else}} - - - + {{/if}} {{else}} @@ -329,9 +328,7 @@ Performance - - - +
{{/if}} {{/if}} diff --git a/ui/lib/replication/addon/templates/mode/secondaries/add.hbs b/ui/lib/replication/addon/templates/mode/secondaries/add.hbs index 838af13f4442..c6a6cbaabd2f 100644 --- a/ui/lib/replication/addon/templates/mode/secondaries/add.hbs +++ b/ui/lib/replication/addon/templates/mode/secondaries/add.hbs @@ -25,7 +25,7 @@ name="activation-token-id" id="activation-token-id" @value={{this.id}} - data-test-replication-secondary-id={{true}} + data-test-replication-secondary-id />

@@ -45,18 +45,11 @@ {{#if (eq this.replicationMode "performance")}} {{/if}} -

-
- -
-
- - Cancel - -
-
+
+ + + + {{#if this.isModalActive}} diff --git a/ui/lib/replication/addon/templates/mode/secondaries/config-create.hbs b/ui/lib/replication/addon/templates/mode/secondaries/config-create.hbs index 3fbf861065d3..1858da83be36 100644 --- a/ui/lib/replication/addon/templates/mode/secondaries/config-create.hbs +++ b/ui/lib/replication/addon/templates/mode/secondaries/config-create.hbs @@ -11,16 +11,9 @@
-
-
- -
-
- - Cancel - -
-
+
+ + + + \ No newline at end of file diff --git a/ui/lib/replication/addon/templates/mode/secondaries/config-edit.hbs b/ui/lib/replication/addon/templates/mode/secondaries/config-edit.hbs index 77a4b57f33d4..03b5dbd18de0 100644 --- a/ui/lib/replication/addon/templates/mode/secondaries/config-edit.hbs +++ b/ui/lib/replication/addon/templates/mode/secondaries/config-edit.hbs @@ -12,18 +12,9 @@
-
-
-
- -
-
- - Cancel - -
-
-
+
+ + + + \ No newline at end of file diff --git a/ui/lib/replication/addon/templates/mode/secondaries/revoke.hbs b/ui/lib/replication/addon/templates/mode/secondaries/revoke.hbs index fa52a6becde6..1143f5170a5e 100644 --- a/ui/lib/replication/addon/templates/mode/secondaries/revoke.hbs +++ b/ui/lib/replication/addon/templates/mode/secondaries/revoke.hbs @@ -20,21 +20,20 @@ The secondary id to revoke; given initially to generate a secondary token.

-
-
- -
-
- {{#unless this.isRevoking}} - - Cancel - - {{/unless}} -
-
\ No newline at end of file +
+ + + + \ No newline at end of file From 913481fb1f438888fafc15f1131d6091ef8d7c9d Mon Sep 17 00:00:00 2001 From: Scott Miller Date: Tue, 21 Nov 2023 10:25:01 -0600 Subject: [PATCH 11/74] OSS fixes (#24200) --- command/server.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/command/server.go b/command/server.go index 3c1861099047..9cf3f73bdb3e 100644 --- a/command/server.go +++ b/command/server.go @@ -2567,10 +2567,16 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma Priority: 1, Name: "shamir", }) - case 1: - // If there's only one seal and it's disabled assume they want to + default: + allSealsDisabled := true + for _, c := range config.Seals { + if !c.Disabled { + allSealsDisabled = false + } + } + // If all seals are disabled assume they want to // migrate to a shamir seal and simply didn't provide it - if config.Seals[0].Disabled { + if allSealsDisabled { config.Seals = append(config.Seals, &configutil.KMS{ Type: vault.SealConfigTypeShamir.String(), Priority: 1, From b7dff9777db22512fe11d53b28d3ff2ec4dd212a Mon Sep 17 00:00:00 2001 From: Steven Clark Date: Tue, 21 Nov 2023 14:36:49 -0500 Subject: [PATCH 12/74] Allow backends to extract credentials from payloads and trigger an authentication workflow (#23924) * wip * Work on the tuneable allowance and some bugs * Call handleCancellableRequest instead, which gets the audit order more correct and includes the preauth response * Get rid of no longer needed operation * Phew, this wasn't necessary * Add auth error handling by the backend, and fix a bug with handleInvalidCredentials * Cleanup req/resp naming * Use the new form, and data * Discovered that tokens werent really being checked because isLoginRequest returns true for the re-request into the backend, when it shouldnt * Add a few more checks in the delegated request handler for bad inputs - Protect the delegated handler from bad inputs from the backend such as an empty accessor, a path that isn't registered as a login request - Add similar protections for bad auth results as we do in the normal login request paths. Technically not 100% needed but if somehow the handleCancelableRequest doesn't use the handleLoginRequest code path we could get into trouble in the future - Add delegated-auth-accessors flag to the secrets tune command and api-docs * Unit tests and some small fixes * Remove transit preauth test, rely on unit tests * Cleanup and add a little more commentary in tests * Fix typos, add another failure use-case which we reference a disabled auth mount * PR Feedback - Use router to lookup mount instead of defining a new lookup method - Enforce auth table types and namespace when mount is found - Define a type alias for the handleInvalidCreds - Fix typos/grammar - Clean up globals in test * Additional PR feedback - Add test for delegated auth handler - Force batch token usage - Add a test to validate failures if a non-batch token is used - Check for Data member being nil in test cases * Update failure error message around requiring batch tokens * Trap MFA requests * Reword some error messages * Add test and fixes for delegated response wrapping * Move MFA test to dedicated mount - If the delegated auth tests were running in parallel, the MFA test case might influence the other tests, so move the MFA to a dedicated mount * PR feedback: use textproto.CanonicalMIMEHeaderKey - Change the X-Vault-Wrap-Ttl constant to X-Vault-Wrap-TTL and use textproto.CanonicalMIMEHeaderKey to format it within the delete call. - This protects the code around changes of the constant typing * PR feedback - Append Error to RequestDelegatedAuth - Force error interface impl through explicit nil var assignment on RequestDelegatedAuthError - Clean up test factory and leverage NewTestSoloCluster - Leverage newer maps.Clone as this is 1.16 only --------- Co-authored-by: Scott G. Miller --- api/sys_mounts.go | 3 + command/commands.go | 3 + command/secrets_tune.go | 13 + helper/testhelpers/testhelpers.go | 11 +- sdk/helper/consts/consts.go | 4 + sdk/logical/error.go | 46 +- sdk/logical/request.go | 1 + .../delegated_auth/delegated_auth_test.go | 530 ++++++++++++++++++ vault/logical_system.go | 33 ++ vault/logical_system_paths.go | 9 + vault/mount.go | 8 + vault/request_handling.go | 135 ++++- website/content/api-docs/system/mounts.mdx | 6 + 13 files changed, 795 insertions(+), 7 deletions(-) create mode 100644 vault/external_tests/delegated_auth/delegated_auth_test.go diff --git a/api/sys_mounts.go b/api/sys_mounts.go index a6c2a0f5412e..16aedcce4750 100644 --- a/api/sys_mounts.go +++ b/api/sys_mounts.go @@ -271,6 +271,7 @@ type MountConfigInput struct { AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` PluginVersion string `json:"plugin_version,omitempty"` UserLockoutConfig *UserLockoutConfigInput `json:"user_lockout_config,omitempty"` + DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` // Deprecated: This field will always be blank for newer server responses. PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"` } @@ -303,6 +304,8 @@ type MountConfigOutput struct { TokenType string `json:"token_type,omitempty" mapstructure:"token_type"` AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` UserLockoutConfig *UserLockoutConfigOutput `json:"user_lockout_config,omitempty"` + DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` + // Deprecated: This field will always be blank for newer server responses. PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"` } diff --git a/command/commands.go b/command/commands.go index b1867e428da3..1a52e121a1df 100644 --- a/command/commands.go +++ b/command/commands.go @@ -161,6 +161,9 @@ const ( // flagNameLogLevel is used to specify the log level applied to logging // Supported log levels: Trace, Debug, Error, Warn, Info flagNameLogLevel = "log-level" + // flagNameDelegatedAuthAccessors allows operators to specify the allowed mount accessors a backend can delegate + // authentication + flagNameDelegatedAuthAccessors = "delegated-auth-accessors" ) var ( diff --git a/command/secrets_tune.go b/command/secrets_tune.go index 66a409fc9602..eef9e577c2f4 100644 --- a/command/secrets_tune.go +++ b/command/secrets_tune.go @@ -35,6 +35,7 @@ type SecretsTuneCommand struct { flagVersion int flagPluginVersion string flagAllowedManagedKeys []string + flagDelegatedAuthAccessors []string } func (c *SecretsTuneCommand) Synopsis() string { @@ -158,6 +159,14 @@ func (c *SecretsTuneCommand) Flags() *FlagSets { "the plugin catalog, and will not start running until the plugin is reloaded.", }) + f.StringSliceVar(&StringSliceVar{ + Name: flagNameDelegatedAuthAccessors, + Target: &c.flagDelegatedAuthAccessors, + Usage: "A list of permitted authentication accessors this backend can delegate authentication to. " + + "Note that multiple values may be specified by providing this option multiple times, " + + "each time with 1 accessor.", + }) + return set } @@ -242,6 +251,10 @@ func (c *SecretsTuneCommand) Run(args []string) int { if fl.Name == flagNamePluginVersion { mountConfigInput.PluginVersion = c.flagPluginVersion } + + if fl.Name == flagNameDelegatedAuthAccessors { + mountConfigInput.DelegatedAuthAccessors = c.flagDelegatedAuthAccessors + } }) if err := client.Sys().TuneMount(mountPath, mountConfigInput); err != nil { diff --git a/helper/testhelpers/testhelpers.go b/helper/testhelpers/testhelpers.go index 261e03b6fcb9..c3499b8b6b0c 100644 --- a/helper/testhelpers/testhelpers.go +++ b/helper/testhelpers/testhelpers.go @@ -811,9 +811,15 @@ func RetryUntil(t testing.T, timeout time.Duration, f func() error) { t.Fatalf("did not complete before deadline, err: %v", err) } -// CreateEntityAndAlias clones an existing client and creates an entity/alias. +// CreateEntityAndAlias clones an existing client and creates an entity/alias, uses userpass mount path // It returns the cloned client, entityID, and aliasID. func CreateEntityAndAlias(t testing.T, client *api.Client, mountAccessor, entityName, aliasName string) (*api.Client, string, string) { + return CreateEntityAndAliasWithinMount(t, client, mountAccessor, "userpass", entityName, aliasName) +} + +// CreateEntityAndAliasWithinMount clones an existing client and creates an entity/alias, within the specified mountPath +// It returns the cloned client, entityID, and aliasID. +func CreateEntityAndAliasWithinMount(t testing.T, client *api.Client, mountAccessor, mountPath, entityName, aliasName string) (*api.Client, string, string) { t.Helper() userClient, err := client.Clone() if err != nil { @@ -841,7 +847,8 @@ func CreateEntityAndAlias(t testing.T, client *api.Client, mountAccessor, entity if aliasID == "" { t.Fatal("Alias ID not present in response") } - _, err = client.Logical().WriteWithContext(context.Background(), fmt.Sprintf("auth/userpass/users/%s", aliasName), map[string]interface{}{ + path := fmt.Sprintf("auth/%s/users/%s", mountPath, aliasName) + _, err = client.Logical().WriteWithContext(context.Background(), path, map[string]interface{}{ "password": "testpassword", }) if err != nil { diff --git a/sdk/helper/consts/consts.go b/sdk/helper/consts/consts.go index 036ccf55d425..ccc7494a281c 100644 --- a/sdk/helper/consts/consts.go +++ b/sdk/helper/consts/consts.go @@ -19,6 +19,10 @@ const ( // SSRF protection. RequestHeaderName = "X-Vault-Request" + // WrapTTLHeaderName is the name of the header containing a directive to + // wrap the response + WrapTTLHeaderName = "X-Vault-Wrap-TTL" + // PerformanceReplicationALPN is the negotiated protocol used for // performance replication. PerformanceReplicationALPN = "replication_v1" diff --git a/sdk/logical/error.go b/sdk/logical/error.go index 5605784b3e13..95445afac537 100644 --- a/sdk/logical/error.go +++ b/sdk/logical/error.go @@ -3,7 +3,10 @@ package logical -import "errors" +import ( + "context" + "errors" +) var ( // ErrUnsupportedOperation is returned if the operation is not supported @@ -61,6 +64,47 @@ var ( ErrPathFunctionalityRemoved = errors.New("functionality on this path has been removed") ) +type DelegatedAuthErrorHandler func(ctx context.Context, initiatingRequest, authRequest *Request, authResponse *Response, err error) (*Response, error) + +var _ error = &RequestDelegatedAuthError{} + +// RequestDelegatedAuthError Special error indicating the backend wants to delegate authentication elsewhere +type RequestDelegatedAuthError struct { + mountAccessor string + path string + data map[string]interface{} + errHandler DelegatedAuthErrorHandler +} + +func NewDelegatedAuthenticationRequest(mountAccessor, path string, data map[string]interface{}, errHandler DelegatedAuthErrorHandler) *RequestDelegatedAuthError { + return &RequestDelegatedAuthError{ + mountAccessor: mountAccessor, + path: path, + data: data, + errHandler: errHandler, + } +} + +func (d *RequestDelegatedAuthError) Error() string { + return "authentication delegation requested" +} + +func (d *RequestDelegatedAuthError) MountAccessor() string { + return d.mountAccessor +} + +func (d *RequestDelegatedAuthError) Path() string { + return d.path +} + +func (d *RequestDelegatedAuthError) Data() map[string]interface{} { + return d.data +} + +func (d *RequestDelegatedAuthError) AuthErrorHandler() DelegatedAuthErrorHandler { + return d.errHandler +} + type HTTPCodedError interface { Error() string Code() int diff --git a/sdk/logical/request.go b/sdk/logical/request.go index 4b617dbc100e..a4850e0eb504 100644 --- a/sdk/logical/request.go +++ b/sdk/logical/request.go @@ -56,6 +56,7 @@ const ( NoClientToken ClientTokenSource = iota ClientTokenFromVaultHeader ClientTokenFromAuthzHeader + ClientTokenFromInternalAuth ) type WALState struct { diff --git a/vault/external_tests/delegated_auth/delegated_auth_test.go b/vault/external_tests/delegated_auth/delegated_auth_test.go new file mode 100644 index 000000000000..c50077ffe5f9 --- /dev/null +++ b/vault/external_tests/delegated_auth/delegated_auth_test.go @@ -0,0 +1,530 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package delegated_auth + +import ( + "context" + "fmt" + paths "path" + "testing" + + "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/builtin/credential/userpass" + "github.com/hashicorp/vault/builtin/logical/totp" + "github.com/hashicorp/vault/helper/testhelpers" + "github.com/hashicorp/vault/helper/testhelpers/minimal" + "github.com/hashicorp/vault/sdk/framework" + "github.com/hashicorp/vault/sdk/logical" + "github.com/hashicorp/vault/vault" + "github.com/stretchr/testify/require" +) + +func buildDaError(defaults map[string]string, d *framework.FieldData) *logical.RequestDelegatedAuthError { + fieldDataOrDefault := func(field string, d *framework.FieldData) string { + if val, ok := d.GetOk(field); ok { + return val.(string) + } + + return defaults[field] + } + + accessor := fieldDataOrDefault("accessor", d) + path := fieldDataOrDefault("path", d) + username := fieldDataOrDefault("username", d) + password := fieldDataOrDefault("password", d) + var errorHandler logical.DelegatedAuthErrorHandler + if handleErrorRaw, ok := d.GetOk("handle_error"); ok { + if handleErrorRaw.(bool) { + errorHandler = func(ctx context.Context, initiatingRequest, authRequest *logical.Request, authResponse *logical.Response, err error) (*logical.Response, error) { + return logical.ErrorResponse(fmt.Sprintf("my custom handler: %v", err)), nil + } + } + } + + loginPath := paths.Join(path, username) + data := map[string]interface{}{"password": password} + + return logical.NewDelegatedAuthenticationRequest(accessor, loginPath, data, errorHandler) +} + +func buildDelegatedAuthFactory(defaults map[string]string) logical.Factory { + opHandler := func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + daError := buildDaError(defaults, d) + if req.ClientToken == "" || req.ClientTokenSource != logical.ClientTokenFromInternalAuth { + return nil, daError + } + + if req.Operation == logical.ListOperation { + return logical.ListResponse([]string{"success", req.ClientToken}), nil + } + + if d.Get("loop").(bool) { + return nil, daError + } + + if d.Get("perform_write").(bool) { + entry, err := logical.StorageEntryJSON("test", map[string]string{"test": "value"}) + if err != nil { + return nil, err + } + if err = req.Storage.Put(ctx, entry); err != nil { + return nil, err + } + } + + return &logical.Response{ + Data: map[string]interface{}{ + "success": true, + "token": req.ClientToken, + }, + }, nil + } + + return func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) { + b := new(framework.Backend) + b.BackendType = logical.TypeLogical + b.Paths = []*framework.Path{ + { + Pattern: "preauth-test/list/?", + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ListOperation: &framework.PathOperation{Callback: opHandler}, + }, + }, + { + Pattern: "preauth-test", + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{Callback: opHandler}, + logical.PatchOperation: &framework.PathOperation{Callback: opHandler}, + logical.UpdateOperation: &framework.PathOperation{Callback: opHandler}, + logical.DeleteOperation: &framework.PathOperation{Callback: opHandler}, + }, + Fields: map[string]*framework.FieldSchema{ + "accessor": {Type: framework.TypeString}, + "path": {Type: framework.TypeString}, + "username": {Type: framework.TypeString}, + "password": {Type: framework.TypeString}, + "loop": {Type: framework.TypeBool}, + "perform_write": {Type: framework.TypeBool}, + "handle_error": {Type: framework.TypeBool}, + }, + }, + } + b.PathsSpecial = &logical.Paths{Unauthenticated: []string{"preauth-test", "preauth-test/*"}} + err := b.Setup(ctx, config) + return b, err + } +} + +func TestDelegatedAuth(t *testing.T) { + t.Parallel() + + // A map of success values to be populated once and used in request + // operations that can't pass in values + delegatedReqDefaults := map[string]string{ + "username": "allowed-est", + "password": "test", + "path": "login", + } + + delegatedAuthFactory := buildDelegatedAuthFactory(delegatedReqDefaults) + coreConfig := &vault.CoreConfig{ + CredentialBackends: map[string]logical.Factory{ + "userpass": userpass.Factory, + }, + LogicalBackends: map[string]logical.Factory{ + "delegateauthtest": delegatedAuthFactory, + "totp": totp.Factory, + }, + } + + cluster := minimal.NewTestSoloCluster(t, coreConfig) + cluster.Start() + defer cluster.Cleanup() + + client := testhelpers.WaitForActiveNode(t, cluster).Client + + // Setup two users, one with an allowed policy, another without a policy within userpass + err := client.Sys().PutPolicy("allow-est", + `path "dat/preauth-test" { capabilities = ["read", "create", "update", "patch", "delete"] } + path "dat/preauth-test/*" { capabilities = ["read","list"] }`) + require.NoError(t, err, "Failed to write policy allow-est") + + err = client.Sys().EnableAuthWithOptions("userpass", &api.EnableAuthOptions{ + Type: "userpass", + }) + require.NoError(t, err, "failed mounting userpass endpoint") + + _, err = client.Logical().Write("auth/userpass/users/allowed-est", map[string]interface{}{ + "password": "test", + "policies": "allow-est", + "token_type": "batch", + }) + require.NoError(t, err, "failed to create allowed-est user") + + _, err = client.Logical().Write("auth/userpass/users/not-allowed-est", map[string]interface{}{ + "password": "test", + "token_type": "batch", + }) + require.NoError(t, err, "failed to create not-allowed-est user") + + _, err = client.Logical().Write("auth/userpass/users/bad-token-type-est", map[string]interface{}{ + "password": "test", + "token_type": "service", + }) + require.NoError(t, err, "failed to create bad-token-type-est user") + + // Setup another auth mount so we can test multiple accessors in mount tuning works later + err = client.Sys().EnableAuthWithOptions("userpass2", &api.EnableAuthOptions{ + Type: "userpass", + }) + require.NoError(t, err, "failed mounting userpass2") + + _, err = client.Logical().Write("auth/userpass2/users/allowed-est-2", map[string]interface{}{ + "password": "test", + "policies": "allow-est", + "token_type": "batch", + }) + require.NoError(t, err, "failed to create allowed-est-2 user") + + // Setup a dedicated mount for MFA purposes + err = client.Sys().EnableAuthWithOptions("userpass-mfa", &api.EnableAuthOptions{ + Type: "userpass", + }) + require.NoError(t, err, "failed mounting userpass-mfa") + + // Fetch the userpass auth accessors + resp, err := client.Logical().Read("/sys/mounts/auth/userpass") + require.NoError(t, err, "failed to query for mount accessor") + require.NotNil(t, resp, "received nil response from mount accessor query") + require.NotNil(t, resp.Data, "received response with nil Data") + require.NotEmpty(t, resp.Data["accessor"], "Accessor field was empty: %v", resp) + upAccessor := resp.Data["accessor"].(string) + + resp, err = client.Logical().Read("/sys/mounts/auth/userpass2") + require.NoError(t, err, "failed to query for mount accessor for userpass2") + require.NotNil(t, resp, "received nil response from mount accessor query for userpass2") + require.NotNil(t, resp.Data, "received response with nil Data") + require.NotEmpty(t, resp.Data["accessor"], "Accessor field was empty: %v", resp) + upAccessor2 := resp.Data["accessor"].(string) + + resp, err = client.Logical().Read("/sys/mounts/auth/userpass-mfa") + require.NoError(t, err, "failed to query for MFA mount accessor") + require.NotNil(t, resp, "received nil response from MFA mount accessor query") + require.NotNil(t, resp.Data, "received response with nil Data for MFA mount accessor query") + require.NotEmpty(t, resp.Data["accessor"], "MFA mount Accessor field was empty: %v", resp) + upAccessorMFA := resp.Data["accessor"].(string) + + resp, err = client.Logical().Read("/sys/mounts/cubbyhole") + require.NoError(t, err, "failed to query for mount accessor for cubbyhole") + require.NotNil(t, resp, "received nil response from mount accessor query for cubbyhole") + require.NotNil(t, resp.Data, "received response with nil Data") + require.NotEmpty(t, resp.Data["accessor"], "Accessor field was empty: %v", resp) + cubbyAccessor := resp.Data["accessor"].(string) + + // Set up our backend mount that will delegate its auth to the userpass mount + err = client.Sys().Mount("dat", &api.MountInput{ + Type: "delegateauthtest", + Config: api.MountConfigInput{ + DelegatedAuthAccessors: []string{ + upAccessor, upAccessorMFA, "an-accessor-that-does-not-exist", + cubbyAccessor, + }, + }, + }) + require.NoError(t, err, "failed mounting delegated auth endpoint") + + delegatedReqDefaults["accessor"] = upAccessor + + // We want a client without any previous tokens set to make sure we aren't using + // the other token. + clientNoToken, err := client.Clone() + require.NoError(t, err, "failed cloning client") + clientNoToken.ClearToken() + + // Happy path test for the various operation types we want to support, make sure + // for each one that we don't error out and we get back a token value from the backend + // call. + for _, test := range []string{"delete", "read", "list", "write"} { + t.Run("op-"+test, func(st *testing.T) { + switch test { + case "delete": + resp, err = clientNoToken.Logical().Delete("dat/preauth-test") + case "read": + resp, err = clientNoToken.Logical().Read("dat/preauth-test") + case "list": + resp, err = clientNoToken.Logical().List("dat/preauth-test/list/") + case "write": + resp, err = clientNoToken.Logical().Write("dat/preauth-test", map[string]interface{}{ + "accessor": delegatedReqDefaults["accessor"], + "path": delegatedReqDefaults["path"], + "username": delegatedReqDefaults["username"], + "password": delegatedReqDefaults["password"], + }) + } + + require.NoErrorf(st, err, "failed making %s pre-auth call with allowed-est", test) + require.NotNilf(st, resp, "pre-auth %s call returned nil", test) + require.NotNil(t, resp.Data, "received response with nil Data") + if test != "list" { + require.Equalf(st, true, resp.Data["success"], "Got an incorrect response from %s call in success field", test) + require.NotEmptyf(st, resp.Data["token"], "no token returned by %s handler", test) + } else { + require.NotEmpty(st, resp.Data["keys"], "list operation did not contain keys in response") + keys := resp.Data["keys"].([]interface{}) + require.Equal(st, 2, len(keys), "keys field did not contain expected 2 elements") + require.Equal(st, "success", keys[0], "the first keys field did not contain expected value") + require.NotEmpty(st, keys[1], "the second keys field did not contain a token") + } + }) + } + + // Test various failure scenarios + failureTests := []struct { + name string + accessor string + path string + username string + password string + errorContains string + forceLoop bool + }{ + { + name: "policy-denies-user", + accessor: upAccessor, + path: "login", + username: "not-allowed-est", + password: "test", + errorContains: "permission denied", + }, + { + name: "bad-password", + accessor: upAccessor, + path: "login", + username: "allowed-est", + password: "bad-password", + errorContains: "invalid credentials", + }, + { + name: "unknown-user", + accessor: upAccessor, + path: "login", + username: "non-existant-user", + password: "test", + errorContains: "invalid username or password", + }, + { + name: "missing-user", + accessor: upAccessor, + path: "login", + username: "", + password: "test", + errorContains: "was not considered a login request", + }, + { + name: "missing-password", + accessor: upAccessor, + path: "login", + username: "allowed-est", + password: "", + errorContains: "missing password", + }, + { + name: "bad-path-within-delegated-auth-error", + accessor: upAccessor, + path: "not-the-login-path", + username: "allowed-est", + password: "test", + errorContains: "was not considered a login request", + }, + { + name: "empty-path-within-delegated-auth-error", + accessor: upAccessor, + path: "", + username: "allowed-est", + password: "test", + errorContains: "was not considered a login request", + }, + { + name: "empty-accessor-within-delegated-auth-error", + accessor: "", + path: "login", + username: "allowed-est", + password: "test", + errorContains: "backend returned an invalid mount accessor", + }, + { + name: "accessor-does-not-exist-within-delegated-auth-error", + accessor: "an-accessor-that-does-not-exist", + path: "login", + username: "allowed-est", + password: "test", + errorContains: "requested delegate authentication accessor 'an-accessor-that-does-not-exist' was not found", + }, + { + name: "non-allowed-accessor-within-delegated-auth-error", + accessor: upAccessor2, + path: "login", + username: "allowed-est-2", + password: "test", + errorContains: fmt.Sprintf("delegated auth to accessor %s not permitted", upAccessor2), + }, + { + name: "force-constant-login-request-loop", + accessor: upAccessor, + path: "login", + username: "allowed-est", + password: "test", + forceLoop: true, + errorContains: "delegated authentication requested but authentication token present", + }, + { + name: "non-auth-mount-accessor", + accessor: cubbyAccessor, + path: "login", + username: "allowed-est", + password: "test", + errorContains: fmt.Sprintf("requested delegate authentication mount '%s' was not an auth mount", cubbyAccessor), + }, + { + name: "fails-on-non-batch-token", + accessor: upAccessor, + path: "login", + username: "bad-token-type-est", + password: "test", + errorContains: "delegated auth requests must be configured to issue batch tokens", + }, + } + for _, test := range failureTests { + t.Run(test.name, func(st *testing.T) { + resp, err = clientNoToken.Logical().Write("dat/preauth-test", map[string]interface{}{ + "accessor": test.accessor, + "path": test.path, + "username": test.username, + "password": test.password, + "loop": test.forceLoop, + }) + if test.errorContains != "" { + require.ErrorContains(st, err, test.errorContains, + "pre-auth call should have failed due to policy restriction got resp: %v err: %v", resp, err) + } else { + require.Error(st, err, "Expected failure got resp: %v err: %v", resp, err) + } + }) + } + + // Make sure we can add an accessor to the mount that previously failed above, + // and the request handling code does use both accessor values. + t.Run("multiple-accessors", func(st *testing.T) { + err = client.Sys().TuneMount("dat", api.MountConfigInput{DelegatedAuthAccessors: []string{upAccessor, upAccessor2, upAccessorMFA}}) + require.NoError(t, err, "Failed to tune mount to update delegated auth accessors") + + resp, err = clientNoToken.Logical().Write("dat/preauth-test", map[string]interface{}{ + "accessor": upAccessor2, + "path": "login", + "username": "allowed-est-2", + "password": "test", + }) + + require.NoError(st, err, "failed making pre-auth call with allowed-est-2") + require.NotNil(st, resp, "pre-auth %s call returned nil with allowed-est-2") + require.NotNil(t, resp.Data, "received response with nil Data") + require.Equal(st, true, resp.Data["success"], "Got an incorrect response from call in success field with allowed-est-2") + require.NotEmpty(st, resp.Data["token"], "no token returned with allowed-est-2 user") + }) + + // Test we can delegate a permission denied error back to the originating + // backend for processing/response to the client + t.Run("backend-handles-permission-denied", func(st *testing.T) { + resp, err = clientNoToken.Logical().Write("dat/preauth-test", map[string]interface{}{ + "accessor": upAccessor, + "path": "login", + "username": "allowed-est", + "password": "test2", + "handle_error": true, + }) + + require.ErrorContains(st, err, "my custom handler: invalid credentials") + }) + + // Test we can delegate a permission denied error back to the originating + // backend for processing/response to the client + t.Run("mfa-request-is-denied", func(st *testing.T) { + // Mount the totp secrets engine + testhelpers.SetupTOTPMount(st, client) + + // Create a test entity and alias + totpUser := "test-totp" + testhelpers.CreateEntityAndAliasWithinMount(st, client, upAccessorMFA, "userpass-mfa", "entity1", totpUser) + + // Configure a default TOTP method + methodID := testhelpers.SetupTOTPMethod(st, client, map[string]interface{}{ + "issuer": "yCorp", + "period": 5, + "algorithm": "SHA256", + "digits": 6, + "skew": 1, + "key_size": 20, + "qr_size": 200, + "max_validation_attempts": 5, + "method_name": "foo", + }) + + // Configure a login enforcement specific to the MFA userpass mount to avoid conflicts on others. + enforcementConfig := map[string]interface{}{ + "auth_method_accessors": []string{upAccessorMFA}, + "name": methodID[0:4], + "mfa_method_ids": []string{methodID}, + } + + testhelpers.SetupMFALoginEnforcement(st, client, enforcementConfig) + + _, err = clientNoToken.Logical().Write("dat/preauth-test", map[string]interface{}{ + "accessor": upAccessorMFA, + "path": "login", + "username": totpUser, + "password": "testpassword", + "handle_error": false, + }) + + require.ErrorContains(st, err, "delegated auth request requiring MFA is not supported") + }) + + // Test the behavior around receiving a request asking for response wrapping and + // being delegated to the secondary query we do + t.Run("response-wrapping-test", func(st *testing.T) { + resWrapClient, err := client.Clone() + require.NoError(st, err, "failed cloning client for response wrapping") + + resWrapClient.SetWrappingLookupFunc(func(operation, path string) string { + if path == "dat/preauth-test" { + return "15s" + } + return "" + }) + + resp, err = resWrapClient.Logical().Write("dat/preauth-test", map[string]interface{}{ + "accessor": upAccessor, + "path": "login", + "username": "allowed-est", + "password": "test", + }) + require.NoError(st, err, "failed calling preauth-test api with response wrapping") + require.NotNil(st, resp, "Got nil, nil response from preauth-test api with response wrapping") + require.NotNil(st, resp.WrapInfo, "response object didn't contain wrapped info") + + unwrapClient, err := client.Clone() + require.NoError(st, err, "failed cloning client for lookups") + wrapToken := resp.WrapInfo.Token + unwrapClient.SetToken(wrapToken) + + unwrapResp, err := unwrapClient.Logical().Write("sys/wrapping/unwrap", map[string]interface{}{}) + require.NoError(st, err, "failed unwrap call") + require.NotNil(st, unwrapResp, "unwrap response was nil") + require.NotNil(st, unwrapResp.Data, "unwrap response did not contain Data") + require.Contains(st, unwrapResp.Data, "success", "unwrap response data did not contain success field") + require.Contains(st, unwrapResp.Data, "token", "unwrap response data did not contain token field") + require.Equal(st, true, unwrapResp.Data["success"], "Got an incorrect response in success field within unwrap call") + require.NotEmptyf(st, unwrapResp.Data["token"], "no token returned by handler within unwrap call") + }) +} diff --git a/vault/logical_system.go b/vault/logical_system.go index e978095a1908..564e0e971593 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -1426,6 +1426,9 @@ func (b *SystemBackend) handleMount(ctx context.Context, req *logical.Request, d if len(apiConfig.AllowedManagedKeys) > 0 { config.AllowedManagedKeys = apiConfig.AllowedManagedKeys } + if len(apiConfig.DelegatedAuthAccessors) > 0 { + config.DelegatedAuthAccessors = apiConfig.DelegatedAuthAccessors + } // Create the mount entry me := &MountEntry{ @@ -2370,6 +2373,32 @@ func (b *SystemBackend) handleTuneWriteCommon(ctx context.Context, path string, } } + if rawVal, ok := data.GetOk("delegated_auth_accessors"); ok { + delegatedAuthAccessors := rawVal.([]string) + + oldVal := mountEntry.Config.DelegatedAuthAccessors + mountEntry.Config.DelegatedAuthAccessors = delegatedAuthAccessors + + // Update the mount table + var err error + switch { + case strings.HasPrefix(path, "auth/"): + err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local) + default: + err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local) + } + if err != nil { + mountEntry.Config.DelegatedAuthAccessors = oldVal + return handleError(err) + } + + mountEntry.SyncCache() + + if b.Core.logger.IsInfo() { + b.Core.logger.Info("mount tuning of delegated_auth_accessors successful", "path", path) + } + } + var err error var resp *logical.Response var options map[string]string @@ -6409,4 +6438,8 @@ This path responds to the following HTTP methods. Returns the available and enabled experiments. `, }, + "delegated_auth_accessors": { + "A list of auth accessors that the mount is allowed to delegate authentication too", + "", + }, } diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go index 5f4df09ecb31..637f77bf5859 100644 --- a/vault/logical_system_paths.go +++ b/vault/logical_system_paths.go @@ -4427,6 +4427,10 @@ func (b *SystemBackend) mountPaths() []*framework.Path { Type: framework.TypeCommaStringSlice, Description: strings.TrimSpace(sysHelp["tune_allowed_managed_keys"][0]), }, + "delegated_auth_accessors": { + Type: framework.TypeCommaStringSlice, + Description: strings.TrimSpace(sysHelp["allowed_delegated_auth_accessors"][0]), + }, "plugin_version": { Type: framework.TypeString, Description: strings.TrimSpace(sysHelp["plugin-catalog_version"][0]), @@ -4477,6 +4481,11 @@ func (b *SystemBackend) mountPaths() []*framework.Path { Description: strings.TrimSpace(sysHelp["tune_allowed_managed_keys"][0]), Required: false, }, + "delegated_auth_accessors": { + Type: framework.TypeCommaStringSlice, + Description: strings.TrimSpace(sysHelp["delegated_auth_accessors"][0]), + Required: false, + }, "allowed_response_headers": { Type: framework.TypeCommaStringSlice, Description: strings.TrimSpace(sysHelp["allowed_response_headers"][0]), diff --git a/vault/mount.go b/vault/mount.go index b72e22a054b1..d06c331ea12a 100644 --- a/vault/mount.go +++ b/vault/mount.go @@ -364,6 +364,7 @@ type MountConfig struct { TokenType logical.TokenType `json:"token_type,omitempty" structs:"token_type" mapstructure:"token_type"` AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` UserLockoutConfig *UserLockoutConfig `json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config"` + DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` // PluginName is the name of the plugin registered in the catalog. // @@ -399,6 +400,7 @@ type APIMountConfig struct { AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` UserLockoutConfig *UserLockoutConfig `json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config"` PluginVersion string `json:"plugin_version,omitempty" mapstructure:"plugin_version"` + DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` // PluginName is the name of the plugin registered in the catalog. // @@ -500,6 +502,12 @@ func (e *MountEntry) SyncCache() { } else { e.synthesizedConfigCache.Store("allowed_managed_keys", e.Config.AllowedManagedKeys) } + + if len(e.Config.DelegatedAuthAccessors) == 0 { + e.synthesizedConfigCache.Delete("delegated_auth_accessors") + } else { + e.synthesizedConfigCache.Store("delegated_auth_accessors", e.Config.DelegatedAuthAccessors) + } } func (entry *MountEntry) Deserialize() map[string]interface{} { diff --git a/vault/request_handling.go b/vault/request_handling.go index 1330b94688f5..b8296f34f115 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -9,7 +9,11 @@ import ( "encoding/base64" "errors" "fmt" + "maps" + "net/textproto" "os" + paths "path" + "slices" "strconv" "strings" "time" @@ -767,7 +771,7 @@ func (c *Core) handleCancelableRequest(ctx context.Context, req *logical.Request walState := &logical.WALState{} ctx = logical.IndexStateContext(ctx, walState) var auth *logical.Auth - if c.isLoginRequest(ctx, req) { + if c.isLoginRequest(ctx, req) && req.ClientTokenSource != logical.ClientTokenFromInternalAuth { resp, auth, err = c.handleLoginRequest(ctx, req) } else { resp, auth, err = c.handleRequest(ctx, req) @@ -1379,6 +1383,10 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp // Return the response and error if routeErr != nil { + if _, ok := routeErr.(*logical.RequestDelegatedAuthError); ok { + routeErr = fmt.Errorf("delegated authentication requested but authentication token present") + } + retErr = multierror.Append(retErr, routeErr) } @@ -1496,15 +1504,23 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re // Route the request resp, routeErr := c.doRouting(ctx, req) - // if routeErr has invalid credentials error, update the userFailedLoginMap - if routeErr != nil && routeErr == logical.ErrInvalidCredentials { + handleInvalidCreds := func(err error) (*logical.Response, *logical.Auth, error) { if !isUserLockoutDisabled { err := c.failedUserLoginProcess(ctx, entry, req) if err != nil { return nil, nil, err } } - return resp, nil, routeErr + return resp, nil, err + } + + if routeErr != nil { + // if routeErr has invalid credentials error, update the userFailedLoginMap + if routeErr == logical.ErrInvalidCredentials { + return handleInvalidCreds(routeErr) + } else if da, ok := routeErr.(*logical.RequestDelegatedAuthError); ok { + return c.handleDelegatedAuth(ctx, req, da, entry, handleInvalidCreds) + } } if resp != nil { @@ -1837,6 +1853,117 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re return resp, auth, routeErr } +type invalidCredentialHandler func(err error) (*logical.Response, *logical.Auth, error) + +// handleDelegatedAuth when a backend request returns logical.RequestDelegatedAuthError, it is requesting that +// an authentication workflow of its choosing be implemented prior to it being able to accept it. Normally +// this is used for standard protocols that communicate the credential information in a non-standard Vault way +func (c *Core) handleDelegatedAuth(ctx context.Context, origReq *logical.Request, da *logical.RequestDelegatedAuthError, entry *MountEntry, invalidCredHandler invalidCredentialHandler) (*logical.Response, *logical.Auth, error) { + // Make sure we didn't get into a routing loop. + if origReq.ClientTokenSource == logical.ClientTokenFromInternalAuth { + return nil, nil, fmt.Errorf("%w: original request had delegated auth token, "+ + "forbidding another delegated request from path '%s'", ErrInternalError, origReq.Path) + } + + // Backend has requested internally delegated authentication + requestedAccessor := da.MountAccessor() + if strings.TrimSpace(requestedAccessor) == "" { + return nil, nil, fmt.Errorf("%w: backend returned an invalid mount accessor '%s'", ErrInternalError, requestedAccessor) + } + // First, is this allowed by the mount tunable? + if !slices.Contains(entry.Config.DelegatedAuthAccessors, requestedAccessor) { + return nil, nil, fmt.Errorf("delegated auth to accessor %s not permitted", requestedAccessor) + } + + reqNamespace, err := namespace.FromContext(ctx) + if err != nil { + return nil, nil, fmt.Errorf("failed looking up namespace from context: %w", err) + } + + mount := c.router.MatchingMountByAccessor(requestedAccessor) + if mount == nil { + return nil, nil, fmt.Errorf("%w: requested delegate authentication accessor '%s' was not found", logical.ErrPermissionDenied, requestedAccessor) + } + if mount.Table != credentialTableType { + return nil, nil, fmt.Errorf("%w: requested delegate authentication mount '%s' was not an auth mount", logical.ErrPermissionDenied, requestedAccessor) + } + if mount.NamespaceID != reqNamespace.ID { + return nil, nil, fmt.Errorf("%w: requested delegate authentication mount was in a different namespace than request", logical.ErrPermissionDenied) + } + + // Found it, now form the login path and issue the request + path := paths.Join("auth", mount.Path, da.Path()) + authReq, err := origReq.Clone() + if err != nil { + return nil, nil, err + } + authReq.MountAccessor = requestedAccessor + authReq.Path = path + authReq.Operation = logical.UpdateOperation + + // filter out any response wrapping headers, for our embedded login request + delete(authReq.Headers, textproto.CanonicalMIMEHeaderKey(consts.WrapTTLHeaderName)) + authReq.WrapInfo = nil + + // Insert the data fields from the delegated auth error in our auth request + authReq.Data = maps.Clone(da.Data()) + + // Make sure we are going to perform a login request and not expose other backend types to this request + if !c.isLoginRequest(ctx, authReq) { + return nil, nil, fmt.Errorf("delegated path '%s' was not considered a login request", authReq.Path) + } + + authResp, err := c.handleCancelableRequest(ctx, authReq) + if err != nil || authResp.IsError() { + // see if the backend wishes to handle the failed auth + if da.AuthErrorHandler() != nil { + resp, err := da.AuthErrorHandler()(ctx, origReq, authReq, authResp, err) + return resp, nil, err + } + switch err { + case nil: + return authResp, nil, nil + case logical.ErrInvalidCredentials: + return invalidCredHandler(err) + default: + return authResp, nil, err + } + } + if authResp == nil { + return nil, nil, fmt.Errorf("%w: delegated auth request returned empty response for request_path: %s", ErrInternalError, authReq.Path) + } + // A login request should never return a secret! + if authResp.Secret != nil { + return nil, nil, fmt.Errorf("%w: unexpected Secret response for login path for request_path: %s", ErrInternalError, authReq.Path) + } + if authResp.Auth == nil { + return nil, nil, fmt.Errorf("%w: Auth response was nil for request_path: %s", ErrInternalError, authReq.Path) + } + if authResp.Auth.ClientToken == "" { + if authResp.Auth.MFARequirement != nil { + return nil, nil, fmt.Errorf("%w: delegated auth request requiring MFA is not supported: %s", logical.ErrPermissionDenied, authReq.Path) + } + return nil, nil, fmt.Errorf("%w: delegated auth request did not return a client token for login path: %s", ErrInternalError, authReq.Path) + } + + // Delegated auth tokens should only be batch tokens, as we don't want to incur + // the cost of storage/tidying for protocols that will be generating a token per + // request. + if !IsBatchToken(authResp.Auth.ClientToken) { + return nil, nil, fmt.Errorf("%w: delegated auth requests must be configured to issue batch tokens", logical.ErrPermissionDenied) + } + + // Authentication successful, use the resulting ClientToken to reissue the original request + secondReq, err := origReq.Clone() + if err != nil { + return nil, nil, err + } + secondReq.ClientToken = authResp.Auth.ClientToken + secondReq.ClientTokenSource = logical.ClientTokenFromInternalAuth + resp, err := c.handleCancelableRequest(ctx, secondReq) + return resp, nil, err +} + // LoginCreateToken creates a token as a result of a login request. // If MFA is enforced, mfa/validate endpoint calls this functions // after successful MFA validation to generate the token. diff --git a/website/content/api-docs/system/mounts.mdx b/website/content/api-docs/system/mounts.mdx index 3bee236c5ce4..5486e9f70251 100644 --- a/website/content/api-docs/system/mounts.mdx +++ b/website/content/api-docs/system/mounts.mdx @@ -174,6 +174,9 @@ This endpoint enables a new secrets engine at the given path. - `allowed_managed_keys` `(array: [])` - List of managed key registry entry names that the mount in question is allowed to access. + - `delegated_auth_accessors` `(array: [])` - List of allowed authentication mount + accessors the backend can request delegated authentication for. + - `options` `(map: nil)` - Specifies mount type specific options that are passed to the backend. @@ -382,6 +385,9 @@ This endpoint tunes configuration parameters for a given mount point. - `plugin_version` `(string: "")` – Specifies the semantic version of the plugin to use, e.g. "v1.0.0". Changes will not take effect until the mount is reloaded. +- `delegated_auth_accessors` `(array: [])` - List of allowed authentication mount + accessors the backend can request delegated authentication for. + ### Sample payload ```json From 18e6385e0589895c354e0d2aed95e8321f1ee84f Mon Sep 17 00:00:00 2001 From: Mike Palmiotto Date: Tue, 21 Nov 2023 15:45:07 -0500 Subject: [PATCH 13/74] Consistently use OperationHandler for entity paths (#24225) --- vault/identity_store_entities.go | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/vault/identity_store_entities.go b/vault/identity_store_entities.go index f59457f95098..05c83c7bd2b0 100644 --- a/vault/identity_store_entities.go +++ b/vault/identity_store_entities.go @@ -68,8 +68,10 @@ func entityPaths(i *IdentityStore) []*framework.Path { }, Fields: entityPathFields(), - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: i.handleEntityUpdateCommon(), + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: i.handleEntityUpdateCommon(), + }, }, HelpSynopsis: strings.TrimSpace(entityHelp["entity"][0]), @@ -158,8 +160,10 @@ func entityPaths(i *IdentityStore) []*framework.Path { }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: i.handleEntityBatchDelete(), + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: i.handleEntityBatchDelete(), + }, }, HelpSynopsis: strings.TrimSpace(entityHelp["batch-delete"][0]), @@ -173,8 +177,10 @@ func entityPaths(i *IdentityStore) []*framework.Path { OperationSuffix: "by-name", }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: i.pathEntityNameList(), + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ListOperation: &framework.PathOperation{ + Callback: i.pathEntityNameList(), + }, }, HelpSynopsis: strings.TrimSpace(entityHelp["entity-name-list"][0]), @@ -188,8 +194,10 @@ func entityPaths(i *IdentityStore) []*framework.Path { OperationSuffix: "by-id", }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: i.pathEntityIDList(), + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ListOperation: &framework.PathOperation{ + Callback: i.pathEntityIDList(), + }, }, HelpSynopsis: strings.TrimSpace(entityHelp["entity-id-list"][0]), @@ -221,8 +229,10 @@ func entityPaths(i *IdentityStore) []*framework.Path { Description: "Setting this will follow the 'mine' strategy for merging MFA secrets. If there are secrets of the same type both in entities that are merged from and in entity into which all others are getting merged, secrets in the destination will be unaltered. If not set, this API will throw an error containing all the conflicts.", }, }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: i.pathEntityMergeID(), + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: i.pathEntityMergeID(), + }, }, HelpSynopsis: strings.TrimSpace(entityHelp["entity-merge-id"][0]), From 82ca52d44763f8be6cc2885f4aada68fee05e55e Mon Sep 17 00:00:00 2001 From: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:11:14 -0600 Subject: [PATCH 14/74] UI: Fix KV v2 json editor (#24224) * Fix JSON editor in KVv2 unable to paste. Fixes #23940 * Default to JSON view on edit with secret is complex * Add changelog --- changelog/24224.txt | 3 +++ ui/lib/kv/addon/components/kv-data-fields.hbs | 2 +- ui/lib/kv/addon/components/kv-data-fields.js | 12 ++++++++---- ui/lib/kv/addon/components/page/secret/edit.hbs | 9 +++++++-- ui/lib/kv/addon/components/page/secret/edit.js | 5 +++++ .../backend/kv/kv-v2-workflow-edge-cases-test.js | 15 +++++++++++++++ .../components/kv/kv-data-fields-test.js | 2 +- 7 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 changelog/24224.txt diff --git a/changelog/24224.txt b/changelog/24224.txt new file mode 100644 index 000000000000..040b42d94da8 --- /dev/null +++ b/changelog/24224.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fix JSON editor in KV V2 unable to handle pasted values +``` diff --git a/ui/lib/kv/addon/components/kv-data-fields.hbs b/ui/lib/kv/addon/components/kv-data-fields.hbs index 76c5c2dfd3a0..b384d3e4c2e3 100644 --- a/ui/lib/kv/addon/components/kv-data-fields.hbs +++ b/ui/lib/kv/addon/components/kv-data-fields.hbs @@ -15,7 +15,7 @@ {{#if @showJson}} {{#if (or @modelValidations.secretData.errors this.lintingErrors)}} diff --git a/ui/lib/kv/addon/components/kv-data-fields.js b/ui/lib/kv/addon/components/kv-data-fields.js index bb1f759810d1..663df063e4c9 100644 --- a/ui/lib/kv/addon/components/kv-data-fields.js +++ b/ui/lib/kv/addon/components/kv-data-fields.js @@ -6,7 +6,7 @@ import Component from '@glimmer/component'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; -import KVObject from 'vault/lib/kv-object'; +import { stringify } from 'core/helpers/stringify'; /** * @module KvDataFields is used for rendering the fields associated with kv secret data, it hides/shows a json editor and renders validation errors for the json editor @@ -28,10 +28,13 @@ import KVObject from 'vault/lib/kv-object'; export default class KvDataFields extends Component { @tracked lintingErrors; + @tracked codeMirrorString; - get emptyJson() { - // if secretData is null, this specially formats a blank object and renders a nice initial state for the json editor - return KVObject.create({ content: [{ name: '', value: '' }] }).toJSONString(true); + constructor() { + super(...arguments); + this.codeMirrorString = this.args.secret?.secretData + ? stringify([this.args.secret.secretData], {}) + : '{ "": "" }'; } @action @@ -41,5 +44,6 @@ export default class KvDataFields extends Component { if (!this.lintingErrors) { this.args.secret.secretData = JSON.parse(value); } + this.codeMirrorString = value; } } diff --git a/ui/lib/kv/addon/components/page/secret/edit.hbs b/ui/lib/kv/addon/components/page/secret/edit.hbs index 34a0365d1608..8a5be5646996 100644 --- a/ui/lib/kv/addon/components/page/secret/edit.hbs +++ b/ui/lib/kv/addon/components/page/secret/edit.hbs @@ -5,7 +5,12 @@ <:toolbarFilters> - + JSON @@ -38,7 +43,7 @@ 0) { + // Dumb way to check if there's a nested object in the secret + this.secretDataIsAdvanced = true; + } } get showOldVersionAlert() { diff --git a/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-edge-cases-test.js b/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-edge-cases-test.js index c92914102ba9..87aad84cc2c0 100644 --- a/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-edge-cases-test.js +++ b/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-edge-cases-test.js @@ -24,6 +24,7 @@ import { } from 'vault/tests/helpers/policy-generator/kv'; import { clearRecords, writeSecret, writeVersionedSecret } from 'vault/tests/helpers/kv/kv-run-commands'; import { FORM, PAGE } from 'vault/tests/helpers/kv/kv-selectors'; +import codemirror from 'vault/tests/helpers/codemirror'; /** * This test set is for testing edge cases, such as specific bug fixes or reported user workflows @@ -269,6 +270,20 @@ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) { await click(PAGE.breadcrumbAtIdx(2)); assert.dom(PAGE.list.item()).exists({ count: 2 }, 'two secrets are listed'); }); + + test('complex values default to JSON display', async function (assert) { + await visit(`/vault/secrets/${this.backend}/kv/create`); + await fillIn(FORM.inputByAttr('path'), 'complex'); + + await click(FORM.toggleJson); + assert.strictEqual(codemirror().getValue(), '{ "": "" }'); + codemirror().setValue('{ "foo3": { "name": "bar3" } }'); + await click(FORM.saveBtn); + // Future: test that json is automatic on details too + await click(PAGE.detail.createNewVersion); + assert.dom(FORM.toggleJson).isDisabled(); + assert.dom(FORM.toggleJson).isChecked(); + }); }); // NAMESPACE TESTS diff --git a/ui/tests/integration/components/kv/kv-data-fields-test.js b/ui/tests/integration/components/kv/kv-data-fields-test.js index 954e4d0e1fa7..44ed1c9a2b1c 100644 --- a/ui/tests/integration/components/kv/kv-data-fields-test.js +++ b/ui/tests/integration/components/kv/kv-data-fields-test.js @@ -43,7 +43,7 @@ module('Integration | Component | kv-v2 | KvDataFields', function (hooks) { assert.strictEqual( codemirror().getValue(' '), - `{ \"\": \"\" }`, // eslint-disable-line no-useless-escape + `{ \"\": \"\" }`, // eslint-disable-line no-useless-escape 'json editor initializes with empty object' ); await fillIn(`${FORM.jsonEditor} textarea`, 'blah'); From 8e8bc82a5ac384748f1928e1825958be3c18000f Mon Sep 17 00:00:00 2001 From: Marccio Silva Date: Tue, 21 Nov 2023 22:36:58 +0100 Subject: [PATCH 15/74] Update go-jose dependency to 3.0.1 (#24226) --- api/go.mod | 2 +- api/go.sum | 4 ++-- go.mod | 4 ++-- go.sum | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/api/go.mod b/api/go.mod index 20fb4617af23..184b9e4d03b9 100644 --- a/api/go.mod +++ b/api/go.mod @@ -9,7 +9,7 @@ go 1.19 require ( github.com/cenkalti/backoff/v3 v3.0.0 - github.com/go-jose/go-jose/v3 v3.0.0 + github.com/go-jose/go-jose/v3 v3.0.1 github.com/go-test/deep v1.0.2 github.com/hashicorp/errwrap v1.1.0 github.com/hashicorp/go-cleanhttp v0.5.2 diff --git a/api/go.sum b/api/go.sum index e8f5f1811f8f..bb993047e5ca 100644 --- a/api/go.sum +++ b/api/go.sum @@ -7,8 +7,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= +github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= diff --git a/go.mod b/go.mod index ca8ab3deab56..ce7b32a48ea8 100644 --- a/go.mod +++ b/go.mod @@ -62,7 +62,7 @@ require ( github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/go-errors/errors v1.5.0 github.com/go-git/go-git/v5 v5.7.0 - github.com/go-jose/go-jose/v3 v3.0.0 + github.com/go-jose/go-jose/v3 v3.0.1 github.com/go-ldap/ldap/v3 v3.4.4 github.com/go-sql-driver/mysql v1.7.1 github.com/go-test/deep v1.1.0 @@ -204,6 +204,7 @@ require ( github.com/sethvargo/go-limiter v0.7.1 github.com/shirou/gopsutil/v3 v3.22.6 github.com/stretchr/testify v1.8.4 + go.etcd.io/bbolt v1.3.7 go.etcd.io/etcd/client/pkg/v3 v3.5.7 go.etcd.io/etcd/client/v2 v2.305.5 go.etcd.io/etcd/client/v3 v3.5.7 @@ -507,7 +508,6 @@ require ( github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/zclconf/go-cty v1.12.1 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect - go.etcd.io/bbolt v1.3.7 // indirect go.etcd.io/etcd/api/v3 v3.5.7 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/metric v1.16.0 // indirect diff --git a/go.sum b/go.sum index f32079238fa6..13ebf88ef11b 100644 --- a/go.sum +++ b/go.sum @@ -1696,8 +1696,9 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.66.6/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= +github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= 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= From 39762174206ee353e8cb2d1eab2c544723b91c2d Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Wed, 22 Nov 2023 19:54:47 +0000 Subject: [PATCH 16/74] Audit: logging a response uses a separate 5 second timeout (#24238) * added a 5s timeout to attempts to process nodes in the audit pipeline for logging a response * added changelog * ensure we supply namespace to the new context --- changelog/24238.txt | 3 +++ vault/audit_broker.go | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 changelog/24238.txt diff --git a/changelog/24238.txt b/changelog/24238.txt new file mode 100644 index 000000000000..207a61d60952 --- /dev/null +++ b/changelog/24238.txt @@ -0,0 +1,3 @@ +```release-note:bug +core/audit: Audit logging a Vault response will now use a 5 second context timeout, separate from the original request. +``` \ No newline at end of file diff --git a/vault/audit_broker.go b/vault/audit_broker.go index 7fcce78e29f7..7ad214513e0f 100644 --- a/vault/audit_broker.go +++ b/vault/audit_broker.go @@ -10,6 +10,8 @@ import ( "sync" "time" + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/internal/observability/event" metrics "github.com/armon/go-metrics" @@ -297,7 +299,22 @@ func (a *AuditBroker) LogResponse(ctx context.Context, in *logical.LogInput, hea e.Data = in - status, err := a.broker.Send(ctx, eventlogger.EventType(event.AuditType.String()), e) + // In cases where we are trying to audit the response, we detach + // ourselves from the original context (keeping only the namespace). + // This is so that we get a fair run at writing audit entries if Vault + // Took up a lot of time handling the request before audit (response) + // is triggered. Pipeline nodes may check for a cancelled context and + // refuse to process the nodes further. + ns, err := namespace.FromContext(ctx) + if err != nil { + retErr = multierror.Append(retErr, fmt.Errorf("namespace missing from context: %w", err)) + return retErr.ErrorOrNil() + } + + auditContext, auditCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer auditCancel() + auditContext = namespace.ContextWithNamespace(auditContext, ns) + status, err := a.broker.Send(auditContext, eventlogger.EventType(event.AuditType.String()), e) if err != nil { retErr = multierror.Append(retErr, multierror.Append(err, status.Warnings...)) } From 511ce92852eea274f0a72a8b718c3217de47dbcb Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 24 Nov 2023 09:30:10 +0000 Subject: [PATCH 17/74] fix import formatting (#24248) --- vault/audit_broker.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/vault/audit_broker.go b/vault/audit_broker.go index 7ad214513e0f..342a4387008e 100644 --- a/vault/audit_broker.go +++ b/vault/audit_broker.go @@ -10,15 +10,13 @@ import ( "sync" "time" - "github.com/hashicorp/vault/helper/namespace" - - "github.com/hashicorp/vault/internal/observability/event" - metrics "github.com/armon/go-metrics" "github.com/hashicorp/eventlogger" log "github.com/hashicorp/go-hclog" multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/vault/audit" + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/internal/observability/event" "github.com/hashicorp/vault/sdk/logical" ) From 904c08e1e4ffc7bade5ed2f606cb3a1490ea515b Mon Sep 17 00:00:00 2001 From: Christopher Swenson Date: Mon, 27 Nov 2023 09:11:01 -0800 Subject: [PATCH 18/74] Remove runtime patch for SHA1 support in X.509 certs (#24243) This code only executes when the Vault version is <1.11, so is now dead code and can be removed safely. --- builtin/credential/aws/pkcs7/sign.go | 6 --- internal/go118_sha1_patch.go | 59 ---------------------------- main.go | 6 --- 3 files changed, 71 deletions(-) delete mode 100644 internal/go118_sha1_patch.go diff --git a/builtin/credential/aws/pkcs7/sign.go b/builtin/credential/aws/pkcs7/sign.go index 72b99388548e..b64fcb11da47 100644 --- a/builtin/credential/aws/pkcs7/sign.go +++ b/builtin/credential/aws/pkcs7/sign.go @@ -12,14 +12,8 @@ import ( "fmt" "math/big" "time" - - "github.com/hashicorp/vault/internal" ) -func init() { - internal.PatchSha1() -} - // SignedData is an opaque data structure for creating signed data payloads type SignedData struct { sd signedData diff --git a/internal/go118_sha1_patch.go b/internal/go118_sha1_patch.go deleted file mode 100644 index bce531889681..000000000000 --- a/internal/go118_sha1_patch.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package internal - -import ( - "fmt" - "os" - "sync" - _ "unsafe" // for go:linkname - - goversion "github.com/hashicorp/go-version" - "github.com/hashicorp/vault/version" -) - -const sha1PatchVersionsBefore = "1.12.0" - -var patchSha1 sync.Once - -//go:linkname debugAllowSHA1 crypto/x509.debugAllowSHA1 -var debugAllowSHA1 bool - -// PatchSha1 patches Go 1.18+ to allow certificates with signatures containing SHA-1 hashes to be allowed. -// It is safe to call this function multiple times. -// This is necessary to allow Vault 1.10 and 1.11 to work with Go 1.18+ without breaking backwards compatibility -// with these certificates. See https://go.dev/doc/go1.18#sha1 and -// https://developer.hashicorp.com/vault/docs/deprecation/faq#q-what-is-the-impact-of-removing-support-for-x-509-certificates-with-signatures-that-use-sha-1 -// for more details. -// TODO: remove when Vault <=1.11 is no longer supported -func PatchSha1() { - patchSha1.Do(func() { - // for Go 1.19.4 and later - godebug := os.Getenv("GODEBUG") - if godebug != "" { - godebug += "," - } - godebug += "x509sha1=1" - os.Setenv("GODEBUG", godebug) - - // for Go 1.19.3 and earlier, patch the variable - patchBefore, err := goversion.NewSemver(sha1PatchVersionsBefore) - if err != nil { - panic(err) - } - - patch := false - v, err := goversion.NewSemver(version.GetVersion().Version) - if err == nil { - patch = v.LessThan(patchBefore) - } else { - fmt.Fprintf(os.Stderr, "Cannot parse version %s; going to apply SHA-1 deprecation patch workaround\n", version.GetVersion().Version) - patch = true - } - - if patch { - debugAllowSHA1 = true - } - }) -} diff --git a/main.go b/main.go index a48e21b6942c..35d2f584ee96 100644 --- a/main.go +++ b/main.go @@ -7,14 +7,8 @@ import ( "os" "github.com/hashicorp/vault/command" - "github.com/hashicorp/vault/internal" ) -func init() { - // this is a good place to patch SHA-1 support back into x509 - internal.PatchSha1() -} - func main() { os.Exit(command.Run(os.Args[1:])) } From 0ca6135f6895bbaeef27c685b5217f4a3c5cfe28 Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Mon, 27 Nov 2023 10:21:35 -0700 Subject: [PATCH 19/74] Glimmerize Splash Page (#24104) * make splash page view only block content * change invocation of component * address some of the pr comments * add test coverage * remove conditional because of issue with it always showing * solve for mfa errors * move altcontent outside --- ui/app/components/splash-page.hbs | 21 ++ ui/app/components/splash-page.js | 34 --- .../components/splash-page/splash-content.js | 10 - .../components/splash-page/splash-footer.js | 10 - .../components/splash-page/splash-header.js | 10 - ui/app/templates/components/splash-page.hbs | 26 -- ui/app/templates/vault/cluster/auth.hbs | 240 +++++++++--------- ui/app/templates/vault/cluster/init.hbs | 38 ++- ui/app/templates/vault/cluster/mfa-setup.hbs | 10 +- ui/app/templates/vault/cluster/unseal.hbs | 16 +- .../components/splash-page-test.js | 37 +++ 11 files changed, 211 insertions(+), 241 deletions(-) create mode 100644 ui/app/components/splash-page.hbs delete mode 100644 ui/app/components/splash-page.js delete mode 100644 ui/app/components/splash-page/splash-content.js delete mode 100644 ui/app/components/splash-page/splash-footer.js delete mode 100644 ui/app/components/splash-page/splash-header.js delete mode 100644 ui/app/templates/components/splash-page.hbs create mode 100644 ui/tests/integration/components/splash-page-test.js diff --git a/ui/app/components/splash-page.hbs b/ui/app/components/splash-page.hbs new file mode 100644 index 000000000000..187687ac4071 --- /dev/null +++ b/ui/app/components/splash-page.hbs @@ -0,0 +1,21 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +~}} + +
+
+
+
+ {{yield to="header"}} +
+
+ {{yield to="subHeader"}} +
+ + {{yield to="footer"}} +
+
+
\ No newline at end of file diff --git a/ui/app/components/splash-page.js b/ui/app/components/splash-page.js deleted file mode 100644 index ec1634e6d521..000000000000 --- a/ui/app/components/splash-page.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -/** - * @module SplashPage - * SplashPage component is used as a landing page with a box horizontally and center aligned on the page. It's used as the login landing page. - * - * - * @example - * ```js - * - * content here - * -
-
-
- {{yield (hash header=(component "splash-page/splash-header"))}} -
-
- {{yield (hash sub-header=(component "splash-page/splash-header"))}} -
- - {{yield (hash footer=(component "splash-page/splash-content"))}} -
-
- -{{/if}} \ No newline at end of file diff --git a/ui/app/templates/vault/cluster/auth.hbs b/ui/app/templates/vault/cluster/auth.hbs index 4d75b44acc01..13d291f2a24f 100644 --- a/ui/app/templates/vault/cluster/auth.hbs +++ b/ui/app/templates/vault/cluster/auth.hbs @@ -3,126 +3,130 @@ SPDX-License-Identifier: BUSL-1.1 ~}} - - -
- - - -
-
- - {{#if this.oidcProvider}} -
- -
- {{else}} -
-
- +{{#if this.mfaErrors}} +
+ + + +
+{{else}} + + <:header> + {{#if this.oidcProvider}} +
+
-
-
- {{#if this.mfaAuthData}} - - {{else if this.waitingForOktaNumberChallenge}} - - {{/if}} -

- {{if (or this.mfaAuthData this.waitingForOktaNumberChallenge) "Authenticate" "Sign in to Vault"}} -

-
- {{/if}} - - {{#unless this.mfaAuthData}} - {{#if (has-feature "Namespaces")}} - - -
-
- -
- {{#if this.managedNamespaceRoot}} -
- /{{this.managedNamespaceRoot}} + {{else}} +
+
+ +
+
+
+ {{#if this.mfaAuthData}} + + {{else if this.waitingForOktaNumberChallenge}} + + {{/if}} +

+ {{if (or this.mfaAuthData this.waitingForOktaNumberChallenge) "Authenticate" "Sign in to Vault"}} +

+
+ {{/if}} + + + <:subHeader> + {{#if (has-feature "Namespaces")}} + {{#unless this.mfaAuthData}} + +
+
+
- {{/if}} -
-
-
- + {{#if this.managedNamespaceRoot}} +
+ /{{this.managedNamespaceRoot}} +
+ {{/if}} +
+
+
+ +
-
- - - {{/if}} - {{/unless}} - - {{#if this.mfaAuthData}} - - {{else}} - - {{/if}} - - -
-

- {{#if this.oidcProvider}} - Once you log in, you will be redirected back to your application. If you require login credentials, contact your - administrator. - {{else}} - Contact your administrator for login credentials - {{/if}} -

-
-
- \ No newline at end of file + + {{/unless}} + {{/if}} + + + <:content> + {{#if this.mfaAuthData}} + + {{else}} + + {{/if}} + + + <:footer> +
+

+ {{#if this.oidcProvider}} + Once you log in, you will be redirected back to your application. If you require login credentials, contact your + administrator. + {{else}} + Contact your administrator for login credentials. + {{/if}} +

+
+ + +{{/if}} \ No newline at end of file diff --git a/ui/app/templates/vault/cluster/init.hbs b/ui/app/templates/vault/cluster/init.hbs index 70d44deb8e90..172f8c0668de 100644 --- a/ui/app/templates/vault/cluster/init.hbs +++ b/ui/app/templates/vault/cluster/init.hbs @@ -3,18 +3,13 @@ SPDX-License-Identifier: BUSL-1.1 ~}} - - {{#if (and this.model.usingRaft (not this.prefersInit))}} - + + <:header> + {{#if (and this.model.usingRaft (not this.prefersInit))}}

Raft Storage

-
- - - - {{else if this.keyData}} - + {{else if this.keyData}} {{#let (or this.keyData.recovery_keys this.keyData.keys) as |keyArray|}}

Vault has been initialized! @@ -26,8 +21,16 @@ {{/if}}

{{/let}} -
- + {{else}} +

+ Let's set up the initial set of root keys that you will need in case of an emergency. +

+ {{/if}} + + <:content> + {{#if (and this.model.usingRaft (not this.prefersInit))}} + + {{else if this.keyData}}

@@ -104,14 +107,7 @@ />

-
- {{else}} - -

- Let's set up the initial set of root keys that you’ll need in case of an emergency -

-
- + {{else}}
- - {{/if}} + {{/if}} + \ No newline at end of file diff --git a/ui/app/templates/vault/cluster/mfa-setup.hbs b/ui/app/templates/vault/cluster/mfa-setup.hbs index b4d3419f356f..3c203179fe37 100644 --- a/ui/app/templates/vault/cluster/mfa-setup.hbs +++ b/ui/app/templates/vault/cluster/mfa-setup.hbs @@ -3,11 +3,11 @@ SPDX-License-Identifier: BUSL-1.1 ~}} - - + + <:header>

MFA setup

-
- + + <:content>
{{#if (eq this.onStep 1)}} @@ -31,5 +31,5 @@ {{/if}}
-
+
\ No newline at end of file diff --git a/ui/app/templates/vault/cluster/unseal.hbs b/ui/app/templates/vault/cluster/unseal.hbs index ed9a02983d4b..ebaca1beae08 100644 --- a/ui/app/templates/vault/cluster/unseal.hbs +++ b/ui/app/templates/vault/cluster/unseal.hbs @@ -23,13 +23,14 @@
{{else}} - - + + <:header>

Unseal Vault

-
- + + + <:content>

{{capitalize this.model.name}} @@ -57,8 +58,9 @@ /> {{/if}}

-
- + + + <:footer>

@@ -66,6 +68,6 @@

-
+
{{/if}} \ No newline at end of file diff --git a/ui/tests/integration/components/splash-page-test.js b/ui/tests/integration/components/splash-page-test.js new file mode 100644 index 000000000000..82ddef0c9e93 --- /dev/null +++ b/ui/tests/integration/components/splash-page-test.js @@ -0,0 +1,37 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | splash-page', function (hooks) { + setupRenderingTest(hooks); + + test('it should render', async function (assert) { + assert.expect(4); + await render(hbs` + <:header> + Header here + + <:subHeader> + sub header + + <:content> + content + + <:footer> +
footer
+ + +
+ `); + assert.dom('[data-test-splash-page-header]').includesText('Header here', 'Header renders'); + assert.dom('[data-test-splash-page-sub-header]').includesText('sub header', 'SubHeader renders'); + assert.dom('[data-test-splash-page-content]').includesText('content', 'Content renders'); + assert.dom('[data-test-footer]').includesText('footer', 'Footer renders'); + }); +}); From e69b0b2bcf88c7498565aaf6ca27384bc5b08758 Mon Sep 17 00:00:00 2001 From: Raymond Ho Date: Mon, 27 Nov 2023 09:46:20 -0800 Subject: [PATCH 20/74] add custom permissions for azurekv (#23298) --- website/content/docs/sync/azurekv.mdx | 44 +++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/website/content/docs/sync/azurekv.mdx b/website/content/docs/sync/azurekv.mdx index 8266420d0143..d86ec901388c 100644 --- a/website/content/docs/sync/azurekv.mdx +++ b/website/content/docs/sync/azurekv.mdx @@ -27,8 +27,9 @@ Prerequisites: 1. Once the service principal is created, the next step is to [grant the service principal](https://learn.microsoft.com/en-us/azure/key-vault/general/rbac-guide?tabs=azure-cli) - access to Azure Key Vault. We recommend using the "Key Vault Secrets Officer" built-in role, - which gives sufficient access to manage secrets. + access to Azure Key Vault. To quickly get started, we recommend using the "Key Vault Secrets Officer" built-in role, + which gives sufficient access to manage secrets. For more information, see the [Permissions](#permissions) section. + 1. Configure a sync destination with the service principal credentials and Key Vault URI created in the previous steps. @@ -127,6 +128,45 @@ Moving forward, any modification on the Vault secret will be propagated in near counterpart. Creating a new secret version in Vault will create a new version in Azure Key Vault. Deleting the secret or the association in Vault will delete the secret in your Azure Key Vault as well. + +## Permissions + +For a more minimal set of permissions, you can create a +[custom role](https://learn.microsoft.com/en-us/azure/role-based-access-control/custom-roles#steps-to-create-a-custom-role) +using the following JSON role definition. Be sure to replace the subscription id placeholder. + +```json +{ + "properties": { + "roleName": "Key Vault Secrets Reader Writer", + "description": "Custom role for reading and updating Azure Key Vault secrets.", + "permissions": [ + { + "actions": [ + "Microsoft.KeyVault/vaults/secrets/read", + "Microsoft.KeyVault/vaults/secrets/write" + ], + "notActions": [], + "dataActions": [ + "Microsoft.KeyVault/vaults/secrets/delete", + "Microsoft.KeyVault/vaults/secrets/backup/action", + "Microsoft.KeyVault/vaults/secrets/purge/action", + "Microsoft.KeyVault/vaults/secrets/recover/action", + "Microsoft.KeyVault/vaults/secrets/restore/action", + "Microsoft.KeyVault/vaults/secrets/readMetadata/action", + "Microsoft.KeyVault/vaults/secrets/getSecret/action", + "Microsoft.KeyVault/vaults/secrets/setSecret/action" + ], + "notDataActions": [] + } + ], + "assignableScopes": [ + "/subscriptions/{subscriptionId}/" + ] + } +} +``` + ## API Please see the [secrets sync API](/vault/api-docs/system/secrets-sync) for more details. From 9b7d06839f9c18de00f2fc2958a1fb6f210c2f2e Mon Sep 17 00:00:00 2001 From: kpcraig <3031348+kpcraig@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:48:16 -0500 Subject: [PATCH 21/74] Add a /config/rotate-root path to the ldap auth backend (#24099) --- builtin/credential/ldap/backend.go | 9 +- builtin/credential/ldap/path_config.go | 19 +++ .../ldap/path_config_rotate_root.go | 115 ++++++++++++++++++ .../ldap/path_config_rotate_root_test.go | 66 ++++++++++ changelog/24099.txt | 3 + 5 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 builtin/credential/ldap/path_config_rotate_root.go create mode 100644 builtin/credential/ldap/path_config_rotate_root_test.go create mode 100644 changelog/24099.txt diff --git a/builtin/credential/ldap/backend.go b/builtin/credential/ldap/backend.go index d938a4fea9f0..3f203fb13bb0 100644 --- a/builtin/credential/ldap/backend.go +++ b/builtin/credential/ldap/backend.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "strings" + "sync" "github.com/hashicorp/cap/ldap" "github.com/hashicorp/go-secure-stdlib/strutil" @@ -17,8 +18,9 @@ import ( ) const ( - operationPrefixLDAP = "ldap" - errUserBindFailed = "ldap operation failed: failed to bind as user" + operationPrefixLDAP = "ldap" + errUserBindFailed = "ldap operation failed: failed to bind as user" + defaultPasswordLength = 64 // length to use for configured root password on rotations by default ) func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) { @@ -51,6 +53,7 @@ func Backend() *backend { pathUsers(&b), pathUsersList(&b), pathLogin(&b), + pathConfigRotateRoot(&b), }, AuthRenew: b.pathLoginRenew, @@ -62,6 +65,8 @@ func Backend() *backend { type backend struct { *framework.Backend + + mu sync.RWMutex } func (b *backend) Login(ctx context.Context, req *logical.Request, username string, password string, usernameAsAlias bool) (string, []string, *logical.Response, []string, error) { diff --git a/builtin/credential/ldap/path_config.go b/builtin/credential/ldap/path_config.go index f6e7a152dfa4..e24d04b295c7 100644 --- a/builtin/credential/ldap/path_config.go +++ b/builtin/credential/ldap/path_config.go @@ -48,6 +48,12 @@ func pathConfig(b *backend) *framework.Path { tokenutil.AddTokenFields(p.Fields) p.Fields["token_policies"].Description += ". This will apply to all tokens generated by this auth method, in addition to any configured for specific users/groups." + + p.Fields["password_policy"] = &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Password policy to use to rotate the root password", + } + return p } @@ -118,6 +124,9 @@ func (b *backend) Config(ctx context.Context, req *logical.Request) (*ldapConfig } func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + b.mu.RLock() + defer b.mu.RUnlock() + cfg, err := b.Config(ctx, req) if err != nil { return nil, err @@ -128,6 +137,7 @@ func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, d *f data := cfg.PasswordlessMap() cfg.PopulateTokenData(data) + data["password_policy"] = cfg.PasswordPolicy resp := &logical.Response{ Data: data, @@ -164,6 +174,9 @@ func (b *backend) checkConfigUserFilter(cfg *ldapConfigEntry) []string { } func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + b.mu.Lock() + defer b.mu.Unlock() + cfg, err := b.Config(ctx, req) if err != nil { return nil, err @@ -194,6 +207,10 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d * return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest } + if passwordPolicy, ok := d.GetOk("password_policy"); ok { + cfg.PasswordPolicy = passwordPolicy.(string) + } + entry, err := logical.StorageEntryJSON("config", cfg) if err != nil { return nil, err @@ -234,6 +251,8 @@ func (b *backend) getConfigFieldData() (*framework.FieldData, error) { type ldapConfigEntry struct { tokenutil.TokenParams *ldaputil.ConfigEntry + + PasswordPolicy string `json:"password_policy"` } const pathConfigHelpSyn = ` diff --git a/builtin/credential/ldap/path_config_rotate_root.go b/builtin/credential/ldap/path_config_rotate_root.go new file mode 100644 index 000000000000..1aa008f4ddc3 --- /dev/null +++ b/builtin/credential/ldap/path_config_rotate_root.go @@ -0,0 +1,115 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-2.0 + +package ldap + +import ( + "context" + + "github.com/go-ldap/ldap/v3" + + "github.com/hashicorp/vault/sdk/helper/base62" + "github.com/hashicorp/vault/sdk/helper/ldaputil" + + "github.com/hashicorp/vault/sdk/framework" + "github.com/hashicorp/vault/sdk/logical" +) + +func pathConfigRotateRoot(b *backend) *framework.Path { + return &framework.Path{ + Pattern: "config/rotate-root", + + DisplayAttrs: &framework.DisplayAttributes{ + OperationPrefix: operationPrefixLDAP, + OperationVerb: "rotate", + OperationSuffix: "root-credentials", + }, + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.pathConfigRotateRootUpdate, + ForwardPerformanceSecondary: true, + ForwardPerformanceStandby: true, + }, + }, + + HelpSynopsis: pathConfigRotateRootHelpSyn, + HelpDescription: pathConfigRotateRootHelpDesc, + } +} + +func (b *backend) pathConfigRotateRootUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + // lock the backend's state - really just the config state - for mutating + b.mu.Lock() + defer b.mu.Unlock() + + cfg, err := b.Config(ctx, req) + if err != nil { + return nil, err + } + if cfg == nil { + return logical.ErrorResponse("attempted to rotate root on an undefined config"), nil + } + + u, p := cfg.BindDN, cfg.BindPassword + if u == "" || p == "" { + return logical.ErrorResponse("auth is not using authenticated search, no root to rotate"), nil + } + + // grab our ldap client + client := ldaputil.Client{ + Logger: b.Logger(), + LDAP: ldaputil.NewLDAP(), + } + + conn, err := client.DialLDAP(cfg.ConfigEntry) + if err != nil { + return nil, err + } + + err = conn.Bind(u, p) + if err != nil { + return nil, err + } + + lreq := &ldap.ModifyRequest{ + DN: cfg.BindDN, + } + + var newPassword string + if cfg.PasswordPolicy != "" { + newPassword, err = b.System().GeneratePasswordFromPolicy(ctx, cfg.PasswordPolicy) + } else { + newPassword, err = base62.Random(defaultPasswordLength) + } + if err != nil { + return nil, err + } + + lreq.Replace("userPassword", []string{newPassword}) + + err = conn.Modify(lreq) + if err != nil { + return nil, err + } + // update config with new password + cfg.BindPassword = newPassword + entry, err := logical.StorageEntryJSON("config", cfg) + if err != nil { + return nil, err + } + if err := req.Storage.Put(ctx, entry); err != nil { + // we might have to roll-back the password here? + return nil, err + } + + return nil, nil +} + +const pathConfigRotateRootHelpSyn = ` +Request to rotate the LDAP credentials used by Vault +` + +const pathConfigRotateRootHelpDesc = ` +This path attempts to rotate the LDAP bindpass used by Vault for this mount. +` diff --git a/builtin/credential/ldap/path_config_rotate_root_test.go b/builtin/credential/ldap/path_config_rotate_root_test.go new file mode 100644 index 000000000000..65073472ca00 --- /dev/null +++ b/builtin/credential/ldap/path_config_rotate_root_test.go @@ -0,0 +1,66 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-2.0 + +package ldap + +import ( + "context" + "os" + "testing" + + "github.com/hashicorp/vault/helper/testhelpers/ldap" + logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical" + "github.com/hashicorp/vault/sdk/logical" +) + +// This test relies on a docker ldap server with a suitable person object (cn=admin,dc=planetexpress,dc=com) +// with bindpassword "admin". `PrepareTestContainer` does this for us. - see the backend_test for more details +func TestRotateRoot(t *testing.T) { + if os.Getenv(logicaltest.TestEnvVar) == "" { + t.Skip("skipping rotate root tests because VAULT_ACC is unset") + } + ctx := context.Background() + + b, store := createBackendWithStorage(t) + cleanup, cfg := ldap.PrepareTestContainer(t, "latest") + defer cleanup() + // set up auth config + req := &logical.Request{ + Operation: logical.UpdateOperation, + Path: "config", + Storage: store, + Data: map[string]interface{}{ + "url": cfg.Url, + "binddn": cfg.BindDN, + "bindpass": cfg.BindPassword, + "userdn": cfg.UserDN, + }, + } + + resp, err := b.HandleRequest(ctx, req) + if err != nil { + t.Fatalf("failed to initialize ldap auth config: %s", err) + } + if resp != nil && resp.IsError() { + t.Fatalf("failed to initialize ldap auth config: %s", resp.Data["error"]) + } + + req = &logical.Request{ + Operation: logical.UpdateOperation, + Path: "config/rotate-root", + Storage: store, + } + + _, err = b.HandleRequest(ctx, req) + if err != nil { + t.Fatalf("failed to rotate password: %s", err) + } + + newCFG, err := b.Config(ctx, req) + if newCFG.BindDN != cfg.BindDN { + t.Fatalf("a value in config that should have stayed the same changed: %s", cfg.BindDN) + } + if newCFG.BindPassword == cfg.BindPassword { + t.Fatalf("the password should have changed, but it didn't") + } +} diff --git a/changelog/24099.txt b/changelog/24099.txt new file mode 100644 index 000000000000..bc33a184f988 --- /dev/null +++ b/changelog/24099.txt @@ -0,0 +1,3 @@ +```release-note:feature +**Rotate Root for LDAP auth**: Rotate root operations are now supported for the LDAP auth engine. +``` From c329ed8d3b02b92dfded30065317c82648d3cae3 Mon Sep 17 00:00:00 2001 From: Steven Clark Date: Mon, 27 Nov 2023 15:50:41 -0500 Subject: [PATCH 22/74] api/leader: fix deadlock when namespace is set on leader calls (#24256) * api/leader: fix deadlock when namespace is set on leader calls * Add cl --- changelog/24256.txt | 4 ++++ vault/ha.go | 23 ++++++++++++++++------- vault/logical_system.go | 17 ++++++++++++----- vault/raft.go | 3 +++ 4 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 changelog/24256.txt diff --git a/changelog/24256.txt b/changelog/24256.txt new file mode 100644 index 000000000000..74124710b8a8 --- /dev/null +++ b/changelog/24256.txt @@ -0,0 +1,4 @@ +```release-note:bug +api: Fix deadlock on calls to sys/leader with a namespace configured +on the request. +``` diff --git a/vault/ha.go b/vault/ha.go index 8ef86b0fefcb..15d6f180f7cb 100644 --- a/vault/ha.go +++ b/vault/ha.go @@ -150,30 +150,41 @@ func (c *Core) Leader() (isLeader bool, leaderAddr, clusterAddr string, err erro if c.Sealed() { return false, "", "", consts.ErrSealed } - c.stateLock.RLock() + defer c.stateLock.RUnlock() + + return c.LeaderLocked() +} + +func (c *Core) LeaderLocked() (isLeader bool, leaderAddr, clusterAddr string, err error) { + // Check if HA enabled. We don't need the lock for this check as it's set + // on startup and never modified + if c.ha == nil { + return false, "", "", ErrHANotEnabled + } + + // Check if sealed + if c.Sealed() { + return false, "", "", consts.ErrSealed + } // Check if we are the leader if !c.standby { - c.stateLock.RUnlock() return true, c.redirectAddr, c.ClusterAddr(), nil } // Initialize a lock lock, err := c.ha.LockWith(CoreLockPath, "read") if err != nil { - c.stateLock.RUnlock() return false, "", "", err } // Read the value held, leaderUUID, err := lock.Value() if err != nil { - c.stateLock.RUnlock() return false, "", "", err } if !held { - c.stateLock.RUnlock() return false, "", "", nil } @@ -188,13 +199,11 @@ func (c *Core) Leader() (isLeader bool, leaderAddr, clusterAddr string, err erro // If the leader hasn't changed, return the cached value; nothing changes // mid-leadership, and the barrier caches anyways if leaderUUID == localLeaderUUID && localRedirectAddr != "" { - c.stateLock.RUnlock() return false, localRedirectAddr, localClusterAddr, nil } c.logger.Trace("found new active node information, refreshing") - defer c.stateLock.RUnlock() c.leaderParamsLock.Lock() defer c.leaderParamsLock.Unlock() diff --git a/vault/logical_system.go b/vault/logical_system.go index 564e0e971593..30ffdf0e3e47 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -5150,8 +5150,15 @@ type LeaderResponse struct { } func (core *Core) GetLeaderStatus() (*LeaderResponse, error) { + core.stateLock.RLock() + defer core.stateLock.RUnlock() + + return core.GetLeaderStatusLocked() +} + +func (core *Core) GetLeaderStatusLocked() (*LeaderResponse, error) { haEnabled := true - isLeader, address, clusterAddr, err := core.Leader() + isLeader, address, clusterAddr, err := core.LeaderLocked() if errwrap.Contains(err, ErrHANotEnabled.Error()) { haEnabled = false err = nil @@ -5165,10 +5172,10 @@ func (core *Core) GetLeaderStatus() (*LeaderResponse, error) { IsSelf: isLeader, LeaderAddress: address, LeaderClusterAddress: clusterAddr, - PerfStandby: core.PerfStandby(), + PerfStandby: core.perfStandby, } if isLeader { - resp.ActiveTime = core.ActiveTime() + resp.ActiveTime = core.activeTime } if resp.PerfStandby { resp.PerfStandbyLastRemoteWAL = core.EntLastRemoteWAL() @@ -5176,7 +5183,7 @@ func (core *Core) GetLeaderStatus() (*LeaderResponse, error) { resp.LastWAL = core.EntLastWAL() } - resp.RaftCommittedIndex, resp.RaftAppliedIndex = core.GetRaftIndexes() + resp.RaftCommittedIndex, resp.RaftAppliedIndex = core.GetRaftIndexesLocked() return resp, nil } @@ -5200,7 +5207,7 @@ func (b *SystemBackend) handleSealStatus(ctx context.Context, req *logical.Reque } func (b *SystemBackend) handleLeaderStatus(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - status, err := b.Core.GetLeaderStatus() + status, err := b.Core.GetLeaderStatusLocked() if err != nil { return nil, err } diff --git a/vault/raft.go b/vault/raft.go index 9e4df481d9ce..b2b26496d12f 100644 --- a/vault/raft.go +++ b/vault/raft.go @@ -70,7 +70,10 @@ func (c *Core) GetRaftNodeID() string { func (c *Core) GetRaftIndexes() (committed uint64, applied uint64) { c.stateLock.RLock() defer c.stateLock.RUnlock() + return c.GetRaftIndexesLocked() +} +func (c *Core) GetRaftIndexesLocked() (committed uint64, applied uint64) { raftStorage, ok := c.underlyingPhysical.(*raft.RaftBackend) if !ok { return 0, 0 From 5781891292c6beb4cd07fbf565469ac04fa4d86a Mon Sep 17 00:00:00 2001 From: Steven Clark Date: Mon, 27 Nov 2023 15:50:54 -0500 Subject: [PATCH 23/74] PKI: Address some errors that were not wrapped properly (#24118) --- builtin/logical/pki/acme_challenges.go | 2 +- builtin/logical/pki/path_resign_crls.go | 4 ++-- builtin/logical/pki/test_helpers.go | 24 ++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/builtin/logical/pki/acme_challenges.go b/builtin/logical/pki/acme_challenges.go index 290c8ec78c7f..85c051c86e0e 100644 --- a/builtin/logical/pki/acme_challenges.go +++ b/builtin/logical/pki/acme_challenges.go @@ -311,7 +311,7 @@ func ValidateTLSALPN01Challenge(domain string, token string, thumbprint string, // checks for the parent certificate having the IsCA basic constraint set. err := cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature) if err != nil { - return fmt.Errorf("server under test returned a non-self-signed certificate: %v", err) + return fmt.Errorf("server under test returned a non-self-signed certificate: %w", err) } if !bytes.Equal(cert.RawSubject, cert.RawIssuer) { diff --git a/builtin/logical/pki/path_resign_crls.go b/builtin/logical/pki/path_resign_crls.go index 6113010dc8c5..23e1a0b0ed43 100644 --- a/builtin/logical/pki/path_resign_crls.go +++ b/builtin/logical/pki/path_resign_crls.go @@ -252,7 +252,7 @@ func (b *backend) pathUpdateResignCrlsHandler(ctx context.Context, request *logi if deltaCrlBaseNumber > -1 { ext, err := certutil.CreateDeltaCRLIndicatorExt(int64(deltaCrlBaseNumber)) if err != nil { - return nil, fmt.Errorf("could not create crl delta indicator extension: %v", err) + return nil, fmt.Errorf("could not create crl delta indicator extension: %w", err) } template.ExtraExtensions = []pkix.Extension{ext} } @@ -325,7 +325,7 @@ func (b *backend) pathUpdateSignRevocationListHandler(ctx context.Context, reque if deltaCrlBaseNumber > -1 { ext, err := certutil.CreateDeltaCRLIndicatorExt(int64(deltaCrlBaseNumber)) if err != nil { - return nil, fmt.Errorf("could not create crl delta indicator extension: %v", err) + return nil, fmt.Errorf("could not create crl delta indicator extension: %w", err) } crlExtensions = append(crlExtensions, ext) } diff --git a/builtin/logical/pki/test_helpers.go b/builtin/logical/pki/test_helpers.go index 1cdc98795e71..c80856b661c5 100644 --- a/builtin/logical/pki/test_helpers.go +++ b/builtin/logical/pki/test_helpers.go @@ -436,3 +436,27 @@ func performOcspPost(t *testing.T, cert *x509.Certificate, issuerCert *x509.Cert require.NoError(t, err, "parsing ocsp get response") return ocspResp } + +func requireCertMissingFromStorage(t *testing.T, client *api.Client, cert *x509.Certificate) { + serial := serialFromCert(cert) + requireSerialMissingFromStorage(t, client, serial) +} + +func requireSerialMissingFromStorage(t *testing.T, client *api.Client, serial string) { + resp, err := client.Logical().ReadWithContext(context.Background(), "pki/cert/"+serial) + require.NoErrorf(t, err, "failed reading certificate with serial %s", serial) + require.Nilf(t, resp, "expected a nil response looking up serial %s got: %v", serial, resp) +} + +func requireCertInStorage(t *testing.T, client *api.Client, cert *x509.Certificate) { + serial := serialFromCert(cert) + requireSerialInStorage(t, client, serial) +} + +func requireSerialInStorage(t *testing.T, client *api.Client, serial string) { + resp, err := client.Logical().ReadWithContext(context.Background(), "pki/cert/"+serial) + require.NoErrorf(t, err, "failed reading certificate with serial %s", serial) + require.NotNilf(t, resp, "reading certificate returned a nil response for serial: %s", serial) + require.NotNilf(t, resp.Data, "reading certificate returned a nil data response for serial: %s", serial) + require.NotEmpty(t, resp.Data["certificate"], "certificate field was empty for serial: %s", serial) +} From 83a6ffcff6d1190c7c6432c43431bc33122b2e5b Mon Sep 17 00:00:00 2001 From: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:51:46 -0600 Subject: [PATCH 24/74] UI: Replace instances of with (#24257) * Remove and replace InfoTable on replication-secondary-card * Raft storage table update * Known secondaries table replace * remove vlt-table class and styles * Fix tests --- ui/app/styles/components/vlt-table.scss | 63 -------- ui/app/styles/core.scss | 1 - .../components/raft-storage-overview.hbs | 145 +++++++----------- .../core/addon/components/confirm-action.hbs | 1 + ui/lib/core/addon/components/info-table.hbs | 28 ---- ui/lib/core/addon/components/info-table.js | 29 ---- .../components/replication-secondary-card.hbs | 36 +++-- ui/lib/core/app/components/info-table.js | 6 - .../components/known-secondaries-card.hbs | 10 +- .../components/known-secondaries-table.hbs | 84 ++++------ ui/tests/acceptance/raft-storage-test.js | 2 +- .../components/confirm-action-test.js | 6 +- .../integration/components/info-table-test.js | 42 ----- .../components/known-secondaries-card-test.js | 4 +- .../known-secondaries-table-test.js | 48 +++--- .../replication-secondary-card-test.js | 8 +- 16 files changed, 155 insertions(+), 358 deletions(-) delete mode 100644 ui/app/styles/components/vlt-table.scss delete mode 100644 ui/lib/core/addon/components/info-table.hbs delete mode 100644 ui/lib/core/addon/components/info-table.js delete mode 100644 ui/lib/core/app/components/info-table.js delete mode 100644 ui/tests/integration/components/info-table-test.js diff --git a/ui/app/styles/components/vlt-table.scss b/ui/app/styles/components/vlt-table.scss deleted file mode 100644 index 1d397c0db77c..000000000000 --- a/ui/app/styles/components/vlt-table.scss +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -.vlt-table { - .is-collapsed { - visibility: collapse; - height: 0; - } - - &.sticky-header { - thead th { - position: sticky; - background: #fff; - box-shadow: 0 1px 0px 0px rgba($grey-dark, 0.3); - top: 0; - } - } - - table { - border-collapse: collapse; - border-spacing: 0; - } - - th, - td { - padding: $spacing-12; - } - - th { - color: $grey-dark; - font-weight: 500; - font-size: $size-8; - text-align: left; - vertical-align: top; - } - - tbody th { - font-size: $size-7; - } - - tr { - border-bottom: 1px solid $grey-light; - } - - td { - color: $grey-darkest; - } - - td.middle { - vertical-align: middle; - } - - td.no-padding { - padding: 0; - } - - code { - font-size: $size-7; - color: $black; - } -} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index eefc125be3cb..1c8dcc675d65 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -113,4 +113,3 @@ @import './components/unseal-warning'; // @import './components/ui-wizard'; // remove, see PR https://github.com/hashicorp/vault/pull/19220 @import './components/vault-loading'; -@import './components/vlt-table'; diff --git a/ui/app/templates/components/raft-storage-overview.hbs b/ui/app/templates/components/raft-storage-overview.hbs index 006566b775e8..7c4e00100fc3 100644 --- a/ui/app/templates/components/raft-storage-overview.hbs +++ b/ui/app/templates/components/raft-storage-overview.hbs @@ -3,98 +3,67 @@ SPDX-License-Identifier: BUSL-1.1 ~}} - - -

- Raft Storage -

-
-
- - - - - Snapshots - - - - - - - - + + Raft Storage + + + + {{#if this.useServiceWorker}} + + {{else}} + + {{/if}} + + + + -
- - - - - - - - - + + <:head as |H|> + + Address + Leader + Voter + Actions + + + <:body as |B|> {{#each @model as |server|}} - - - - - + + + + + + + + + + {{/each}} - -
AddressVoter
- {{server.address}} - {{#if server.leader}} - Leader + + {{server.address}} + {{#if server.leader}} + + {{else}} + {{/if}} - - + + {{#if server.voter}} - + {{else}} - + {{/if}} - - - - -
\ No newline at end of file + + \ No newline at end of file diff --git a/ui/lib/core/addon/components/confirm-action.hbs b/ui/lib/core/addon/components/confirm-action.hbs index 3e5cd5021a0a..2c22eedd8ea8 100644 --- a/ui/lib/core/addon/components/confirm-action.hbs +++ b/ui/lib/core/addon/components/confirm-action.hbs @@ -27,6 +27,7 @@ {{#if this.showConfirmModal}} - - - - - - - - - {{#each @items as |item|}} - - - - {{/each}} - -
- {{@header}} -
- -
-
\ No newline at end of file diff --git a/ui/lib/core/addon/components/info-table.js b/ui/lib/core/addon/components/info-table.js deleted file mode 100644 index 00eb57bfedb8..000000000000 --- a/ui/lib/core/addon/components/info-table.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Component from '@glimmer/component'; - -/** - * @module InfoTable - * InfoTable components are a table with a single column and header. They are used to render a list of InfoTableRow components. - * - * @example - * ```js - * - * ``` - * @param {String} [title=Info Table] - The title of the table. Used for accessibility purposes. - * @param {String} header=null - The column header. - * @param {Array} items=null - An array of strings which will be used as the InfoTableRow value. - */ - -export default class InfoTable extends Component { - get title() { - return this.args.title || 'Info Table'; - } -} diff --git a/ui/lib/core/addon/templates/components/replication-secondary-card.hbs b/ui/lib/core/addon/templates/components/replication-secondary-card.hbs index 317ea44a7247..053188128d6b 100644 --- a/ui/lib/core/addon/templates/components/replication-secondary-card.hbs +++ b/ui/lib/core/addon/templates/components/replication-secondary-card.hbs @@ -107,16 +107,34 @@
{{#if (is-empty this.knownPrimaryClusterAddrs)}} - - - Learn more - - + + + + + + + {{else}} - + + <:head as |H|> + + cluster_addr + + + <:body as |B|> + {{#each this.knownPrimaryClusterAddrs as |item|}} + + {{item}} + + {{/each}} + + {{/if}}
{{/if}} diff --git a/ui/lib/core/app/components/info-table.js b/ui/lib/core/app/components/info-table.js deleted file mode 100644 index f7ced1e2b209..000000000000 --- a/ui/lib/core/app/components/info-table.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -export { default } from 'core/components/info-table'; diff --git a/ui/lib/replication/addon/templates/components/known-secondaries-card.hbs b/ui/lib/replication/addon/templates/components/known-secondaries-card.hbs index 5cd2c244200e..b25504e3c41b 100644 --- a/ui/lib/replication/addon/templates/components/known-secondaries-card.hbs +++ b/ui/lib/replication/addon/templates/components/known-secondaries-card.hbs @@ -17,10 +17,12 @@ {{#if this.replicationAttrs.secondaries}} {{else}} - + + + + {{/if}} {{#if this.cluster.canAddSecondary}} diff --git a/ui/lib/replication/addon/templates/components/known-secondaries-table.hbs b/ui/lib/replication/addon/templates/components/known-secondaries-table.hbs index 39479f3b54a1..b447dd29bf3c 100644 --- a/ui/lib/replication/addon/templates/components/known-secondaries-table.hbs +++ b/ui/lib/replication/addon/templates/components/known-secondaries-table.hbs @@ -3,54 +3,36 @@ SPDX-License-Identifier: BUSL-1.1 ~}} - \ No newline at end of file + + <:head as |H|> + + Secondary ID + URL + Connected? + + + <:body as |B|> + {{#each this.secondaries as |secondary|}} + + {{secondary.node_id}} + + {{#if secondary.api_address}} + + {{else}} +

URL unavailable

+ {{/if}} +
+ + + {{secondary.connection_status}} + + +
+ {{/each}} + +
\ No newline at end of file diff --git a/ui/tests/acceptance/raft-storage-test.js b/ui/tests/acceptance/raft-storage-test.js index 35ddc099aa87..56c2aa7ec931 100644 --- a/ui/tests/acceptance/raft-storage-test.js +++ b/ui/tests/acceptance/raft-storage-test.js @@ -66,7 +66,7 @@ module('Acceptance | raft storage', function (hooks) { await visit('/vault/storage/raft'); assert.dom('[data-raft-row]').exists({ count: 2 }, '2 raft peers render in table'); - await click('[data-raft-row]:nth-child(2) [data-test-popup-menu-trigger]'); + await click('[data-raft-row]:nth-child(2) [data-test-raft-actions] button'); await click('[data-test-confirm-action-trigger]'); await click('[data-test-confirm-button]'); assert.dom('[data-raft-row]').exists({ count: 1 }, 'Raft peer successfully removed'); diff --git a/ui/tests/integration/components/confirm-action-test.js b/ui/tests/integration/components/confirm-action-test.js index 839840e5f3ed..139bc100162b 100644 --- a/ui/tests/integration/components/confirm-action-test.js +++ b/ui/tests/integration/components/confirm-action-test.js @@ -36,7 +36,7 @@ module('Integration | Component | confirm-action', function (hooks) { // hasClass assertion wasn't working so this is the workaround assert.strictEqual( find('#confirm-action-modal').className, - 'hds-modal hds-modal--size-small hds-modal--color-critical', + 'hds-modal hds-modal--size-small hds-modal--color-critical has-text-left', 'renders critical modal color by default' ); assert.strictEqual( @@ -110,7 +110,7 @@ module('Integration | Component | confirm-action', function (hooks) { await click(SELECTORS.modalToggle); assert.strictEqual( find('#confirm-action-modal').className, - 'hds-modal hds-modal--size-small hds-modal--color-neutral', + 'hds-modal hds-modal--size-small hds-modal--color-neutral has-text-left', 'renders critical modal color by default' ); assert.dom(SELECTORS.title).hasText('Not allowed', 'renders disabled title'); @@ -144,7 +144,7 @@ module('Integration | Component | confirm-action', function (hooks) { await click(SELECTORS.modalToggle); assert.strictEqual( find('#confirm-action-modal').className, - 'hds-modal hds-modal--size-small hds-modal--color-warning', + 'hds-modal hds-modal--size-small hds-modal--color-warning has-text-left', 'renders warning modal' ); assert.strictEqual( diff --git a/ui/tests/integration/components/info-table-test.js b/ui/tests/integration/components/info-table-test.js deleted file mode 100644 index 96e3e5ec27ec..000000000000 --- a/ui/tests/integration/components/info-table-test.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render } from '@ember/test-helpers'; -import hbs from 'htmlbars-inline-precompile'; - -const TITLE = 'My Table'; -const HEADER = 'Cool Header'; -const ITEMS = ['https://127.0.0.1:8201', 'hello', '3']; - -module('Integration | Component | InfoTable', function (hooks) { - setupRenderingTest(hooks); - - hooks.beforeEach(function () { - this.set('title', TITLE); - this.set('header', HEADER); - this.set('items', ITEMS); - }); - - test('it renders', async function (assert) { - assert.expect(6); - await render(hbs``); - - assert.dom('[data-test-info-table]').exists(); - assert.dom('[data-test-info-table] th').includesText(HEADER, `shows the table header`); - - const rows = document.querySelectorAll('.info-table-row'); - assert.strictEqual(rows.length, ITEMS.length, 'renders an InfoTableRow for each item'); - - rows.forEach((row, i) => { - assert.strictEqual(row.innerText, ITEMS[i], 'handles strings and numbers as row values'); - }); - }); -}); diff --git a/ui/tests/integration/components/known-secondaries-card-test.js b/ui/tests/integration/components/known-secondaries-card-test.js index 40fcf2b6048b..ac343147659e 100644 --- a/ui/tests/integration/components/known-secondaries-card-test.js +++ b/ui/tests/integration/components/known-secondaries-card-test.js @@ -58,8 +58,8 @@ module('Integration | Component | replication known-secondaries-card', function .dom('[data-test-known-secondaries-table]') .doesNotExist('does not show the known secondaries table'); assert - .dom('.empty-state') - .includesText('No known dr secondary clusters', 'has a message with the replication mode'); + .dom('.hds-application-state') + .includesText('No known dr secondary clusters', 'empty state has a message with the replication mode'); }); test('it renders an Add secondary link if user has capabilites', async function (assert) { diff --git a/ui/tests/integration/components/known-secondaries-table-test.js b/ui/tests/integration/components/known-secondaries-table-test.js index 0a4fee2421cc..e0c9f8ebb8cf 100644 --- a/ui/tests/integration/components/known-secondaries-table-test.js +++ b/ui/tests/integration/components/known-secondaries-table-test.js @@ -32,37 +32,31 @@ module('Integration | Component | replication known-secondaries-table', function }); test('it shows the secondary URL and connection_status', async function (assert) { - assert.expect(9); + assert.expect(13); await render(hbs``, this.context); SECONDARIES.forEach((secondary) => { - assert.strictEqual( - this.element.querySelector(`[data-test-secondaries=row-for-${secondary.node_id}]`).innerHTML.trim(), - secondary.node_id, - 'shows a table row and ID for each known secondary' - ); - - if (secondary.api_address) { - const expectedUrl = `${secondary.api_address}/ui/`; - - assert.strictEqual( - this.element.querySelector(`[data-test-secondaries=api-address-for-${secondary.node_id}]`).href, - expectedUrl, - 'renders a URL to the secondary UI' - ); - } else { - assert.notOk( - this.element.querySelector(`[data-test-secondaries=api-address-for-${secondary.node_id}]`) - ); - } + assert + .dom(`[data-test-secondaries-node="${secondary.node_id}"]`) + .hasText(secondary.node_id, 'shows a table row and ID for each known secondary'); + const expectedAPIAddr = secondary.api_address || 'URL unavailable'; + const expectedTag = secondary.api_address ? 'a' : 'p'; + assert.dom(`[data-test-secondaries-api-address="${secondary.node_id}"]`).hasText(expectedAPIAddr); + assert + .dom(`[data-test-secondaries-api-address="${secondary.node_id}"] ${expectedTag}`) + .exists('has correct tag'); + + assert + .dom(`[data-test-secondaries-connection-status="${secondary.node_id}"]`) + .hasText(secondary.connection_status, 'shows the connection status'); + }); - assert.strictEqual( - this.element - .querySelector(`[data-test-secondaries=connection-status-for-${secondary.node_id}]`) - .innerHTML.trim(), - secondary.connection_status, - 'shows the connection status' + assert + .dom(`[data-test-secondaries-api-address="secondary-1"] a`) + .hasAttribute( + 'href', + 'https://127.0.0.1:52304/ui/', + 'secondary with API address has correct href attribute' ); - }); }); }); diff --git a/ui/tests/integration/components/replication-secondary-card-test.js b/ui/tests/integration/components/replication-secondary-card-test.js index d89d9a2a4c8f..1c8261a90173 100644 --- a/ui/tests/integration/components/replication-secondary-card-test.js +++ b/ui/tests/integration/components/replication-secondary-card-test.js @@ -48,10 +48,10 @@ module('Integration | Component | replication-secondary-card', function (hooks) assert.dom('[data-test-info-table]').exists('it shows the known primary cluster details'); - const url = this.element.querySelector('[data-test-primary-link]').href; const expectedUrl = `${REPLICATION_DETAILS.primaries[0].api_address}/ui/`; - - assert.strictEqual(url, expectedUrl, 'it renders a link to the primary cluster UI'); + assert + .dom('[data-test-primary-link]') + .hasAttribute('href', expectedUrl, 'it renders a link to the primary cluster UI'); }); test('it does not render a link to the primary cluster UI when the primary api address or known primaries are unknown', async function (assert) { @@ -68,7 +68,7 @@ module('Integration | Component | replication-secondary-card', function (hooks) await render( hbs`` ); - assert.dom('[data-test-component="empty-state"]').exists(); + assert.dom('[data-test-empty-state]').exists('shows empty state'); }); test('it renders tooltip with check-circle-outline when state is stream-wals', async function (assert) { From 3726d8fb1d86670580d2d5fcccfe9b901144cb96 Mon Sep 17 00:00:00 2001 From: Robert <17119716+robmonte@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:10:37 -0600 Subject: [PATCH 25/74] Add configuration section to sync API docs (#24179) * Add configuration section * Add restricted root namespace alert --- .../content/api-docs/system/secrets-sync.mdx | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/website/content/api-docs/system/secrets-sync.mdx b/website/content/api-docs/system/secrets-sync.mdx index b258a8082ceb..beb52f137fa2 100644 --- a/website/content/api-docs/system/secrets-sync.mdx +++ b/website/content/api-docs/system/secrets-sync.mdx @@ -11,6 +11,56 @@ The `/sys/sync` endpoints are used to configure destinations and associate secre Each destination type has its own endpoint for creation & update operations, but share the same endpoints for read & delete operations. +## Configuration + +The `sys/sync/config` endpoint is used to set configuration parameters for the sync system as a whole. + +@include 'alerts/restricted-root.mdx' + +| Method | Path | +|:--------|:------------------| +| `PATCH` | `sys/sync/config` | + +### Parameters + +- `disabled` `(bool: false)` - Disables sync operations from sending secrets in Vault to external destinations when +set to true. While disabled, actions performed in Vault which trigger a sync operation will instead get queued to be +processed once syncing is reactivated. Queued operations will have a status of `PENDING` until they are completed. +This is provided as a safety mechanism for emergencies. + +### Sample payload +```json +{ + "disabled": "true" +} +``` + +### Sample request + +```shell-session +$ curl \ + --header "X-Vault-Token: ..." \ + --request PATCH \ + --data @payload.json + http://127.0.0.1:8200/v1/sys/sync/config +``` + +### Sample response + +```json +{ + "request_id": "uuid", + "lease_id": "", + "lease_duration": 0, + "renewable": false, + "data": { + "disabled": true + }, + "warnings": null, + "mount_type": "system" +} +``` + ## List destinations This endpoint lists all configured sync destination names regrouped by destination type. From 030bba4e68751ddffaf9734853da3dfb395d2f28 Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Tue, 28 Nov 2023 14:07:07 +0000 Subject: [PATCH 26/74] Support rootless plugin containers (#24236) * Pulls in github.com/go-secure-stdlib/plugincontainer@v0.3.0 which exposes a new `Config.Rootless` option to opt in to extra container configuration options that allow establishing communication with a non-root plugin within a rootless container runtime. * Adds a new "rootless" option for plugin runtimes, so Vault needs to be explicitly told whether the container runtime on the machine is rootless or not. It defaults to false as rootless installs are not the default. * Updates `run_config.go` to use the new option when the plugin runtime is rootless. * Adds new `-rootless` flag to `vault plugin runtime register`, and `rootless` API option to the register API. * Adds rootless Docker installation to CI to support tests for the new functionality. * Minor test refactor to minimise the number of test Vault cores that need to be made for the external plugin container tests. * Documentation for the new rootless configuration and the new (reduced) set of restrictions for plugin containers. * As well as adding rootless support, we've decided to drop explicit support for podman for now, but there's no barrier other than support burden to adding it back again in future so it will depend on demand. --- .github/workflows/test-go.yml | 32 +++- api/sys_plugins_runtimes.go | 1 + changelog/24236.txt | 3 + command/plugin_runtime_register.go | 11 ++ command/plugin_runtime_register_test.go | 8 +- go.mod | 5 +- go.sum | 16 +- sdk/go.mod | 5 +- sdk/go.sum | 11 +- sdk/helper/pluginruntimeutil/config.go | 1 + sdk/helper/pluginutil/run_config.go | 6 +- vault/external_plugin_container_test.go | 140 +++++++++--------- vault/external_plugin_test.go | 8 +- .../plugin/external_plugin_test.go | 24 +-- vault/logical_system.go | 8 + vault/logical_system_paths.go | 9 ++ vault/logical_system_test.go | 3 + vault/testdata/Dockerfile | 4 + vault/testing.go | 38 ++--- .../system/plugins-runtimes-catalog.mdx | 4 + .../docs/commands/plugin/runtime/register.mdx | 4 + .../docs/plugins/containerized-plugins.mdx | 38 ++--- 22 files changed, 236 insertions(+), 143 deletions(-) create mode 100644 changelog/24236.txt diff --git a/.github/workflows/test-go.yml b/.github/workflows/test-go.yml index 2b498be4d011..24825b5272b6 100644 --- a/.github/workflows/test-go.yml +++ b/.github/workflows/test-go.yml @@ -286,14 +286,37 @@ jobs: "runsc": { "path": "/usr/local/bin/runsc", "runtimeArgs": [ - "--host-uds=all", - "--host-fifo=open" + "--host-uds=create" ] } } } EOF sudo systemctl reload docker + - name: Install rootless Docker + # Enterprise repo runners do not allow sudo, so can't system packages there yet. + if: ${{ !inputs.enterprise }} + run: | + sudo apt-get install -y uidmap dbus-user-session + export FORCE_ROOTLESS_INSTALL=1 + curl -fsSL https://get.docker.com/rootless | sh + mkdir -p ~/.config/docker/ + tee ~/.config/docker/daemon.json <)` – Part of the request URL. Specifies the plugin runtime name. Use the runtime name to look up plugin runtimes in the catalog. +- `rootless` `(bool: false)` - Whether the container runtime is running as a + non-privileged user. Must be set if plugin container images are also configured + to run as a non-root user. + - `oci_runtime` `(string: )` – Specifies OCI-compliant container runtime to use. Default is "runsc", gVisor's OCI runtime. diff --git a/website/content/docs/commands/plugin/runtime/register.mdx b/website/content/docs/commands/plugin/runtime/register.mdx index 698ee318db6f..75119d4e7268 100644 --- a/website/content/docs/commands/plugin/runtime/register.mdx +++ b/website/content/docs/commands/plugin/runtime/register.mdx @@ -45,6 +45,10 @@ flags](/vault/docs/commands) included on all commands. - `-type` `(string: )` - Plugin runtime type. Vault currently only supports `container` as a runtime type. +- `-rootless` `(bool: false)` - Whether the container runtime is running as a + non-privileged user. Must be set if plugin container images are also configured + to run as a non-root user. + - `-cgroup_parent` `(string: "")` - Parent cgroup to set for each container. Use `cgroup_parent` to control the total resource usage for a group of plugins. diff --git a/website/content/docs/plugins/containerized-plugins.mdx b/website/content/docs/plugins/containerized-plugins.mdx index 1bd4cb2de797..748d6d680923 100644 --- a/website/content/docs/plugins/containerized-plugins.mdx +++ b/website/content/docs/plugins/containerized-plugins.mdx @@ -39,7 +39,7 @@ increases the isolation between plugins, and between plugins and Vault. All plugins have the following basic requirements to be containerized: -- **Your plugin must be built with at least v1.5.0 of the HashiCorp +- **Your plugin must be built with at least v1.6.0 of the HashiCorp [`go-plugin`](https://github.com/hashicorp/go-plugin) library**. - **The image entrypoint should run the plugin binary**. @@ -52,39 +52,39 @@ in [supported configurations](#supported-configurations). Vault's containerized plugins are compatible with a variety of configurations. In particular, it has been tested with the following: -- Docker and Podman. -- Default and rootless container engine. -- OCI runtimes runsc and runc. -- Plugin container images with root and non-root users. +- Default and [rootless](https://docs.docker.com/engine/security/rootless/) Docker. +- OCI-compatible runtimes `runsc` and `runc`. +- Plugin container images running as root and non-root users. - [Mlock](/vault/docs/configuration#disable_mlock) disabled or enabled. Not all combinations work and some have additional requirements, listed below. If you use a configuration that matches multiple headings, you should combine the requirements from each matching heading. -### Rootless installation with non-root container user +### `runsc` runtime -Not currently supported. We are hoping to provide support in future. +- You must pass an additional `--host-uds=create` flag to the `runsc` runtime. -### runsc runtime +### Rootless Docker with `runsc` runtime -- You must pass an additional `--host-uds=all` flag to the `runsc` runtime. +- You must pass an additional `--ignore-cgroups` flag to the `runsc` runtime. + - Cgroup limits are not currently supported for this configuration. -### Rootless installation with `runsc` +### Rootless Docker with non-root container user -- Does not currently support cgroup limits. -- You must pass an additional `--ignore-cgroups` flag to the `runsc` runtime. +- You must use a container plugin runtime with + [`rootless`](/vault/docs/commands/plugin/runtime/register#rootless) enabled. +- Your filesystem must have Posix 1e ACL support, available by default in most + modern Linux file systems. +- Only supported for gVisor's `runsc` runtime. -### Non-root container user with mlock enabled +### Rootless Docker with mlock enabled -- You must set the IPC_LOCK capability on the plugin binary. +- Only supported for gVisor's `runsc` runtime. -### Rootless container engine with mlock enabled +### Non-root container user with mlock enabled -- You must set the IPC_LOCK capability on the container engine's binary. -- You do not need to set the IPC_LOCK capability if running with Docker and runsc. - The `runsc` runtime supports mlock syscalls in rootless Docker without needing - IPC_LOCK itself. +- You must set the `IPC_LOCK` capability on the plugin binary. ## Container lifecycle and metadata From 51d99fc7cfeafe7dd95e6c1c633d81c1f05c42f3 Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Tue, 28 Nov 2023 14:13:26 +0000 Subject: [PATCH 27/74] cli: Improve error handling for plugin commands (#24250) * Stop supporting vault plugin info and deregister without a type argument * Make a best-effort attempt to report whether a plugin was actually deregistered and give more descriptive errors * Fix error message for vault plugin reload --- changelog/24250.txt | 6 ++++ command/plugin_deregister.go | 48 +++++++++++++++++++++++++------ command/plugin_deregister_test.go | 31 +++++++++++++++++--- command/plugin_info.go | 20 ++++++------- command/plugin_info_test.go | 6 ++++ command/plugin_reload.go | 8 ++++-- command/plugin_reload_test.go | 6 ++-- 7 files changed, 95 insertions(+), 30 deletions(-) create mode 100644 changelog/24250.txt diff --git a/changelog/24250.txt b/changelog/24250.txt new file mode 100644 index 000000000000..e6aca7096ac3 --- /dev/null +++ b/changelog/24250.txt @@ -0,0 +1,6 @@ +```release-note:change +cli: `vault plugin info` and `vault plugin deregister` now require 2 positional arguments instead of accepting either 1 or 2. +``` +```release-note:improvement +cli: Improved error messages for `vault plugin` sub-commands. +``` diff --git a/command/plugin_deregister.go b/command/plugin_deregister.go index 4b9de504db84..5355a951576d 100644 --- a/command/plugin_deregister.go +++ b/command/plugin_deregister.go @@ -4,7 +4,9 @@ package command import ( + "context" "fmt" + "net/http" "strings" semver "github.com/hashicorp/go-version" @@ -83,18 +85,16 @@ func (c *PluginDeregisterCommand) Run(args []string) int { var pluginNameRaw, pluginTypeRaw string args = f.Args() - switch len(args) { - case 0: - c.UI.Error("Not enough arguments (expected 1, or 2, got 0)") + positionalArgsCount := len(args) + switch positionalArgsCount { + case 0, 1: + c.UI.Error(fmt.Sprintf("Not enough arguments (expected 2, got %d)", positionalArgsCount)) return 1 - case 1: - pluginTypeRaw = "unknown" - pluginNameRaw = args[0] case 2: pluginTypeRaw = args[0] pluginNameRaw = args[1] default: - c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, or 2, got %d)", len(args))) + c.UI.Error(fmt.Sprintf("Too many arguments (expected 2, got %d)", positionalArgsCount)) return 1 } @@ -118,7 +118,33 @@ func (c *PluginDeregisterCommand) Run(args []string) int { } } - if err := client.Sys().DeregisterPlugin(&api.DeregisterPluginInput{ + // The deregister endpoint returns 200 if the plugin doesn't exist, so first + // try fetching the plugin to help improve info printed to the user. + // 404 => Return early with a descriptive message. + // Other error => Continue attempting to deregister the plugin anyway. + // Plugin exists but is builtin => Error early. + // Otherwise => If deregister succeeds, we can report that the plugin really + // was deregistered (and not just already absent). + var pluginExists bool + if info, err := client.Sys().GetPluginWithContext(context.Background(), &api.GetPluginInput{ + Name: pluginName, + Type: pluginType, + Version: c.flagPluginVersion, + }); err != nil { + if respErr, ok := err.(*api.ResponseError); ok && respErr.StatusCode == http.StatusNotFound { + c.UI.Output(fmt.Sprintf("Plugin %q (type: %q, version %q) does not exist in the catalog", pluginName, pluginType, c.flagPluginVersion)) + return 0 + } + // Best-effort check, continue trying to deregister. + } else if info != nil { + if info.Builtin { + c.UI.Error(fmt.Sprintf("Plugin %q (type: %q) is a builtin plugin and cannot be deregistered", pluginName, pluginType)) + return 2 + } + pluginExists = true + } + + if err := client.Sys().DeregisterPluginWithContext(context.Background(), &api.DeregisterPluginInput{ Name: pluginName, Type: pluginType, Version: c.flagPluginVersion, @@ -127,6 +153,10 @@ func (c *PluginDeregisterCommand) Run(args []string) int { return 2 } - c.UI.Output(fmt.Sprintf("Success! Deregistered plugin (if it was registered): %s", pluginName)) + if pluginExists { + c.UI.Output(fmt.Sprintf("Success! Deregistered %s plugin: %s", pluginType, pluginName)) + } else { + c.UI.Output(fmt.Sprintf("Success! Deregistered %s plugin (if it was registered): %s", pluginType, pluginName)) + } return 0 } diff --git a/command/plugin_deregister_test.go b/command/plugin_deregister_test.go index f23c8b6c433c..b5bf4f7be92c 100644 --- a/command/plugin_deregister_test.go +++ b/command/plugin_deregister_test.go @@ -35,7 +35,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) { }{ { "not_enough_args", - nil, + []string{"foo"}, "Not enough arguments", 1, }, @@ -109,7 +109,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) { t.Errorf("expected %d to be %d", code, exp) } - expected := "Success! Deregistered plugin (if it was registered): " + expected := "Success! Deregistered auth plugin: " combined := ui.OutputWriter.String() + ui.ErrorWriter.String() if !strings.Contains(combined, expected) { t.Errorf("expected %q to contain %q", combined, expected) @@ -159,7 +159,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) { t.Errorf("expected %d to be %d", code, exp) } - expected := "Success! Deregistered plugin (if it was registered): " + expected := "Success! Deregistered auth plugin: " combined := ui.OutputWriter.String() + ui.ErrorWriter.String() if !strings.Contains(combined, expected) { t.Errorf("expected %q to contain %q", combined, expected) @@ -206,7 +206,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) { t.Errorf("expected %d to be %d", code, exp) } - expected := "Success! Deregistered plugin (if it was registered): " + expected := "does not exist in the catalog" combined := ui.OutputWriter.String() + ui.ErrorWriter.String() if !strings.Contains(combined, expected) { t.Errorf("expected %q to contain %q", combined, expected) @@ -230,6 +230,29 @@ func TestPluginDeregisterCommand_Run(t *testing.T) { } }) + t.Run("deregister builtin", func(t *testing.T) { + t.Parallel() + + pluginDir, cleanup := corehelpers.MakeTestPluginDir(t) + defer cleanup(t) + + client, _, closer := testVaultServerPluginDir(t, pluginDir) + defer closer() + + ui, cmd := testPluginDeregisterCommand(t) + cmd.client = client + + expected := "is a builtin plugin" + if code := cmd.Run([]string{ + consts.PluginTypeCredential.String(), + "github", + }); code != 2 { + t.Errorf("expected %d to be %d", code, 2) + } else if !strings.Contains(ui.ErrorWriter.String(), expected) { + t.Errorf("expected %q to contain %q", ui.ErrorWriter.String(), expected) + } + }) + t.Run("communication_failure", func(t *testing.T) { t.Parallel() diff --git a/command/plugin_info.go b/command/plugin_info.go index b1cf7acb04a2..0211555027e2 100644 --- a/command/plugin_info.go +++ b/command/plugin_info.go @@ -77,23 +77,19 @@ func (c *PluginInfoCommand) Run(args []string) int { var pluginNameRaw, pluginTypeRaw string args = f.Args() + positionalArgsCount := len(args) switch { - case len(args) < 1: - c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1 or 2, got %d)", len(args))) + case positionalArgsCount < 2: + c.UI.Error(fmt.Sprintf("Not enough arguments (expected 2, got %d)", positionalArgsCount)) return 1 - case len(args) > 2: - c.UI.Error(fmt.Sprintf("Too many arguments (expected 1 or 2, got %d)", len(args))) + case positionalArgsCount > 2: + c.UI.Error(fmt.Sprintf("Too many arguments (expected 2, got %d)", positionalArgsCount)) return 1 - - // These cases should come after invalid cases have been checked - case len(args) == 1: - pluginTypeRaw = "unknown" - pluginNameRaw = args[0] - case len(args) == 2: - pluginTypeRaw = args[0] - pluginNameRaw = args[1] } + pluginTypeRaw = args[0] + pluginNameRaw = args[1] + client, err := c.Client() if err != nil { c.UI.Error(err.Error()) diff --git a/command/plugin_info_test.go b/command/plugin_info_test.go index 367124301671..3ffaa6972010 100644 --- a/command/plugin_info_test.go +++ b/command/plugin_info_test.go @@ -34,6 +34,12 @@ func TestPluginInfoCommand_Run(t *testing.T) { out string code int }{ + { + "not_enough_args", + []string{"foo"}, + "Not enough arguments", + 1, + }, { "too_many_args", []string{"foo", "bar", "fizz"}, diff --git a/command/plugin_reload.go b/command/plugin_reload.go index 4ec7acb99c83..22cd91275d16 100644 --- a/command/plugin_reload.go +++ b/command/plugin_reload.go @@ -90,12 +90,16 @@ func (c *PluginReloadCommand) Run(args []string) int { return 1 } + positionalArgs := len(f.Args()) switch { + case positionalArgs != 0: + c.UI.Error(fmt.Sprintf("Too many arguments (expected 0, got %d)", positionalArgs)) + return 1 case c.plugin == "" && len(c.mounts) == 0: - c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args))) + c.UI.Error("No plugins specified, must specify exactly one of -plugin or -mounts") return 1 case c.plugin != "" && len(c.mounts) > 0: - c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) + c.UI.Error("Must specify exactly one of -plugin or -mounts") return 1 case c.scope != "" && c.scope != "global": c.UI.Error(fmt.Sprintf("Invalid reload scope: %s", c.scope)) diff --git a/command/plugin_reload_test.go b/command/plugin_reload_test.go index cf9f2d149c54..7ae082ed840b 100644 --- a/command/plugin_reload_test.go +++ b/command/plugin_reload_test.go @@ -46,13 +46,13 @@ func TestPluginReloadCommand_Run(t *testing.T) { { "not_enough_args", nil, - "Not enough arguments", + "No plugins specified, must specify exactly one of -plugin or -mounts", 1, }, { "too_many_args", []string{"-plugin", "foo", "-mounts", "bar"}, - "Too many arguments", + "Must specify exactly one of -plugin or -mounts", 1, }, } @@ -147,7 +147,7 @@ func TestPluginReloadStatusCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - ui, cmd := testPluginReloadCommand(t) + ui, cmd := testPluginReloadStatusCommand(t) cmd.client = client args := append([]string{}, tc.args...) From 8f064b90ecd27587f6cd8c0f263ccd398db4b093 Mon Sep 17 00:00:00 2001 From: Kuba Wieczorek Date: Tue, 28 Nov 2023 14:22:33 +0000 Subject: [PATCH 28/74] [VAULT-22270] API: add enterprise field to the response from /sys/health/ endpoint (#24270) --- changelog/24270.txt | 3 +++ http/sys_health.go | 3 +++ http/sys_health_test.go | 7 +++++++ 3 files changed, 13 insertions(+) create mode 100644 changelog/24270.txt diff --git a/changelog/24270.txt b/changelog/24270.txt new file mode 100644 index 000000000000..eb8e4c04fb7c --- /dev/null +++ b/changelog/24270.txt @@ -0,0 +1,3 @@ +```release-note:change +api: add the `enterprise` parameter to the `/sys/health` endpoint +``` diff --git a/http/sys_health.go b/http/sys_health.go index d0c4362a8ab6..b5e92961d150 100644 --- a/http/sys_health.go +++ b/http/sys_health.go @@ -12,6 +12,7 @@ import ( "time" "github.com/hashicorp/go-secure-stdlib/parseutil" + "github.com/hashicorp/vault/helper/constants" "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/vault" "github.com/hashicorp/vault/version" @@ -204,6 +205,7 @@ func getSysHealth(core *vault.Core, r *http.Request) (int, *HealthResponse, erro ReplicationDRMode: replicationState.GetDRString(), ServerTimeUTC: time.Now().UTC().Unix(), Version: version.GetVersion().VersionNumber(), + Enterprise: constants.IsEnterprise, ClusterName: clusterName, ClusterID: clusterID, } @@ -245,6 +247,7 @@ type HealthResponse struct { ReplicationDRMode string `json:"replication_dr_mode"` ServerTimeUTC int64 `json:"server_time_utc"` Version string `json:"version"` + Enterprise bool `json:"enterprise"` ClusterName string `json:"cluster_name,omitempty"` ClusterID string `json:"cluster_id,omitempty"` LastWAL uint64 `json:"last_wal,omitempty"` diff --git a/http/sys_health_test.go b/http/sys_health_test.go index 48caa6e1ca41..83ec63db0010 100644 --- a/http/sys_health_test.go +++ b/http/sys_health_test.go @@ -10,6 +10,7 @@ import ( "reflect" "testing" + "github.com/hashicorp/vault/helper/constants" "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/vault" ) @@ -26,6 +27,7 @@ func TestSysHealth_get(t *testing.T) { var actual map[string]interface{} expected := map[string]interface{}{ + "enterprise": constants.IsEnterprise, "replication_performance_mode": consts.ReplicationUnknown.GetPerformanceString(), "replication_dr_mode": consts.ReplicationUnknown.GetDRString(), "initialized": false, @@ -60,6 +62,7 @@ func TestSysHealth_get(t *testing.T) { actual = map[string]interface{}{} expected = map[string]interface{}{ + "enterprise": constants.IsEnterprise, "replication_performance_mode": consts.ReplicationUnknown.GetPerformanceString(), "replication_dr_mode": consts.ReplicationUnknown.GetDRString(), "initialized": true, @@ -98,6 +101,7 @@ func TestSysHealth_get(t *testing.T) { actual = map[string]interface{}{} expected = map[string]interface{}{ + "enterprise": constants.IsEnterprise, "replication_performance_mode": consts.ReplicationPerformanceDisabled.GetPerformanceString(), "replication_dr_mode": consts.ReplicationDRDisabled.GetDRString(), "initialized": true, @@ -141,6 +145,7 @@ func TestSysHealth_customcodes(t *testing.T) { var actual map[string]interface{} expected := map[string]interface{}{ + "enterprise": constants.IsEnterprise, "replication_performance_mode": consts.ReplicationUnknown.GetPerformanceString(), "replication_dr_mode": consts.ReplicationUnknown.GetDRString(), "initialized": false, @@ -176,6 +181,7 @@ func TestSysHealth_customcodes(t *testing.T) { actual = map[string]interface{}{} expected = map[string]interface{}{ + "enterprise": constants.IsEnterprise, "replication_performance_mode": consts.ReplicationUnknown.GetPerformanceString(), "replication_dr_mode": consts.ReplicationUnknown.GetDRString(), "initialized": true, @@ -215,6 +221,7 @@ func TestSysHealth_customcodes(t *testing.T) { actual = map[string]interface{}{} expected = map[string]interface{}{ + "enterprise": constants.IsEnterprise, "replication_performance_mode": consts.ReplicationPerformanceDisabled.GetPerformanceString(), "replication_dr_mode": consts.ReplicationDRDisabled.GetDRString(), "initialized": true, From 2e54ae0d6155fac13d9596ee1a8a5d30a159c933 Mon Sep 17 00:00:00 2001 From: Victor Rodriguez Date: Tue, 28 Nov 2023 09:56:39 -0500 Subject: [PATCH 29/74] Check that multi-seal wrappers provide unique key IDs (#24266) * Remove duplicate function NewToggleableTestSeal. NewToggleableTestSeal is almost the same as NewTestSeal, so remove it and adapt the callers to use the duplicated function. * Remove unnecessary function CreateTestSealWrappers. The only caller of CreateTestSealWrappers can use NewTestSeal instead and obtain the wrappers from the seal Access object instead. * Ensure NewTestSeal does not generate "duplicate" wrappers. NewTestSeal uses TestWrappers to create multi-seal Access objects. However, the default behaviour for TestWrapper is to reverse the byte slice, which means that two different wrappers will be identical, which is a problem for testing since one wrapper will be able do "decrypt" another wrapper's encryption. To fix this problem, NewTestSeal now creates TestWrappers with a different secret for each one. * Make NewTestSeal give unique Key IDs to its test wrappers. * Fix some typos. * Detect multi-seal wrappers producing duplicate Key IDs. The Access object relies on all the encryption wrappers generating distinct key IDs, so guard against this happening. If a duplicate key ID is detected, do not use the encrypted value produced by the wrappers that generated it. Return an error instead. --- vault/external_tests/raft/raft_test.go | 4 +- vault/seal/seal.go | 44 ++++++++-- vault/seal/seal_test.go | 47 +++++++++++ vault/seal/seal_testing.go | 106 ++++++++----------------- vault/seal_autoseal_test.go | 10 +-- vault/seal_testing_util.go | 26 ++++++ 6 files changed, 150 insertions(+), 87 deletions(-) diff --git a/vault/external_tests/raft/raft_test.go b/vault/external_tests/raft/raft_test.go index efe209c44e89..73eab754bfb7 100644 --- a/vault/external_tests/raft/raft_test.go +++ b/vault/external_tests/raft/raft_test.go @@ -514,7 +514,7 @@ func TestRaft_SnapshotAPI_MidstreamFailure(t *testing.T) { // defer goleak.VerifyNone(t) t.Parallel() - seal, setErr := vaultseal.NewToggleableTestSeal(nil) + seal, wrappers := vaultseal.NewTestSeal(nil) autoSeal := vault.NewAutoSeal(seal) cluster, _ := raftCluster(t, &RaftClusterOpts{ NumCores: 1, @@ -547,7 +547,7 @@ func TestRaft_SnapshotAPI_MidstreamFailure(t *testing.T) { wg.Done() }() - setErr[0](errors.New("seal failure")) + wrappers[0].SetError(errors.New("seal failure")) // Take a snapshot err := leaderClient.Sys().RaftSnapshot(w) w.Close() diff --git a/vault/seal/seal.go b/vault/seal/seal.go index 3786f37c866f..cd49f052edc4 100644 --- a/vault/seal/seal.go +++ b/vault/seal/seal.go @@ -203,13 +203,13 @@ func haveCommonSeal(existingSealKmsConfigs, newSealKmsConfigs []*configutil.KMS) } func findRenamedDisabledSeals(configs []*configutil.KMS) []*configutil.KMS { - diabledSeals := []*configutil.KMS{} + disabledSeals := []*configutil.KMS{} for _, seal := range configs { if seal.Disabled && strings.HasSuffix(seal.Name, configutil.KmsRenameDisabledSuffix) { - diabledSeals = append(diabledSeals, seal) + disabledSeals = append(disabledSeals, seal) } } - return diabledSeals + return disabledSeals } func compareKMSConfigByNameAndType() cmp.Option { @@ -468,7 +468,10 @@ func (a *access) Init(ctx context.Context, options ...wrapping.Option) error { a.logger.Warn("cannot determine key ID for seal", "seal", sealWrapper.Name, "err", err) return fmt.Errorf("cannod determine key ID for seal %s: %w", sealWrapper.Name, err) } - keyIds = append(keyIds, keyId) + if keyId != "" { + // Some wrappers may not yet know their key id. For emample, see gcpkms.Wrapper. + keyIds = append(keyIds, keyId) + } } } a.keyIdSet.setIds(keyIds) @@ -477,7 +480,7 @@ func (a *access) Init(ctx context.Context, options ...wrapping.Option) error { func (a *access) IsUpToDate(ctx context.Context, value *MultiWrapValue, forceKeyIdRefresh bool) (bool, error) { // Note that we don't compare generations when the value is transitory, since all single-blobInfo - // values are unmarshalled as transitory values. + // values (i.e. not yet upgraded to MultiWrapValues) are unmarshalled as transitory values. if value.Generation != 0 && value.Generation != a.Generation() { return false, nil } @@ -558,6 +561,34 @@ GATHER_RESULTS: } } + { + // Check for duplicate Key IDs. + // If any wrappers produce duplicated IDs, their BlobInfo will be replaced by an error. + + keyIdToSealWrapperNameMap := make(map[string]string) + for _, sealWrapper := range enabledWrappersByPriority { + wrapperName := sealWrapper.Name + if result, ok := results[wrapperName]; ok { + if result.err != nil { + continue + } + if result.ciphertext.KeyInfo == nil { + // Can this really happen? Probably not? + continue + } + keyId := result.ciphertext.KeyInfo.KeyId + duplicateWrapperName, isDuplicate := keyIdToSealWrapperNameMap[keyId] + if isDuplicate { + for _, name := range []string{wrapperName, duplicateWrapperName} { + results[name].err = fmt.Errorf("seal %s has returned duplicate key ID %s, key IDs must be unique", name, keyId) + results[name].ciphertext = nil + } + } + keyIdToSealWrapperNameMap[keyId] = wrapperName + } + } + } + // Sort out the successful results from the errors var slots []*wrapping.BlobInfo errs := make(map[string]error) @@ -587,6 +618,7 @@ GATHER_RESULTS: a.logger.Trace("successfully encrypted value", "encryption seal wrappers", len(slots), "total enabled seal wrappers", len(a.GetEnabledSealWrappersByPriority())) + ret := &MultiWrapValue{ Generation: a.Generation(), Slots: slots, @@ -748,7 +780,7 @@ GATHER_RESULTS: return nil, false, errors.New("context timeout exceeded") } -// tryDecrypt returns the plaintext and a flad indicating whether the decryption was done by the "unwrapSeal" (see +// tryDecrypt returns the plaintext and a flag indicating whether the decryption was done by the "unwrapSeal" (see // sealWrapMigration.Decrypt). func (a *access) tryDecrypt(ctx context.Context, sealWrapper *SealWrapper, ciphertextByKeyId map[string]*wrapping.BlobInfo, options []wrapping.Option) ([]byte, bool, error) { now := time.Now() diff --git a/vault/seal/seal_test.go b/vault/seal/seal_test.go index 07abe465dba1..c5bc5224136a 100644 --- a/vault/seal/seal_test.go +++ b/vault/seal/seal_test.go @@ -4,6 +4,9 @@ package seal import ( + "context" + "fmt" + "github.com/stretchr/testify/require" "testing" wrapping "github.com/hashicorp/go-kms-wrapping/v2" @@ -95,3 +98,47 @@ func Test_keyIdSet(t *testing.T) { runTest(tt.name+".setIDs", useSetIds) } } + +// Test_Encrypt_duplicate_keyIds verifies that if two seal wrappers produce the same Key ID, an error +// will be returned for both. +func Test_Encrypt_duplicate_keyIds(t *testing.T) { + ctx := context.Background() + + setId := func(w *SealWrapper, keyId string) { + testWrapper := w.Wrapper.(*ToggleableWrapper).Wrapper.(*wrapping.TestWrapper) + testWrapper.SetKeyId(keyId) + } + + getId := func(w *SealWrapper) string { + id, err := w.Wrapper.KeyId(ctx) + if err != nil { + t.Fatal(err) + } + return id + } + + access, _ := NewTestSeal(&TestSealOpts{WrapperCount: 3}) + + // Set up - make the key IDs the same for the last two wrappers + wrappers := access.GetAllSealWrappersByPriority() + setId(wrappers[1], "this-key-is-duplicated") + setId(wrappers[2], "this-key-is-duplicated") + + // Some sanity checks + require.NotEqual(t, wrappers[0].Name, wrappers[1].Name) + require.NotEqual(t, wrappers[1].Name, wrappers[2].Name) + require.NotEqual(t, getId(wrappers[0]), getId(wrappers[1])) + require.Equal(t, getId(wrappers[1]), getId(wrappers[2])) + + // Encrypt a value + mwv, errorMap := access.Encrypt(ctx, []byte("Rinconete y Cortadillo")) + + // Assertions + require.NotNilf(t, mwv, "seal 0 should have succeeded") + + requireDuplicateErr := func(w *SealWrapper) { + require.ErrorContains(t, errorMap[w.Name], fmt.Sprintf("seal %v has returned duplicate key ID", w.Name)) + } + requireDuplicateErr(wrappers[1]) + requireDuplicateErr(wrappers[2]) +} diff --git a/vault/seal/seal_testing.go b/vault/seal/seal_testing.go index 56a7bcb46cfb..f376564c2053 100644 --- a/vault/seal/seal_testing.go +++ b/vault/seal/seal_testing.go @@ -6,6 +6,7 @@ package seal import ( "context" "fmt" + UUID "github.com/hashicorp/go-uuid" "sync" "github.com/hashicorp/vault/sdk/helper/logging" @@ -17,7 +18,7 @@ import ( type TestSealOpts struct { Logger hclog.Logger StoredKeys StoredKeysSupport - Secret []byte + Secrets [][]byte Name wrapping.WrapperType WrapperCount int Generation uint64 @@ -37,6 +38,29 @@ func NewTestSealOpts(opts *TestSealOpts) *TestSealOpts { // we might at some point need to allow Generation == 0 opts.Generation = 1 } + switch len(opts.Secrets) { + case opts.WrapperCount: + // all good, each wrapper has its own secret + + case 0: + if opts.WrapperCount == 1 { + // If there is only one wrapper, the default TestWrapper behaviour of reversing + // the bytes slice is fine. + opts.Secrets = [][]byte{nil} + } else { + // If there is more than one wrapper, each one needs a different secret + for i := 0; i < opts.WrapperCount; i++ { + uuid, err := UUID.GenerateUUID() + if err != nil { + panic(fmt.Sprintf("error generating secret: %v", err)) + } + opts.Secrets = append(opts.Secrets, []byte(uuid)) + } + } + + default: + panic(fmt.Sprintf("wrong number of secrets %d vs %d wrappers", len(opts.Secrets), opts.WrapperCount)) + } return opts } @@ -46,7 +70,12 @@ func NewTestSeal(opts *TestSealOpts) (Access, []*ToggleableWrapper) { sealWrappers := make([]*SealWrapper, opts.WrapperCount) ctx := context.Background() for i := 0; i < opts.WrapperCount; i++ { - wrappers[i] = &ToggleableWrapper{Wrapper: wrapping.NewTestWrapper(opts.Secret)} + wrapperName := fmt.Sprintf("%s-%d", opts.Name, i+1) + wrappers[i] = &ToggleableWrapper{Wrapper: wrapping.NewTestWrapper(opts.Secrets[i])} + _, err := wrappers[i].Wrapper.SetConfig(context.Background(), wrapping.WithKeyId(wrapperName)) + if err != nil { + panic(err) + } wrapperType, err := wrappers[i].Type(ctx) if err != nil { panic(err) @@ -54,7 +83,7 @@ func NewTestSeal(opts *TestSealOpts) (Access, []*ToggleableWrapper) { sealWrappers[i] = NewSealWrapper( wrappers[i], i+1, - fmt.Sprintf("%s-%d", opts.Name, i+1), + wrapperName, wrapperType.String(), false, true, @@ -75,77 +104,6 @@ type TestSealWrapperOpts struct { WrapperCount int } -func CreateTestSealWrapperOpts(opts *TestSealWrapperOpts) *TestSealWrapperOpts { - if opts == nil { - opts = new(TestSealWrapperOpts) - } - if opts.WrapperCount == 0 { - opts.WrapperCount = 1 - } - if opts.Logger == nil { - opts.Logger = logging.NewVaultLogger(hclog.Debug) - } - return opts -} - -func CreateTestSealWrappers(opts *TestSealWrapperOpts) []*SealWrapper { - opts = CreateTestSealWrapperOpts(opts) - wrappers := make([]*ToggleableWrapper, opts.WrapperCount) - sealWrappers := make([]*SealWrapper, opts.WrapperCount) - ctx := context.Background() - for i := 0; i < opts.WrapperCount; i++ { - wrappers[i] = &ToggleableWrapper{Wrapper: wrapping.NewTestWrapper(opts.Secret)} - wrapperType, err := wrappers[i].Type(ctx) - if err != nil { - panic(err) - } - sealWrappers[i] = NewSealWrapper( - wrappers[i], - i+1, - fmt.Sprintf("%s-%d", opts.Name, i+1), - wrapperType.String(), - false, - true, - ) - } - - return sealWrappers -} - -func NewToggleableTestSeal(opts *TestSealOpts) (Access, []func(error)) { - opts = NewTestSealOpts(opts) - - wrappers := make([]*ToggleableWrapper, opts.WrapperCount) - sealWrappers := make([]*SealWrapper, opts.WrapperCount) - funcs := make([]func(error), opts.WrapperCount) - ctx := context.Background() - for i := 0; i < opts.WrapperCount; i++ { - w := &ToggleableWrapper{Wrapper: wrapping.NewTestWrapper(opts.Secret)} - wrapperType, err := w.Type(ctx) - if err != nil { - panic(err) - } - - wrappers[i] = w - sealWrappers[i] = NewSealWrapper( - wrappers[i], - i+1, - fmt.Sprintf("%s-%d", opts.Name, i+1), - wrapperType.String(), - false, - true, - ) - funcs[i] = w.SetError - } - - sealAccess, err := NewAccessFromSealWrappers(nil, opts.Generation, true, sealWrappers) - if err != nil { - panic(err) - } - - return sealAccess, funcs -} - type ToggleableWrapper struct { wrapping.Wrapper wrapperType *wrapping.WrapperType diff --git a/vault/seal_autoseal_test.go b/vault/seal_autoseal_test.go index d4f52ecc182e..e070471a35e3 100644 --- a/vault/seal_autoseal_test.go +++ b/vault/seal_autoseal_test.go @@ -183,7 +183,7 @@ func TestAutoSeal_HealthCheck(t *testing.T) { metrics.NewGlobal(metricsConf, inmemSink) pBackend := newTestBackend(t) - testSealAccess, setErrs := seal.NewToggleableTestSeal(&seal.TestSealOpts{Name: "health-test"}) + testSealAccess, wrappers := seal.NewTestSeal(&seal.TestSealOpts{Name: "health-test"}) core, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{ MetricSink: metricsutil.NewClusterMetricSink("", inmemSink), Physical: pBackend, @@ -195,7 +195,7 @@ func TestAutoSeal_HealthCheck(t *testing.T) { core.seal = autoSeal autoSeal.StartHealthCheck() defer autoSeal.StopHealthCheck() - setErrs[0](errors.New("disconnected")) + wrappers[0].SetError(errors.New("disconnected")) tries := 10 for tries = 10; tries > 0; tries-- { @@ -208,7 +208,7 @@ func TestAutoSeal_HealthCheck(t *testing.T) { t.Fatalf("Expected to detect unhealthy seals") } - setErrs[0](nil) + wrappers[0].SetError(nil) time.Sleep(50 * time.Millisecond) if !autoSeal.Healthy() { t.Fatal("Expected seals to be healthy") @@ -216,8 +216,8 @@ func TestAutoSeal_HealthCheck(t *testing.T) { } func TestAutoSeal_BarrierSealConfigType(t *testing.T) { - singleWrapperAccess, _ := seal.NewToggleableTestSeal(&seal.TestSealOpts{WrapperCount: 1}) - multipleWrapperAccess, _ := seal.NewToggleableTestSeal(&seal.TestSealOpts{WrapperCount: 2}) + singleWrapperAccess, _ := seal.NewTestSeal(&seal.TestSealOpts{WrapperCount: 1}) + multipleWrapperAccess, _ := seal.NewTestSeal(&seal.TestSealOpts{WrapperCount: 2}) require.Equalf(t, singleWrapperAccess.GetAllSealWrappersByPriority()[0].SealConfigType, NewAutoSeal(singleWrapperAccess).BarrierSealConfigType().String(), "autoseals that have a single seal wrapper report that wrapper's as the barrier seal type") diff --git a/vault/seal_testing_util.go b/vault/seal_testing_util.go index 6139ed25ebee..ea735b087505 100644 --- a/vault/seal_testing_util.go +++ b/vault/seal_testing_util.go @@ -10,6 +10,8 @@ import ( testing "github.com/mitchellh/go-testing-interface" ) +// NewTestSeal creates a new seal for testing. If you want to use the same seal multiple times, such as for +// a cluster, use NewTestSealFunc instead. func NewTestSeal(t testing.T, opts *seal.TestSealOpts) Seal { t.Helper() opts = seal.NewTestSealOpts(opts) @@ -50,3 +52,27 @@ func NewTestSeal(t testing.T, opts *seal.TestSealOpts) Seal { return NewAutoSeal(access) } } + +// NewTestSealFunc returns a function that creates seals. All such seals will have TestWrappers that +// share the same secret, thus making them equivalent. +func NewTestSealFunc(t testing.T, opts *seal.TestSealOpts) func() Seal { + testSeal := NewTestSeal(t, opts) + + return func() Seal { + return cloneTestSeal(t, testSeal) + } +} + +// CloneTestSeal creates a new test seal that shares the same seal wrappers as `testSeal`. +func cloneTestSeal(t testing.T, testSeal Seal) Seal { + logger := corehelpers.NewTestLogger(t).Named("sealAccess") + + access, err := seal.NewAccessFromSealWrappers(logger, testSeal.GetAccess().Generation(), testSeal.GetAccess().GetSealGenerationInfo().IsRewrapped(), testSeal.GetAccess().GetAllSealWrappersByPriority()) + if err != nil { + t.Fatal("error cloning seal %v", err) + } + if testSeal.StoredKeysSupported() == seal.StoredKeysNotSupported { + return NewDefaultSeal(access) + } + return NewAutoSeal(access) +} From 625cb00b61539cb265e79bce6d9e9c9cbb7de094 Mon Sep 17 00:00:00 2001 From: Victor Rodriguez Date: Tue, 28 Nov 2023 10:37:28 -0500 Subject: [PATCH 30/74] Run make fmt. (#24272) --- vault/seal/seal_test.go | 3 ++- vault/seal/seal_testing.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/vault/seal/seal_test.go b/vault/seal/seal_test.go index c5bc5224136a..75562431823a 100644 --- a/vault/seal/seal_test.go +++ b/vault/seal/seal_test.go @@ -6,9 +6,10 @@ package seal import ( "context" "fmt" - "github.com/stretchr/testify/require" "testing" + "github.com/stretchr/testify/require" + wrapping "github.com/hashicorp/go-kms-wrapping/v2" ) diff --git a/vault/seal/seal_testing.go b/vault/seal/seal_testing.go index f376564c2053..db3a77ee79bb 100644 --- a/vault/seal/seal_testing.go +++ b/vault/seal/seal_testing.go @@ -6,9 +6,10 @@ package seal import ( "context" "fmt" - UUID "github.com/hashicorp/go-uuid" "sync" + UUID "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/sdk/helper/logging" "github.com/hashicorp/go-hclog" From a823fdb3ef43cd2926a7f195e9890c16052fbf59 Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Tue, 28 Nov 2023 17:35:00 +0000 Subject: [PATCH 31/74] testfix: Skip runsc test earlier (#24274) --- vault/external_plugin_container_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vault/external_plugin_container_test.go b/vault/external_plugin_container_test.go index aeebd50e47f5..03aca4ca6d1a 100644 --- a/vault/external_plugin_container_test.go +++ b/vault/external_plugin_container_test.go @@ -74,12 +74,12 @@ func TestExternalPluginInContainer_MountAndUnmount(t *testing.T) { }) t.Run("rootless runsc", func(t *testing.T) { - t.Setenv("DOCKER_HOST", fmt.Sprintf("unix:///run/user/%d/docker.sock", os.Getuid())) - c, plugins := testClusterWithContainerPlugins(t, []consts.PluginType{consts.PluginTypeCredential}) - if _, err := exec.LookPath("runsc"); err != nil { t.Skip("Skipping test as runsc not found on path") } + + t.Setenv("DOCKER_HOST", fmt.Sprintf("unix:///run/user/%d/docker.sock", os.Getuid())) + c, plugins := testClusterWithContainerPlugins(t, []consts.PluginType{consts.PluginTypeCredential}) mountAndUnmountContainerPlugin_WithRuntime(t, c, plugins[0], "runsc", true) }) } From e9f7c5bcefddc33aa06ae8a055c7d58afbfa30be Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Tue, 28 Nov 2023 10:56:33 -0700 Subject: [PATCH 32/74] Fix failing LDAP test with new attribute (#24273) * add in new attribute to fix failing api test * fix replication test failures --- .../addon/templates/components/known-secondaries-card.hbs | 5 ++++- ui/tests/acceptance/enterprise-replication-test.js | 2 +- ui/tests/helpers/openapi/auth-model-attributes.js | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ui/lib/replication/addon/templates/components/known-secondaries-card.hbs b/ui/lib/replication/addon/templates/components/known-secondaries-card.hbs index b25504e3c41b..f190d43ff5e1 100644 --- a/ui/lib/replication/addon/templates/components/known-secondaries-card.hbs +++ b/ui/lib/replication/addon/templates/components/known-secondaries-card.hbs @@ -18,7 +18,10 @@ {{else}} - + diff --git a/ui/tests/acceptance/enterprise-replication-test.js b/ui/tests/acceptance/enterprise-replication-test.js index ea70e3b97639..9a89204d587f 100644 --- a/ui/tests/acceptance/enterprise-replication-test.js +++ b/ui/tests/acceptance/enterprise-replication-test.js @@ -123,7 +123,7 @@ module('Acceptance | Enterprise | replication', function (hooks) { await click('[data-test-replication-link="details"]'); assert - .dom(`[data-test-secondaries=row-for-${secondaryName}]`) + .dom(`[data-test-secondaries-node=${secondaryName}]`) .exists('shows a table row the recently added secondary'); // nav to DR diff --git a/ui/tests/helpers/openapi/auth-model-attributes.js b/ui/tests/helpers/openapi/auth-model-attributes.js index 50c86000057d..ec455509f736 100644 --- a/ui/tests/helpers/openapi/auth-model-attributes.js +++ b/ui/tests/helpers/openapi/auth-model-attributes.js @@ -822,6 +822,12 @@ const ldap = { fieldGroup: 'default', type: 'number', }, + passwordPolicy: { + editType: 'string', + fieldGroup: 'default', + helpText: 'Password policy to use to rotate the root password', + type: 'string', + }, requestTimeout: { editType: 'ttl', helpText: From 78d756acdb98b7c9c7bcd8c4c1a7792c85545365 Mon Sep 17 00:00:00 2001 From: Scott Miller Date: Tue, 28 Nov 2023 12:03:24 -0600 Subject: [PATCH 33/74] Provide a more reasonable error message for disabled Shamir seals (#24275) --- command/server.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/command/server.go b/command/server.go index 9cf3f73bdb3e..3f5b78964ea9 100644 --- a/command/server.go +++ b/command/server.go @@ -2572,6 +2572,8 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma for _, c := range config.Seals { if !c.Disabled { allSealsDisabled = false + } else if c.Type == vault.SealConfigTypeShamir.String() { + return nil, errors.New("shamir seals cannot be set disabled (they should simply not be set)") } } // If all seals are disabled assume they want to @@ -2722,9 +2724,6 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma return nil, errors.Join(sealConfigWarning, errors.New("no enabled Seals in configuration")) case configuredSeals == 0: return nil, errors.Join(sealConfigWarning, errors.New("no seals were successfully initialized")) - case containsShamir(enabledSealWrappers) && containsShamir(disabledSealWrappers): - return nil, errors.Join(sealConfigWarning, errors.New("shamir seals cannot be set disabled (they should simply not be set)")) - case len(enabledSealWrappers) == 1 && containsShamir(enabledSealWrappers): // The barrier seal is Shamir. If there are any disabled seals, then we put them all in the same // autoSeal. From ef14ae87a5f0086b15de1b22f50a7ec6949bd65a Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Tue, 28 Nov 2023 14:00:28 -0700 Subject: [PATCH 34/74] Fix KV "View secret" or "View list" HDS button styling (#24278) * wip * remove is-flex and put input on same different row * remove wide --- ui/lib/kv/addon/components/page/list.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/lib/kv/addon/components/page/list.hbs b/ui/lib/kv/addon/components/page/list.hbs index b0ce8be7dbe9..a7b5bb5d41fc 100644 --- a/ui/lib/kv/addon/components/page/list.hbs +++ b/ui/lib/kv/addon/components/page/list.hbs @@ -29,7 +29,7 @@ @cardTitle="View secret" @subText="Type the path of the secret you want to view. Include a trailing slash to navigate to the list view." > -
+ Date: Tue, 28 Nov 2023 16:52:21 -0500 Subject: [PATCH 35/74] Correct required policy in static secret caching docs (#24282) --- .../proxy/caching/static-secret-caching.mdx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/website/content/docs/agent-and-proxy/proxy/caching/static-secret-caching.mdx b/website/content/docs/agent-and-proxy/proxy/caching/static-secret-caching.mdx index 08636d477697..3a16a1a9b919 100644 --- a/website/content/docs/agent-and-proxy/proxy/caching/static-secret-caching.mdx +++ b/website/content/docs/agent-and-proxy/proxy/caching/static-secret-caching.mdx @@ -18,11 +18,19 @@ appropriate cache updates. 1. Enable [auto-auth](/vault/docs/agent-and-proxy/autoauth). 1. Create an auto-auth token with permission to subscribe to KV event updates with the [Vault event system](/vault/docs/concepts/events). For example, to -create a policy that grants access to static secret (KVv1 and KVv2) events: +create a policy that grants access to static secret (KVv1 and KVv2) events, +we need permission to subscribe to the `events` endpoint, as well as the +`list` and `subscribe` permissions on KV secrets we want to get secrets +from: ```hcl - path "sys/events/subscribe/kv*" { - capabilities = ["read"] - } + path "sys/events/subscribe/kv*" { + capabilities = ["read"] + } + + path "*" { + capabilities = ["list", "subscribe"] + subscribe_event_types = ["kv*"] + } ``` Subscribing to KV events means that Proxy receives updates as soon as a secret @@ -30,7 +38,6 @@ changes, which reduces staleness in the cache. Vault Proxy only checks for a secret update if an event notification indicates that the related secret was updated. - ## Step 2: Ensure tokens have `capabilities-self` access Tokens require `update` access to the @@ -76,7 +83,7 @@ same token can then access this secret from the cache instead of Vault. Vault Proxy uses the [event system](/vault/docs/concepts/events) to keep the cache up to date. It monitors the KV event feed for events related to any secret currently stored in the cache, including modification events like updates and -deletes. When Proxy detects a change in a cached secrete, it will update or +deletes. When Proxy detects a change in a cached secret, it will update or evict the cache entry as appropriate. Vault Proxy also checks and refreshes the access permissions of known tokens From 64dfff080a658890a4eb7924fd465e20d71c386a Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Wed, 29 Nov 2023 12:46:18 +0000 Subject: [PATCH 36/74] Fix non-JSON log messages when using `-log-format` JSON (#24252) * Fix non-JSON log messages when using -log-format JSON Removed the call to consul-template's logging.Setup inside the created of config for the Runner. Instead we call it when we assign the logger to the Agent command. * The elusive extra line * Adjust the approach * changelog * Infer levels *with* timestamp prefix * InferLeveslWithTimestamp required InferLevels * Test to show -log-format and -log-file working in consul-template generated messages * classic typo --------- Co-authored-by: Violet Hynes --- changelog/24252.txt | 3 + command/agent.go | 35 +++--- .../agent/internal/ctmanager/runner_config.go | 1 - command/agent_test.go | 104 +++++++++++++++++- 4 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 changelog/24252.txt diff --git a/changelog/24252.txt b/changelog/24252.txt new file mode 100644 index 000000000000..343811bfd050 --- /dev/null +++ b/changelog/24252.txt @@ -0,0 +1,3 @@ +```release-note:bug +agent/logging: Agent should now honor correct -log-format and -log-file settings in logs generated by the consul-template library. +``` \ No newline at end of file diff --git a/command/agent.go b/command/agent.go index 220679beb35b..b410da8c8a35 100644 --- a/command/agent.go +++ b/command/agent.go @@ -20,7 +20,7 @@ import ( systemd "github.com/coreos/go-systemd/daemon" ctconfig "github.com/hashicorp/consul-template/config" - log "github.com/hashicorp/go-hclog" + hclog "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-secure-stdlib/gatedwriter" "github.com/hashicorp/go-secure-stdlib/parseutil" @@ -78,7 +78,7 @@ type AgentCommand struct { logWriter io.Writer logGate *gatedwriter.Writer - logger log.Logger + logger hclog.Logger // Telemetry object metricsHelper *metricsutil.MetricsHelper @@ -210,7 +210,16 @@ func (c *AgentCommand) Run(args []string) int { c.outputErrors(err) return 1 } + + // Update the logger and then base the log writer on that logger. + // Log writer is supplied to consul-template runners for templates and execs. + // We want to ensure that consul-template will honor the settings, for example + // if the -log-format is JSON we want JSON, not a mix of JSON and non-JSON messages. c.logger = l + c.logWriter = l.StandardWriter(&hclog.StandardLoggerOptions{ + InferLevels: true, + InferLevelsWithTimestamp: true, + }) infoKeys := make([]string, 0, 10) info := make(map[string]string) @@ -1093,31 +1102,31 @@ func (c *AgentCommand) handleQuit(enabled bool) http.Handler { } // newLogger creates a logger based on parsed config field on the Agent Command struct. -func (c *AgentCommand) newLogger() (log.InterceptLogger, error) { +func (c *AgentCommand) newLogger() (hclog.InterceptLogger, error) { if c.config == nil { return nil, fmt.Errorf("cannot create logger, no config") } - var errors error + var errs *multierror.Error // Parse all the log related config logLevel, err := logging.ParseLogLevel(c.config.LogLevel) if err != nil { - errors = multierror.Append(errors, err) + errs = multierror.Append(errs, err) } logFormat, err := logging.ParseLogFormat(c.config.LogFormat) if err != nil { - errors = multierror.Append(errors, err) + errs = multierror.Append(errs, err) } logRotateDuration, err := parseutil.ParseDurationSecond(c.config.LogRotateDuration) if err != nil { - errors = multierror.Append(errors, err) + errs = multierror.Append(errs, err) } - if errors != nil { - return nil, errors + if errs != nil { + return nil, errs } logCfg := &logging.LogConfig{ @@ -1140,20 +1149,20 @@ func (c *AgentCommand) newLogger() (log.InterceptLogger, error) { // loadConfig attempts to generate an Agent config from the file(s) specified. func (c *AgentCommand) loadConfig(paths []string) (*agentConfig.Config, error) { - var errors error + var errs *multierror.Error cfg := agentConfig.NewConfig() for _, configPath := range paths { configFromPath, err := agentConfig.LoadConfig(configPath) if err != nil { - errors = multierror.Append(errors, fmt.Errorf("error loading configuration from %s: %w", configPath, err)) + errs = multierror.Append(errs, fmt.Errorf("error loading configuration from %s: %w", configPath, err)) } else { cfg = cfg.Merge(configFromPath) } } - if errors != nil { - return nil, errors + if errs != nil { + return nil, errs } if err := cfg.ValidateConfig(); err != nil { diff --git a/command/agent/internal/ctmanager/runner_config.go b/command/agent/internal/ctmanager/runner_config.go index 21b3bc8e82a1..408602998df5 100644 --- a/command/agent/internal/ctmanager/runner_config.go +++ b/command/agent/internal/ctmanager/runner_config.go @@ -11,7 +11,6 @@ import ( ctconfig "github.com/hashicorp/consul-template/config" ctlogging "github.com/hashicorp/consul-template/logging" "github.com/hashicorp/go-hclog" - "github.com/hashicorp/vault/command/agent/config" "github.com/hashicorp/vault/sdk/helper/pointerutil" ) diff --git a/command/agent_test.go b/command/agent_test.go index d3868d43a114..3ab66b462389 100644 --- a/command/agent_test.go +++ b/command/agent_test.go @@ -4,6 +4,7 @@ package command import ( + "bufio" "crypto/tls" "crypto/x509" "encoding/json" @@ -826,7 +827,7 @@ auto_auth { verify := func(suffix string) { t.Helper() // We need to poll for a bit to give Agent time to render the - // templates. Without this this, the test will attempt to read + // templates. Without this, the test will attempt to read // the temp dir before Agent has had time to render and will // likely fail the test tick := time.Tick(1 * time.Second) @@ -3070,6 +3071,107 @@ vault { } } +// TestAgent_Logging_ConsulTemplate attempts to ensure two things about Vault Agent logs: +// 1. When -log-format command line arg is set to JSON, it is honored as the output format +// for messages generated from within the consul-template library. +// 2. When -log-file command line arg is supplied, a file receives all log messages +// generated by the consul-template library (they don't just go to stdout/stderr). +// Should prevent a regression of: https://github.com/hashicorp/vault/issues/21109 +func TestAgent_Logging_ConsulTemplate(t *testing.T) { + const ( + runnerLogMessage = "(runner) creating new runner (dry: false, once: false)" + ) + + // Configure a Vault server so Agent can successfully communicate and render its templates + cluster := minimal.NewTestSoloCluster(t, nil) + apiClient := cluster.Cores[0].Client + t.Setenv(api.EnvVaultAddress, apiClient.Address()) + tempDir := t.TempDir() + roleIDPath, secretIDPath := setupAppRoleAndKVMounts(t, apiClient) + + // Create relevant configs for Vault Agent (config, template config) + templateSrc := filepath.Join(tempDir, "render_1.tmpl") + err := os.WriteFile(templateSrc, []byte(templateContents(1)), 0o600) + require.NoError(t, err) + templateConfig := fmt.Sprintf(templateConfigString, templateSrc, tempDir, "render_1.json") + + config := ` +vault { + address = "%s" + tls_skip_verify = true +} + +auto_auth { + method "approle" { + mount_path = "auth/approle" + config = { + role_id_file_path = "%s" + secret_id_file_path = "%s" + remove_secret_id_file_after_reading = false + } + } +} + +%s +` + config = fmt.Sprintf(config, apiClient.Address(), roleIDPath, secretIDPath, templateConfig) + configFileName := filepath.Join(tempDir, "config.hcl") + err = os.WriteFile(configFileName, []byte(config), 0o600) + require.NoError(t, err) + _, cmd := testAgentCommand(t, nil) + logFilePath := filepath.Join(tempDir, "agent") + + // Start Vault Agent + go func() { + code := cmd.Run([]string{"-config", configFileName, "-log-format", "json", "-log-file", logFilePath, "-log-level", "trace"}) + require.Equalf(t, 0, code, "Vault Agent returned a non-zero exit code") + }() + + select { + case <-cmd.startedCh: + case <-time.After(5 * time.Second): + t.Fatal("timeout starting agent") + } + + // Give Vault Agent some time to render our template. + time.Sleep(3 * time.Second) + + // This flag will be used to capture whether we saw a consul-template log + // message in the log file (the presence of the log file is also part of the test) + found := false + + // Vault Agent file logs will match agent-{timestamp}.log based on the + // cmd line argument we supplied, e.g. agent-1701258869573205000.log + m, err := filepath.Glob(logFilePath + "*") + require.NoError(t, err) + require.Truef(t, len(m) > 0, "no files were found") + + for _, p := range m { + f, err := os.Open(p) + require.NoError(t, err) + + fs := bufio.NewScanner(f) + fs.Split(bufio.ScanLines) + + for fs.Scan() { + s := fs.Text() + entry := make(map[string]string) + err := json.Unmarshal([]byte(s), &entry) + require.NoError(t, err) + v, ok := entry["@message"] + if !ok { + continue + } + if v == runnerLogMessage { + found = true + break + } + } + } + + require.Truef(t, found, "unable to find consul-template partial message in logs", runnerLogMessage) +} + // Get a randomly assigned port and then free it again before returning it. // There is still a race when trying to use it, but should work better // than a static port. From ef3021f1a4ea9f32a596f6608987ddb82e62439b Mon Sep 17 00:00:00 2001 From: Violet Hynes Date: Wed, 29 Nov 2023 09:35:59 -0500 Subject: [PATCH 37/74] Fix bug in static secret caching where no token is present in a request to Proxy (#24287) --- command/agentproxyshared/cache/lease_cache.go | 6 +- command/proxy_test.go | 197 ++++++++++++++++++ 2 files changed, 200 insertions(+), 3 deletions(-) diff --git a/command/agentproxyshared/cache/lease_cache.go b/command/agentproxyshared/cache/lease_cache.go index 1b6dcc1e1147..6e8b564a620a 100644 --- a/command/agentproxyshared/cache/lease_cache.go +++ b/command/agentproxyshared/cache/lease_cache.go @@ -400,18 +400,18 @@ func (c *LeaseCache) Send(ctx context.Context, req *SendRequest) (*SendResponse, return nil, err } if cachedResp != nil { - c.logger.Debug("returning cached response", "path", req.Request.URL.Path) + c.logger.Debug("returning cached dynamic secret response", "path", req.Request.URL.Path) return cachedResp, nil } // Check if the response for this request is already in the static secret cache - if staticSecretCacheId != "" && req.Request.Method == http.MethodGet { + if staticSecretCacheId != "" && req.Request.Method == http.MethodGet && req.Token != "" { cachedResp, err = c.checkCacheForStaticSecretRequest(staticSecretCacheId, req) if err != nil { return nil, err } if cachedResp != nil { - c.logger.Debug("returning cached response", "id", staticSecretCacheId, "path", req.Request.URL.Path) + c.logger.Debug("returning cached static secret response", "id", staticSecretCacheId, "path", req.Request.URL.Path) return cachedResp, nil } } diff --git a/command/proxy_test.go b/command/proxy_test.go index ef856e9127a6..c8feb3e53e2d 100644 --- a/command/proxy_test.go +++ b/command/proxy_test.go @@ -1278,6 +1278,203 @@ log_level = "trace" // We expect that the cached value got updated by the event system. require.Equal(t, secretData2, data2) + // Lastly, ensure that a client without a token fails to access the secret. + proxyClient.SetToken("") + req = proxyClient.NewRequest(http.MethodGet, "/v1/secret-v2/data/my-secret") + _, err = proxyClient.RawRequest(req) + require.NotNil(t, err) + + _, err = proxyClient.KVv2("secret-v2").Get(context.Background(), "my-secret") + require.NotNil(t, err) + + close(cmd.ShutdownCh) + wg.Wait() +} + +// TestProxy_Cache_EventSystemUpdatesCacheUseAutoAuthToken Tests that the cache successfully caches a static secret +// going through the Proxy for a KVV2 secret, and that the cache works as expected with the +// use_auto_auth_token=force option. +func TestProxy_Cache_EventSystemUpdatesCacheUseAutoAuthToken(t *testing.T) { + logger := logging.NewVaultLogger(hclog.Trace) + cluster := vault.NewTestCluster(t, &vault.CoreConfig{ + LogicalBackends: map[string]logical.Factory{ + "kv": logicalKv.VersionedKVFactory, + }, + }, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + + serverClient := cluster.Cores[0].Client + + // Unset the environment variable so that proxy picks up the right test + // cluster address + defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress)) + os.Unsetenv(api.EnvVaultAddress) + + tokenFileName := makeTempFile(t, "token-file", serverClient.Token()) + defer os.Remove(tokenFileName) + // We need auto-auth so that the event system can run. + // For ease, we use the token file path with the root token. + autoAuthConfig := fmt.Sprintf(` +auto_auth { + method { + type = "token_file" + config = { + token_file_path = "%s" + } + } +}`, tokenFileName) + + cacheConfig := ` +cache { + cache_static_secrets = true +} +` + listenAddr := generateListenerAddress(t) + listenConfig := fmt.Sprintf(` +listener "tcp" { + address = "%s" + tls_disable = true +} +`, listenAddr) + + config := fmt.Sprintf(` +vault { + address = "%s" + tls_skip_verify = true +} +%s +%s +%s + +api_proxy { + use_auto_auth_token = "force" +} + +log_level = "trace" +`, serverClient.Address(), cacheConfig, listenConfig, autoAuthConfig) + configPath := makeTempFile(t, "config.hcl", config) + defer os.Remove(configPath) + + // Start proxy + ui, cmd := testProxyCommand(t, logger) + cmd.startedCh = make(chan struct{}) + + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + cmd.Run([]string{"-config", configPath}) + wg.Done() + }() + + select { + case <-cmd.startedCh: + case <-time.After(5 * time.Second): + t.Errorf("timeout") + t.Errorf("stdout: %s", ui.OutputWriter.String()) + t.Errorf("stderr: %s", ui.ErrorWriter.String()) + } + + proxyClient, err := api.NewClient(api.DefaultConfig()) + if err != nil { + t.Fatal(err) + } + proxyClient.SetToken(serverClient.Token()) + proxyClient.SetMaxRetries(0) + err = proxyClient.SetAddress("http://" + listenAddr) + if err != nil { + t.Fatal(err) + } + + secretData := map[string]interface{}{ + "foo": "bar", + } + + secretData2 := map[string]interface{}{ + "bar": "baz", + } + + // Wait for the event system to successfully connect. + // This is longer than it needs to be to account for unnatural slowness/avoiding + // flakiness. + time.Sleep(5 * time.Second) + + // Mount the KVV2 engine + err = serverClient.Sys().Mount("secret-v2", &api.MountInput{ + Type: "kv-v2", + }) + if err != nil { + t.Fatal(err) + } + + // Create kvv2 secret + _, err = serverClient.KVv2("secret-v2").Put(context.Background(), "my-secret", secretData) + if err != nil { + t.Fatal(err) + } + + // We use raw requests so we can check the headers for cache hit/miss. + req := proxyClient.NewRequest(http.MethodGet, "/v1/secret-v2/data/my-secret") + resp1, err := proxyClient.RawRequest(req) + if err != nil { + t.Fatal(err) + } + + cacheValue := resp1.Header.Get("X-Cache") + require.Equal(t, "MISS", cacheValue) + + // Update the secret using the proxy client + _, err = proxyClient.KVv2("secret-v2").Put(context.Background(), "my-secret", secretData2) + if err != nil { + t.Fatal(err) + } + + // Give some time for the event to actually get sent and the cache to be updated. + // This is longer than it needs to be to account for unnatural slowness/avoiding + // flakiness. + time.Sleep(5 * time.Second) + + // We expect this to be a cache hit, with the new value + resp2, err := proxyClient.RawRequest(req) + if err != nil { + t.Fatal(err) + } + + cacheValue = resp2.Header.Get("X-Cache") + require.Equal(t, "HIT", cacheValue) + + // Lastly, we check to make sure the actual data we received is + // as we expect. We must use ParseSecret due to the raw requests. + secret1, err := api.ParseSecret(resp1.Body) + require.Nil(t, err) + data, ok := secret1.Data["data"] + require.True(t, ok) + require.Equal(t, secretData, data) + + secret2, err := api.ParseSecret(resp2.Body) + require.Nil(t, err) + data2, ok := secret2.Data["data"] + require.True(t, ok) + // We expect that the cached value got updated by the event system. + require.Equal(t, secretData2, data2) + + // Lastly, ensure that a client without a token succeeds + // at accessing the secret, due to the use_auto_auth_token = "force" + // option. + proxyClient.SetToken("") + req = proxyClient.NewRequest(http.MethodGet, "/v1/secret-v2/data/my-secret") + resp3, err := proxyClient.RawRequest(req) + require.Nil(t, err) + cacheValue = resp3.Header.Get("X-Cache") + require.Equal(t, "HIT", cacheValue) + + secret3, err := api.ParseSecret(resp3.Body) + require.Nil(t, err) + data3, ok := secret3.Data["data"] + require.True(t, ok) + // We expect that the cached value got updated by the event system. + require.Equal(t, secretData2, data3) + close(cmd.ShutdownCh) wg.Wait() } From 31f399d14773d847b6b91a396eeeb28e7258c5e5 Mon Sep 17 00:00:00 2001 From: Scott Miller Date: Wed, 29 Nov 2023 15:42:07 -0600 Subject: [PATCH 38/74] Re-wrap partial failure improvements, CE side (#24293) * Re-wrap partial failure improvements, CE side * Resolve import cycle --- vault/seal/seal_wrapper.go | 8 ++++++++ vault/seal_util.go | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/vault/seal/seal_wrapper.go b/vault/seal/seal_wrapper.go index 067a557f8058..421024c7e8a1 100644 --- a/vault/seal/seal_wrapper.go +++ b/vault/seal/seal_wrapper.go @@ -15,6 +15,14 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping/v2" ) +type PartialSealWrapError struct { + Err error +} + +func (p *PartialSealWrapError) Error() string { + return p.Err.Error() +} + // SealWrapper contains a Wrapper and related information needed by the seal that uses it. // Use NewSealWrapper to construct new instances, do not do it directly. type SealWrapper struct { diff --git a/vault/seal_util.go b/vault/seal_util.go index 3435ba9c51b9..4ba94f6a98c6 100644 --- a/vault/seal_util.go +++ b/vault/seal_util.go @@ -23,7 +23,7 @@ type PartialWrapFailCallback func(context.Context, map[string]error) error // Helper function to use for partial wrap fail callbacks where we don't want to allow a partial failure. See // for example barrier or recovery key wrapping. Just don't allow for those risky scenarios var DisallowPartialSealWrap = func(ctx context.Context, errs map[string]error) error { - return seal.JoinSealWrapErrors("not allowing operation to proceed without full wrapping involving all configured seals", errs) + return &seal.PartialSealWrapError{seal.JoinSealWrapErrors("not allowing operation to proceed without full wrapping involving all configured seals", errs)} } // SealWrapValue creates a SealWrappedValue wrapper with the entryValue being optionally encrypted with the give seal Access. From b0ed4297bf3a24941e5df491fabde5c30d4f936e Mon Sep 17 00:00:00 2001 From: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:40:40 -0600 Subject: [PATCH 39/74] UI: Prevent replication disable action from sending data payload (#24292) * Prevent replication disable action from sending data payload * Add changelog --- changelog/24292.txt | 3 +++ ui/lib/core/addon/components/replication-action-disable.js | 7 +++++++ .../templates/components/replication-action-disable.hbs | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 changelog/24292.txt diff --git a/changelog/24292.txt b/changelog/24292.txt new file mode 100644 index 000000000000..784e2e38f4c7 --- /dev/null +++ b/changelog/24292.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fix payload sent when disabling replication +``` diff --git a/ui/lib/core/addon/components/replication-action-disable.js b/ui/lib/core/addon/components/replication-action-disable.js index ecdf51f781da..77c5108e1f22 100644 --- a/ui/lib/core/addon/components/replication-action-disable.js +++ b/ui/lib/core/addon/components/replication-action-disable.js @@ -9,4 +9,11 @@ import layout from '../templates/components/replication-action-disable'; export default Actions.extend({ layout, tagName: '', + + actions: { + onSubmit(replicationMode, clusterMode, evt) { + // No data is submitted for disable request + return this.onSubmit(replicationMode, clusterMode, null, evt); + }, + }, }); diff --git a/ui/lib/core/addon/templates/components/replication-action-disable.hbs b/ui/lib/core/addon/templates/components/replication-action-disable.hbs index cb23af901f3a..1ac951ee7356 100644 --- a/ui/lib/core/addon/templates/components/replication-action-disable.hbs +++ b/ui/lib/core/addon/templates/components/replication-action-disable.hbs @@ -31,8 +31,8 @@ @isActive={{this.isModalActive}} @confirmText={{this.model.replicationModeForDisplay}} @toConfirmMsg="disabling {{this.model.replicationModeForDisplay}} Replication on this cluster" - @onConfirm={{action - "onSubmit" + @onConfirm={{fn + (action "onSubmit") "disable" (if (eq this.model.replicationAttrs.modeForUrl "bootstrapping") this.mode this.model.replicationAttrs.modeForUrl) }} From 2e9578bc96b2d5afa9170b030edbda5037a4d62e Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Thu, 30 Nov 2023 09:36:26 -0700 Subject: [PATCH 40/74] Default to Json editor if KV secret is nested (#24290) * initial fix * changelog * fix * fix test and add test coverage * remove useless escape characters * pr comments add more test coverage --- changelog/24290.txt | 3 ++ ui/lib/kv/addon/components/kv-data-fields.hbs | 17 ++++++-- ui/lib/kv/addon/components/kv-data-fields.js | 4 +- .../addon/components/page/secret/details.hbs | 24 +++++------ .../addon/components/page/secret/details.js | 10 +++++ .../kv/addon/components/page/secret/edit.hbs | 2 +- .../addon/components/page/secrets/create.hbs | 1 + .../components/kv/kv-data-fields-test.js | 43 ++++++++++++++++--- .../kv/page/kv-page-secret-details-test.js | 42 ++++++++++++++++++ 9 files changed, 121 insertions(+), 25 deletions(-) create mode 100644 changelog/24290.txt diff --git a/changelog/24290.txt b/changelog/24290.txt new file mode 100644 index 000000000000..3533146b7d23 --- /dev/null +++ b/changelog/24290.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: When Kv v2 secret is an object, fix so details view defaults to readOnly JSON editor. +``` \ No newline at end of file diff --git a/ui/lib/kv/addon/components/kv-data-fields.hbs b/ui/lib/kv/addon/components/kv-data-fields.hbs index b384d3e4c2e3..f981b2b8c412 100644 --- a/ui/lib/kv/addon/components/kv-data-fields.hbs +++ b/ui/lib/kv/addon/components/kv-data-fields.hbs @@ -4,9 +4,9 @@ ~}} {{#let (find-by "name" "path" @secret.allFields) as |attr|}} - {{#if @isEdit}} + {{#if (eq @type "edit")}} - {{else}} + {{else if (eq @type "create")}} {{/if}} {{/let}} @@ -14,9 +14,10 @@
{{#if @showJson}} {{#if (or @modelValidations.secretData.errors this.lintingErrors)}} @@ -27,10 +28,18 @@ {{/if}} {{/if}} +{{else if (eq @type "details")}} + {{#each-in @secret.secretData as |key value|}} + + + + {{else}} + + {{/each-in}} {{else}} @@ -23,7 +23,7 @@ import { stringify } from 'core/helpers/stringify'; * @param {boolean} showJson - boolean passed from parent to hide/show json editor * @param {object} [modelValidations] - object of errors. If attr.name is in object and has error message display in AlertInline. * @param {callback} [pathValidations] - callback function fired for the path input on key up - * @param {boolean} [isEdit=false] - if true, this is a new secret version rather than a new secret. Used to change text for some form labels + * @param {boolean} [type=null] - can be edit, create, or details. Used to change text for some form labels */ export default class KvDataFields extends Component { diff --git a/ui/lib/kv/addon/components/page/secret/details.hbs b/ui/lib/kv/addon/components/page/secret/details.hbs index 6c2dd065f2f5..7aee68f28f1b 100644 --- a/ui/lib/kv/addon/components/page/secret/details.hbs +++ b/ui/lib/kv/addon/components/page/secret/details.hbs @@ -15,7 +15,12 @@ <:toolbarFilters> {{#unless this.emptyState}} - + JSON {{/unless}} @@ -93,15 +98,10 @@ {{/if}} {{else}} - {{#if this.showJsonView}} - - {{else}} - {{#each-in @secret.secretData as |key value|}} - - - - {{else}} - - {{/each-in}} - {{/if}} + {{/if}} \ No newline at end of file diff --git a/ui/lib/kv/addon/components/page/secret/details.js b/ui/lib/kv/addon/components/page/secret/details.js index 844706f8dbcd..d5c6189bac2b 100644 --- a/ui/lib/kv/addon/components/page/secret/details.js +++ b/ui/lib/kv/addon/components/page/secret/details.js @@ -35,6 +35,16 @@ export default class KvSecretDetails extends Component { @tracked showJsonView = false; @tracked wrappedData = null; + secretDataIsAdvanced; + + constructor() { + super(...arguments); + this.originalSecret = JSON.stringify(this.args.secret.secretData || {}); + if (this.originalSecret.lastIndexOf('{') > 0) { + // Dumb way to check if there's a nested object in the secret + this.secretDataIsAdvanced = true; + } + } @action closeVersionMenu(dropdown) { diff --git a/ui/lib/kv/addon/components/page/secret/edit.hbs b/ui/lib/kv/addon/components/page/secret/edit.hbs index 8a5be5646996..28ba62d89f78 100644 --- a/ui/lib/kv/addon/components/page/secret/edit.hbs +++ b/ui/lib/kv/addon/components/page/secret/edit.hbs @@ -46,7 +46,7 @@ @showJson={{or this.showJsonView this.secretDataIsAdvanced}} @secret={{@secret}} @modelValidations={{this.modelValidations}} - @isEdit={{true}} + @type="edit" />
diff --git a/ui/lib/kv/addon/components/page/secrets/create.hbs b/ui/lib/kv/addon/components/page/secrets/create.hbs index 326ecab52f9a..4eefecf19854 100644 --- a/ui/lib/kv/addon/components/page/secrets/create.hbs +++ b/ui/lib/kv/addon/components/page/secrets/create.hbs @@ -21,6 +21,7 @@ @secret={{@secret}} @modelValidations={{this.modelValidations}} @pathValidations={{this.pathValidations}} + @type="create" /> `, { owner: this.engine }); - + await render(hbs``, { + owner: this.engine, + }); await fillIn(FORM.inputByAttr('path'), this.path); await fillIn(FORM.keyInput(), 'foo'); await fillIn(FORM.maskedValueInput(), 'bar'); @@ -40,7 +41,6 @@ module('Integration | Component | kv-v2 | KvDataFields', function (hooks) { assert.expect(3); await render(hbs``, { owner: this.engine }); - assert.strictEqual( codemirror().getValue(' '), `{ \"\": \"\" }`, // eslint-disable-line no-useless-escape @@ -63,7 +63,7 @@ module('Integration | Component | kv-v2 | KvDataFields', function (hooks) { secretData: this.secret.secretData, }); - await render(hbs``, { + await render(hbs``, { owner: this.engine, }); @@ -73,4 +73,35 @@ module('Integration | Component | kv-v2 | KvDataFields', function (hooks) { assert.dom(FORM.maskedValueInput()).hasValue('bar'); assert.dom(FORM.dataInputLabel({ isJson: false })).hasText('Version data'); }); + + test('it shows readonly info rows when viewing secret details of simple secret', async function (assert) { + assert.expect(3); + this.secret.secretData = { foo: 'bar' }; + this.secret.path = this.path; + + await render(hbs``, { + owner: this.engine, + }); + assert.dom(PAGE.infoRow).exists({ count: 1 }, '1 row of data shows'); + assert.dom(PAGE.infoRowValue('foo')).hasText('***********'); + await click(PAGE.infoRowToggleMasked('foo')); + assert.dom(PAGE.infoRowValue('foo')).hasText('bar', 'secret value shows after toggle'); + }); + + test('it shows readonly json editor when viewing secret details of complex secret', async function (assert) { + assert.expect(3); + this.secret.secretData = { + foo: { + bar: 'baz', + }, + }; + this.secret.path = this.path; + + await render(hbs``, { + owner: this.engine, + }); + assert.dom(PAGE.infoRowValue('foo')).doesNotExist('does not render rows of secret data'); + assert.dom('[data-test-component="code-mirror-modifier"]').hasClass('readonly-codemirror'); + assert.dom('[data-test-component="code-mirror-modifier"]').includesText(`{ "foo": { "bar": "baz" }}`); + }); }); diff --git a/ui/tests/integration/components/kv/page/kv-page-secret-details-test.js b/ui/tests/integration/components/kv/page/kv-page-secret-details-test.js index 1520818eb598..e570753989a8 100644 --- a/ui/tests/integration/components/kv/page/kv-page-secret-details-test.js +++ b/ui/tests/integration/components/kv/page/kv-page-secret-details-test.js @@ -23,8 +23,10 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub()); this.backend = 'kv-engine'; this.path = 'my-secret'; + this.pathComplex = 'my-secret-object'; this.version = 2; this.dataId = kvDataPath(this.backend, this.path); + this.dataIdComplex = kvDataPath(this.backend, this.pathComplex); this.metadataId = kvMetadataPath(this.backend, this.path); this.secretData = { foo: 'bar' }; @@ -38,6 +40,22 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook destroyed: false, version: this.version, }); + // nested secret + this.secretDataComplex = { + foo: { + bar: 'baz', + }, + }; + this.store.pushPayload('kv/data', { + modelName: 'kv/data', + id: this.dataIdComplex, + secret_data: this.secretDataComplex, + created_time: '2023-08-20T02:12:17.379762Z', + custom_metadata: null, + deletion_time: '', + destroyed: false, + version: this.version, + }); const metadata = this.server.create('kv-metadatum'); metadata.id = this.metadataId; @@ -48,6 +66,7 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook this.metadata = this.store.peekRecord('kv/metadata', this.metadataId); this.secret = this.store.peekRecord('kv/data', this.dataId); + this.secretComplex = this.store.peekRecord('kv/data', this.dataIdComplex); // this is the route model, not an ember data model this.model = { @@ -61,6 +80,12 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook { label: this.model.backend, route: 'list' }, { label: this.model.path }, ]; + this.modelComplex = { + backend: this.backend, + path: this.pathComplex, + secret: this.secretComplex, + metadata: this.metadata, + }; }); test('it renders secret details and toggles json view', async function (assert) { @@ -90,6 +115,23 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook .includesText(`Version ${this.version} created`, 'renders version and time created'); }); + test('it renders json view when secret is complex', async function (assert) { + assert.expect(3); + await render( + hbs` + + `, + { owner: this.engine } + ); + assert.dom(PAGE.infoRowValue('foo')).doesNotExist('does not render rows of secret data'); + assert.dom(FORM.toggleJson).isDisabled(); + assert.dom('[data-test-component="code-mirror-modifier"]').includesText(`{ "foo": { "bar": "baz" }}`); + }); + test('it renders deleted empty state', async function (assert) { assert.expect(3); this.secret.deletionTime = '2023-07-23T02:12:17.379762Z'; From 56f793d0c876d5993314391684892e345cb205d2 Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Thu, 30 Nov 2023 09:49:45 -0700 Subject: [PATCH 41/74] =?UTF-8?q?=F0=9F=A7=B9=20HDS=20button=20replacement?= =?UTF-8?q?=20(#24230)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * namespace things * kmip * init and mount-info: * ssh sign * replication and remove type button * fix learn more on replication mode summary * use dropdown. * clean up * Update ui/lib/kv/addon/components/kv-version-dropdown.hbs Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> * pr comments * Update replication-mode-summary.hbs * blah * fix * Update replication-mode-summary.hbs * add back mount-info --------- Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> --- .../templates/components/namespace-link.hbs | 2 +- .../templates/components/namespace-picker.hbs | 18 +++++++--- .../components/wizard/features-selection.hbs | 1 - .../components/{ => wizard}/mount-info.hbs | 0 .../vault/cluster/access/identity/index.hbs | 7 +--- ui/app/templates/vault/cluster/init.hbs | 35 +++++++++--------- .../vault/cluster/secrets/backend/sign.hbs | 19 +++++----- .../addon/components/confirmation-modal.hbs | 1 - .../components/replication-mode-summary.hbs | 30 +++++++++------- .../components/edit-form-kmip-role.hbs | 36 +++++++++---------- .../addon/components/kv-version-dropdown.hbs | 2 +- 11 files changed, 76 insertions(+), 75 deletions(-) rename ui/app/templates/components/{ => wizard}/mount-info.hbs (100%) diff --git a/ui/app/templates/components/namespace-link.hbs b/ui/app/templates/components/namespace-link.hbs index 14f99ced3b04..2c867bdc698f 100644 --- a/ui/app/templates/components/namespace-link.hbs +++ b/ui/app/templates/components/namespace-link.hbs @@ -14,7 +14,7 @@
{{this.namespaceDisplay}} - +
{{/if}} diff --git a/ui/app/templates/components/namespace-picker.hbs b/ui/app/templates/components/namespace-picker.hbs index 9af86128ebe8..787c42d5baaf 100644 --- a/ui/app/templates/components/namespace-picker.hbs +++ b/ui/app/templates/components/namespace-picker.hbs @@ -42,7 +42,9 @@ {{/if}}
- {{#unless this.isUserRootNamespace}} + {{#if this.isUserRootNamespace}} +
Namespaces
+ {{else}} - +
+ +

Namespaces

+
- {{/unless}} -
Namespaces
+ {{/if}}
{{#if (includes "" this.lastMenuLeaves)}} {{! leaf is '' which is the root namespace, and then we need to iterate the root leaves }} diff --git a/ui/app/templates/components/wizard/features-selection.hbs b/ui/app/templates/components/wizard/features-selection.hbs index 0fe8c47426aa..0ebb3f77985c 100644 --- a/ui/app/templates/components/wizard/features-selection.hbs +++ b/ui/app/templates/components/wizard/features-selection.hbs @@ -39,7 +39,6 @@ />
  • {{#if item.disabled}} - + {{else if (eq this.identityType "entity")}}
    -
    + {{#if (and this.model.sealed (not this.keyData.recovery_keys))}} -
    - - Continue to Unseal - -
    + {{else}} -
    - - Continue to Authenticate - -
    + {{/if}} -
    +
    {{else}}
    -
    + -
    -
    - - Cancel - -
    + data-test-secret-generate-cancel + /> +
    {{/if}} \ No newline at end of file diff --git a/ui/lib/core/addon/components/confirmation-modal.hbs b/ui/lib/core/addon/components/confirmation-modal.hbs index 6f1eb4ea7dab..4e8f9bea5252 100644 --- a/ui/lib/core/addon/components/confirmation-modal.hbs +++ b/ui/lib/core/addon/components/confirmation-modal.hbs @@ -34,7 +34,6 @@ Performance Replication is a feature of Vault Enterprise Premium. + + Upgrade +

    -

    + {{else if (and (eq this.mode "dr") (not (has-feature "DR Replication")))}} +

    + Disaster Recovery is a feature of Vault Enterprise Premium. - Learn more - + Upgrade

    {{else if this.replicationEnabled}} @@ -103,18 +111,14 @@
  • {{#if this.replicationDisabled}} - - Enable - + /> {{else}} - - Details - + {{/if}}
    diff --git a/ui/lib/kmip/addon/templates/components/edit-form-kmip-role.hbs b/ui/lib/kmip/addon/templates/components/edit-form-kmip-role.hbs index 381a07ae1c94..0266f1ba69ea 100644 --- a/ui/lib/kmip/addon/templates/components/edit-form-kmip-role.hbs +++ b/ui/lib/kmip/addon/templates/components/edit-form-kmip-role.hbs @@ -83,29 +83,25 @@ {{/each}} +
    -
    -
    + + + {{#if this.cancelLink}} -
    - {{#if this.cancelLink}} -
    - - Cancel - -
    {{/if}} -
    +
    \ No newline at end of file diff --git a/ui/lib/kv/addon/components/kv-version-dropdown.hbs b/ui/lib/kv/addon/components/kv-version-dropdown.hbs index f8170a1eb4b1..a5557d4ea69f 100644 --- a/ui/lib/kv/addon/components/kv-version-dropdown.hbs +++ b/ui/lib/kv/addon/components/kv-version-dropdown.hbs @@ -15,7 +15,7 @@ {{#each @metadata.sortedVersions as |versionData|}}
  • {{#if @onSelect}} - {{! TODO Hds::Button manual update }} + {{! TODO use Hds::Dropdown instead https://helios.hashicorp.design/components/dropdown }}
  • {{#if (is-before (now interval=1000) this.auth.tokenExpirationDate)}} diff --git a/ui/app/styles/helper-classes/layout.scss b/ui/app/styles/helper-classes/layout.scss index 4fa01150b685..83f778ef3ccf 100644 --- a/ui/app/styles/helper-classes/layout.scss +++ b/ui/app/styles/helper-classes/layout.scss @@ -68,7 +68,7 @@ } .is-medium-width { - width: $desktop / 3; + width: calc($desktop / 3); } .is-medium-height { diff --git a/ui/lib/core/addon/components/certificate-card.hbs b/ui/lib/core/addon/components/certificate-card.hbs index a089650e8f38..044067b817e1 100644 --- a/ui/lib/core/addon/components/certificate-card.hbs +++ b/ui/lib/core/addon/components/certificate-card.hbs @@ -20,13 +20,13 @@ {{@data}} -
    +
    \ No newline at end of file diff --git a/ui/lib/core/addon/components/code-snippet.hbs b/ui/lib/core/addon/components/code-snippet.hbs index edd1e492f825..c5be6d878c33 100644 --- a/ui/lib/core/addon/components/code-snippet.hbs +++ b/ui/lib/core/addon/components/code-snippet.hbs @@ -15,5 +15,6 @@ @textToCopy={{or @clipboardCode @codeBlock}} @isIconOnly={{@isIconOnly}} @container={{@container}} + data-test-copy-button={{or @clipboardCode @codeBlock}} />
    \ No newline at end of file diff --git a/ui/lib/core/addon/components/copy-secret-dropdown.hbs b/ui/lib/core/addon/components/copy-secret-dropdown.hbs index 0e50926c5e85..0818adddc5d6 100644 --- a/ui/lib/core/addon/components/copy-secret-dropdown.hbs +++ b/ui/lib/core/addon/components/copy-secret-dropdown.hbs @@ -19,7 +19,7 @@ @isFullWidth={{true}} class="in-dropdown link is-flex-start" {{on "click" (action (set-flash-message "JSON Copied!"))}} - data-test-copy-button + data-test-copy-button={{@clipboardText}} />
  • diff --git a/ui/lib/core/addon/components/info-table-row.hbs b/ui/lib/core/addon/components/info-table-row.hbs index 4a7d7c95b942..a79b00821694 100644 --- a/ui/lib/core/addon/components/info-table-row.hbs +++ b/ui/lib/core/addon/components/info-table-row.hbs @@ -41,7 +41,7 @@ @isIconOnly={{true}} @textToCopy={{@value}} class="transparent has-padding-xxs" - data-test-copy-button + data-test-copy-button={{@value}} /> {{/if}} {{#if (has-block)}} @@ -95,7 +95,7 @@ @isIconOnly={{true}} @textToCopy={{@tooltipText}} class="transparent white-icon" - data-test-tooltip-copy + data-test-tooltip-copy={{@tooltipText}} /> {{/if}} diff --git a/ui/lib/core/addon/components/json-editor.hbs b/ui/lib/core/addon/components/json-editor.hbs index b7b15e3b72e6..c3a29b72335b 100644 --- a/ui/lib/core/addon/components/json-editor.hbs +++ b/ui/lib/core/addon/components/json-editor.hbs @@ -32,7 +32,7 @@ @isIconOnly={{true}} @textToCopy={{@value}} class="transparent" - data-test-copy-button + data-test-copy-button={{@value}} /> diff --git a/ui/lib/core/addon/components/masked-input.hbs b/ui/lib/core/addon/components/masked-input.hbs index 221e6b0fbb8c..ae34e9289ab5 100644 --- a/ui/lib/core/addon/components/masked-input.hbs +++ b/ui/lib/core/addon/components/masked-input.hbs @@ -40,7 +40,7 @@ @isIconOnly={{true}} @textToCopy={{@value}} class="transparent has-padding-xxs" - data-test-copy-button + data-test-copy-button={{or @value true}} /> {{/if}} {{#if @allowDownload}} diff --git a/ui/lib/kv/addon/components/page/secret/paths.hbs b/ui/lib/kv/addon/components/page/secret/paths.hbs index eca19d362676..fd9a400d23bf 100644 --- a/ui/lib/kv/addon/components/page/secret/paths.hbs +++ b/ui/lib/kv/addon/components/page/secret/paths.hbs @@ -21,7 +21,13 @@
    {{#each this.paths as |path|}} - + {{path.snippet}} diff --git a/ui/package.json b/ui/package.json index 7e199ccd3df3..f82c2c3b15c7 100644 --- a/ui/package.json +++ b/ui/package.json @@ -247,8 +247,8 @@ ] }, "dependencies": { - "@hashicorp/design-system-components": "^2.13.0", - "@hashicorp/ember-flight-icons": "^3.1.3", + "@hashicorp/design-system-components": "^3.3.0", + "@hashicorp/ember-flight-icons": "^4.0.4", "handlebars": "4.7.7", "highlight.js": "^10.4.1", "node-notifier": "^8.0.1", diff --git a/ui/tests/acceptance/pki/pki-cross-sign-test.js b/ui/tests/acceptance/pki/pki-cross-sign-test.js index 6403cc0bcb32..5ed81e6cf057 100644 --- a/ui/tests/acceptance/pki/pki-cross-sign-test.js +++ b/ui/tests/acceptance/pki/pki-cross-sign-test.js @@ -47,13 +47,13 @@ module('Acceptance | pki/pki cross sign', function (hooks) { await fillIn(SELECTORS.inputByName('type'), 'internal'); await fillIn(SELECTORS.inputByName('commonName'), 'Short-Lived Int R1'); await click('[data-test-save]'); - const csr = find(SELECTORS.copyButton('CSR')).getAttribute('data-clipboard-text'); + const csr = find(SELECTORS.copyButton('CSR')).getAttribute('data-test-copy-button'); await visit(`vault/secrets/${this.parentMountPath}/pki/issuers/${this.oldParentIssuerName}/sign`); await fillIn(SELECTORS.inputByName('csr'), csr); await fillIn(SELECTORS.inputByName('format'), 'pem_bundle'); await click('[data-test-pki-sign-intermediate-save]'); const pemBundle = find(SELECTORS.copyButton('CA Chain')) - .getAttribute('data-clipboard-text') + .getAttribute('data-test-copy-button') .replace(/,/, '\n'); await visit(`vault/secrets/${this.intMountPath}/pki/configuration/create`); await click(SELECTORS.configure.optionByKey('import')); @@ -64,7 +64,7 @@ module('Acceptance | pki/pki cross sign', function (hooks) { await click('[data-test-is-default]'); // name default issuer of intermediate const oldIntIssuerId = find(SELECTORS.rowValue('Issuer ID')).innerText; - const oldIntCert = find(SELECTORS.copyButton('Certificate')).getAttribute('data-clipboard-text'); + const oldIntCert = find(SELECTORS.copyButton('Certificate')).getAttribute('data-test-copy-button'); await click(SELECTORS.details.configure); await fillIn(SELECTORS.inputByName('issuerName'), this.intIssuerName); await click('[data-test-save]'); @@ -84,7 +84,7 @@ module('Acceptance | pki/pki cross sign', function (hooks) { // get certificate data of newly signed issuer await click(`${SELECTORS.signedIssuerCol('newCrossSignedIssuer')} a`); - const newIntCert = find(SELECTORS.copyButton('Certificate')).getAttribute('data-clipboard-text'); + const newIntCert = find(SELECTORS.copyButton('Certificate')).getAttribute('data-test-copy-button'); // verify cross-sign was accurate by creating a role to issue a leaf certificate const myRole = 'some-role'; @@ -98,7 +98,7 @@ module('Acceptance | pki/pki cross sign', function (hooks) { await fillIn(SELECTORS.inputByName('commonName'), 'my-leaf'); await fillIn('[data-test-ttl-value="TTL"]', '3600'); await click('[data-test-pki-generate-button]'); - const myLeafCert = find(SELECTORS.copyButton('Certificate')).getAttribute('data-clipboard-text'); + const myLeafCert = find(SELECTORS.copyButton('Certificate')).getAttribute('data-test-copy-button'); // see comments in utils/parse-pki-cert.js for step-by-step explanation of of verifyCertificates method assert.true( diff --git a/ui/tests/integration/components/certificate-card-test.js b/ui/tests/integration/components/certificate-card-test.js index e54fcbb3d08a..3d16b68cff8d 100644 --- a/ui/tests/integration/components/certificate-card-test.js +++ b/ui/tests/integration/components/certificate-card-test.js @@ -15,6 +15,7 @@ const SELECTORS = { value: '[data-test-certificate-value]', icon: '[data-test-certificate-icon]', copyButton: '[data-test-copy-button]', + copyIcon: '[data-test-icon="clipboard-copy"]', }; module('Integration | Component | certificate-card', function (hooks) { @@ -26,7 +27,7 @@ module('Integration | Component | certificate-card', function (hooks) { assert.dom(SELECTORS.label).hasNoText('There is no label because there is no value'); assert.dom(SELECTORS.value).hasNoText('There is no value because none was provided'); assert.dom(SELECTORS.icon).exists('The certificate icon exists'); - assert.dom(SELECTORS.copyButton).exists('The copy button exists'); + assert.dom(SELECTORS.copyIcon).exists('The copy icon renders'); }); test('it renders with an example PEM Certificate', async function (assert) { diff --git a/ui/tests/integration/components/copy-secret-dropdown-test.js b/ui/tests/integration/components/copy-secret-dropdown-test.js index 26c88dd9c4bd..3729c07e7d12 100644 --- a/ui/tests/integration/components/copy-secret-dropdown-test.js +++ b/ui/tests/integration/components/copy-secret-dropdown-test.js @@ -11,7 +11,7 @@ import { hbs } from 'ember-cli-htmlbars'; const SELECTORS = { dropdown: '[data-test-copy-menu-trigger]', copyButton: '[data-test-copy-button]', - clipboard: 'data-clipboard-text', + clipboard: 'data-test-copy-button', wrapButton: '[data-test-wrap-button]', masked: '[data-test-masked-input]', }; @@ -44,7 +44,7 @@ module('Integration | Component | copy-secret-dropdown', function (hooks) { assert.dom(SELECTORS.wrapButton).hasText('Wrap secret'); assert .dom(SELECTORS.copyButton) - .hasAttribute('data-clipboard-text', `${this.data}`, 'it renders copyable data'); + .hasAttribute('data-test-copy-button', `${this.data}`, 'it renders copyable data'); await click(SELECTORS.wrapButton); await click(SELECTORS.dropdown); @@ -88,6 +88,6 @@ module('Integration | Component | copy-secret-dropdown', function (hooks) { await click(SELECTORS.dropdown); assert .dom(`${SELECTORS.masked} ${SELECTORS.copyButton}`) - .hasAttribute('data-clipboard-text', this.wrappedData, 'it renders wrapped data'); + .hasAttribute('data-test-copy-button', this.wrappedData, 'it renders wrapped data'); }); }); diff --git a/ui/tests/integration/components/info-table-row-test.js b/ui/tests/integration/components/info-table-row-test.js index 238c019e2dd3..d6ba44a25ee0 100644 --- a/ui/tests/integration/components/info-table-row-test.js +++ b/ui/tests/integration/components/info-table-row-test.js @@ -96,7 +96,7 @@ module('Integration | Component | InfoTableRow', function (hooks) { assert.dom('[data-test-tooltip-copy]').exists('Tooltip has copy button'); assert .dom('[data-test-tooltip-copy]') - .hasAttribute('data-clipboard-text', 'Foo bar', 'Copy button will copy the tooltip text'); + .hasAttribute('data-test-tooltip-copy', 'Foo bar', 'Copy button will copy the tooltip text'); }); test('it renders a string with no link if isLink is true and the item type is not an array.', async function (assert) { diff --git a/ui/tests/integration/components/kv/page/kv-page-secret-paths-test.js b/ui/tests/integration/components/kv/page/kv-page-secret-paths-test.js index 342e7e90e6c2..5e6fe4a449bd 100644 --- a/ui/tests/integration/components/kv/page/kv-page-secret-paths-test.js +++ b/ui/tests/integration/components/kv/page/kv-page-secret-paths-test.js @@ -25,7 +25,7 @@ module('Integration | Component | kv-v2 | Page::Secret::Paths', function (hooks) ]; this.assertClipboard = (assert, element, expected) => { - assert.dom(element).hasAttribute('data-clipboard-text', expected); + assert.dom(element).hasAttribute('data-test-copy-button', expected); }; }); @@ -110,9 +110,9 @@ module('Integration | Component | kv-v2 | Page::Secret::Paths', function (hooks) ); assert.dom(PAGE.paths.codeSnippet('cli')).hasText(expected.cli); - assert.dom(PAGE.paths.snippetCopy('cli')).hasAttribute('data-clipboard-text', expected.cli); + assert.dom(PAGE.paths.snippetCopy('cli')).hasAttribute('data-test-copy-button', expected.cli); assert.dom(PAGE.paths.codeSnippet('api')).hasText(expected.apiDisplay); - assert.dom(PAGE.paths.snippetCopy('api')).hasAttribute('data-clipboard-text', expected.apiCopy); + assert.dom(PAGE.paths.snippetCopy('api')).hasAttribute('data-test-copy-button', expected.apiCopy); }); test('it renders copyable encoded mount and path commands', async function (assert) { @@ -141,8 +141,8 @@ module('Integration | Component | kv-v2 | Page::Secret::Paths', function (hooks) ); assert.dom(PAGE.paths.codeSnippet('cli')).hasText(expected.cli); - assert.dom(PAGE.paths.snippetCopy('cli')).hasAttribute('data-clipboard-text', expected.cli); + assert.dom(PAGE.paths.snippetCopy('cli')).hasAttribute('data-test-copy-button', expected.cli); assert.dom(PAGE.paths.codeSnippet('api')).hasText(expected.apiDisplay); - assert.dom(PAGE.paths.snippetCopy('api')).hasAttribute('data-clipboard-text', expected.apiCopy); + assert.dom(PAGE.paths.snippetCopy('api')).hasAttribute('data-test-copy-button', expected.apiCopy); }); }); diff --git a/ui/tests/integration/components/oidc/scope-form-test.js b/ui/tests/integration/components/oidc/scope-form-test.js index 866861428e34..40d960de3a25 100644 --- a/ui/tests/integration/components/oidc/scope-form-test.js +++ b/ui/tests/integration/components/oidc/scope-form-test.js @@ -177,7 +177,11 @@ module('Integration | Component | oidc/scope-form', function (hooks) { assert.dom(MODAL('text')).hasText('Example of a JSON template for scopes:', 'Modal text renders'); assert .dom('#scope-template-modal [data-test-copy-button]') - .hasAttribute('data-clipboard-text', exampleTemplate, 'Modal copy button copies the example template'); + .hasAttribute( + 'data-test-copy-button', + exampleTemplate, + 'Modal copy button copies the example template' + ); assert.dom('.cm-string').hasText('"username"', 'Example template json renders'); await click('[data-test-close-modal]'); assert.dom('.hds#scope-template-modal').doesNotExist('Modal is hidden'); diff --git a/ui/tests/integration/components/pki/pki-generate-csr-test.js b/ui/tests/integration/components/pki/pki-generate-csr-test.js index 6f4706a5b410..4245a2128747 100644 --- a/ui/tests/integration/components/pki/pki-generate-csr-test.js +++ b/ui/tests/integration/components/pki/pki-generate-csr-test.js @@ -112,15 +112,16 @@ module('Integration | Component | pki-generate-csr', function (hooks) { 'Next steps Copy the CSR below for a parent issuer to sign and then import the signed certificate back into this mount. The private_key is only available once. Make sure you copy and save it now.', 'renders Next steps alert banner' ); + assert .dom('[data-test-value-div="CSR"] [data-test-certificate-card] button') - .hasAttribute('data-clipboard-text', this.model.csr, 'it renders copyable csr'); + .hasAttribute('data-test-copy-button', this.model.csr, 'it renders copyable csr'); assert .dom('[data-test-value-div="Key ID"] button') - .hasAttribute('data-clipboard-text', this.model.keyId, 'it renders copyable key_id'); + .hasAttribute('data-test-copy-button', this.model.keyId, 'it renders copyable key_id'); assert .dom('[data-test-value-div="Private key"] [data-test-certificate-card] button') - .hasAttribute('data-clipboard-text', this.model.privateKey, 'it renders copyable private_key'); + .hasAttribute('data-test-copy-button', this.model.privateKey, 'it renders copyable private_key'); assert .dom('[data-test-value-div="Private key type"]') .hasText(this.model.privateKeyType, 'renders private_key_type'); @@ -145,10 +146,10 @@ module('Integration | Component | pki-generate-csr', function (hooks) { ); assert .dom('[data-test-value-div="CSR"] [data-test-certificate-card] button') - .hasAttribute('data-clipboard-text', this.model.csr, 'it renders copyable csr'); + .hasAttribute('data-test-copy-button', this.model.csr, 'it renders copyable csr'); assert .dom('[data-test-value-div="Key ID"] button') - .hasAttribute('data-clipboard-text', this.model.keyId, 'it renders copyable key_id'); + .hasAttribute('data-test-copy-button', this.model.keyId, 'it renders copyable key_id'); assert.dom('[data-test-value-div="Private key"]').hasText('internal', 'does not render private key'); assert .dom('[data-test-value-div="Private key type"]') diff --git a/ui/tests/integration/components/sidebar/user-menu-test.js b/ui/tests/integration/components/sidebar/user-menu-test.js index 0b5b0dfc382c..f43a948c26b3 100644 --- a/ui/tests/integration/components/sidebar/user-menu-test.js +++ b/ui/tests/integration/components/sidebar/user-menu-test.js @@ -35,7 +35,7 @@ module('Integration | Component | sidebar-user-menu', function (hooks) { assert.dom('.menu-label').hasText('Token', 'Auth data display name renders'); assert.dom('li').exists({ count: 2 }, 'Correct number of menu items render'); - assert.dom('[data-clipboard-text="root"]').exists('Copy token action renders'); + assert.dom('[data-test-copy-button="root"]').exists('Copy token action renders'); assert.dom('#logout').hasText('Log out', 'Log out action renders'); }); diff --git a/ui/yarn.lock b/ui/yarn.lock index ee60cec2407d..85a24bfdc034 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -67,6 +67,16 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.22.13": + version: 7.23.5 + resolution: "@babel/code-frame@npm:7.23.5" + dependencies: + "@babel/highlight": ^7.23.4 + chalk: ^2.4.2 + checksum: d90981fdf56a2824a9b14d19a4c0e8db93633fd488c772624b4e83e0ceac6039a27cd298a247c3214faa952bf803ba23696172ae7e7235f3b97f43ba278c569a + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.22.5": version: 7.22.5 resolution: "@babel/code-frame@npm:7.22.5" @@ -104,6 +114,13 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.23.3, @babel/compat-data@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/compat-data@npm:7.23.5" + checksum: 06ce244cda5763295a0ea924728c09bae57d35713b675175227278896946f922a63edf803c322f855a3878323d48d0255a2a3023409d2a123483c8a69ebb4744 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.22.9": version: 7.22.9 resolution: "@babel/compat-data@npm:7.22.9" @@ -375,6 +392,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.22.15" + dependencies: + "@babel/types": ^7.22.15 + checksum: 639c697a1c729f9fafa2dd4c9af2e18568190299b5907bd4c2d0bc818fcbd1e83ffeecc2af24327a7faa7ac4c34edd9d7940510a5e66296c19bad17001cf5c7a + languageName: node + linkType: hard + "@babel/helper-compilation-targets@npm:^7.12.0, @babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.13.16, @babel/helper-compilation-targets@npm:^7.13.8, @babel/helper-compilation-targets@npm:^7.16.0": version: 7.16.3 resolution: "@babel/helper-compilation-targets@npm:7.16.3" @@ -431,6 +457,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.6": + version: 7.22.15 + resolution: "@babel/helper-compilation-targets@npm:7.22.15" + dependencies: + "@babel/compat-data": ^7.22.9 + "@babel/helper-validator-option": ^7.22.15 + browserslist: ^4.21.9 + lru-cache: ^5.1.1 + semver: ^6.3.1 + checksum: ce85196769e091ae54dd39e4a80c2a9df1793da8588e335c383d536d54f06baf648d0a08fc873044f226398c4ded15c4ae9120ee18e7dfd7c639a68e3cdc9980 + languageName: node + linkType: hard + "@babel/helper-compilation-targets@npm:^7.22.9": version: 7.22.9 resolution: "@babel/helper-compilation-targets@npm:7.22.9" @@ -513,6 +552,25 @@ __metadata: languageName: node linkType: hard +"@babel/helper-create-class-features-plugin@npm:^7.21.0, @babel/helper-create-class-features-plugin@npm:^7.22.15, @babel/helper-create-class-features-plugin@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/helper-create-class-features-plugin@npm:7.23.5" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-function-name": ^7.23.0 + "@babel/helper-member-expression-to-functions": ^7.23.0 + "@babel/helper-optimise-call-expression": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.20 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: fe7c6c0baca1838bba76ac1330df47b661d932354115ea9e2ea65b179f80b717987d3c3da7e1525fd648e5f2d86c620efc959cabda4d7562b125a27c3ac780d0 + languageName: node + linkType: hard + "@babel/helper-create-class-features-plugin@npm:^7.22.6": version: 7.22.9 resolution: "@babel/helper-create-class-features-plugin@npm:7.22.9" @@ -584,6 +642,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.15, @babel/helper-create-regexp-features-plugin@npm:^7.22.5": + version: 7.22.15 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.15" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + regexpu-core: ^5.3.1 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 0243b8d4854f1dc8861b1029a46d3f6393ad72f366a5a08e36a4648aa682044f06da4c6e87a456260e1e1b33c999f898ba591a0760842c1387bcc93fbf2151a6 + languageName: node + linkType: hard + "@babel/helper-define-polyfill-provider@npm:^0.2.2": version: 0.2.3 resolution: "@babel/helper-define-polyfill-provider@npm:0.2.3" @@ -656,6 +727,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-define-polyfill-provider@npm:^0.4.3": + version: 0.4.3 + resolution: "@babel/helper-define-polyfill-provider@npm:0.4.3" + dependencies: + "@babel/helper-compilation-targets": ^7.22.6 + "@babel/helper-plugin-utils": ^7.22.5 + debug: ^4.1.1 + lodash.debounce: ^4.0.8 + resolve: ^1.14.2 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 5d21e3f47b320e4b5b644195ec405e7ebc3739e48e65899efc808c5fa9c3bf5b06ce0d8ff5246ca99d1411e368f4557bc66730196c5781a5c4e986ee703bee79 + languageName: node + linkType: hard + "@babel/helper-environment-visitor@npm:^7.16.7": version: 7.16.7 resolution: "@babel/helper-environment-visitor@npm:7.16.7" @@ -679,6 +765,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-environment-visitor@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-environment-visitor@npm:7.22.20" + checksum: d80ee98ff66f41e233f36ca1921774c37e88a803b2f7dca3db7c057a5fea0473804db9fb6729e5dbfd07f4bed722d60f7852035c2c739382e84c335661590b69 + languageName: node + linkType: hard + "@babel/helper-environment-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-environment-visitor@npm:7.22.5" @@ -756,6 +849,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-function-name@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/helper-function-name@npm:7.23.0" + dependencies: + "@babel/template": ^7.22.15 + "@babel/types": ^7.23.0 + checksum: e44542257b2d4634a1f979244eb2a4ad8e6d75eb6761b4cfceb56b562f7db150d134bc538c8e6adca3783e3bc31be949071527aa8e3aab7867d1ad2d84a26e10 + languageName: node + linkType: hard + "@babel/helper-get-function-arity@npm:^7.16.0": version: 7.16.0 resolution: "@babel/helper-get-function-arity@npm:7.16.0" @@ -837,6 +940,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-member-expression-to-functions@npm:^7.22.15, @babel/helper-member-expression-to-functions@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/helper-member-expression-to-functions@npm:7.23.0" + dependencies: + "@babel/types": ^7.23.0 + checksum: 494659361370c979ada711ca685e2efe9460683c36db1b283b446122596602c901e291e09f2f980ecedfe6e0f2bd5386cb59768285446530df10c14df1024e75 + languageName: node + linkType: hard + "@babel/helper-member-expression-to-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-member-expression-to-functions@npm:7.22.5" @@ -873,6 +985,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/helper-module-imports@npm:7.22.15" + dependencies: + "@babel/types": ^7.22.15 + checksum: ecd7e457df0a46f889228f943ef9b4a47d485d82e030676767e6a2fdcbdaa63594d8124d4b55fd160b41c201025aec01fc27580352b1c87a37c9c6f33d116702 + languageName: node + linkType: hard + "@babel/helper-module-imports@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-module-imports@npm:7.22.5" @@ -961,6 +1082,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/helper-module-transforms@npm:7.23.3" + dependencies: + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-module-imports": ^7.22.15 + "@babel/helper-simple-access": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/helper-validator-identifier": ^7.22.20 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 5d0895cfba0e16ae16f3aa92fee108517023ad89a855289c4eb1d46f7aef4519adf8e6f971e1d55ac20c5461610e17213f1144097a8f932e768a9132e2278d71 + languageName: node + linkType: hard + "@babel/helper-optimise-call-expression@npm:^7.12.13, @babel/helper-optimise-call-expression@npm:^7.16.0": version: 7.16.0 resolution: "@babel/helper-optimise-call-expression@npm:7.16.0" @@ -1025,7 +1161,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.22.5": +"@babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-plugin-utils@npm:7.22.5" checksum: c0fc7227076b6041acd2f0e818145d2e8c41968cc52fb5ca70eed48e21b8fe6dd88a0a91cbddf4951e33647336eb5ae184747ca706817ca3bef5e9e905151ff5 @@ -1054,6 +1190,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-remap-async-to-generator@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-remap-async-to-generator@npm:7.22.20" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-wrap-function": ^7.22.20 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 2fe6300a6f1b58211dffa0aed1b45d4958506d096543663dba83bd9251fe8d670fa909143a65b45e72acb49e7e20fbdb73eae315d9ddaced467948c3329986e7 + languageName: node + linkType: hard + "@babel/helper-replace-supers@npm:^7.13.12, @babel/helper-replace-supers@npm:^7.16.0": version: 7.16.0 resolution: "@babel/helper-replace-supers@npm:7.16.0" @@ -1105,6 +1254,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-replace-supers@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-replace-supers@npm:7.22.20" + dependencies: + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-member-expression-to-functions": ^7.22.15 + "@babel/helper-optimise-call-expression": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: a0008332e24daedea2e9498733e3c39b389d6d4512637e000f96f62b797e702ee24a407ccbcd7a236a551590a38f31282829a8ef35c50a3c0457d88218cae639 + languageName: node + linkType: hard + "@babel/helper-replace-supers@npm:^7.22.5, @babel/helper-replace-supers@npm:^7.22.9": version: 7.22.9 resolution: "@babel/helper-replace-supers@npm:7.22.9" @@ -1238,6 +1400,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/helper-string-parser@npm:7.23.4" + checksum: c0641144cf1a7e7dc93f3d5f16d5327465b6cf5d036b48be61ecba41e1eece161b48f46b7f960951b67f8c3533ce506b16dece576baef4d8b3b49f8c65410f90 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.14.0, @babel/helper-validator-identifier@npm:^7.15.7": version: 7.15.7 resolution: "@babel/helper-validator-identifier@npm:7.15.7" @@ -1266,6 +1435,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-validator-identifier@npm:7.22.20" + checksum: 136412784d9428266bcdd4d91c32bcf9ff0e8d25534a9d94b044f77fe76bc50f941a90319b05aafd1ec04f7d127cd57a179a3716009ff7f3412ef835ada95bdc + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-identifier@npm:7.22.5" @@ -1294,6 +1470,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/helper-validator-option@npm:7.23.5" + checksum: 537cde2330a8aede223552510e8a13e9c1c8798afee3757995a7d4acae564124fe2bf7e7c3d90d62d3657434a74340a274b3b3b1c6f17e9a2be1f48af29cb09e + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-option@npm:7.22.5" @@ -1325,6 +1508,17 @@ __metadata: languageName: node linkType: hard +"@babel/helper-wrap-function@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-wrap-function@npm:7.22.20" + dependencies: + "@babel/helper-function-name": ^7.22.5 + "@babel/template": ^7.22.15 + "@babel/types": ^7.22.19 + checksum: 221ed9b5572612aeb571e4ce6a256f2dee85b3c9536f1dd5e611b0255e5f59a3d0ec392d8d46d4152149156a8109f92f20379b1d6d36abb613176e0e33f05fca + languageName: node + linkType: hard + "@babel/helpers@npm:^7.14.0, @babel/helpers@npm:^7.16.0": version: 7.16.3 resolution: "@babel/helpers@npm:7.16.3" @@ -1424,6 +1618,17 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/highlight@npm:7.23.4" + dependencies: + "@babel/helper-validator-identifier": ^7.22.20 + chalk: ^2.4.2 + js-tokens: ^4.0.0 + checksum: 643acecdc235f87d925979a979b539a5d7d1f31ae7db8d89047269082694122d11aa85351304c9c978ceeb6d250591ccadb06c366f358ccee08bb9c122476b89 + languageName: node + linkType: hard + "@babel/parser@npm:^7.12.3, @babel/parser@npm:^7.4.5": version: 7.14.0 resolution: "@babel/parser@npm:7.14.0" @@ -1469,6 +1674,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.22.15": + version: 7.23.5 + resolution: "@babel/parser@npm:7.23.5" + bin: + parser: ./bin/babel-parser.js + checksum: ea763629310f71580c4a3ea9d3705195b7ba994ada2cc98f9a584ebfdacf54e92b2735d351672824c2c2b03c7f19206899f4d95650d85ce514a822b19a8734c7 + languageName: node + linkType: hard + "@babel/parser@npm:^7.22.5, @babel/parser@npm:^7.22.7": version: 7.22.7 resolution: "@babel/parser@npm:7.22.7" @@ -1500,6 +1714,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: ddbaf2c396b7780f15e80ee01d6dd790db076985f3dfeb6527d1a8d4cacf370e49250396a3aa005b2c40233cac214a106232f83703d5e8491848bde273938232 + languageName: node + linkType: hard + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.13.12": version: 7.16.0 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.16.0" @@ -1539,6 +1764,31 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/plugin-transform-optional-chaining": ^7.23.3 + peerDependencies: + "@babel/core": ^7.13.0 + checksum: 434b9d710ae856fa1a456678cc304fbc93915af86d581ee316e077af746a709a741ea39d7e1d4f5b98861b629cc7e87f002d3138f5e836775632466d4c74aef2 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.23.3" + dependencies: + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 4690123f0ef7c11d6bf1a9579e4f463ce363563b75ec3f6ca66cf68687e39d8d747a82c833847653962f79da367eca895d9095c60d8ebb224a1d4277003acc11 + languageName: node + linkType: hard + "@babel/plugin-proposal-async-generator-functions@npm:^7.13.15": version: 7.16.4 resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.16.4" @@ -1694,6 +1944,21 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-decorators@npm:^7.20.13": + version: 7.23.5 + resolution: "@babel/plugin-proposal-decorators@npm:7.23.5" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.23.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.20 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/plugin-syntax-decorators": ^7.23.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e925fe7a82c03aa372b92b312e69a05453d55aaaf2fd48336c88f4fe2b7e81bf9e8e52b93d4f785a02f2b8deedee6964054153566b40c92886dcf795843a243e + languageName: node + linkType: hard + "@babel/plugin-proposal-decorators@npm:^7.21.0": version: 7.22.7 resolution: "@babel/plugin-proposal-decorators@npm:7.22.7" @@ -2060,6 +2325,15 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2": + version: 7.21.0-placeholder-for-preset-env.2 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d97745d098b835d55033ff3a7fb2b895b9c5295b08a5759e4f20df325aa385a3e0bc9bd5ad8f2ec554a44d4e6525acfc257b8c5848a1345cb40f26a30e277e91 + languageName: node + linkType: hard + "@babel/plugin-proposal-private-property-in-object@npm:^7.14.0": version: 7.16.0 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.16.0" @@ -2102,6 +2376,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-private-property-in-object@npm:^7.20.5": + version: 7.21.11 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.11" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-create-class-features-plugin": ^7.21.0 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1b880543bc5f525b360b53d97dd30807302bb82615cd42bf931968f59003cac75629563d6b104868db50abd22235b3271fdf679fea5db59a267181a99cc0c265 + languageName: node + linkType: hard + "@babel/plugin-proposal-unicode-property-regex@npm:^7.12.13, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": version: 7.16.0 resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.16.0" @@ -2215,6 +2503,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-decorators@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-syntax-decorators@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 07f6e488df0a061428e02629af9a1908b2e8abdcac2e5cfaa267be66dc30897be6e29df7c7f952d33f3679f9585ac9fcf6318f9c827790ab0b0928d5514305cd + languageName: node + linkType: hard + "@babel/plugin-syntax-dynamic-import@npm:^7.8.3": version: 7.8.3 resolution: "@babel/plugin-syntax-dynamic-import@npm:7.8.3" @@ -2248,6 +2547,39 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-import-assertions@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 883e6b35b2da205138caab832d54505271a3fee3fc1e8dc0894502434fc2b5d517cbe93bbfbfef8068a0fb6ec48ebc9eef3f605200a489065ba43d8cddc1c9a7 + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-attributes@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 9aed7661ffb920ca75df9f494757466ca92744e43072e0848d87fa4aa61a3f2ee5a22198ac1959856c036434b5614a8f46f1fb70298835dbe28220cdd1d4c11e + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-meta@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 166ac1125d10b9c0c430e4156249a13858c0366d38844883d75d27389621ebe651115cb2ceb6dc011534d5055719fa1727b59f39e1ab3ca97820eef3dcab5b9b + languageName: node + linkType: hard + "@babel/plugin-syntax-json-strings@npm:^7.8.3": version: 7.8.3 resolution: "@babel/plugin-syntax-json-strings@npm:7.8.3" @@ -2380,6 +2712,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-typescript@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-syntax-typescript@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: abfad3a19290d258b028e285a1f34c9b8a0cbe46ef79eafed4ed7ffce11b5d0720b5e536c82f91cbd8442cde35a3dd8e861fa70366d87ff06fdc0d4756e30876 + languageName: node + linkType: hard + +"@babel/plugin-syntax-unicode-sets-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-syntax-unicode-sets-regex@npm:7.18.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: a651d700fe63ff0ddfd7186f4ebc24447ca734f114433139e3c027bc94a900d013cf1ef2e2db8430425ba542e39ae160c3b05f06b59fd4656273a3df97679e9c + languageName: node + linkType: hard + "@babel/plugin-transform-arrow-functions@npm:^7.13.0": version: 7.16.0 resolution: "@babel/plugin-transform-arrow-functions@npm:7.16.0" @@ -2413,6 +2768,31 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-arrow-functions@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1e99118176e5366c2636064d09477016ab5272b2a92e78b8edb571d20bc3eaa881789a905b20042942c3c2d04efc530726cf703f937226db5ebc495f5d067e66 + languageName: node + linkType: hard + +"@babel/plugin-transform-async-generator-functions@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.23.4" + dependencies: + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-remap-async-to-generator": ^7.22.20 + "@babel/plugin-syntax-async-generators": ^7.8.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e2fc132c9033711d55209f4781e1fc73f0f4da5e0ca80a2da73dec805166b73c92a6e83571a8994cd2c893a28302e24107e90856202b24781bab734f800102bb + languageName: node + linkType: hard + "@babel/plugin-transform-async-to-generator@npm:^7.13.0": version: 7.16.0 resolution: "@babel/plugin-transform-async-to-generator@npm:7.16.0" @@ -2452,6 +2832,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-async-to-generator@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.23.3" + dependencies: + "@babel/helper-module-imports": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-remap-async-to-generator": ^7.22.20 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2e9d9795d4b3b3d8090332104e37061c677f29a1ce65bcbda4099a32d243e5d9520270a44bbabf0fb1fb40d463bd937685b1a1042e646979086c546d55319c3c + languageName: node + linkType: hard + "@babel/plugin-transform-block-scoped-functions@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.16.0" @@ -2474,6 +2867,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-block-scoped-functions@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e63b16d94ee5f4d917e669da3db5ea53d1e7e79141a2ec873c1e644678cdafe98daa556d0d359963c827863d6b3665d23d4938a94a4c5053a1619c4ebd01d020 + languageName: node + linkType: hard + "@babel/plugin-transform-block-scoping@npm:^7.12.1": version: 7.13.16 resolution: "@babel/plugin-transform-block-scoping@npm:7.13.16" @@ -2529,6 +2933,42 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-block-scoping@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-block-scoping@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fc4b2100dd9f2c47d694b4b35ae8153214ccb4e24ef545c259a9db17211b18b6a430f22799b56db8f6844deaeaa201af45a03331d0c80cc28b0c4e3c814570e4 + languageName: node + linkType: hard + +"@babel/plugin-transform-class-properties@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-class-properties@npm:7.23.3" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 9c6f8366f667897541d360246de176dd29efc7a13d80a5b48361882f7173d9173be4646c3b7d9b003ccc0e01e25df122330308f33db921fa553aa17ad544b3fc + languageName: node + linkType: hard + +"@babel/plugin-transform-class-static-block@npm:^7.22.11, @babel/plugin-transform-class-static-block@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-class-static-block@npm:7.23.4" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-class-static-block": ^7.14.5 + peerDependencies: + "@babel/core": ^7.12.0 + checksum: c8bfaba19a674fc2eb54edad71e958647360474e3163e8226f1acd63e4e2dbec32a171a0af596c1dc5359aee402cc120fea7abd1fb0e0354b6527f0fc9e8aa1e + languageName: node + linkType: hard + "@babel/plugin-transform-classes@npm:^7.13.0": version: 7.16.0 resolution: "@babel/plugin-transform-classes@npm:7.16.0" @@ -2582,6 +3022,25 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-classes@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/plugin-transform-classes@npm:7.23.5" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-compilation-targets": ^7.22.15 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-function-name": ^7.23.0 + "@babel/helper-optimise-call-expression": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.20 + "@babel/helper-split-export-declaration": ^7.22.6 + globals: ^11.1.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6d0dd3b0828e84a139a51b368f33f315edee5688ef72c68ba25e0175c68ea7357f9c8810b3f61713e368a3063cdcec94f3a2db952e453b0b14ef428a34aa8169 + languageName: node + linkType: hard + "@babel/plugin-transform-computed-properties@npm:^7.13.0": version: 7.16.0 resolution: "@babel/plugin-transform-computed-properties@npm:7.16.0" @@ -2615,6 +3074,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-computed-properties@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-computed-properties@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/template": ^7.22.15 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 80452661dc25a0956f89fe98cb562e8637a9556fb6c00d312c57653ce7df8798f58d138603c7e1aad96614ee9ccd10c47e50ab9ded6b6eded5adeb230d2a982e + languageName: node + linkType: hard + "@babel/plugin-transform-destructuring@npm:^7.13.17": version: 7.16.0 resolution: "@babel/plugin-transform-destructuring@npm:7.16.0" @@ -2648,6 +3119,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-destructuring@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-destructuring@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 9e015099877272501162419bfe781689aec5c462cd2aec752ee22288f209eec65969ff11b8fdadca2eaddea71d705d3bba5b9c60752fcc1be67874fcec687105 + languageName: node + linkType: hard + "@babel/plugin-transform-dotall-regex@npm:^7.12.13, @babel/plugin-transform-dotall-regex@npm:^7.4.4": version: 7.16.0 resolution: "@babel/plugin-transform-dotall-regex@npm:7.16.0" @@ -2672,6 +3154,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-dotall-regex@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.23.3" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a2dbbf7f1ea16a97948c37df925cb364337668c41a3948b8d91453f140507bd8a3429030c7ce66d09c299987b27746c19a2dd18b6f17dcb474854b14fd9159a3 + languageName: node + linkType: hard + "@babel/plugin-transform-duplicate-keys@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.16.0" @@ -2705,6 +3199,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-duplicate-keys@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c2a21c34dc0839590cd945192cbc46fde541a27e140c48fe1808315934664cdbf18db64889e23c4eeb6bad9d3e049482efdca91d29de5734ffc887c4fbabaa16 + languageName: node + linkType: hard + +"@babel/plugin-transform-dynamic-import@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 57a722604c430d9f3dacff22001a5f31250e34785d4969527a2ae9160fa86858d0892c5b9ff7a06a04076f8c76c9e6862e0541aadca9c057849961343aab0845 + languageName: node + linkType: hard + "@babel/plugin-transform-exponentiation-operator@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.16.0" @@ -2729,6 +3246,30 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-exponentiation-operator@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.23.3" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 00d05ab14ad0f299160fcf9d8f55a1cc1b740e012ab0b5ce30207d2365f091665115557af7d989cd6260d075a252d9e4283de5f2b247dfbbe0e42ae586e6bf66 + languageName: node + linkType: hard + +"@babel/plugin-transform-export-namespace-from@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 9f770a81bfd03b48d6ba155d452946fd56d6ffe5b7d871e9ec2a0b15e0f424273b632f3ed61838b90015b25bbda988896b7a46c7d964fbf8f6feb5820b309f93 + languageName: node + linkType: hard + "@babel/plugin-transform-for-of@npm:^7.13.0": version: 7.16.0 resolution: "@babel/plugin-transform-for-of@npm:7.16.0" @@ -2762,6 +3303,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-for-of@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-for-of@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a6288122a5091d96c744b9eb23dc1b2d4cce25f109ac1e26a0ea03c4ea60330e6f3cc58530b33ba7369fa07163b71001399a145238b7e92bff6270ef3b9c32a0 + languageName: node + linkType: hard + "@babel/plugin-transform-function-name@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-function-name@npm:7.16.0" @@ -2787,6 +3339,31 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-function-name@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-function-name@npm:7.23.3" + dependencies: + "@babel/helper-compilation-targets": ^7.22.15 + "@babel/helper-function-name": ^7.23.0 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 355c6dbe07c919575ad42b2f7e020f320866d72f8b79181a16f8e0cd424a2c761d979f03f47d583d9471b55dcd68a8a9d829b58e1eebcd572145b934b48975a6 + languageName: node + linkType: hard + +"@babel/plugin-transform-json-strings@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-json-strings@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-json-strings": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f9019820233cf8955d8ba346df709a0683c120fe86a24ed1c9f003f2db51197b979efc88f010d558a12e1491210fc195a43cd1c7fee5e23b92da38f793a875de + languageName: node + linkType: hard + "@babel/plugin-transform-literals@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-literals@npm:7.16.0" @@ -2820,6 +3397,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-literals@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-literals@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 519a544cd58586b9001c4c9b18da25a62f17d23c48600ff7a685d75ca9eb18d2c5e8f5476f067f0a8f1fea2a31107eff950b9864833061e6076dcc4bdc3e71ed + languageName: node + linkType: hard + +"@babel/plugin-transform-logical-assignment-operators@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2ae1dc9b4ff3bf61a990ff3accdecb2afe3a0ca649b3e74c010078d1cdf29ea490f50ac0a905306a2bcf9ac177889a39ac79bdcc3a0fdf220b3b75fac18d39b5 + languageName: node + linkType: hard + "@babel/plugin-transform-member-expression-literals@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.16.0" @@ -2842,6 +3442,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-member-expression-literals@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 95cec13c36d447c5aa6b8e4c778b897eeba66dcb675edef01e0d2afcec9e8cb9726baf4f81b4bbae7a782595aed72e6a0d44ffb773272c3ca180fada99bf92db + languageName: node + linkType: hard + "@babel/plugin-transform-modules-amd@npm:^7.13.0, @babel/plugin-transform-modules-amd@npm:^7.14.0": version: 7.16.0 resolution: "@babel/plugin-transform-modules-amd@npm:7.16.0" @@ -2893,6 +3504,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-amd@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-modules-amd@npm:7.23.3" + dependencies: + "@babel/helper-module-transforms": ^7.23.3 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d163737b6a3d67ea579c9aa3b83d4df4b5c34d9dcdf25f415f027c0aa8cded7bac2750d2de5464081f67a042ad9e1c03930c2fab42acd79f9e57c00cf969ddff + languageName: node + linkType: hard + "@babel/plugin-transform-modules-commonjs@npm:^7.14.0": version: 7.16.0 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.16.0" @@ -2935,6 +3558,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-commonjs@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.23.3" + dependencies: + "@babel/helper-module-transforms": ^7.23.3 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-simple-access": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 720a231ceade4ae4d2632478db4e7fecf21987d444942b72d523487ac8d715ca97de6c8f415c71e939595e1a4776403e7dc24ed68fe9125ad4acf57753c9bff7 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-systemjs@npm:^7.13.8": version: 7.16.0 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.16.0" @@ -2980,6 +3616,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-systemjs@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.23.3" + dependencies: + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-module-transforms": ^7.23.3 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.20 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0d2fdd993c785aecac9e0850cd5ed7f7d448f0fbb42992a950cc0590167144df25d82af5aac9a5c99ef913d2286782afa44e577af30c10901c5ee8984910fa1f + languageName: node + linkType: hard + "@babel/plugin-transform-modules-umd@npm:^7.14.0": version: 7.16.0 resolution: "@babel/plugin-transform-modules-umd@npm:7.16.0" @@ -3016,6 +3666,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-umd@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-modules-umd@npm:7.23.3" + dependencies: + "@babel/helper-module-transforms": ^7.23.3 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 586a7a2241e8b4e753a37af9466a9ffa8a67b4ba9aa756ad7500712c05d8fa9a8c1ed4f7bd25fae2a8265e6cf8fe781ec85a8ee885dd34cf50d8955ee65f12dc + languageName: node + linkType: hard + "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.16.0" @@ -3050,36 +3712,98 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.12.13": - version: 7.16.0 - resolution: "@babel/plugin-transform-new-target@npm:7.16.0" +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.5" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 3ee564ddee620c035b928fdc942c5d17e9c4b98329b76f9cefac65c111135d925eb94ed324064cd7556d4f5123beec79abea1d4b97d1c8a2a5c748887a2eb623 + languageName: node + linkType: hard + +"@babel/plugin-transform-new-target@npm:^7.12.13": + version: 7.16.0 + resolution: "@babel/plugin-transform-new-target@npm:7.16.0" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c741ba3e84c182f1af3174cb7f00c4e434080ff882e72c7b2743d1d636eebcf12c865772be051a323c823bd4ebdfbae19cb78e95218d6b14c338f27a64608e31 + languageName: node + linkType: hard + +"@babel/plugin-transform-new-target@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/plugin-transform-new-target@npm:7.16.7" + dependencies: + "@babel/helper-plugin-utils": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7410c3e68abc835f87a98d40269e65fb1a05c131decbb6721a80ed49a01bd0c53abb6b8f7f52d5055815509022790e1accca32e975c02f2231ac3cf13d8af768 + languageName: node + linkType: hard + +"@babel/plugin-transform-new-target@npm:^7.17.12": + version: 7.17.12 + resolution: "@babel/plugin-transform-new-target@npm:7.17.12" + dependencies: + "@babel/helper-plugin-utils": ^7.17.12 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bec26350fa49c9a9431d23b4ff234f8eb60554b8cdffca432a94038406aae5701014f343568c0e0cc8afae6f95d492f6bae0d0e2c101c1a484fb20eec75b2c07 + languageName: node + linkType: hard + +"@babel/plugin-transform-new-target@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-new-target@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e5053389316fce73ad5201b7777437164f333e24787fbcda4ae489cd2580dbbbdfb5694a7237bad91fabb46b591d771975d69beb1c740b82cb4761625379f00b + languageName: node + linkType: hard + +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.23.4" dependencies: - "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c741ba3e84c182f1af3174cb7f00c4e434080ff882e72c7b2743d1d636eebcf12c865772be051a323c823bd4ebdfbae19cb78e95218d6b14c338f27a64608e31 + checksum: a27d73ea134d3d9560a6b2e26ab60012fba15f1db95865aa0153c18f5ec82cfef6a7b3d8df74e3c2fca81534fa5efeb6cacaf7b08bdb7d123e3dafdd079886a3 languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/plugin-transform-new-target@npm:7.16.7" +"@babel/plugin-transform-numeric-separator@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.23.4" dependencies: - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7410c3e68abc835f87a98d40269e65fb1a05c131decbb6721a80ed49a01bd0c53abb6b8f7f52d5055815509022790e1accca32e975c02f2231ac3cf13d8af768 + checksum: 6ba0e5db3c620a3ec81f9e94507c821f483c15f196868df13fa454cbac719a5449baf73840f5b6eb7d77311b24a2cf8e45db53700d41727f693d46f7caf3eec3 languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.17.12": - version: 7.17.12 - resolution: "@babel/plugin-transform-new-target@npm:7.17.12" +"@babel/plugin-transform-object-rest-spread@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.23.4" dependencies: - "@babel/helper-plugin-utils": ^7.17.12 + "@babel/compat-data": ^7.23.3 + "@babel/helper-compilation-targets": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-object-rest-spread": ^7.8.3 + "@babel/plugin-transform-parameters": ^7.23.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bec26350fa49c9a9431d23b4ff234f8eb60554b8cdffca432a94038406aae5701014f343568c0e0cc8afae6f95d492f6bae0d0e2c101c1a484fb20eec75b2c07 + checksum: 73fec495e327ca3959c1c03d07a621be09df00036c69fff0455af9a008291677ee9d368eec48adacdc6feac703269a649747568b4af4c4e9f134aa71cc5b378d languageName: node linkType: hard @@ -3107,6 +3831,43 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-object-super@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-object-super@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.20 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e495497186f621fa79026e183b4f1fbb172fd9df812cbd2d7f02c05b08adbe58012b1a6eb6dd58d11a30343f6ec80d0f4074f9b501d70aa1c94df76d59164c53 + languageName: node + linkType: hard + +"@babel/plugin-transform-optional-catch-binding@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d50b5ee142cdb088d8b5de1ccf7cea85b18b85d85b52f86618f6e45226372f01ad4cdb29abd4fd35ea99a71fefb37009e0107db7a787dcc21d4d402f97470faf + languageName: node + linkType: hard + +"@babel/plugin-transform-optional-chaining@npm:^7.23.3, @babel/plugin-transform-optional-chaining@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.4" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/plugin-syntax-optional-chaining": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e7a4c08038288057b7a08d68c4d55396ada9278095509ca51ed8dfb72a7f13f26bdd7c5185de21079fe0a9d60d22c227cb32e300d266c1bda40f70eee9f4bc1e + languageName: node + linkType: hard + "@babel/plugin-transform-parameters@npm:^7.13.0, @babel/plugin-transform-parameters@npm:^7.16.0": version: 7.16.3 resolution: "@babel/plugin-transform-parameters@npm:7.16.3" @@ -3140,6 +3901,43 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-parameters@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-parameters@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a735b3e85316d17ec102e3d3d1b6993b429bdb3b494651c9d754e3b7d270462ee1f1a126ccd5e3d871af5e683727e9ef98c9d34d4a42204fffaabff91052ed16 + languageName: node + linkType: hard + +"@babel/plugin-transform-private-methods@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-private-methods@npm:7.23.3" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: cedc1285c49b5a6d9a3d0e5e413b756ac40b3ac2f8f68bdfc3ae268bc8d27b00abd8bb0861c72756ff5dd8bf1eb77211b7feb5baf4fdae2ebbaabe49b9adc1d0 + languageName: node + linkType: hard + +"@babel/plugin-transform-private-property-in-object@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.23.4" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-create-class-features-plugin": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fb7adfe94ea97542f250a70de32bddbc3e0b802381c92be947fec83ebffda57e68533c4d0697152719a3496fdd3ebf3798d451c024cd4ac848fc15ac26b70aa7 + languageName: node + linkType: hard + "@babel/plugin-transform-property-literals@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-property-literals@npm:7.16.0" @@ -3162,6 +3960,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-property-literals@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-property-literals@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 16b048c8e87f25095f6d53634ab7912992f78e6997a6ff549edc3cf519db4fca01c7b4e0798530d7f6a05228ceee479251245cdd850a5531c6e6f404104d6cc9 + languageName: node + linkType: hard + "@babel/plugin-transform-regenerator@npm:^7.13.15": version: 7.16.0 resolution: "@babel/plugin-transform-regenerator@npm:7.16.0" @@ -3196,6 +4005,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-regenerator@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-regenerator@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + regenerator-transform: ^0.15.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7fdacc7b40008883871b519c9e5cdea493f75495118ccc56ac104b874983569a24edd024f0f5894ba1875c54ee2b442f295d6241c3280e61c725d0dd3317c8e6 + languageName: node + linkType: hard + "@babel/plugin-transform-reserved-words@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-reserved-words@npm:7.16.0" @@ -3229,6 +4050,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-reserved-words@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-reserved-words@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 298c4440ddc136784ff920127cea137168e068404e635dc946ddb5d7b2a27b66f1dd4c4acb01f7184478ff7d5c3e7177a127279479926519042948fb7fa0fa48 + languageName: node + linkType: hard + "@babel/plugin-transform-runtime@npm:^7.12.1": version: 7.13.15 resolution: "@babel/plugin-transform-runtime@npm:7.13.15" @@ -3283,6 +4115,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-shorthand-properties@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 5d677a03676f9fff969b0246c423d64d77502e90a832665dc872a5a5e05e5708161ce1effd56bb3c0f2c20a1112fca874be57c8a759d8b08152755519281f326 + languageName: node + linkType: hard + "@babel/plugin-transform-spread@npm:^7.13.0": version: 7.16.0 resolution: "@babel/plugin-transform-spread@npm:7.16.0" @@ -3319,6 +4162,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-spread@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-spread@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8fd5cac201e77a0b4825745f4e07a25f923842f282f006b3a79223c00f61075c8868d12eafec86b2642cd0b32077cdd32314e27bcb75ee5e6a68c0144140dcf2 + languageName: node + linkType: hard + "@babel/plugin-transform-sticky-regex@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-sticky-regex@npm:7.16.0" @@ -3341,6 +4196,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-sticky-regex@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 53e55eb2575b7abfdb4af7e503a2bf7ef5faf8bf6b92d2cd2de0700bdd19e934e5517b23e6dfed94ba50ae516b62f3f916773ef7d9bc81f01503f585051e2949 + languageName: node + linkType: hard + "@babel/plugin-transform-template-literals@npm:^7.13.0": version: 7.16.0 resolution: "@babel/plugin-transform-template-literals@npm:7.16.0" @@ -3374,6 +4240,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-template-literals@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-template-literals@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b16c5cb0b8796be0118e9c144d15bdc0d20a7f3f59009c6303a6e9a8b74c146eceb3f05186f5b97afcba7cfa87e34c1585a22186e3d5b22f2fd3d27d959d92b2 + languageName: node + linkType: hard + "@babel/plugin-transform-typeof-symbol@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.16.0" @@ -3407,6 +4284,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typeof-symbol@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0af7184379d43afac7614fc89b1bdecce4e174d52f4efaeee8ec1a4f2c764356c6dba3525c0685231f1cbf435b6dd4ee9e738d7417f3b10ce8bbe869c32f4384 + languageName: node + linkType: hard + "@babel/plugin-transform-typescript@npm:^7.13.0": version: 7.16.1 resolution: "@babel/plugin-transform-typescript@npm:7.16.1" @@ -3433,6 +4321,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typescript@npm:^7.20.13": + version: 7.23.5 + resolution: "@babel/plugin-transform-typescript@npm:7.23.5" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-create-class-features-plugin": ^7.23.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-typescript": ^7.23.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d77b5cc22cf48fe461de07e4f058dc9c0d8c4e3ca49de0e3a336a94ab39bfa4f4732598e36c479bec0dd1bf4aff9154bc2dcbfbe3145a751e4771ccae5afaaf8 + languageName: node + linkType: hard + "@babel/plugin-transform-typescript@npm:~7.4.0": version: 7.4.5 resolution: "@babel/plugin-transform-typescript@npm:7.4.5" @@ -3493,6 +4395,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-escapes@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 561c429183a54b9e4751519a3dfba6014431e9cdc1484fad03bdaf96582dfc72c76a4f8661df2aeeae7c34efd0fa4d02d3b83a2f63763ecf71ecc925f9cc1f60 + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-property-regex@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.23.3" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2298461a194758086d17c23c26c7de37aa533af910f9ebf31ebd0893d4aa317468043d23f73edc782ec21151d3c46cf0ff8098a83b725c49a59de28a1d4d6225 + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-regex@npm:^7.12.13": version: 7.16.0 resolution: "@babel/plugin-transform-unicode-regex@npm:7.16.0" @@ -3517,6 +4442,30 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-regex@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.23.3" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c5f835d17483ba899787f92e313dfa5b0055e3deab332f1d254078a2bba27ede47574b6599fcf34d3763f0c048ae0779dc21d2d8db09295edb4057478dc80a9a + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-sets-regex@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.23.3" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 79d0b4c951955ca68235c87b91ab2b393c96285f8aeaa34d6db416d2ddac90000c9bd6e8c4d82b60a2b484da69930507245035f28ba63c6cae341cf3ba68fdef + languageName: node + linkType: hard + "@babel/polyfill@npm:^7.11.5": version: 7.12.1 resolution: "@babel/polyfill@npm:7.12.1" @@ -3779,6 +4728,109 @@ __metadata: languageName: node linkType: hard +"@babel/preset-env@npm:^7.20.2": + version: 7.23.5 + resolution: "@babel/preset-env@npm:7.23.5" + dependencies: + "@babel/compat-data": ^7.23.5 + "@babel/helper-compilation-targets": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-option": ^7.23.5 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.23.3 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.23.3 + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ^7.23.3 + "@babel/plugin-proposal-private-property-in-object": 7.21.0-placeholder-for-preset-env.2 + "@babel/plugin-syntax-async-generators": ^7.8.4 + "@babel/plugin-syntax-class-properties": ^7.12.13 + "@babel/plugin-syntax-class-static-block": ^7.14.5 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + "@babel/plugin-syntax-import-assertions": ^7.23.3 + "@babel/plugin-syntax-import-attributes": ^7.23.3 + "@babel/plugin-syntax-import-meta": ^7.10.4 + "@babel/plugin-syntax-json-strings": ^7.8.3 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 + "@babel/plugin-syntax-object-rest-spread": ^7.8.3 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + "@babel/plugin-syntax-optional-chaining": ^7.8.3 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + "@babel/plugin-syntax-top-level-await": ^7.14.5 + "@babel/plugin-syntax-unicode-sets-regex": ^7.18.6 + "@babel/plugin-transform-arrow-functions": ^7.23.3 + "@babel/plugin-transform-async-generator-functions": ^7.23.4 + "@babel/plugin-transform-async-to-generator": ^7.23.3 + "@babel/plugin-transform-block-scoped-functions": ^7.23.3 + "@babel/plugin-transform-block-scoping": ^7.23.4 + "@babel/plugin-transform-class-properties": ^7.23.3 + "@babel/plugin-transform-class-static-block": ^7.23.4 + "@babel/plugin-transform-classes": ^7.23.5 + "@babel/plugin-transform-computed-properties": ^7.23.3 + "@babel/plugin-transform-destructuring": ^7.23.3 + "@babel/plugin-transform-dotall-regex": ^7.23.3 + "@babel/plugin-transform-duplicate-keys": ^7.23.3 + "@babel/plugin-transform-dynamic-import": ^7.23.4 + "@babel/plugin-transform-exponentiation-operator": ^7.23.3 + "@babel/plugin-transform-export-namespace-from": ^7.23.4 + "@babel/plugin-transform-for-of": ^7.23.3 + "@babel/plugin-transform-function-name": ^7.23.3 + "@babel/plugin-transform-json-strings": ^7.23.4 + "@babel/plugin-transform-literals": ^7.23.3 + "@babel/plugin-transform-logical-assignment-operators": ^7.23.4 + "@babel/plugin-transform-member-expression-literals": ^7.23.3 + "@babel/plugin-transform-modules-amd": ^7.23.3 + "@babel/plugin-transform-modules-commonjs": ^7.23.3 + "@babel/plugin-transform-modules-systemjs": ^7.23.3 + "@babel/plugin-transform-modules-umd": ^7.23.3 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.22.5 + "@babel/plugin-transform-new-target": ^7.23.3 + "@babel/plugin-transform-nullish-coalescing-operator": ^7.23.4 + "@babel/plugin-transform-numeric-separator": ^7.23.4 + "@babel/plugin-transform-object-rest-spread": ^7.23.4 + "@babel/plugin-transform-object-super": ^7.23.3 + "@babel/plugin-transform-optional-catch-binding": ^7.23.4 + "@babel/plugin-transform-optional-chaining": ^7.23.4 + "@babel/plugin-transform-parameters": ^7.23.3 + "@babel/plugin-transform-private-methods": ^7.23.3 + "@babel/plugin-transform-private-property-in-object": ^7.23.4 + "@babel/plugin-transform-property-literals": ^7.23.3 + "@babel/plugin-transform-regenerator": ^7.23.3 + "@babel/plugin-transform-reserved-words": ^7.23.3 + "@babel/plugin-transform-shorthand-properties": ^7.23.3 + "@babel/plugin-transform-spread": ^7.23.3 + "@babel/plugin-transform-sticky-regex": ^7.23.3 + "@babel/plugin-transform-template-literals": ^7.23.3 + "@babel/plugin-transform-typeof-symbol": ^7.23.3 + "@babel/plugin-transform-unicode-escapes": ^7.23.3 + "@babel/plugin-transform-unicode-property-regex": ^7.23.3 + "@babel/plugin-transform-unicode-regex": ^7.23.3 + "@babel/plugin-transform-unicode-sets-regex": ^7.23.3 + "@babel/preset-modules": 0.1.6-no-external-plugins + babel-plugin-polyfill-corejs2: ^0.4.6 + babel-plugin-polyfill-corejs3: ^0.8.5 + babel-plugin-polyfill-regenerator: ^0.5.3 + core-js-compat: ^3.31.0 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: adddd58d14fc1b2e5f8cf90995f522879362a0543e316afe9e5783f1bd715bb1e92300cd49d7ce3a95c64a96d60788d0089651e2cf4cac937f5469aac1087bb1 + languageName: node + linkType: hard + +"@babel/preset-modules@npm:0.1.6-no-external-plugins": + version: 0.1.6-no-external-plugins + resolution: "@babel/preset-modules@npm:0.1.6-no-external-plugins" + dependencies: + "@babel/helper-plugin-utils": ^7.0.0 + "@babel/types": ^7.4.4 + esutils: ^2.0.2 + peerDependencies: + "@babel/core": ^7.0.0-0 || ^8.0.0-0 <8.0.0 + checksum: 4855e799bc50f2449fb5210f78ea9e8fd46cf4f242243f1e2ed838e2bd702e25e73e822e7f8447722a5f4baa5e67a8f7a0e403f3e7ce04540ff743a9c411c375 + languageName: node + linkType: hard + "@babel/preset-modules@npm:^0.1.4, @babel/preset-modules@npm:^0.1.5": version: 0.1.5 resolution: "@babel/preset-modules@npm:0.1.5" @@ -3794,6 +4846,13 @@ __metadata: languageName: node linkType: hard +"@babel/regjsgen@npm:^0.8.0": + version: 0.8.0 + resolution: "@babel/regjsgen@npm:0.8.0" + checksum: 89c338fee774770e5a487382170711014d49a68eb281e74f2b5eac88f38300a4ad545516a7786a8dd5702e9cf009c94c2f582d200f077ac5decd74c56b973730 + languageName: node + linkType: hard + "@babel/runtime@npm:7.12.18": version: 7.12.18 resolution: "@babel/runtime@npm:7.12.18" @@ -3890,6 +4949,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/template@npm:7.22.15" + dependencies: + "@babel/code-frame": ^7.22.13 + "@babel/parser": ^7.22.15 + "@babel/types": ^7.22.15 + checksum: 1f3e7dcd6c44f5904c184b3f7fe280394b191f2fed819919ffa1e529c259d5b197da8981b6ca491c235aee8dbad4a50b7e31304aa531271cb823a4a24a0dd8fd + languageName: node + linkType: hard + "@babel/template@npm:^7.22.5": version: 7.22.5 resolution: "@babel/template@npm:7.22.5" @@ -4068,6 +5138,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.23.0": + version: 7.23.5 + resolution: "@babel/types@npm:7.23.5" + dependencies: + "@babel/helper-string-parser": ^7.23.4 + "@babel/helper-validator-identifier": ^7.22.20 + to-fast-properties: ^2.0.0 + checksum: 3d21774480a459ef13b41c2e32700d927af649e04b70c5d164814d8e04ab584af66a93330602c2925e1a6925c2b829cc153418a613a4e7d79d011be1f29ad4b2 + languageName: node + linkType: hard + "@babel/types@npm:^7.22.5": version: 7.22.5 resolution: "@babel/types@npm:7.22.5" @@ -4422,7 +5503,7 @@ __metadata: languageName: node linkType: hard -"@ember/string@npm:^3.0.1": +"@ember/string@npm:^3.0.1, @ember/string@npm:^3.1.1": version: 3.1.1 resolution: "@ember/string@npm:3.1.1" dependencies: @@ -4461,15 +5542,25 @@ __metadata: languageName: node linkType: hard -"@ember/test-waiters@npm:^3.0.2": - version: 3.0.2 - resolution: "@ember/test-waiters@npm:3.0.2" +"@ember/test-waiters@npm:^3.1.0": + version: 3.1.0 + resolution: "@ember/test-waiters@npm:3.1.0" dependencies: calculate-cache-key-for-tree: ^2.0.0 ember-cli-babel: ^7.26.6 ember-cli-version-checker: ^5.1.2 semver: ^7.3.5 - checksum: 4ad673fd6c2edc45d7fe378c318dd7f86eabb74eabded5e36b073baa580e2fa2cbb74b52037a2af94a2de98777335129d236b9a33bd33efd30927b0f5e706eaf + checksum: f5d8c75ec060f01d038c4f8f419f844e50928b9d7af518b00318844238b43a7b740fc2ec941f45c61fdf698977f2f8a47b34898faa7da2d09e3a38a8137ac8cf + languageName: node + linkType: hard + +"@embroider/addon-shim@npm:1.8.3": + version: 1.8.3 + resolution: "@embroider/addon-shim@npm:1.8.3" + dependencies: + "@embroider/shared-internals": ^1.8.3 + semver: ^7.3.5 + checksum: 11dfdb956631179656727599eb6d6223369f6a04f0e28ac04b793064338c577a89a77c59254a4a665acc46e174cf87196d0f335ab8049be7fe8a2c71ef695974 languageName: node linkType: hard @@ -4567,6 +5658,22 @@ __metadata: languageName: node linkType: hard +"@embroider/shared-internals@npm:^1.8.3": + version: 1.8.3 + resolution: "@embroider/shared-internals@npm:1.8.3" + dependencies: + babel-import-util: ^1.1.0 + ember-rfc176-data: ^0.3.17 + fs-extra: ^9.1.0 + js-string-escape: ^1.0.1 + lodash: ^4.17.21 + resolve-package-path: ^4.0.1 + semver: ^7.3.5 + typescript-memoize: ^1.0.1 + checksum: de636225b3e85379caad5bf4cb3c4f8cc8d95d1c9fab05668faf55654ae2cfe1ca2632c5cc4b81f0b52781d4fc5b80879e6cd143801976de46c591430957040f + languageName: node + linkType: hard + "@embroider/shared-internals@npm:^2.0.0": version: 2.0.0 resolution: "@embroider/shared-internals@npm:2.0.0" @@ -4981,31 +6088,33 @@ __metadata: languageName: node linkType: hard -"@hashicorp/design-system-components@npm:^2.13.0": - version: 2.13.0 - resolution: "@hashicorp/design-system-components@npm:2.13.0" +"@hashicorp/design-system-components@npm:^3.3.0": + version: 3.3.0 + resolution: "@hashicorp/design-system-components@npm:3.3.0" dependencies: "@ember/render-modifiers": ^2.0.5 - "@ember/test-waiters": ^3.0.2 + "@ember/string": ^3.1.1 + "@ember/test-waiters": ^3.1.0 "@hashicorp/design-system-tokens": ^1.9.0 - "@hashicorp/ember-flight-icons": ^3.1.3 + "@hashicorp/ember-flight-icons": ^4.0.4 dialog-polyfill: ^0.5.6 ember-a11y-refocus: ^3.0.2 ember-auto-import: ^2.6.3 - ember-cached-decorator-polyfill: ^0.1.4 - ember-cli-babel: ^7.26.11 - ember-cli-clipboard: ^1.0.0 - ember-cli-htmlbars: ^6.2.0 - ember-cli-sass: ^10.0.1 - ember-composable-helpers: ^4.5.0 - ember-focus-trap: ^1.0.2 - ember-keyboard: ^8.2.0 + ember-cached-decorator-polyfill: ^1.0.2 + ember-cli-babel: ^8.2.0 + ember-cli-htmlbars: ^6.3.0 + ember-cli-sass: ^11.0.1 + ember-composable-helpers: ^5.0.0 + ember-element-helper: ^0.8.5 + ember-focus-trap: ^1.1.0 + ember-keyboard: ^8.2.1 ember-stargate: ^0.4.3 ember-style-modifier: ^3.0.1 ember-truth-helpers: ^3.1.1 - sass: ^1.62.1 + prismjs: ^1.29.0 + sass: ^1.69.5 tippy.js: ^6.3.7 - checksum: 20171b9c3f581214b36205a7a0f5b4a1e37866058a7bfd9d07d51269bcf413da551bcd0738a248de881671e918a32717ebbfd8bedb7534b11de96b5284e3beb9 + checksum: 47b3262ec9a31e03110309c80833e1b504baa126851106328b0f5668b7c4f312116e35a9f876aedea80ed71de7a32ee8b530498a947adc8f1fe3db8102a6a227 languageName: node linkType: hard @@ -5016,22 +6125,23 @@ __metadata: languageName: node linkType: hard -"@hashicorp/ember-flight-icons@npm:^3.1.3": - version: 3.1.3 - resolution: "@hashicorp/ember-flight-icons@npm:3.1.3" +"@hashicorp/ember-flight-icons@npm:^4.0.4": + version: 4.0.4 + resolution: "@hashicorp/ember-flight-icons@npm:4.0.4" dependencies: - "@hashicorp/flight-icons": ^2.20.0 + "@hashicorp/flight-icons": ^2.23.0 ember-auto-import: ^2.6.3 - ember-cli-babel: ^7.26.11 - ember-cli-htmlbars: ^6.2.0 - checksum: 196dec75deb983cbfeae6738c5bba7482f645d8ad419b1ab79c0f12f64d0f06e9c7ffb01ce13540e80aabb5ceb490c05c6cd56a7b25ceb3413ebe8274889a298 + ember-cli-babel: ^8.2.0 + ember-cli-htmlbars: ^6.3.0 + ember-get-config: ^2.1.1 + checksum: fb0a0f26feb80d1dc663b6fd4e1b6849b8c93e4607f8d790f2fa646cee96b4f3df339951b8adddbf85c39052c72fdf54fc4b910baf2fd64c188d660635c8ba73 languageName: node linkType: hard -"@hashicorp/flight-icons@npm:^2.20.0": - version: 2.20.0 - resolution: "@hashicorp/flight-icons@npm:2.20.0" - checksum: 4ec750e46cd780f007a5046e4cd2cf9e69f946ef131d30b3090c24616c2c0a407f327d0135ebbb0af6578cfdbbb65ab59b18c6ab95599ec04ed2a04827aa24ec +"@hashicorp/flight-icons@npm:^2.23.0": + version: 2.23.0 + resolution: "@hashicorp/flight-icons@npm:2.23.0" + checksum: 0a216a868244bcb3220e13061e5353fd16a0296c1aa9f7444ba2a7148e78532698e7c36a1318573b8ec7fe0283c68c93e7bfbd79a19eb24b2f0006ab78d3b191 languageName: node linkType: hard @@ -7941,6 +9051,19 @@ __metadata: languageName: node linkType: hard +"babel-plugin-module-resolver@npm:^5.0.0": + version: 5.0.0 + resolution: "babel-plugin-module-resolver@npm:5.0.0" + dependencies: + find-babel-config: ^2.0.0 + glob: ^8.0.3 + pkg-up: ^3.1.0 + reselect: ^4.1.7 + resolve: ^1.22.1 + checksum: d6880e49fc8e7bac509a2c183b4303ee054a47a80032a59a6f7844bb468ebe5e333b5dc5378443afdab5839e2da2b31a6c8d9a985a0047cd076b82bb9161cc78 + languageName: node + linkType: hard + "babel-plugin-polyfill-corejs2@npm:^0.2.0": version: 0.2.3 resolution: "babel-plugin-polyfill-corejs2@npm:0.2.3" @@ -7967,6 +9090,19 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-corejs2@npm:^0.4.6": + version: 0.4.6 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.6" + dependencies: + "@babel/compat-data": ^7.22.6 + "@babel/helper-define-polyfill-provider": ^0.4.3 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 08896811df31530be6a9bcdd630cb9fd4b5ae5181039d18db3796efbc54e38d57a42af460845c10a04434e1bc45c0d47743c7e6c860383cc6b141083cde22030 + languageName: node + linkType: hard + "babel-plugin-polyfill-corejs3@npm:^0.2.0": version: 0.2.5 resolution: "babel-plugin-polyfill-corejs3@npm:0.2.5" @@ -7995,11 +9131,23 @@ __metadata: version: 0.5.2 resolution: "babel-plugin-polyfill-corejs3@npm:0.5.2" dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.1 - core-js-compat: ^3.21.0 + "@babel/helper-define-polyfill-provider": ^0.3.1 + core-js-compat: ^3.21.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2f3184c73f80f00ac876a5ebcad945fd8d2ae70e5f85b7ab6cc3bc69bc74025f4f7070de7abbb2a7274c78e130bd34fc13f4c85342da28205930364a1ef0aa21 + languageName: node + linkType: hard + +"babel-plugin-polyfill-corejs3@npm:^0.8.5": + version: 0.8.6 + resolution: "babel-plugin-polyfill-corejs3@npm:0.8.6" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.4.3 + core-js-compat: ^3.33.1 peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 2f3184c73f80f00ac876a5ebcad945fd8d2ae70e5f85b7ab6cc3bc69bc74025f4f7070de7abbb2a7274c78e130bd34fc13f4c85342da28205930364a1ef0aa21 + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 36951c2edac42ac0f05b200502e90d77bf66ccee5b52e2937d23496c6ef2372cce31b8c64144da374b77bd3eb65e2721703a52eac56cad16a152326c092cbf77 languageName: node linkType: hard @@ -8025,6 +9173,17 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-regenerator@npm:^0.5.3": + version: 0.5.3 + resolution: "babel-plugin-polyfill-regenerator@npm:0.5.3" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.4.3 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 2bb546582cda1870d19e646a7183baeb2cccd56e0ef3e4eaeabd28e120daf17cb87399194a9ccdcf32506bcaa68d23e73440fc8ab990a7a0f8c5a77c12d5d4bc + languageName: node + linkType: hard + "babel-plugin-syntax-async-functions@npm:^6.8.0": version: 6.13.0 resolution: "babel-plugin-syntax-async-functions@npm:6.13.0" @@ -8837,6 +9996,24 @@ __metadata: languageName: node linkType: hard +"broccoli-babel-transpiler@npm:^8.0.0": + version: 8.0.0 + resolution: "broccoli-babel-transpiler@npm:8.0.0" + dependencies: + broccoli-persistent-filter: ^3.0.0 + clone: ^2.1.2 + hash-for-dep: ^1.4.7 + heimdalljs: ^0.2.1 + heimdalljs-logger: ^0.1.9 + json-stable-stringify: ^1.0.1 + rsvp: ^4.8.4 + workerpool: ^6.0.2 + peerDependencies: + "@babel/core": ^7.17.9 + checksum: 5ca5f6664e5ef37e0927501256312ba66d0362180edcdbd9bf23fae2cf057294cc33e5c60e314c6d705c5f2c2dca5f5319b3fc1f1089589c7b16724a6f80e190 + languageName: node + linkType: hard + "broccoli-builder@npm:^0.18.14": version: 0.18.14 resolution: "broccoli-builder@npm:0.18.14" @@ -9248,6 +10425,25 @@ __metadata: languageName: node linkType: hard +"broccoli-persistent-filter@npm:^3.0.0": + version: 3.1.3 + resolution: "broccoli-persistent-filter@npm:3.1.3" + dependencies: + async-disk-cache: ^2.0.0 + async-promise-queue: ^1.0.3 + broccoli-plugin: ^4.0.3 + fs-tree-diff: ^2.0.0 + hash-for-dep: ^1.5.0 + heimdalljs: ^0.2.1 + heimdalljs-logger: ^0.1.7 + promise-map-series: ^0.2.1 + rimraf: ^3.0.0 + symlink-or-copy: ^1.0.1 + sync-disk-cache: ^2.0.0 + checksum: c3d23e641d15fbb715b730d8f1b69fe5d6c4c66afdee7a57fb77aba3406d4d9026242ffcec6520fdfc55108d72ebcec411948721d8dc8be5d8bdb9c8c10a14ad + languageName: node + linkType: hard + "broccoli-persistent-filter@npm:^3.1.2": version: 3.1.2 resolution: "broccoli-persistent-filter@npm:3.1.2" @@ -9826,6 +11022,20 @@ __metadata: languageName: node linkType: hard +"browserslist@npm:^4.22.1": + version: 4.22.2 + resolution: "browserslist@npm:4.22.2" + dependencies: + caniuse-lite: ^1.0.30001565 + electron-to-chromium: ^1.4.601 + node-releases: ^2.0.14 + update-browserslist-db: ^1.0.13 + bin: + browserslist: cli.js + checksum: 33ddfcd9145220099a7a1ac533cecfe5b7548ffeb29b313e1b57be6459000a1f8fa67e781cf4abee97268ac594d44134fcc4a6b2b4750ceddc9796e3a22076d9 + languageName: node + linkType: hard + "bser@npm:2.1.1": version: 2.1.1 resolution: "bser@npm:2.1.1" @@ -10083,6 +11293,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001565": + version: 1.0.30001566 + resolution: "caniuse-lite@npm:1.0.30001566" + checksum: 0f9084bf9f7d5c0a9ddb200c2baddb25dd2ad5a2f205f01e7b971f3e98e9a7bb23c2d86bae48237e9bc9782b682cffaaf3406d936937ab9844987dbe2a6401f2 + languageName: node + linkType: hard + "capture-exit@npm:^2.0.0": version: 2.0.0 resolution: "capture-exit@npm:2.0.0" @@ -10473,17 +11690,6 @@ __metadata: languageName: node linkType: hard -"clipboard@npm:^2.0.11": - version: 2.0.11 - resolution: "clipboard@npm:2.0.11" - dependencies: - good-listener: ^1.2.2 - select: ^1.1.2 - tiny-emitter: ^2.0.0 - checksum: 413055a6038e43898e0e895216b58ed54fbf386f091cb00188875ef35b186cefbd258acdf4cb4b0ac87cbc00de936f41b45dde9fe1fd1a57f7babb28363b8748 - languageName: node - linkType: hard - "cliui@npm:^5.0.0": version: 5.0.0 resolution: "cliui@npm:5.0.0" @@ -10957,6 +12163,15 @@ __metadata: languageName: node linkType: hard +"core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.33.1": + version: 3.33.3 + resolution: "core-js-compat@npm:3.33.3" + dependencies: + browserslist: ^4.22.1 + checksum: cb121e83f0f5f18b2b75428cdfb19524936a18459f1e0358f9124c8ff8b75d6a5901495cb996560cfde3a416103973f78eb5947777bb8b2fd877cdf84471465d + languageName: node + linkType: hard + "core-js-compat@npm:^3.9.0": version: 3.19.1 resolution: "core-js-compat@npm:3.19.1" @@ -11923,13 +13138,6 @@ __metadata: languageName: node linkType: hard -"delegate@npm:^3.1.2": - version: 3.2.0 - resolution: "delegate@npm:3.2.0" - checksum: d943058fe05897228b158cbd1bab05164df28c8f54127873231d6b03b0a5acc1b3ee1f98ac70ccc9b79cd84aa47118a7de111fee2923753491583905069da27d - languageName: node - linkType: hard - "delegates@npm:^1.0.0": version: 1.0.0 resolution: "delegates@npm:1.0.0" @@ -12318,6 +13526,13 @@ __metadata: languageName: node linkType: hard +"electron-to-chromium@npm:^1.4.601": + version: 1.4.602 + resolution: "electron-to-chromium@npm:1.4.602" + checksum: a3e30fee47f377f180a99db26f1067acb72891fab720b6800221d74de0c962ac2f595bd977d1f1fd265e0b7f7258b4524c8f9053d17620d01bb8cfa2c0ae32de + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.4.84": version: 1.4.99 resolution: "electron-to-chromium@npm:1.4.99" @@ -12350,20 +13565,6 @@ __metadata: languageName: node linkType: hard -"ember-arg-types@npm:^1.0.0": - version: 1.1.0 - resolution: "ember-arg-types@npm:1.1.0" - dependencies: - "@embroider/macros": ^1.8.1 - ember-auto-import: ^2.4.2 - ember-cli-babel: ^7.26.11 - ember-cli-typescript: ^5.1.1 - ember-get-config: ^2.1.1 - prop-types: ^15.8.1 - checksum: f31733f7749c51f1751673afd450106faac3cad61cc0406abc0ef2938d7623416e35794a80636f02a082aac047e4321a037c5f61796ec30baaf156216ba6d87b - languageName: node - linkType: hard - "ember-asset-loader@npm:^0.6.1": version: 0.6.1 resolution: "ember-asset-loader@npm:0.6.1" @@ -12574,21 +13775,25 @@ __metadata: languageName: node linkType: hard -"ember-cached-decorator-polyfill@npm:^0.1.4": - version: 0.1.4 - resolution: "ember-cached-decorator-polyfill@npm:0.1.4" +"ember-cached-decorator-polyfill@npm:^1.0.1": + version: 1.0.1 + resolution: "ember-cached-decorator-polyfill@npm:1.0.1" dependencies: - "@glimmer/tracking": ^1.0.4 + "@embroider/macros": ^1.8.3 + "@glimmer/tracking": ^1.1.2 + babel-import-util: ^1.2.2 ember-cache-primitive-polyfill: ^1.0.1 - ember-cli-babel: ^7.21.0 + ember-cli-babel: ^7.26.11 ember-cli-babel-plugin-helpers: ^1.1.1 - checksum: 8f8228e8fe578a78045c00cfd9ffc124dd287e45908e912dba9823ba48a14c1cb7e15fce64f4a034b68edcf6987c51c0500fbab7825e982ea4708672b019780a + peerDependencies: + ember-source: ^3.13.0 || ^4.0.0 + checksum: b2589490d897da9f560abc1858c2090fc027a54267dfaa5780ee376e00cec1183b84400df9125f6c25bd7d2b465818d63abb98bb07b3eec0a18b3206a918b804 languageName: node linkType: hard -"ember-cached-decorator-polyfill@npm:^1.0.1": - version: 1.0.1 - resolution: "ember-cached-decorator-polyfill@npm:1.0.1" +"ember-cached-decorator-polyfill@npm:^1.0.2": + version: 1.0.2 + resolution: "ember-cached-decorator-polyfill@npm:1.0.2" dependencies: "@embroider/macros": ^1.8.3 "@glimmer/tracking": ^1.1.2 @@ -12597,8 +13802,8 @@ __metadata: ember-cli-babel: ^7.26.11 ember-cli-babel-plugin-helpers: ^1.1.1 peerDependencies: - ember-source: ^3.13.0 || ^4.0.0 - checksum: b2589490d897da9f560abc1858c2090fc027a54267dfaa5780ee376e00cec1183b84400df9125f6c25bd7d2b465818d63abb98bb07b3eec0a18b3206a918b804 + ember-source: ^3.13.0 || ^4.0.0 || >= 5.0.0 + checksum: bbfaeafdf89a7ab834d85502829d604e3eb439cb154652b21683492e3e59a918bbaf49d39703f1a896016521d7cf03dc05e89cf5cf06de88fd1489b8e00ef8bb languageName: node linkType: hard @@ -12678,19 +13883,40 @@ __metadata: languageName: node linkType: hard -"ember-cli-clipboard@npm:^1.0.0": - version: 1.1.0 - resolution: "ember-cli-clipboard@npm:1.1.0" +"ember-cli-babel@npm:^8.2.0": + version: 8.2.0 + resolution: "ember-cli-babel@npm:8.2.0" dependencies: - "@embroider/macros": ^1.10.0 - clipboard: ^2.0.11 - ember-arg-types: ^1.0.0 - ember-auto-import: ^2.4.2 - ember-cli-babel: ^7.26.11 - ember-cli-htmlbars: ^6.1.0 - ember-modifier: ^3.2.7 || ^4.1.0 - prop-types: ^15.8.1 - checksum: 8977ddf744de59662012421feed13e2cd14f6658bc2742504dd5972d0c1181abd8be30085f8dfaaa4f8478fac162eb6ede912ef5c69159d03dac9cb5a6c0bd31 + "@babel/helper-compilation-targets": ^7.20.7 + "@babel/plugin-proposal-class-properties": ^7.16.5 + "@babel/plugin-proposal-decorators": ^7.20.13 + "@babel/plugin-proposal-private-methods": ^7.16.5 + "@babel/plugin-proposal-private-property-in-object": ^7.20.5 + "@babel/plugin-transform-class-static-block": ^7.22.11 + "@babel/plugin-transform-modules-amd": ^7.20.11 + "@babel/plugin-transform-runtime": ^7.13.9 + "@babel/plugin-transform-typescript": ^7.20.13 + "@babel/preset-env": ^7.20.2 + "@babel/runtime": 7.12.18 + amd-name-resolver: ^1.3.1 + babel-plugin-debug-macros: ^0.3.4 + babel-plugin-ember-data-packages-polyfill: ^0.1.2 + babel-plugin-ember-modules-api-polyfill: ^3.5.0 + babel-plugin-module-resolver: ^5.0.0 + broccoli-babel-transpiler: ^8.0.0 + broccoli-debug: ^0.6.4 + broccoli-funnel: ^3.0.8 + broccoli-source: ^3.0.1 + calculate-cache-key-for-tree: ^2.0.0 + clone: ^2.1.2 + ember-cli-babel-plugin-helpers: ^1.1.1 + ember-cli-version-checker: ^5.1.2 + ensure-posix-path: ^1.0.2 + resolve-package-path: ^4.0.3 + semver: ^7.3.8 + peerDependencies: + "@babel/core": ^7.12.0 + checksum: 26a5abd3107ddfc76221894141b68cab79fb9cdf79ea0fa9646efe47c42f18bda49b854878118f8b57b3ebf5014828cf0ad640e77ffc209e3565f7eb792d1ef5 languageName: node linkType: hard @@ -12892,6 +14118,28 @@ __metadata: languageName: node linkType: hard +"ember-cli-htmlbars@npm:^6.3.0": + version: 6.3.0 + resolution: "ember-cli-htmlbars@npm:6.3.0" + dependencies: + "@ember/edition-utils": ^1.2.0 + babel-plugin-ember-template-compilation: ^2.0.0 + babel-plugin-htmlbars-inline-precompile: ^5.3.0 + broccoli-debug: ^0.6.5 + broccoli-persistent-filter: ^3.1.2 + broccoli-plugin: ^4.0.3 + ember-cli-version-checker: ^5.1.2 + fs-tree-diff: ^2.0.1 + hash-for-dep: ^1.5.1 + heimdalljs-logger: ^0.1.10 + js-string-escape: ^1.0.1 + semver: ^7.3.4 + silent-error: ^1.1.1 + walk-sync: ^2.2.0 + checksum: 30639ab2afd4cdf4edf677d6dfdef61865ee1be46449b123917c12a3119346f769573f55897ec3bb2a2dd05bd0e3a645ecc0090453d53545902fd21fbce3d057 + languageName: node + linkType: hard + "ember-cli-inject-live-reload@npm:^2.1.0": version: 2.1.0 resolution: "ember-cli-inject-live-reload@npm:2.1.0" @@ -13004,7 +14252,7 @@ __metadata: languageName: node linkType: hard -"ember-cli-sass@npm:11.0.1": +"ember-cli-sass@npm:11.0.1, ember-cli-sass@npm:^11.0.1": version: 11.0.1 resolution: "ember-cli-sass@npm:11.0.1" dependencies: @@ -13016,18 +14264,6 @@ __metadata: languageName: node linkType: hard -"ember-cli-sass@npm:^10.0.1": - version: 10.0.1 - resolution: "ember-cli-sass@npm:10.0.1" - dependencies: - broccoli-funnel: ^2.0.1 - broccoli-merge-trees: ^3.0.1 - broccoli-sass-source-maps: ^4.0.0 - ember-cli-version-checker: ^2.1.0 - checksum: 99eb9045b19c82851a90435df14b4ab4108ee0fd57a9e78d085c225a9d3dfb72bae8118707cf2f1a2d4d37abc2c40ad0dd95c0079f43d051ec90526448189ac3 - languageName: node - linkType: hard - "ember-cli-sri@meirish/ember-cli-sri#rooturl": version: 2.1.0 resolution: "ember-cli-sri@https://github.com/meirish/ember-cli-sri.git#commit=1c0ff776a61f09121d1ea69ce16e4653da5e1efa" @@ -13190,7 +14426,7 @@ __metadata: languageName: node linkType: hard -"ember-cli-typescript@npm:^5.1.1, ember-cli-typescript@npm:^5.2.1": +"ember-cli-typescript@npm:^5.2.1": version: 5.2.1 resolution: "ember-cli-typescript@npm:5.2.1" dependencies: @@ -13390,7 +14626,7 @@ __metadata: languageName: node linkType: hard -"ember-composable-helpers@npm:5.0.0": +"ember-composable-helpers@npm:5.0.0, ember-composable-helpers@npm:^5.0.0": version: 5.0.0 resolution: "ember-composable-helpers@npm:5.0.0" dependencies: @@ -13402,18 +14638,6 @@ __metadata: languageName: node linkType: hard -"ember-composable-helpers@npm:^4.5.0": - version: 4.5.0 - resolution: "ember-composable-helpers@npm:4.5.0" - dependencies: - "@babel/core": ^7.0.0 - broccoli-funnel: 2.0.1 - ember-cli-babel: ^7.26.3 - resolve: ^1.10.0 - checksum: 39bc86701eee2c8afde0cd912b757fd7f26b06c25df182fc6bc833722bea86a2c4cc4e51659c489dfdcea5630ed87794010584e68b4c52e6049aee8a8c87808b - languageName: node - linkType: hard - "ember-concurrency-decorators@npm:^2.0.0": version: 2.0.3 resolution: "ember-concurrency-decorators@npm:2.0.3" @@ -13537,6 +14761,18 @@ __metadata: languageName: node linkType: hard +"ember-element-helper@npm:^0.8.5": + version: 0.8.5 + resolution: "ember-element-helper@npm:0.8.5" + dependencies: + "@embroider/addon-shim": 1.8.3 + "@embroider/util": ^1.0.0 + peerDependencies: + ember-source: ^3.8 || ^4.0.0 || >= 5.0.0 + checksum: 7be5fa71b172dc74f24bd10bdc179d9e9dcbaaeab5c78763a24dd6f6d384768d6835291771839c6b530940c3ddd259bd7fdc8242576c9cc31e74d6bb35df435b + languageName: node + linkType: hard + "ember-engines@npm:0.8.23": version: 0.8.23 resolution: "ember-engines@npm:0.8.23" @@ -13588,7 +14824,7 @@ __metadata: languageName: node linkType: hard -"ember-focus-trap@npm:^1.0.2": +"ember-focus-trap@npm:^1.1.0": version: 1.1.0 resolution: "ember-focus-trap@npm:1.1.0" dependencies: @@ -13642,7 +14878,7 @@ __metadata: languageName: node linkType: hard -"ember-keyboard@npm:^8.2.0": +"ember-keyboard@npm:^8.2.1": version: 8.2.1 resolution: "ember-keyboard@npm:8.2.1" dependencies: @@ -13712,7 +14948,7 @@ __metadata: languageName: node linkType: hard -"ember-modifier@npm:^2.1.2 || ^3.1.0 || ^4.0.0, ember-modifier@npm:^3.2.7 || ^4.0.0, ember-modifier@npm:^3.2.7 || ^4.1.0, ember-modifier@npm:^4.1.0": +"ember-modifier@npm:^2.1.2 || ^3.1.0 || ^4.0.0, ember-modifier@npm:^3.2.7 || ^4.0.0, ember-modifier@npm:^4.1.0": version: 4.1.0 resolution: "ember-modifier@npm:4.1.0" dependencies: @@ -15547,6 +16783,16 @@ __metadata: languageName: node linkType: hard +"find-babel-config@npm:^2.0.0": + version: 2.0.0 + resolution: "find-babel-config@npm:2.0.0" + dependencies: + json5: ^2.1.1 + path-exists: ^4.0.0 + checksum: d110308b02fe6a6411a0cfb7fd50af6740fbf5093eada3d6ddacf99b07fc8eea4aa3475356484710a0032433029a21ce733bb3ef88fda1d6e35c29a3e4983014 + languageName: node + linkType: hard + "find-cache-dir@npm:^2.1.0": version: 2.1.0 resolution: "find-cache-dir@npm:2.1.0" @@ -16563,15 +17809,6 @@ __metadata: languageName: node linkType: hard -"good-listener@npm:^1.2.2": - version: 1.2.2 - resolution: "good-listener@npm:1.2.2" - dependencies: - delegate: ^3.1.2 - checksum: f39fb82c4e41524f56104cfd2d7aef1a88e72f3f75139115fbdf98cc7d844e0c1b39218b2e83438c6188727bf904ed78c7f0f2feff67b32833bc3af7f0202b33 - languageName: node - linkType: hard - "gopd@npm:^1.0.1": version: 1.0.1 resolution: "gopd@npm:1.0.1" @@ -18368,6 +19605,15 @@ __metadata: languageName: node linkType: hard +"json5@npm:^2.1.1, json5@npm:^2.2.2": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 2a7436a93393830bce797d4626275152e37e877b265e94ca69c99e3d20c2b9dab021279146a39cdb700e71b2dd32a4cebd1514cd57cee102b1af906ce5040349 + languageName: node + linkType: hard + "json5@npm:^2.1.2": version: 2.2.0 resolution: "json5@npm:2.2.0" @@ -18388,15 +19634,6 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.2.2": - version: 2.2.3 - resolution: "json5@npm:2.2.3" - bin: - json5: lib/cli.js - checksum: 2a7436a93393830bce797d4626275152e37e877b265e94ca69c99e3d20c2b9dab021279146a39cdb700e71b2dd32a4cebd1514cd57cee102b1af906ce5040349 - languageName: node - linkType: hard - "jsondiffpatch@npm:^0.4.1": version: 0.4.1 resolution: "jsondiffpatch@npm:0.4.1" @@ -19227,7 +20464,7 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.0.0, loose-envify@npm:^1.4.0": +"loose-envify@npm:^1.0.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" dependencies: @@ -20539,6 +21776,13 @@ __metadata: languageName: node linkType: hard +"node-releases@npm:^2.0.14": + version: 2.0.14 + resolution: "node-releases@npm:2.0.14" + checksum: 59443a2f77acac854c42d321bf1b43dea0aef55cd544c6a686e9816a697300458d4e82239e2d794ea05f7bbbc8a94500332e2d3ac3f11f52e4b16cbe638b3c41 + languageName: node + linkType: hard + "node-releases@npm:^2.0.2": version: 2.0.2 resolution: "node-releases@npm:2.0.2" @@ -21861,6 +23105,13 @@ __metadata: languageName: node linkType: hard +"prismjs@npm:^1.21.0": + version: 1.29.0 + resolution: "prismjs@npm:1.29.0" + checksum: 007a8869d4456ff8049dc59404e32d5666a07d99c3b0e30a18bd3b7676dfa07d1daae9d0f407f20983865fd8da56de91d09cb08e6aa61f5bc420a27c0beeaf93 + languageName: node + linkType: hard + "private@npm:^0.1.6, private@npm:^0.1.8": version: 0.1.8 resolution: "private@npm:0.1.8" @@ -21949,17 +23200,6 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.8.1": - version: 15.8.1 - resolution: "prop-types@npm:15.8.1" - dependencies: - loose-envify: ^1.4.0 - object-assign: ^4.1.1 - react-is: ^16.13.1 - checksum: c056d3f1c057cb7ff8344c645450e14f088a915d078dcda795041765047fa080d38e5d626560ccaac94a4e16e3aa15f3557c1a9a8d1174530955e992c675e459 - languageName: node - linkType: hard - "proper-lockfile@npm:^4.1.2": version: 4.1.2 resolution: "proper-lockfile@npm:4.1.2" @@ -22218,13 +23458,6 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^16.13.1": - version: 16.13.1 - resolution: "react-is@npm:16.13.1" - checksum: f7a19ac3496de32ca9ae12aa030f00f14a3d45374f1ceca0af707c831b2a6098ef0d6bdae51bd437b0a306d7f01d4677fcc8de7c0d331eb47ad0f46130e53c5f - languageName: node - linkType: hard - "read-pkg-up@npm:^8.0.0": version: 8.0.0 resolution: "read-pkg-up@npm:8.0.0" @@ -22366,6 +23599,15 @@ __metadata: languageName: node linkType: hard +"regenerate-unicode-properties@npm:^10.1.0": + version: 10.1.1 + resolution: "regenerate-unicode-properties@npm:10.1.1" + dependencies: + regenerate: ^1.4.2 + checksum: b80958ef40f125275824c2c47d5081dfaefebd80bff26c76761e9236767c748a4a95a69c053fe29d2df881177f2ca85df4a71fe70a82360388b31159ef19adcf + languageName: node + linkType: hard + "regenerate-unicode-properties@npm:^9.0.0": version: 9.0.0 resolution: "regenerate-unicode-properties@npm:9.0.0" @@ -22446,6 +23688,15 @@ __metadata: languageName: node linkType: hard +"regenerator-transform@npm:^0.15.2": + version: 0.15.2 + resolution: "regenerator-transform@npm:0.15.2" + dependencies: + "@babel/runtime": ^7.8.4 + checksum: 20b6f9377d65954980fe044cfdd160de98df415b4bff38fbade67b3337efaf078308c4fed943067cd759827cc8cfeca9cb28ccda1f08333b85d6a2acbd022c27 + languageName: node + linkType: hard + "regex-not@npm:^1.0.0, regex-not@npm:^1.0.2": version: 1.0.2 resolution: "regex-not@npm:1.0.2" @@ -22530,6 +23781,20 @@ __metadata: languageName: node linkType: hard +"regexpu-core@npm:^5.3.1": + version: 5.3.2 + resolution: "regexpu-core@npm:5.3.2" + dependencies: + "@babel/regjsgen": ^0.8.0 + regenerate: ^1.4.2 + regenerate-unicode-properties: ^10.1.0 + regjsparser: ^0.9.1 + unicode-match-property-ecmascript: ^2.0.0 + unicode-match-property-value-ecmascript: ^2.1.0 + checksum: 95bb97088419f5396e07769b7de96f995f58137ad75fac5811fb5fe53737766dfff35d66a0ee66babb1eb55386ef981feaef392f9df6d671f3c124812ba24da2 + languageName: node + linkType: hard + "regjsgen@npm:^0.2.0": version: 0.2.0 resolution: "regjsgen@npm:0.2.0" @@ -22584,6 +23849,17 @@ __metadata: languageName: node linkType: hard +"regjsparser@npm:^0.9.1": + version: 0.9.1 + resolution: "regjsparser@npm:0.9.1" + dependencies: + jsesc: ~0.5.0 + bin: + regjsparser: bin/parser + checksum: 5e1b76afe8f1d03c3beaf9e0d935dd467589c3625f6d65fb8ffa14f224d783a0fed4bf49c2c1b8211043ef92b6117313419edf055a098ed8342e340586741afc + languageName: node + linkType: hard + "remark-footnotes@npm:^3.0.0": version: 3.0.0 resolution: "remark-footnotes@npm:3.0.0" @@ -22773,6 +24049,13 @@ __metadata: languageName: node linkType: hard +"reselect@npm:^4.1.7": + version: 4.1.8 + resolution: "reselect@npm:4.1.8" + checksum: a4ac87cedab198769a29be92bc221c32da76cfdad6911eda67b4d3e7136dca86208c3b210e31632eae31ebd2cded18596f0dd230d3ccc9e978df22f233b5583e + languageName: node + linkType: hard + "resolve-dir@npm:^1.0.0, resolve-dir@npm:^1.0.1": version: 1.0.1 resolution: "resolve-dir@npm:1.0.1" @@ -23280,16 +24563,16 @@ __metadata: languageName: node linkType: hard -"sass@npm:^1.62.1": - version: 1.68.0 - resolution: "sass@npm:1.68.0" +"sass@npm:^1.69.5": + version: 1.69.5 + resolution: "sass@npm:1.69.5" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 65ccede83c96768beeb8dcaf67957b7c76b12ff1276bfd2849d7be151d46ba1400048a67717e6e5e4969bc75e87348e5530f5f272833f2e60a891c21a33d8ab0 + checksum: c66f4f02882e7aa7aa49703824dadbb5a174dcd05e3cd96f17f73687889aab6027d078b518e2c04b594dfd89b2f574824bf935c7aee46c770aa6be9aafcc49f2 languageName: node linkType: hard @@ -23354,13 +24637,6 @@ __metadata: languageName: node linkType: hard -"select@npm:^1.1.2": - version: 1.1.2 - resolution: "select@npm:1.1.2" - checksum: 4346151e94f226ea6131e44e68e6d837f3fdee64831b756dd657cc0b02f4cb5107f867cb34a1d1216ab7737d0bf0645d44546afb030bbd8d64e891f5e4c4814e - languageName: node - linkType: hard - "semver-compare@npm:^1.0.0": version: 1.0.0 resolution: "semver-compare@npm:1.0.0" @@ -25020,13 +26296,6 @@ __metadata: languageName: node linkType: hard -"tiny-emitter@npm:^2.0.0": - version: 2.1.0 - resolution: "tiny-emitter@npm:2.1.0" - checksum: fbcfb5145751a0e3b109507a828eb6d6d4501352ab7bb33eccef46e22e9d9ad3953158870a6966a59e57ab7c3f9cfac7cab8521db4de6a5e757012f4677df2dd - languageName: node - linkType: hard - "tiny-glob@npm:0.2.9": version: 0.2.9 resolution: "tiny-glob@npm:0.2.9" @@ -25544,6 +26813,13 @@ __metadata: languageName: node linkType: hard +"unicode-match-property-value-ecmascript@npm:^2.1.0": + version: 2.1.0 + resolution: "unicode-match-property-value-ecmascript@npm:2.1.0" + checksum: 8d6f5f586b9ce1ed0e84a37df6b42fdba1317a05b5df0c249962bd5da89528771e2d149837cad11aa26bcb84c35355cb9f58a10c3d41fa3b899181ece6c85220 + languageName: node + linkType: hard + "unicode-property-aliases-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-property-aliases-ecmascript@npm:2.0.0" @@ -25723,6 +26999,20 @@ __metadata: languageName: node linkType: hard +"update-browserslist-db@npm:^1.0.13": + version: 1.0.13 + resolution: "update-browserslist-db@npm:1.0.13" + dependencies: + escalade: ^3.1.1 + picocolors: ^1.0.0 + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 1e47d80182ab6e4ad35396ad8b61008ae2a1330221175d0abd37689658bdb61af9b705bfc41057fd16682474d79944fb2d86767c5ed5ae34b6276b9bed353322 + languageName: node + linkType: hard + "update-section@npm:^0.3.3": version: 0.3.3 resolution: "update-section@npm:0.3.3" @@ -25910,8 +27200,8 @@ __metadata: "@ember/test-waiters": ^3.0.0 "@glimmer/component": ^1.1.2 "@glimmer/tracking": ^1.1.2 - "@hashicorp/design-system-components": ^2.13.0 - "@hashicorp/ember-flight-icons": ^3.1.3 + "@hashicorp/design-system-components": ^3.3.0 + "@hashicorp/ember-flight-icons": ^4.0.4 "@hashicorp/structure-icons": ^1.3.0 "@icholy/duration": ^5.1.0 "@tsconfig/ember": ^1.0.1 @@ -26576,6 +27866,13 @@ __metadata: languageName: node linkType: hard +"workerpool@npm:^6.0.2": + version: 6.5.1 + resolution: "workerpool@npm:6.5.1" + checksum: f86d13f9139c3a57c5a5867e81905cd84134b499849405dec2ffe5b1acd30dabaa1809f6f6ee603a7c65e1e4325f21509db6b8398eaf202c8b8f5809e26a2e16 + languageName: node + linkType: hard + "workerpool@npm:^6.4.0": version: 6.4.0 resolution: "workerpool@npm:6.4.0"