From 4ef1f53b020263cf0a2809ef14e48d410bfbc5aa Mon Sep 17 00:00:00 2001 From: "Luis (LT) Carbonell" Date: Mon, 23 Sep 2024 14:55:20 +0000 Subject: [PATCH] backport of commit 7c1a83422b906654682441a3af57ab684cfbb623 --- .github/actions/containerize/action.yml | 4 +- .github/workflows/build-artifacts-ce.yml | 2 +- .github/workflows/enos-run-k8s.yml | 2 +- .release/versions.hcl | 3 - CHANGELOG.md | 233 +++--------------- builtin/logical/aws/path_static_roles.go | 6 - changelog/27033.txt | 3 - changelog/27927.txt | 6 - changelog/28330.txt | 3 - command/server.go | 59 +---- physical/dynamodb/dynamodb.go | 18 +- physical/s3/s3.go | 8 +- ui/app/adapters/kmip/role.js | 33 ++- ui/app/adapters/named-path.js | 2 +- ui/app/app.js | 1 - ui/app/controllers/vault/cluster/init.js | 3 +- ui/app/initializers/deprecation-filter.js | 3 +- ui/app/models/auth-config.js | 7 +- ui/app/models/kmip/role.js | 75 ++++-- ui/app/styles/components/kmip-role-edit.scss | 5 +- .../components/replication-mode-summary.scss | 16 ++ ui/app/styles/core.scss | 1 + ui/app/utils/kmip-role-fields.js | 27 -- ui/config/deprecation-workflow.js | 21 +- .../components/replication-mode-summary.js | 68 +++++ .../components/replication-mode-summary.hbs | 122 +++++++++ .../components/replication-mode-summary.js | 6 + .../addon/components/edit-form-kmip-role.js | 55 +++++ .../kmip/addon/components/kmip/role-form.hbs | 82 ------ .../kmip/addon/components/kmip/role-form.js | 107 -------- .../kmip/addon/routes/scope/roles/create.js | 1 - .../components/edit-form-kmip-role.hbs | 107 ++++++++ ui/lib/kmip/addon/templates/role/edit.hbs | 5 +- .../addon/templates/scope/roles/create.hbs | 6 +- ui/lib/kmip/index.js | 4 - .../page/library/details/accounts.ts | 2 +- .../components/replication-overview-mode.hbs | 49 ---- .../components/replication-overview-mode.js | 39 --- .../addon/components/replication-summary.js | 8 +- .../components/replication-summary.hbs | 113 ++++++--- ui/lib/replication/addon/templates/index.hbs | 31 +-- ui/mirage/handlers/ldap.js | 71 ++---- ui/mirage/models/ldap-account-status.js | 13 - ui/mirage/scenarios/ldap.js | 13 +- ui/tests/acceptance/enterprise-kmip-test.js | 46 ++-- .../secrets/backend/ldap/libraries-test.js | 39 +-- .../secrets/backend/ldap/overview-test.js | 62 ++--- .../secrets/backend/ldap/roles-test.js | 36 +-- ui/tests/helpers/ldap/ldap-helpers.js | 10 +- .../page/library/details/accounts-test.js | 5 +- .../components/page/mode-index-test.js | 2 +- .../replication-overview-mode-test.js | 124 ---------- ui/tests/pages/secrets/backend/kmip/roles.js | 2 +- ui/tests/unit/adapters/kmip/role-test.js | 10 +- ui/tests/unit/utils/kmip-role-fields-test.js | 52 ---- vault/external_tests/audit/audit_test.go | 214 ---------------- version/VERSION | 2 +- .../api-docs/system/storage/raftautopilot.mdx | 112 +++++---- .../content/docs/commands/operator/raft.mdx | 165 +++++-------- website/content/docs/commands/server.mdx | 5 - .../concepts/integrated-storage/autopilot.mdx | 43 +++- .../concepts/integrated-storage/index.mdx | 18 +- website/content/docs/enterprise/lts.mdx | 18 +- .../docs/enterprise/redundancy-zones.mdx | 4 - .../docs/internals/integrated-storage.mdx | 22 -- .../docs/platform/aws/lambda-extension.mdx | 3 +- .../content/docs/platform/k8s/helm/index.mdx | 9 - .../content/docs/platform/k8s/helm/run.mdx | 20 +- website/content/docs/release-notes/1.17.0.mdx | 1 - .../docs/secrets/databases/mongodbatlas.mdx | 4 +- .../docs/upgrading/upgrade-to-1.17.x.mdx | 4 - website/content/partials/autopilot/config.mdx | 53 ---- .../content/partials/autopilot/node-types.mdx | 6 - .../partials/autopilot/redundancy-zones.mdx | 25 -- .../known-issues/aws-auth-external-id.mdx | 19 -- ...ync-activation-flags-cache-not-updated.mdx | 23 -- 76 files changed, 851 insertions(+), 1750 deletions(-) delete mode 100644 changelog/27033.txt delete mode 100644 changelog/27927.txt delete mode 100644 changelog/28330.txt create mode 100644 ui/app/styles/components/replication-mode-summary.scss delete mode 100644 ui/app/utils/kmip-role-fields.js create mode 100644 ui/lib/core/addon/components/replication-mode-summary.js create mode 100644 ui/lib/core/addon/templates/components/replication-mode-summary.hbs create mode 100644 ui/lib/core/app/components/replication-mode-summary.js create mode 100644 ui/lib/kmip/addon/components/edit-form-kmip-role.js delete mode 100644 ui/lib/kmip/addon/components/kmip/role-form.hbs delete mode 100644 ui/lib/kmip/addon/components/kmip/role-form.js create mode 100644 ui/lib/kmip/addon/templates/components/edit-form-kmip-role.hbs delete mode 100644 ui/lib/replication/addon/components/replication-overview-mode.hbs delete mode 100644 ui/lib/replication/addon/components/replication-overview-mode.js delete mode 100644 ui/mirage/models/ldap-account-status.js delete mode 100644 ui/tests/integration/components/replication-overview-mode-test.js delete mode 100644 ui/tests/unit/utils/kmip-role-fields-test.js delete mode 100644 vault/external_tests/audit/audit_test.go delete mode 100644 website/content/partials/autopilot/config.mdx delete mode 100644 website/content/partials/autopilot/node-types.mdx delete mode 100644 website/content/partials/autopilot/redundancy-zones.mdx delete mode 100644 website/content/partials/known-issues/aws-auth-external-id.mdx delete mode 100644 website/content/partials/known-issues/sync-activation-flags-cache-not-updated.mdx diff --git a/.github/actions/containerize/action.yml b/.github/actions/containerize/action.yml index 2c43266a6ff0..e269298e52b7 100644 --- a/.github/actions/containerize/action.yml +++ b/.github/actions/containerize/action.yml @@ -90,7 +90,7 @@ runs: [[ ! -d "$dest_dir" ]] && mkdir -p "$dest_dir" [[ ! -f "$dest_path" ]] && cp ${{ inputs.vault-binary-path }} "${dest_path}" - if: inputs.docker == 'true' - uses: hashicorp/actions-docker-build@f22d5ac7d36868afaa4be1cc1203ec1b5865cadd + uses: hashicorp/actions-docker-build@v2 with: arch: ${{ inputs.goarch }} do_zip_extract_step: 'false' # Don't download and extract an already present binary @@ -99,7 +99,7 @@ runs: revision: ${{ steps.vars.outputs.revision }} version: ${{ steps.vars.outputs.container-version }} - if: inputs.redhat == 'true' - uses: hashicorp/actions-docker-build@f22d5ac7d36868afaa4be1cc1203ec1b5865cadd + uses: hashicorp/actions-docker-build@v2 with: arch: ${{ inputs.goarch }} do_zip_extract_step: 'false' # Don't download and extract an already present binary diff --git a/.github/workflows/build-artifacts-ce.yml b/.github/workflows/build-artifacts-ce.yml index 9669ae3f78dc..c413ca1ac43d 100644 --- a/.github/workflows/build-artifacts-ce.yml +++ b/.github/workflows/build-artifacts-ce.yml @@ -93,7 +93,7 @@ jobs: redhat: true - goos: linux goarch: arm64 - redhat: true + redhat: false fail-fast: true runs-on: ${{ fromJSON(inputs.compute-build) }} name: (${{ matrix.goos }}, ${{ matrix.goarch }}) diff --git a/.github/workflows/enos-run-k8s.yml b/.github/workflows/enos-run-k8s.yml index 0edf7f034e6a..c629e44b47ae 100644 --- a/.github/workflows/enos-run-k8s.yml +++ b/.github/workflows/enos-run-k8s.yml @@ -26,7 +26,7 @@ env: jobs: enos: name: Integration - runs-on: ${{ fromJSON(contains(inputs.artifact-name, 'vault-enterprise') && (contains(inputs.artifact-name, 'arm64') && '["self-hosted","ondemand","os=ubuntu-arm","type=c6g.xlarge"]' || '["self-hosted","linux","small"]') || '"ubuntu-latest"') }} + runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.ELEVATED_GITHUB_TOKEN }} steps: diff --git a/.release/versions.hcl b/.release/versions.hcl index 3cc6940eefeb..f1db6233a6c6 100644 --- a/.release/versions.hcl +++ b/.release/versions.hcl @@ -5,9 +5,6 @@ schema = 1 active_versions { - version "1.18.x" { - ce_active = true - } version "1.17.x" { ce_active = true } diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f7fe531285b..3556b87346ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,199 +2,6 @@ - [v1.0.0 - v1.9.10](CHANGELOG-pre-v1.10.md) - [v0.11.6 and earlier](CHANGELOG-v0.md) -## 1.18.0-rc1 -## September 18, 2024 - -CHANGES: - -* activity (enterprise): filter all fields in client count responses by the request namespace [[GH-27790](https://github.com/hashicorp/vault/pull/27790)] -* activity (enterprise): remove deprecated fields distinct_entities and non_entity_tokens [[GH-27830](https://github.com/hashicorp/vault/pull/27830)] -* activity log: Deprecated the field "default_report_months". Instead, the billing start time will be used to determine the start time -when querying the activity log endpoints. [[GH-27350](https://github.com/hashicorp/vault/pull/27350)] -* activity log: Deprecates the current_billing_period field for /sys/internal/counters/activity. The default start time -will automatically be set the billing period start date. [[GH-27426](https://github.com/hashicorp/vault/pull/27426)] -* activity: The [activity export API](https://developer.hashicorp.com/vault/api-docs/system/internal-counters#activity-export) now requires the `sudo` ACL capability. [[GH-27846](https://github.com/hashicorp/vault/pull/27846)] -* activity: The [activity export API](https://developer.hashicorp.com/vault/api-docs/system/internal-counters#activity-export) now responds with a status of 204 instead 400 when no data exists within the time range specified by `start_time` and `end_time`. [[GH-28064](https://github.com/hashicorp/vault/pull/28064)] -* activity: The startTime will be set to the start of the current billing period by default. -The endTime will be set to the end of the current month. This applies to /sys/internal/counters/activity, -/sys/internal/counters/activity/export, and the vault operator usage command that utilizes /sys/internal/counters/activity. [[GH-27379](https://github.com/hashicorp/vault/pull/27379)] -* api: Update backoff/v3 to backoff/v4.3.0 [[GH-26868](https://github.com/hashicorp/vault/pull/26868)] -* auth/alicloud: Update plugin to v0.19.0 [[GH-28263](https://github.com/hashicorp/vault/pull/28263)] -* auth/azure: Update plugin to v0.19.0 [[GH-28294](https://github.com/hashicorp/vault/pull/28294)] -* auth/cf: Update plugin to v0.18.0 [[GH-27724](https://github.com/hashicorp/vault/pull/27724)] -* auth/cf: Update plugin to v0.19.0 [[GH-28266](https://github.com/hashicorp/vault/pull/28266)] -* auth/gcp: Update plugin to v0.19.0 [[GH-28366](https://github.com/hashicorp/vault/pull/28366)] -* auth/jwt: Update plugin to v0.21.0 [[GH-27498](https://github.com/hashicorp/vault/pull/27498)] -* auth/jwt: Update plugin to v0.22.0 [[GH-28349](https://github.com/hashicorp/vault/pull/28349)] -* auth/kerberos: Update plugin to v0.13.0 [[GH-28264](https://github.com/hashicorp/vault/pull/28264)] -* auth/kubernetes: Update plugin to v0.20.0 [[GH-28289](https://github.com/hashicorp/vault/pull/28289)] -* auth/oci: Update plugin to v0.17.0 [[GH-28307](https://github.com/hashicorp/vault/pull/28307)] -* cli: The undocumented `-dev-three-node` and `-dev-four-cluster` CLI options have been removed. [[GH-27578](https://github.com/hashicorp/vault/pull/27578)] -* consul-template: updated to version 0.39.1 [[GH-27799](https://github.com/hashicorp/vault/pull/27799)] -* core(enterprise): Updated the following two control group related errors responses to respond with response code 400 instead of 500: `control group: could not find token`, and `control group: token is not a valid control group token`. -* core: Bump Go version to 1.22.7 -* database/couchbase: Update plugin to v0.12.0 [[GH-28327](https://github.com/hashicorp/vault/pull/28327)] -* database/elasticsearch: Update plugin to v0.16.0 [[GH-28277](https://github.com/hashicorp/vault/pull/28277)] -* database/mongodbatlas: Update plugin to v0.13.0 [[GH-28268](https://github.com/hashicorp/vault/pull/28268)] -* database/redis-elasticache: Update plugin to v0.5.0 [[GH-28293](https://github.com/hashicorp/vault/pull/28293)] -* database/redis: Update plugin to v0.4.0 [[GH-28404](https://github.com/hashicorp/vault/pull/28404)] -* database/snowflake: Update plugin to v0.12.0 [[GH-28275](https://github.com/hashicorp/vault/pull/28275)] -* sdk: Upgrade to go-secure-stdlib/plugincontainer@v0.4.0, which also bumps github.com/docker/docker to v26.1.5+incompatible [[GH-28269](https://github.com/hashicorp/vault/pull/28269)] -* secrets/ad: Update plugin to v0.19.0 [[GH-28361](https://github.com/hashicorp/vault/pull/28361)] -* secrets/alicloud: Update plugin to v0.18.0 [[GH-28271](https://github.com/hashicorp/vault/pull/28271)] -* secrets/azure: Update plugin to v0.19.2 [[GH-27652](https://github.com/hashicorp/vault/pull/27652)] -* secrets/azure: Update plugin to v0.20.0 [[GH-28267](https://github.com/hashicorp/vault/pull/28267)] -* secrets/gcp: Update plugin to v0.20.0 [[GH-28324](https://github.com/hashicorp/vault/pull/28324)] -* secrets/gcpkms: Update plugin to v0.18.0 [[GH-28300](https://github.com/hashicorp/vault/pull/28300)] -* secrets/gcpkms: Update plugin to v0.19.0 [[GH-28360](https://github.com/hashicorp/vault/pull/28360)] -* secrets/kubernetes: Update plugin to v0.9.0 [[GH-28287](https://github.com/hashicorp/vault/pull/28287)] -* secrets/kv: Update plugin to v0.20.0 [[GH-28334](https://github.com/hashicorp/vault/pull/28334)] -* secrets/mongodbatlas: Update plugin to v0.13.0 [[GH-28348](https://github.com/hashicorp/vault/pull/28348)] -* secrets/openldap: Update plugin to v0.14.0 [[GH-28325](https://github.com/hashicorp/vault/pull/28325)] -* secrets/terraform: Update plugin to v0.10.0 [[GH-28312](https://github.com/hashicorp/vault/pull/28312)] -* secrets/terraform: Update plugin to v0.9.0 [[GH-28016](https://github.com/hashicorp/vault/pull/28016)] -* ui: Uses the internal/counters/activity/export endpoint for client count export data. [[GH-27455](https://github.com/hashicorp/vault/pull/27455)] - -FEATURES: - -* **AWS secrets engine STS session tags support**: Adds support for setting STS -session tags when generating temporary credentials using the AWS secrets -engine. [[GH-27620](https://github.com/hashicorp/vault/pull/27620)] -* **Adaptive Overload Protection (enterprise)**: Enables Adaptive Overload Protection -for write requests as a GA feature (enabled by default) for Integrated Storage. -* **Audit Entry Exclusion**: Audit devices support excluding fields from entries being written to them, with expression-based rules (powered by go-bexpr) to determine when the specific fields are excluded. -* **Workload Identity Federation UI for AWS (enterprise)**: Add WIF fields to AWS secrets engine. [[GH-28148](https://github.com/hashicorp/vault/pull/28148)] -* **KV v2 Patch/Subkey (enterprise)**: Adds GUI support to read the subkeys of a KV v2 secret and patch (partially update) secret data. [[GH-28212](https://github.com/hashicorp/vault/pull/28212)] -* **Self-Managed Static Roles**: Self-Managed Static Roles are now supported for select SQL database engines (Postgres, Oracle). Requires Vault Enterprise. [[GH-28199](https://github.com/hashicorp/vault/pull/28199)] -* **Vault Minimal Version**: Add the ability to build a minimal version of Vault -with only core features using the BUILD_MINIMAL environment variable. [[GH-27394](https://github.com/hashicorp/vault/pull/27394)] -* **Vault PKI 3GPP CMPv2 Server (Enterprise)**: Support for the PKI 3GPP CMPv2 certificate management protocol has been added to the Vault PKI Plugin. This allows standard CMPv2 clients to request certificates from a Vault server with no knowledge of Vault APIs. - -IMPROVEMENTS: - -* activity log: Changes how new client counts in the current month are estimated, in order to return more -visibly sensible totals. [[GH-27547](https://github.com/hashicorp/vault/pull/27547)] -* activity: The [activity export API](https://developer.hashicorp.com/vault/api-docs/system/internal-counters#activity-export) can now be called in non-root namespaces. Resulting records will be filtered to include the requested namespace (via `X-Vault-Namespace` header or within the path) and all child namespaces. [[GH-27846](https://github.com/hashicorp/vault/pull/27846)] -* activity: The [activity export API](https://developer.hashicorp.com/vault/api-docs/system/internal-counters#activity-export) now includes identity metadata about entity clients. [[GH-28064](https://github.com/hashicorp/vault/pull/28064)] -* activity: `/sys/internal/counters/activity` will now include a warning if the specified usage period contains estimated client counts. [[GH-28068](https://github.com/hashicorp/vault/pull/28068)] -* agent/sink: Allow configuration of the user and group ID of the file sink. [[GH-27123](https://github.com/hashicorp/vault/pull/27123)] -* agent: Add metric (vault.agent.authenticated) that is set to 1 when vault agent has a valid token and zero if it does not. [[GH-26570](https://github.com/hashicorp/vault/pull/26570)] -* agent: Add the ability to dump pprof to the filesystem using SIGUSR2 [[GH-27510](https://github.com/hashicorp/vault/pull/27510)] -* audit: Adds TRACE logging to log request/response under certain circumstances, and further improvements to the audit subsystem. [[GH-28056](https://github.com/hashicorp/vault/pull/28056)] -* audit: Ensure that any underyling errors from audit devices are logged even if we consider auditing to be a success. [[GH-27809](https://github.com/hashicorp/vault/pull/27809)] -* audit: Internal implementation changes to the audit subsystem which improve performance. [[GH-27952](https://github.com/hashicorp/vault/pull/27952)] -* audit: Internal implementation changes to the audit subsystem which improve relability. [[GH-28286](https://github.com/hashicorp/vault/pull/28286)] -* audit: sinks (file, socket, syslog) will attempt to log errors to the server operational -log before returning (if there are errors to log, and the context is done). [[GH-27859](https://github.com/hashicorp/vault/pull/27859)] -* auth/cert: Cache full list of role trust information separately to avoid -eviction, and avoid duplicate loading during multiple simultaneous logins on -the same role. [[GH-27902](https://github.com/hashicorp/vault/pull/27902)] -* cli: Add a `--dev-no-kv` flag to prevent auto mounting a key-value secret backend when running a dev server [[GH-16974](https://github.com/hashicorp/vault/pull/16974)] -* cli: Allow vault CLI HTTP headers to be specified using the JSON-encoded VAULT_HEADERS environment variable [[GH-21993](https://github.com/hashicorp/vault/pull/21993)] -* cli: `vault operator usage` will now include a warning if the specified usage period contains estimated client counts. [[GH-28068](https://github.com/hashicorp/vault/pull/28068)] -* core/activity: Ensure client count queries that include the current month return consistent results by sorting the clients before performing estimation [[GH-28062](https://github.com/hashicorp/vault/pull/28062)] -* core/cli: Example 'help' pages for vault read / write docs improved. [[GH-19064](https://github.com/hashicorp/vault/pull/19064)] -* core/identity: allow identity backend to be tuned using standard secrets backend tuning parameters. [[GH-14723](https://github.com/hashicorp/vault/pull/14723)] -* core/metrics: ensure core HA metrics are always output to Prometheus. [[GH-27966](https://github.com/hashicorp/vault/pull/27966)] -* core: make authLock and mountsLock in Core configurable via the detect_deadlocks configuration parameter. [[GH-27633](https://github.com/hashicorp/vault/pull/27633)] -* database/postgres: Add new fields to the plugin's config endpoint for client certificate authentication. [[GH-28024](https://github.com/hashicorp/vault/pull/28024)] -* db/cassandra: Add `disable_host_initial_lookup` option to backend, allowing the disabling of initial host lookup. [[GH-9733](https://github.com/hashicorp/vault/pull/9733)] -* identity: alias metadata is now returned when listing entity aliases [[GH-26073](https://github.com/hashicorp/vault/pull/26073)] -* license utilization reporting (enterprise): Auto-roll billing start date. [[GH-27656](https://github.com/hashicorp/vault/pull/27656)] -* proxy/sink: Allow configuration of the user and group ID of the file sink. [[GH-27123](https://github.com/hashicorp/vault/pull/27123)] -* proxy: Add the ability to dump pprof to the filesystem using SIGUSR2 [[GH-27510](https://github.com/hashicorp/vault/pull/27510)] -* raft-snapshot (enterprise): add support for managed identity credentials for azure snapshots -* raft/autopilot: Persist Raft server versions so autopilot always knows the versions of all servers in the cluster. Include server versions in the Raft bootstrap challenge answer so autopilot immediately knows the versions of new nodes. [[GH-28186](https://github.com/hashicorp/vault/pull/28186)] -* sdk/helper: Allow setting environment variables when using NewTestDockerCluster [[GH-27457](https://github.com/hashicorp/vault/pull/27457)] -* secrets-sync (enterprise): add support for specifying the replication regions for secret storage within GCP Secret Manager destinations -* secrets-sync (enterprise): add support for syncing secrets to github environments within repositories -* secrets-sync (enterprise): add support for syncing secrets to github organizations (beta) -* secrets/database/hana: Update HANA db client to v1.10.1 [[GH-27950](https://github.com/hashicorp/vault/pull/27950)] -* secrets/database: Add support for GCP CloudSQL private IP's. [[GH-26828](https://github.com/hashicorp/vault/pull/26828)] -* secrets/pki: Key Usage can now be set on intermediate and root CAs, and CSRs generated by the PKI secret's engine. [[GH-28237](https://github.com/hashicorp/vault/pull/28237)] -* serviceregistration: Added support for Consul ServiceMeta tags from config file from the new `service_meta` config field. [[GH-11084](https://github.com/hashicorp/vault/pull/11084)] -* storage/azure: Updated metadata endpoint to `GetMSIEndpoint`, which supports more than just the metadata service. [[GH-10624](https://github.com/hashicorp/vault/pull/10624)] -* storage/dynamodb: Speed up list and delete of large directories by only requesting keys from DynamoDB [[GH-21159](https://github.com/hashicorp/vault/pull/21159)] -* storage/etcd: Update etcd3 client to v3.5.13 to allow use of TLSv1.3. [[GH-26660](https://github.com/hashicorp/vault/pull/26660)] -* storage/raft: Bump raft to v1.7.0 which includes pre-vote. This should make clusters more stable during network partitions. [[GH-27605](https://github.com/hashicorp/vault/pull/27605)] -* storage/raft: Improve autopilot logging on startup to show config values clearly and avoid spurious logs [[GH-27464](https://github.com/hashicorp/vault/pull/27464)] -* ui/secrets-sync: Hide Secrets Sync from the sidebar nav if user does not have access to the feature. [[GH-27262](https://github.com/hashicorp/vault/pull/27262)] -* ui: AWS credentials form sets credential_type from backing role [[GH-27405](https://github.com/hashicorp/vault/pull/27405)] -* ui: Creates separate section for updating sensitive creds for Secrets sync create/edit view. [[GH-27538](https://github.com/hashicorp/vault/pull/27538)] -* ui: For AWS and SSH secret engines hide mount configuration details in toggle and display configuration details or cta. [[GH-27831](https://github.com/hashicorp/vault/pull/27831)] -* ui: Mask obfuscated fields when creating/editing a Secrets sync destination. [[GH-27348](https://github.com/hashicorp/vault/pull/27348)] -* ui: Move secret-engine configuration create/edit from routing `vault/settings/secrets/configure/` to `vault/secrets//configuration/edit` [[GH-27918](https://github.com/hashicorp/vault/pull/27918)] -* ui: Remove deprecated `current_billing_period` from dashboard activity log request [[GH-27559](https://github.com/hashicorp/vault/pull/27559)] -* ui: Update the client count dashboard to use API namespace filtering and other UX improvements [[GH-28036](https://github.com/hashicorp/vault/pull/28036)] -* ui: remove initial start/end parameters on the activity call for client counts dashboard. [[GH-27816](https://github.com/hashicorp/vault/pull/27816)] -* ui: simplify the date range editing experience in the client counts dashboard. [[GH-27796](https://github.com/hashicorp/vault/pull/27796)] -* website/docs: Added API documentation for Azure Secrets Engine delete role [[GH-27883](https://github.com/hashicorp/vault/pull/27883)] -* website/docs: corrected invalid json in sample payload for azure secrets engine create/update role [[GH-28076](https://github.com/hashicorp/vault/pull/28076)] - -BUG FIXES: - -* activity: The sys/internal/counters/activity endpoint will return current month data when the end_date parameter is set to a future date. [[GH-28042](https://github.com/hashicorp/vault/pull/28042)] -* agent: Fixed an issue causing excessive CPU usage during normal operation [[GH-27518](https://github.com/hashicorp/vault/pull/27518)] -* auth/appid, auth/cert, auth/github, auth/ldap, auth/okta, auth/radius, auth/userpass: fixed an issue with policy name normalization that would prevent a token associated with a policy containing an uppercase character to be renewed. [[GH-16484](https://github.com/hashicorp/vault/pull/16484)] -* auth/aws: fixes an issue where not supplying an external id was interpreted as an empty external id [[GH-27858](https://github.com/hashicorp/vault/pull/27858)] -* auth/cert: Merge error messages returned in login failures and include error when present [[GH-27202](https://github.com/hashicorp/vault/pull/27202)] -* auth/cert: Use subject's serial number, not issuer's within error message text in OCSP request errors [[GH-27696](https://github.com/hashicorp/vault/pull/27696)] -* auth/cert: ocsp_ca_certificates field was not honored when validating OCSP responses signed by a CA that did not issue the certificate. [[GH-28309](https://github.com/hashicorp/vault/pull/28309)] -* auth/token: fixes an edge case bug that "identity_policies" is nil and causes cli vault login error [[GH-17007](https://github.com/hashicorp/vault/pull/17007)] -* cli: Fixed an erroneous warning appearing about `-address` not being set when it is. [[GH-27265](https://github.com/hashicorp/vault/pull/27265)] -* cli: Fixed issue with `vault hcp connect` where HCP resources with uppercase letters were inaccessible when entering the correct project name. [[GH-27694](https://github.com/hashicorp/vault/pull/27694)] -* command: The `vault secrets move` and `vault auth move` command will no longer attempt to write to storage on performance standby nodes. [[GH-28059](https://github.com/hashicorp/vault/pull/28059)] -* config: Vault TCP listener config now correctly supports the documented proxy_protocol_behavior -setting of 'deny_unauthorized' [[GH-27459](https://github.com/hashicorp/vault/pull/27459)] -* core (enterprise): Fix 500 errors that occurred querying `sys/internal/ui/mounts` for a mount prefixed by a namespace path when path filters are configured. [[GH-27939](https://github.com/hashicorp/vault/pull/27939)] -* core (enterprise): Fix HTTP redirects in namespaces to use the correct path and (in the case of event subscriptions) the correct URI scheme. [[GH-27660](https://github.com/hashicorp/vault/pull/27660)] -* core (enterprise): Fix deletion of MFA login-enforcement configurations on standby nodes -* core/audit: Audit logging a Vault request/response checks if the existing context -is cancelled and will now use a new context with a 5 second timeout. -If the existing context is cancelled a new context, will be used. [[GH-27531](https://github.com/hashicorp/vault/pull/27531)] -* core/config: fix issue when using `proxy_protocol_behavior` with `deny_unauthorized`, -which causes the Vault TCP listener to close after receiving an untrusted upstream proxy connection. [[GH-27589](https://github.com/hashicorp/vault/pull/27589)] -* core/identity: Fixed an issue where deleted/reassigned entity-aliases were not removed from in-memory database. [[GH-27750](https://github.com/hashicorp/vault/pull/27750)] -* core: Fixed an issue where maximum request duration timeout was not being added to all requests containing strings sys/monitor and sys/events. With this change, timeout is now added to all requests except monitor and events endpoint. [[GH-28230](https://github.com/hashicorp/vault/pull/28230)] -* core: Fixed an issue with performance standbys not being able to handle rotate root requests. [[GH-27631](https://github.com/hashicorp/vault/pull/27631)] -* helper/pkcs7: Fix parsing certain messages containing only certificates [[GH-27435](https://github.com/hashicorp/vault/pull/27435)] -* identity/oidc: prevent JWKS from being generated by multiple concurrent requests [[GH-27929](https://github.com/hashicorp/vault/pull/27929)] -* licensing (enterprise): fixed issue where billing start date might not be correctly updated on performance standbys -* proxy/cache (enterprise): Fixed an issue where Proxy with static secret caching enabled would not correctly handle requests to older secret versions for KVv2 secrets. Proxy's static secret cache now properly handles all requests relating to older versions for KVv2 secrets. [[GH-28207](https://github.com/hashicorp/vault/pull/28207)] -* proxy/cache (enterprise): Fixed an issue where Proxy would not correctly update KV secrets when talking to a perf standby. Proxy will now attempt to forward requests to update secrets triggered by events to the active node. Note that this requires `allow_forwarding_via_header` to be configured on the cluster. [[GH-27891](https://github.com/hashicorp/vault/pull/27891)] -* proxy/cache (enterprise): Fixed an issue where cached static secrets could fail to update if the secrets belonged to a non-root namespace. [[GH-27730](https://github.com/hashicorp/vault/pull/27730)] -* proxy: Fixed an issue causing excessive CPU usage during normal operation [[GH-27518](https://github.com/hashicorp/vault/pull/27518)] -* raft/autopilot: Fixed panic that may occur during shutdown [[GH-27726](https://github.com/hashicorp/vault/pull/27726)] -* replication (enterprise): fix cache invalidation issue leading to namespace custom metadata not being shown correctly on performance secondaries -* secrets-sync (enterprise): Destination set/remove operations will no longer be blocked as "purge in progress" after a purge job ended in failure. -* secrets-sync (enterprise): Normalize custom_tag keys and values for recoverable invalid characters. -* secrets-sync (enterprise): Normalize secret key names before storing the external_name in a secret association. -* secrets-sync (enterprise): Patching github sync destination credentials will properly update and save the new credentials. -* secrets-sync (enterprise): Properly remove tags from secrets in AWS when they are removed from the source association -* secrets-sync (enterprise): Return an error immediately on destination creation when providing invalid custom_tags based on destination type. -* secrets-sync (enterprise): Return more accurate error code for invalid connection details -* secrets-sync (enterprise): Secondary nodes in a cluster now properly check activation-flags values. -* secrets-sync (enterprise): Skip invalid GitHub repository names when creating destinations -* secrets-sync (enterprise): Validate corresponding GitHub app parameters `app_name` and `installation_id` are set -* secrets/database: Skip connection verification on reading existing DB connection configuration [[GH-28139](https://github.com/hashicorp/vault/pull/28139)] -* secrets/identity (enterprise): Fix a bug that can cause DR promotion to fail in rare cases where a PR secondary has inconsistent alias information in storage. -* secrets/pki: fix lack of serial number to a certificate read resulting in a server side error. [[GH-27681](https://github.com/hashicorp/vault/pull/27681)] -* secrets/transit (enterprise): Fix an issue that caused input data be returned as part of generated CMAC values. -* storage/azure: Fix invalid account name initialization bug [[GH-27563](https://github.com/hashicorp/vault/pull/27563)] -* storage/raft (enterprise): Fix issue with namespace cache not getting cleared on snapshot restore, resulting in namespaces not found in the snapshot being inaccurately represented by API responses. [[GH-27474](https://github.com/hashicorp/vault/pull/27474)] -* storage/raft: Fix auto_join not working with mDNS provider. [[GH-25080](https://github.com/hashicorp/vault/pull/25080)] -* sys: Fix a bug where mounts of external plugins that were registered before Vault v1.0.0 could not be tuned to -use versioned plugins. [[GH-27881](https://github.com/hashicorp/vault/pull/27881)] -* ui: Allow creation of session_token type roles for AWS secret backend [[GH-27424](https://github.com/hashicorp/vault/pull/27424)] -* ui: Display an error and force a timeout when TOTP passcode is incorrect [[GH-27574](https://github.com/hashicorp/vault/pull/27574)] -* ui: Ensure token expired banner displays when batch token expires [[GH-27479](https://github.com/hashicorp/vault/pull/27479)] -* ui: Fix UI improperly checking capabilities for enabling performance and dr replication [[GH-28371](https://github.com/hashicorp/vault/pull/28371)] -* ui: Fix cursor jump on KVv2 json editor that would occur after pressing ENTER. [[GH-27569](https://github.com/hashicorp/vault/pull/27569)] -* ui: fix issue where enabling then disabling "Tidy ACME" in PKI results in failed API call. [[GH-27742](https://github.com/hashicorp/vault/pull/27742)] -* ui: fix namespace picker not working when in small screen where the sidebar is collapsed by default. [[GH-27728](https://github.com/hashicorp/vault/pull/27728)] -* ui: fixes renew-self being called right after login for non-renewable tokens [[GH-28204](https://github.com/hashicorp/vault/pull/28204)] -* ui: fixes toast (flash) alert message saying "created" when deleting a kv v2 secret [[GH-28093](https://github.com/hashicorp/vault/pull/28093)] - ## 1.17.5 ## August 30, 2024 @@ -2528,6 +2335,46 @@ BUG FIXES: * secrets/transit (enterprise): Apply hashing arguments and defaults to managed key sign/verify operations * secrets/transit: Do not allow auto rotation on managed_key key types [[GH-23723](https://github.com/hashicorp/vault/pull/23723)] +## 1.13.6 +### August 30, 2023 + +CHANGES: + +* core: Bump Go version to 1.20.7. + +IMPROVEMENTS: + +* core: Log rollback manager failures during unmount, remount to prevent replication failures on secondary clusters. [[GH-22235](https://github.com/hashicorp/vault/pull/22235)] +* replication (enterprise): Make reindex less disruptive by allowing writes during the flush phase. +* secrets/database: Improves error logging for static role rotations by including the database and role names. [[GH-22253](https://github.com/hashicorp/vault/pull/22253)] +* storage/raft: Cap the minimum dead_server_last_contact_threshold to 1m. [[GH-22040](https://github.com/hashicorp/vault/pull/22040)] +* ui: KV View Secret card will link to list view if input ends in "/" [[GH-22502](https://github.com/hashicorp/vault/pull/22502)] +* ui: enables create and update KV secret workflow when control group present [[GH-22471](https://github.com/hashicorp/vault/pull/22471)] + +BUG FIXES: + +* activity (enterprise): Fix misattribution of entities to no or child namespace auth methods [[GH-18809](https://github.com/hashicorp/vault/pull/18809)] +* api: Fix breakage with UNIX domain socket addresses introduced by newest Go versions as a security fix. [[GH-22523](https://github.com/hashicorp/vault/pull/22523)] +* core (enterprise): Remove MFA Configuration for namespace when deleting namespace +* core/quotas (enterprise): Fix a case where we were applying login roles to lease count quotas in a non-login context. +Also fix a related potential deadlock. [[GH-21110](https://github.com/hashicorp/vault/pull/21110)] +* core: Remove "expiration manager is nil on tokenstore" error log for unauth requests on DR secondary as they do not have expiration manager. [[GH-22137](https://github.com/hashicorp/vault/pull/22137)] +* core: Fix bug where background thread to update locked user entries runs on DR secondaries. [[GH-22355](https://github.com/hashicorp/vault/pull/22355)] +* core: Fix readonly errors that could occur while loading mounts/auths during unseal [[GH-22362](https://github.com/hashicorp/vault/pull/22362)] +* core: Fixed an instance where incorrect route entries would get tainted. We now pre-calculate namespace specific paths to avoid this. [[GH-21470](https://github.com/hashicorp/vault/pull/21470)] +* expiration: Fix a deadlock that could occur when a revocation failure happens while restoring leases on startup. [[GH-22374](https://github.com/hashicorp/vault/pull/22374)] +* license: Add autoloaded license path to the cache exempt list. This is to ensure the license changes on the active node is observed on the perfStandby node. [[GH-22363](https://github.com/hashicorp/vault/pull/22363)] +* replication (enterprise): Fix bug sync invalidate CoreReplicatedClusterInfoPath +* replication (enterprise): Fix panic when update-primary was called on demoted clusters using update_primary_addrs +* replication (enterprise): Fixing a bug by which the atomicity of a merkle diff result could be affected. This means it could be a source of a merkle-diff & sync process failing to switch into stream-wal mode afterwards. +* sdk/ldaputil: Properly escape user filters when using UPN domains +sdk/ldaputil: use EscapeLDAPValue implementation from cap/ldap [[GH-22249](https://github.com/hashicorp/vault/pull/22249)] +* secrets/ldap: Fix bug causing schema and password_policy to be overwritten in config. [[GH-22331](https://github.com/hashicorp/vault/pull/22331)] +* secrets/transform (enterprise): Tidy operations will be re-scheduled at a minimum of every minute, not a maximum of every minute +* ui: Fix blank page or ghost secret when canceling KV secret create [[GH-22541](https://github.com/hashicorp/vault/pull/22541)] +* ui: fixes `max_versions` default for secret metadata unintentionally overriding kv engine defaults [[GH-22394](https://github.com/hashicorp/vault/pull/22394)] +* ui: fixes model defaults overwriting input value when user tries to clear form input [[GH-22458](https://github.com/hashicorp/vault/pull/22458)] + ## 1.13.8 ### September 27, 2023 diff --git a/builtin/logical/aws/path_static_roles.go b/builtin/logical/aws/path_static_roles.go index 1bc5667e5575..f07eab54ab18 100644 --- a/builtin/logical/aws/path_static_roles.go +++ b/builtin/logical/aws/path_static_roles.go @@ -219,12 +219,6 @@ func (b *backend) pathStaticRolesWrite(ctx context.Context, req *logical.Request if err != nil { return nil, fmt.Errorf("expected an item with name %q, but got an error: %w", config.Name, err) } - // check if i is nil to prevent panic because - // 1. PopByKey returns nil if the key does not exist; and - // 2. the static cred queue is not repopulated on reload (see VAULT-30877) - if i == nil { - return nil, fmt.Errorf("expected an item with name %q, but got nil", config.Name) - } i.Value = config // update the next rotation to occur at now + the new rotation period i.Priority = time.Now().Add(config.RotationPeriod).Unix() diff --git a/changelog/27033.txt b/changelog/27033.txt deleted file mode 100644 index a06152dedc2b..000000000000 --- a/changelog/27033.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -command/server: Add support for dumping pprof files during startup using CLI option `pprof-dump-dir` -``` diff --git a/changelog/27927.txt b/changelog/27927.txt deleted file mode 100644 index afc37a7acbd3..000000000000 --- a/changelog/27927.txt +++ /dev/null @@ -1,6 +0,0 @@ -```release-note:improvement -storage/s3: Pass context to AWS SDK calls -``` -```release-note:improvement -storage/dynamodb: Pass context to AWS SDK calls -``` diff --git a/changelog/28330.txt b/changelog/28330.txt deleted file mode 100644 index 57f8ff979554..000000000000 --- a/changelog/28330.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:bug -auth/aws: Fixed potential panic after step-down and the queue has not repopulated. -``` \ No newline at end of file diff --git a/command/server.go b/command/server.go index 5aba92cbd36d..54e921bbab5f 100644 --- a/command/server.go +++ b/command/server.go @@ -119,7 +119,6 @@ type ServerCommand struct { flagConfigs []string flagRecovery bool flagExperiments []string - flagCLIDump string flagDev bool flagDevTLS bool flagDevTLSCertDir string @@ -222,13 +221,6 @@ func (c *ServerCommand) Flags() *FlagSets { "Valid experiments are: " + strings.Join(experiments.ValidExperiments(), ", "), }) - f.StringVar(&StringVar{ - Name: "pprof-dump-dir", - Target: &c.flagCLIDump, - Completion: complete.PredictDirs("*"), - Usage: "Directory where generated profiles are created. If left unset, files are not generated.", - }) - f = set.NewFlagSet("Dev Options") f.BoolVar(&BoolVar{ @@ -1601,11 +1593,6 @@ func (c *ServerCommand) Run(args []string) int { coreShutdownDoneCh = core.ShutdownDone() } - cliDumpCh := make(chan struct{}) - if c.flagCLIDump != "" { - go func() { cliDumpCh <- struct{}{} }() - } - // Wait for shutdown shutdownTriggered := false retCode := 0 @@ -1720,6 +1707,7 @@ func (c *ServerCommand) Run(args []string) int { // Notify systemd that the server has completed reloading config c.notifySystemd(systemd.SdNotifyReady) + case <-c.SigUSR2Ch: logWriter := c.logger.StandardWriter(&hclog.StandardLoggerOptions{}) pprof.Lookup("goroutine").WriteTo(logWriter, 2) @@ -1771,51 +1759,6 @@ func (c *ServerCommand) Run(args []string) int { } c.logger.Info(fmt.Sprintf("Wrote pprof files to: %s", pprofPath)) - case <-cliDumpCh: - path := c.flagCLIDump - - if _, err := os.Stat(path); err != nil && !errors.Is(err, os.ErrNotExist) { - c.logger.Error("Checking cli dump path failed", "error", err) - continue - } - - pprofPath := filepath.Join(path, "vault-pprof") - err := os.MkdirAll(pprofPath, os.ModePerm) - if err != nil { - c.logger.Error("Could not create temporary directory for pprof", "error", err) - continue - } - - dumps := []string{"goroutine", "heap", "allocs", "threadcreate", "profile"} - for _, dump := range dumps { - pFile, err := os.Create(filepath.Join(pprofPath, dump)) - if err != nil { - c.logger.Error("error creating pprof file", "name", dump, "error", err) - break - } - - if dump != "profile" { - err = pprof.Lookup(dump).WriteTo(pFile, 0) - if err != nil { - c.logger.Error("error generating pprof data", "name", dump, "error", err) - pFile.Close() - break - } - } else { - // CPU profiles need to run for a duration so we're going to run it - // just for one second to avoid blocking here. - if err := pprof.StartCPUProfile(pFile); err != nil { - c.logger.Error("could not start CPU profile: ", err) - pFile.Close() - break - } - time.Sleep(time.Second * 1) - pprof.StopCPUProfile() - } - pFile.Close() - } - - c.logger.Info(fmt.Sprintf("Wrote startup pprof files to: %s", pprofPath)) } } // Notify systemd that the server is shutting down diff --git a/physical/dynamodb/dynamodb.go b/physical/dynamodb/dynamodb.go index bc27def0c987..c4484d20d446 100644 --- a/physical/dynamodb/dynamodb.go +++ b/physical/dynamodb/dynamodb.go @@ -294,7 +294,7 @@ func (d *DynamoDBBackend) Put(ctx context.Context, entry *physical.Entry) error }) } - return d.batchWriteRequests(ctx, requests) + return d.batchWriteRequests(requests) } // Get is used to fetch an entry @@ -304,7 +304,7 @@ func (d *DynamoDBBackend) Get(ctx context.Context, key string) (*physical.Entry, d.permitPool.Acquire() defer d.permitPool.Release() - resp, err := d.client.GetItemWithContext(ctx, &dynamodb.GetItemInput{ + resp, err := d.client.GetItem(&dynamodb.GetItemInput{ TableName: aws.String(d.table), ConsistentRead: aws.Bool(true), Key: map[string]*dynamodb.AttributeValue{ @@ -363,7 +363,7 @@ func (d *DynamoDBBackend) Delete(ctx context.Context, key string) error { excluded = append(excluded, recordKeyForVaultKey(prefixes[index-1])) } - hasChildren, err := d.hasChildren(ctx, prefix, excluded) + hasChildren, err := d.hasChildren(prefix, excluded) if err != nil { return err } @@ -387,7 +387,7 @@ func (d *DynamoDBBackend) Delete(ctx context.Context, key string) error { } } - return d.batchWriteRequests(ctx, requests) + return d.batchWriteRequests(requests) } // List is used to list all the keys under a given @@ -420,7 +420,7 @@ func (d *DynamoDBBackend) List(ctx context.Context, prefix string) ([]string, er d.permitPool.Acquire() defer d.permitPool.Release() - err := d.client.QueryPagesWithContext(ctx, queryInput, func(out *dynamodb.QueryOutput, lastPage bool) bool { + err := d.client.QueryPages(queryInput, func(out *dynamodb.QueryOutput, lastPage bool) bool { var record DynamoDBRecord for _, item := range out.Items { dynamodbattribute.UnmarshalMap(item, &record) @@ -443,7 +443,7 @@ func (d *DynamoDBBackend) List(ctx context.Context, prefix string) ([]string, er // before any deletes take place. To account for that hasChildren accepts a slice of // strings representing values we expect to find that should NOT be counted as children // because they are going to be deleted. -func (d *DynamoDBBackend) hasChildren(ctx context.Context, prefix string, exclude []string) (bool, error) { +func (d *DynamoDBBackend) hasChildren(prefix string, exclude []string) (bool, error) { prefix = strings.TrimSuffix(prefix, "/") prefix = escapeEmptyPath(prefix) @@ -473,7 +473,7 @@ func (d *DynamoDBBackend) hasChildren(ctx context.Context, prefix string, exclud d.permitPool.Acquire() defer d.permitPool.Release() - out, err := d.client.QueryWithContext(ctx, queryInput) + out, err := d.client.Query(queryInput) if err != nil { return false, err } @@ -519,7 +519,7 @@ func (d *DynamoDBBackend) HAEnabled() bool { // batchWriteRequests takes a list of write requests and executes them in badges // with a maximum size of 25 (which is the limit of BatchWriteItem requests). -func (d *DynamoDBBackend) batchWriteRequests(ctx context.Context, requests []*dynamodb.WriteRequest) error { +func (d *DynamoDBBackend) batchWriteRequests(requests []*dynamodb.WriteRequest) error { for len(requests) > 0 { batchSize := int(math.Min(float64(len(requests)), 25)) batch := map[string][]*dynamodb.WriteRequest{d.table: requests[:batchSize]} @@ -534,7 +534,7 @@ func (d *DynamoDBBackend) batchWriteRequests(ctx context.Context, requests []*dy for len(batch) > 0 { var output *dynamodb.BatchWriteItemOutput - output, err = d.client.BatchWriteItemWithContext(ctx, &dynamodb.BatchWriteItemInput{ + output, err = d.client.BatchWriteItem(&dynamodb.BatchWriteItemInput{ RequestItems: batch, }) if err != nil { diff --git a/physical/s3/s3.go b/physical/s3/s3.go index b1687a91622e..da82acccd3ca 100644 --- a/physical/s3/s3.go +++ b/physical/s3/s3.go @@ -183,7 +183,7 @@ func (s *S3Backend) Put(ctx context.Context, entry *physical.Entry) error { putObjectInput.SSEKMSKeyId = aws.String(s.kmsKeyId) } - _, err := s.client.PutObjectWithContext(ctx, putObjectInput) + _, err := s.client.PutObject(putObjectInput) if err != nil { return err } @@ -201,7 +201,7 @@ func (s *S3Backend) Get(ctx context.Context, key string) (*physical.Entry, error // Setup key key = path.Join(s.path, key) - resp, err := s.client.GetObjectWithContext(ctx, &s3.GetObjectInput{ + resp, err := s.client.GetObject(&s3.GetObjectInput{ Bucket: aws.String(s.bucket), Key: aws.String(key), }) @@ -254,7 +254,7 @@ func (s *S3Backend) Delete(ctx context.Context, key string) error { // Setup key key = path.Join(s.path, key) - _, err := s.client.DeleteObjectWithContext(ctx, &s3.DeleteObjectInput{ + _, err := s.client.DeleteObject(&s3.DeleteObjectInput{ Bucket: aws.String(s.bucket), Key: aws.String(key), }) @@ -289,7 +289,7 @@ func (s *S3Backend) List(ctx context.Context, prefix string) ([]string, error) { keys := []string{} - err := s.client.ListObjectsV2PagesWithContext(ctx, params, + err := s.client.ListObjectsV2Pages(params, func(page *s3.ListObjectsV2Output, lastPage bool) bool { if page != nil { // Add truncated 'folder' paths diff --git a/ui/app/adapters/kmip/role.js b/ui/app/adapters/kmip/role.js index 693ae6327e11..9779ad67a564 100644 --- a/ui/app/adapters/kmip/role.js +++ b/ui/app/adapters/kmip/role.js @@ -6,11 +6,10 @@ import BaseAdapter from './base'; import { decamelize } from '@ember/string'; import { getProperties } from '@ember/object'; -import { nonOperationFields } from 'vault/utils/kmip-role-fields'; export default BaseAdapter.extend({ createRecord(store, type, snapshot) { - const name = snapshot.id || snapshot.record.role; + const name = snapshot.id || snapshot.attr('name'); const url = this._url( type.modelName, { @@ -19,11 +18,10 @@ export default BaseAdapter.extend({ }, name ); - const data = this.serialize(snapshot); - return this.ajax(url, 'POST', { data }).then(() => { + return this.ajax(url, 'POST', { data: this.serialize(snapshot) }).then(() => { return { id: name, - role: name, + name, backend: snapshot.record.backend, scope: snapshot.record.scope, }; @@ -31,8 +29,7 @@ export default BaseAdapter.extend({ }, deleteRecord(store, type, snapshot) { - // records must always have IDs - const name = snapshot.id; + const name = snapshot.id || snapshot.attr('name'); const url = this._url( type.modelName, { @@ -44,35 +41,35 @@ export default BaseAdapter.extend({ return this.ajax(url, 'DELETE'); }, - updateRecord() { - return this.createRecord(...arguments); - }, - serialize(snapshot) { // the endpoint here won't allow sending `operation_all` and `operation_none` at the same time or with // other operation_ values, so we manually check for them and send an abbreviated object const json = snapshot.serialize(); - const keys = nonOperationFields(snapshot.record.editableFields).map(decamelize); - const nonOp = getProperties(json, keys); - for (const field in nonOp) { - if (nonOp[field] == null) { - delete nonOp[field]; + const keys = snapshot.record.nonOperationFields.map(decamelize); + const nonOperationFields = getProperties(json, keys); + for (const field in nonOperationFields) { + if (nonOperationFields[field] == null) { + delete nonOperationFields[field]; } } if (json.operation_all) { return { operation_all: true, - ...nonOp, + ...nonOperationFields, }; } if (json.operation_none) { return { operation_none: true, - ...nonOp, + ...nonOperationFields, }; } delete json.operation_none; delete json.operation_all; return json; }, + + updateRecord() { + return this.createRecord(...arguments); + }, }); diff --git a/ui/app/adapters/named-path.js b/ui/app/adapters/named-path.js index 6b8fb7297483..535cf33e3d9a 100644 --- a/ui/app/adapters/named-path.js +++ b/ui/app/adapters/named-path.js @@ -30,7 +30,7 @@ export default class NamedPathAdapter extends ApplicationAdapter { const [store, { modelName }, snapshot] = arguments; const name = snapshot.attr('name'); // throw error if user attempts to create a record with same name, otherwise POST request silently overrides (updates) the existing model - if (store.peekRecord({ type: modelName, id: name }) !== null) { + if (store.hasRecordForId(modelName, name)) { throw new Error(`A record already exists with the name: ${name}`); } else { return this._saveRecord(...arguments); diff --git a/ui/app/app.js b/ui/app/app.js index 5de9c28a3a33..9409b0cd916f 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -8,7 +8,6 @@ import Resolver from 'ember-resolver'; import loadInitializers from 'ember-load-initializers'; import config from 'vault/config/environment'; -// TODO: DEPRECATION https://ember-engines.com/docs/deprecations#-use-alias-for-inject-router-service-from-host-application export default class App extends Application { modulePrefix = config.modulePrefix; podModulePrefix = config.podModulePrefix; diff --git a/ui/app/controllers/vault/cluster/init.js b/ui/app/controllers/vault/cluster/init.js index ddcd72e5e3de..9083f2dddef8 100644 --- a/ui/app/controllers/vault/cluster/init.js +++ b/ui/app/controllers/vault/cluster/init.js @@ -40,8 +40,7 @@ export default Controller.extend(DEFAULTS, { }), actions: { - initCluster(payload) { - const data = { ...payload }; + initCluster(data) { const isCloudSeal = !!this.model.sealType && this.model.sealType !== 'shamir'; if (data.secret_shares) { const shares = parseInt(data.secret_shares, 10); diff --git a/ui/app/initializers/deprecation-filter.js b/ui/app/initializers/deprecation-filter.js index 5e163149da38..3063b376152f 100644 --- a/ui/app/initializers/deprecation-filter.js +++ b/ui/app/initializers/deprecation-filter.js @@ -10,8 +10,7 @@ export function initialize() { registerDeprecationHandler((message, options, next) => { // filter deprecations that are scheduled to be removed in a specific version // when upgrading or addressing deprecation warnings be sure to update this or remove if not needed - if (options?.until.includes('6.0')) { - // ignore all deprecations for 6+ + if (options?.until.includes('5.0') && options?.id.startsWith('ember-data')) { return; } next(message, options); diff --git a/ui/app/models/auth-config.js b/ui/app/models/auth-config.js index 46f7318569ec..46ab4f9700cc 100644 --- a/ui/app/models/auth-config.js +++ b/ui/app/models/auth-config.js @@ -6,10 +6,5 @@ import Model, { belongsTo } from '@ember-data/model'; export default Model.extend({ - backend: belongsTo('auth-method', { - inverse: 'authConfigs', - readOnly: true, - async: false, - as: 'auth-config', - }), + backend: belongsTo('auth-method', { inverse: 'authConfigs', readOnly: true, async: false }), }); diff --git a/ui/app/models/kmip/role.js b/ui/app/models/kmip/role.js index 520efcf1b517..8083acd7b331 100644 --- a/ui/app/models/kmip/role.js +++ b/ui/app/models/kmip/role.js @@ -4,35 +4,52 @@ */ import Model, { attr } from '@ember-data/model'; +import { computed } from '@ember/object'; +import fieldToAttrs, { expandAttributeMeta } from 'vault/utils/field-to-attrs'; import apiPath from 'vault/utils/api-path'; import lazyCapabilities from 'vault/macros/lazy-capabilities'; -import { withExpandedAttributes } from 'vault/decorators/model-expanded-attributes'; -import { operationFields, operationFieldsWithoutSpecial, tlsFields } from 'vault/utils/kmip-role-fields'; import { removeManyFromArray } from 'vault/helpers/remove-from-array'; -@withExpandedAttributes() -export default class KmipRoleModel extends Model { - @attr({ readOnly: true }) backend; - @attr({ readOnly: true }) scope; +const COMPUTEDS = { + operationFields: computed('newFields', function () { + return this.newFields.filter((key) => key.startsWith('operation')); + }), - get editableFields() { - return Object.keys(this.allByKey).filter((k) => !['backend', 'scope', 'role'].includes(k)); - } + operationFieldsWithoutSpecial: computed('operationFields', function () { + return removeManyFromArray(this.operationFields, ['operationAll', 'operationNone']); + }), - get fieldGroups() { - const tls = tlsFields(); - const groups = [{ TLS: tls }]; - // op fields are shown in OperationFieldDisplay - const opFields = operationFields(this.editableFields); - // not op fields, tls fields, or role/backend/scope - const defaultFields = this.editableFields.filter((f) => ![...opFields, ...tls].includes(f)); - if (defaultFields.length) { - groups.unshift({ default: defaultFields }); + tlsFields: computed(function () { + return ['tlsClientKeyBits', 'tlsClientKeyType', 'tlsClientTtl']; + }), + + // For rendering on the create/edit pages + defaultFields: computed('newFields', 'operationFields', 'tlsFields', function () { + const excludeFields = ['role'].concat(this.operationFields, this.tlsFields); + return removeManyFromArray(this.newFields, excludeFields); + }), + + // For adapter/serializer + nonOperationFields: computed('newFields', 'operationFields', function () { + return removeManyFromArray(this.newFields, this.operationFields); + }), +}; + +export default Model.extend(COMPUTEDS, { + backend: attr({ readOnly: true }), + scope: attr({ readOnly: true }), + name: attr({ readOnly: true }), + + fieldGroups: computed('fields', 'defaultFields.length', 'tlsFields', function () { + const groups = [{ TLS: this.tlsFields }]; + if (this.defaultFields.length) { + groups.unshift({ default: this.defaultFields }); } - return this._expandGroups(groups); - } + const ret = fieldToAttrs(this, groups); + return ret; + }), - get operationFormFields() { + operationFormFields: computed('operationFieldsWithoutSpecial', function () { const objects = [ 'operationCreate', 'operationActivate', @@ -45,7 +62,7 @@ export default class KmipRoleModel extends Model { const attributes = ['operationAddAttribute', 'operationGetAttributes']; const server = ['operationDiscoverVersions']; - const others = removeManyFromArray(operationFieldsWithoutSpecial(this.editableFields), [ + const others = removeManyFromArray(this.operationFieldsWithoutSpecial, [ ...objects, ...attributes, ...server, @@ -60,8 +77,14 @@ export default class KmipRoleModel extends Model { Other: others, }); } - return this._expandGroups(groups); - } + return fieldToAttrs(this, groups); + }), + tlsFormFields: computed('tlsFields', function () { + return expandAttributeMeta(this, this.tlsFields); + }), + fields: computed('defaultFields', function () { + return expandAttributeMeta(this, this.defaultFields); + }), - @lazyCapabilities(apiPath`${'backend'}/scope/${'scope'}/role/${'id'}`, 'backend', 'scope', 'id') updatePath; -} + updatePath: lazyCapabilities(apiPath`${'backend'}/scope/${'scope'}/role/${'id'}`, 'backend', 'scope', 'id'), +}); diff --git a/ui/app/styles/components/kmip-role-edit.scss b/ui/app/styles/components/kmip-role-edit.scss index f4fc884be41f..a517c1a54313 100644 --- a/ui/app/styles/components/kmip-role-edit.scss +++ b/ui/app/styles/components/kmip-role-edit.scss @@ -3,14 +3,11 @@ * SPDX-License-Identifier: BUSL-1.1 */ -.kmip-role-operations { - column-count: 2; -} .kmip-role-allowed-operations { @extend .box; flex: 1 1 auto; box-shadow: none; - padding: $spacing-4 0; + padding: 0; } .kmip-role-allowed-operations .field { margin-bottom: $spacing-4; diff --git a/ui/app/styles/components/replication-mode-summary.scss b/ui/app/styles/components/replication-mode-summary.scss new file mode 100644 index 000000000000..a96a3d15c67b --- /dev/null +++ b/ui/app/styles/components/replication-mode-summary.scss @@ -0,0 +1,16 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +.replication-description { + flex-shrink: 1; + + .title { + margin-bottom: $spacing-8; + } + + .detail-tags { + margin-bottom: $spacing-16; + } +} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index 73d9e179635a..2d020970b73c 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -91,6 +91,7 @@ @import './components/read-more'; @import './components/regex-validator'; @import './components/replication-dashboard'; +@import './components/replication-mode-summary'; @import './components/replication-page'; @import './components/replication-summary'; @import './components/role-item'; diff --git a/ui/app/utils/kmip-role-fields.js b/ui/app/utils/kmip-role-fields.js deleted file mode 100644 index 3503ad58f449..000000000000 --- a/ui/app/utils/kmip-role-fields.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { removeManyFromArray } from 'vault/helpers/remove-from-array'; - -export const operationFields = (fieldNames) => { - if (!Array.isArray(fieldNames)) { - throw new Error('fieldNames must be an array'); - } - return fieldNames.filter((key) => key.startsWith('operation')); -}; - -export const operationFieldsWithoutSpecial = (fieldNames) => { - const opFields = operationFields(fieldNames); - return removeManyFromArray(opFields, ['operationAll', 'operationNone']); -}; - -export const nonOperationFields = (fieldNames) => { - const opFields = operationFields(fieldNames); - return removeManyFromArray(fieldNames, opFields); -}; - -export const tlsFields = () => { - return ['tlsClientKeyBits', 'tlsClientKeyType', 'tlsClientTtl']; -}; diff --git a/ui/config/deprecation-workflow.js b/ui/config/deprecation-workflow.js index 7ddd86cbb1d0..cf343738e691 100644 --- a/ui/config/deprecation-workflow.js +++ b/ui/config/deprecation-workflow.js @@ -5,19 +5,20 @@ /* global self */ self.deprecationWorkflow = self.deprecationWorkflow || {}; -self.deprecationWorkflow.config = { - throwOnUnhandled: false, -}; - +//self.deprecationWorkflow.config = { +//throwOnUnhandled: true +//} self.deprecationWorkflow.config = { // current output from deprecationWorkflow.flushDeprecations(); + // deprecations that will not be removed until 5.0.0 are filtered by deprecation-filter initializer rather than silencing below workflow: [ + { handler: 'silence', matchId: 'ember-data:model-save-promise' }, + { handler: 'silence', matchId: 'ember-engines.deprecation-camelized-engine-names' }, { handler: 'silence', matchId: 'ember-engines.deprecation-router-service-from-host' }, - // ember-data - { handler: 'silence', matchId: 'ember-data:deprecate-early-static' }, - { handler: 'silence', matchId: 'ember-data:deprecate-model-reopen' }, - { handler: 'silence', matchId: 'ember-data:deprecate-promise-proxies' }, - { handler: 'silence', matchId: 'ember-data:no-a-with-array-like' }, - { handler: 'silence', matchId: 'ember-data:deprecate-promise-many-array-behaviors' }, + { handler: 'silence', matchId: 'ember-modifier.use-modify' }, + { handler: 'silence', matchId: 'ember-modifier.no-element-property' }, + { handler: 'silence', matchId: 'ember-modifier.no-args-property' }, + { handler: 'silence', matchId: 'ember-cli-mirage-config-routes-only-export' }, + { handler: 'silence', matchId: 'setting-on-hash' }, ], }; diff --git a/ui/lib/core/addon/components/replication-mode-summary.js b/ui/lib/core/addon/components/replication-mode-summary.js new file mode 100644 index 000000000000..0938b0399dc9 --- /dev/null +++ b/ui/lib/core/addon/components/replication-mode-summary.js @@ -0,0 +1,68 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { service } from '@ember/service'; +import { equal } from '@ember/object/computed'; +import { get, computed } from '@ember/object'; +import Component from '@ember/component'; +import layout from '../templates/components/replication-mode-summary'; + +const replicationAttr = function (attr) { + return computed(`cluster.{dr,performance}.${attr}`, 'cluster', 'mode', function () { + const { mode, cluster } = this; + return get(cluster, `${mode}.${attr}`); + }); +}; +export default Component.extend({ + layout, + version: service(), + router: service(), + namespace: service(), + classNameBindings: ['isMenu::box'], + attributeBindings: ['href', 'target'], + display: 'banner', + isMenu: equal('display', 'menu'), + href: computed( + 'cluster.id', + 'display', + 'mode', + 'replicationEnabled', + 'version.hasPerfReplication', + function () { + const display = this.display; + const mode = this.mode; + if (mode === 'performance' && display === 'menu' && this.version.hasPerfReplication === false) { + return 'https://www.hashicorp.com/products/vault'; + } + if (this.replicationEnabled || display === 'menu') { + return this.router.urlFor('vault.cluster.replication.mode.index', this.cluster.id, mode); + } + return null; + } + ), + target: computed('isPerformance', 'version.hasPerfReplication', function () { + if (this.isPerformance && this.version.hasPerfReplication === false) { + return '_blank'; + } + return null; + }), + internalLink: false, + isPerformance: equal('mode', 'performance'), + replicationEnabled: replicationAttr('replicationEnabled'), + replicationUnsupported: equal('cluster.mode', 'unsupported'), + replicationDisabled: replicationAttr('replicationDisabled'), + syncProgressPercent: replicationAttr('syncProgressPercent'), + syncProgress: replicationAttr('syncProgress'), + secondaryId: replicationAttr('secondaryId'), + modeForUrl: replicationAttr('modeForUrl'), + clusterIdDisplay: replicationAttr('clusterIdDisplay'), + mode: null, + cluster: null, + modeState: computed('cluster', 'mode', function () { + const { cluster, mode } = this; + const clusterState = cluster[mode].state; + return clusterState; + }), +}); diff --git a/ui/lib/core/addon/templates/components/replication-mode-summary.hbs b/ui/lib/core/addon/templates/components/replication-mode-summary.hbs new file mode 100644 index 000000000000..bd2eec9950c7 --- /dev/null +++ b/ui/lib/core/addon/templates/components/replication-mode-summary.hbs @@ -0,0 +1,122 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +~}} + +{{#if this.isMenu}} + {{! this is the status menu }} +
+
+ {{#if this.replicationUnsupported}} + Unsupported + {{else if this.replicationEnabled}} +
+ {{concat (if (eq this.mode "performance") "Performance " "Disaster Recovery ") (capitalize this.modeForUrl)}} +
+ {{#if this.secondaryId}} + + + {{this.secondaryId}} + + + {{/if}} + + + {{this.clusterIdDisplay}} + + + {{else if (and (eq this.mode "performance") (not (has-feature "Performance Replication")))}} + Learn more + {{else if this.auth.currentToken}} + Enable + {{if (eq this.mode "performance") "Performance" "Disaster Recovery"}} + {{else}} + + {{if (eq this.mode "performance") "Performance" "Disaster Recovery"}} + + {{/if}} +
+
+ {{#if this.replicationEnabled}} + {{#if (cluster-states this.modeState)}} + + + + {{else if this.syncProgress}} + + {{this.syncProgress.progress}} + of + {{this.syncProgress.total}} + keys + + {{/if}} + {{else}} + + {{/if}} +
+
+{{else}} + {{! this is the replication index page }} +
+
+
+ {{#if (and (eq this.mode "performance") (not (has-feature "Performance Replication")))}} +

+ 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. + + Upgrade + +

+ {{else if this.replicationEnabled}} +
+ Enabled +
+
+ + {{capitalize this.modeForUrl}} + + {{#if this.secondaryId}} + + + {{this.secondaryId}} + + + {{/if}} + + + {{this.clusterIdDisplay}} + + +
+ {{/if}} +

+ {{replication-mode-description this.mode}} +

+
+
+
+ +
+
+{{/if}} \ No newline at end of file diff --git a/ui/lib/core/app/components/replication-mode-summary.js b/ui/lib/core/app/components/replication-mode-summary.js new file mode 100644 index 000000000000..605fceb56334 --- /dev/null +++ b/ui/lib/core/app/components/replication-mode-summary.js @@ -0,0 +1,6 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +export { default } from 'core/components/replication-mode-summary'; diff --git a/ui/lib/kmip/addon/components/edit-form-kmip-role.js b/ui/lib/kmip/addon/components/edit-form-kmip-role.js new file mode 100644 index 000000000000..43a230b80b19 --- /dev/null +++ b/ui/lib/kmip/addon/components/edit-form-kmip-role.js @@ -0,0 +1,55 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import EditForm from 'core/components/edit-form'; +import { computed } from '@ember/object'; +import layout from '../templates/components/edit-form-kmip-role'; + +export default EditForm.extend({ + layout, + model: null, + + cancelLink: computed('cancelLinkParams.[]', function () { + if (!Array.isArray(this.cancelLinkParams) || !this.cancelLinkParams.length) return; + const [route, ...models] = this.cancelLinkParams; + return { route, models }; + }), + + init() { + this._super(...arguments); + + if (this.model.isNew) { + this.model.operationAll = true; + } + }, + + actions: { + toggleOperationSpecial(checked) { + this.model.operationNone = !checked; + this.model.operationAll = checked; + }, + + // when operationAll is true, we want all of the items + // to appear checked, but we don't want to override what items + // a user has selected - so this action creates an object that we + // pass to the FormField component as the model instead of the real model + placeholderOrModel(isOperationAll, attr) { + return isOperationAll ? { [attr.name]: true } : this.model; + }, + + preSave(model) { + // if we have operationAll or operationNone, we want to clear + // out the others so that display shows the right data + if (model.operationAll || model.operationNone) { + model.operationFieldsWithoutSpecial.forEach((field) => model.set(field, null)); + } + // set operationNone if user unchecks 'operationAll' instead of toggling the 'operationNone' input + // doing here instead of on the 'operationNone' input because a user might deselect all, then reselect some options + // and immediately setting operationNone will hide all of the checkboxes in the UI + this.model.operationNone = + model.operationFieldsWithoutSpecial.every((attr) => !model[attr]) && !this.model.operationAll; + }, + }, +}); diff --git a/ui/lib/kmip/addon/components/kmip/role-form.hbs b/ui/lib/kmip/addon/components/kmip/role-form.hbs deleted file mode 100644 index d10fb57add42..000000000000 --- a/ui/lib/kmip/addon/components/kmip/role-form.hbs +++ /dev/null @@ -1,82 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: BUSL-1.1 -~}} - -
- -
- - {{#if @model.isNew}} - {{! Show role name only in create mode }} - - {{/if}} -
- - -
- {{#unless @model.operationNone}} - -

- Allowed Operations -

-
-
- -
-
- {{#each this.operationFormGroups as |group|}} -
-

{{group.name}}

- {{#each group.fields as |attr|}} - - {{/each}} -
- {{/each}} -
-
- {{/unless}} -
-

- TLS -

- {{#each this.tlsFormFields as |attr|}} - - {{/each}} -
- {{#each @model.fields as |attr|}} - - {{/each}} -
- -
- - - - -
- \ No newline at end of file diff --git a/ui/lib/kmip/addon/components/kmip/role-form.js b/ui/lib/kmip/addon/components/kmip/role-form.js deleted file mode 100644 index 496dde4106a0..000000000000 --- a/ui/lib/kmip/addon/components/kmip/role-form.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import AdapterError from '@ember-data/adapter/error'; -import { action } from '@ember/object'; -import { service } from '@ember/service'; -import Component from '@glimmer/component'; -import { task } from 'ember-concurrency'; -import { removeManyFromArray } from 'vault/helpers/remove-from-array'; -import { operationFieldsWithoutSpecial, tlsFields } from 'vault/utils/kmip-role-fields'; - -export default class KmipRoleFormComponent extends Component { - @service flashMessages; - @service store; - - // Actual attribute fields - get tlsFormFields() { - return tlsFields().map((attr) => this.args.model.allByKey[attr]); - } - get operationFormGroups() { - const objects = [ - 'operationCreate', - 'operationActivate', - 'operationGet', - 'operationLocate', - 'operationRekey', - 'operationRevoke', - 'operationDestroy', - ]; - const attributes = ['operationAddAttribute', 'operationGetAttributes']; - const server = ['operationDiscoverVersions']; - const others = removeManyFromArray(operationFieldsWithoutSpecial(this.args.model.editableFields), [ - ...objects, - ...attributes, - ...server, - ]); - const groups = [ - { name: 'Managed Cryptographic Objects', fields: objects }, - { name: 'Object Attributes', fields: attributes }, - { name: 'Server', fields: server }, - ]; - if (others.length) { - groups.push({ - name: 'Other', - fields: others, - }); - } - // expand field names to attributes - return groups.map((group) => ({ - ...group, - fields: group.fields.map((attr) => this.args.model.allByKey[attr]), - })); - } - - placeholderOrModel = (model, attrName) => { - return model.operationAll ? { [attrName]: true } : model; - }; - - preSave() { - const opFieldsWithoutSpecial = operationFieldsWithoutSpecial(this.args.model.editableFields); - // if we have operationAll or operationNone, we want to clear - // out the others so that display shows the right data - if (this.args.model.operationAll || this.args.model.operationNone) { - opFieldsWithoutSpecial.forEach((field) => (this.args.model[field] = null)); - } - // set operationNone if user unchecks 'operationAll' instead of toggling the 'operationNone' input - // doing here instead of on the 'operationNone' input because a user might deselect all, then reselect some options - // and immediately setting operationNone will hide all of the checkboxes in the UI - this.args.model.operationNone = - opFieldsWithoutSpecial.every((attr) => this.args.model[attr] !== true) && !this.args.model.operationAll; - return this.args.model; - } - - @action toggleOperationSpecial(evt) { - const { checked } = evt.target; - this.args.model.operationNone = !checked; - this.args.model.operationAll = checked; - } - - save = task(async (evt) => { - evt.preventDefault(); - const model = this.preSave(); - try { - await model.save(); - this.flashMessages.success(`Saved role ${model.role}`); - } catch (err) { - // err will display via model state - // AdapterErrors are handled by the error-message component - if (err instanceof AdapterError === false) { - throw err; - } - return; - } - this.args.onSave(); - }); - - willDestroy() { - // components are torn down after store is unloaded and will cause an error if attempt to unload record - const noTeardown = this.store && !this.store.isDestroying; - if (noTeardown && this.args?.model?.isDirty) { - this.args.model.rollbackAttributes(); - } - super.willDestroy(); - } -} diff --git a/ui/lib/kmip/addon/routes/scope/roles/create.js b/ui/lib/kmip/addon/routes/scope/roles/create.js index 7070b4c27408..82984cddcb63 100644 --- a/ui/lib/kmip/addon/routes/scope/roles/create.js +++ b/ui/lib/kmip/addon/routes/scope/roles/create.js @@ -21,7 +21,6 @@ export default Route.extend({ const model = this.store.createRecord('kmip/role', { backend: this.secretMountPath.currentPath, scope: this.scope(), - operationAll: true, }); return model; }, 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 new file mode 100644 index 000000000000..3aea47a1d8df --- /dev/null +++ b/ui/lib/kmip/addon/templates/components/edit-form-kmip-role.hbs @@ -0,0 +1,107 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +~}} + +
+ +
+ + {{#if (eq @mode "create")}} + + {{/if}} +
+ + +
+ {{#unless this.model.operationNone}} + +

+ Allowed Operations +

+
+
+ +
+
+
+ {{#each-in (get this.model.operationFormFields 0) as |groupName fieldsInGroup|}} +

{{groupName}}

+ {{#each fieldsInGroup as |attr|}} + + {{/each}} + {{/each-in}} +
+
+ {{#each (drop 1 (or this.model.operationFormFields (array))) as |group|}} +
+ {{#each-in group as |groupName fieldsInGroup|}} +

{{groupName}}

+ {{#each fieldsInGroup as |attr|}} + + {{/each}} + {{/each-in}} +
+ {{/each}} +
+
+
+ {{/unless}} +
+

+ TLS +

+ {{#each this.model.tlsFormFields as |attr|}} + + {{/each}} +
+ {{#each this.model.fields as |attr|}} + + {{/each}} +
+ +
+ + + {{#if this.cancelLink}} + + {{/if}} + +
+ \ No newline at end of file diff --git a/ui/lib/kmip/addon/templates/role/edit.hbs b/ui/lib/kmip/addon/templates/role/edit.hbs index 482083290567..ab2003d85783 100644 --- a/ui/lib/kmip/addon/templates/role/edit.hbs +++ b/ui/lib/kmip/addon/templates/role/edit.hbs @@ -13,9 +13,8 @@ - - \ No newline at end of file diff --git a/ui/lib/kmip/addon/templates/scope/roles/create.hbs b/ui/lib/kmip/addon/templates/scope/roles/create.hbs index 71af81aa1bad..42fcc4bf4074 100644 --- a/ui/lib/kmip/addon/templates/scope/roles/create.hbs +++ b/ui/lib/kmip/addon/templates/scope/roles/create.hbs @@ -13,9 +13,9 @@ - - \ No newline at end of file diff --git a/ui/lib/kmip/index.js b/ui/lib/kmip/index.js index 635b19df758a..2d2ddc66e948 100644 --- a/ui/lib/kmip/index.js +++ b/ui/lib/kmip/index.js @@ -17,10 +17,6 @@ module.exports = EngineAddon.extend({ enabled: true, }, - babel: { - plugins: [require.resolve('ember-concurrency/async-arrow-task-transform')], - }, - isDevelopingAddon() { return true; }, diff --git a/ui/lib/ldap/addon/components/page/library/details/accounts.ts b/ui/lib/ldap/addon/components/page/library/details/accounts.ts index 7e119375dbc0..6e039f56893b 100644 --- a/ui/lib/ldap/addon/components/page/library/details/accounts.ts +++ b/ui/lib/ldap/addon/components/page/library/details/accounts.ts @@ -27,7 +27,7 @@ export default class LdapLibraryDetailsAccountsPageComponent extends Component -
-
- - {{this.details.blockTitle}} -
-
- {{#if (not (has-feature this.details.feature))}} - - {{this.details.upgradeTitle}} - - Upgrade - - - {{else if @model.replicationEnabled}} - ENABLED -
- {{capitalize @model.modeForUrl}} - {{#if @model.secondaryId}} - - {{/if}} - {{#if @model.clusterIdDisplay}} - - {{/if}} -
- {{/if}} - - {{replication-mode-description @mode}} -
- {{#if (has-feature this.details.feature)}} - - {{/if}} -
- \ No newline at end of file diff --git a/ui/lib/replication/addon/components/replication-overview-mode.js b/ui/lib/replication/addon/components/replication-overview-mode.js deleted file mode 100644 index 0a733d90c139..000000000000 --- a/ui/lib/replication/addon/components/replication-overview-mode.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Component from '@glimmer/component'; - -/** - * @module ReplicationOverviewModeComponent - * ReplicationOverviewMode components are used on the Replication index page to display - * details about a given mode (DR or Performance) status. - * - * @example - * - * - * @param {string} mode - should be "dr" or "performance" - * @param {ReplicationAttributesModel} model - either the dr or performance attribute of the cluster model - * @param {string} clusterName - used for the link to the mode details - */ -export default class ReplicationOverviewModeComponent extends Component { - get details() { - if (this.args.mode === 'dr') { - return { - blockTitle: 'Disaster Recovery (DR)', - upgradeTitle: 'Disaster Recovery is a feature of Vault Enterprise Premium.', - upgradeLink: 'https://hashicorp.com/products/vault/trial?source=vaultui_DR%20Replication', - feature: 'DR Replication', - icon: 'replication-direct', - }; - } - return { - blockTitle: 'Performance', - upgradeTitle: 'Performance Replication is a feature of Vault Enterprise Premium.', - upgradeLink: 'https://hashicorp.com/products/vault/trial?source=vaultui_Performance%20Replication', - feature: 'Performance Replication', - icon: 'replication-perf', - }; - } -} diff --git a/ui/lib/replication/addon/components/replication-summary.js b/ui/lib/replication/addon/components/replication-summary.js index 43632c646382..6c9e1f576d7c 100644 --- a/ui/lib/replication/addon/components/replication-summary.js +++ b/ui/lib/replication/addon/components/replication-summary.js @@ -8,13 +8,6 @@ import { computed } from '@ember/object'; import Component from '@ember/component'; import ReplicationActions from 'core/mixins/replication-actions'; -/** - * @module ReplicationSummary - * ReplicationSummary component is a component to show the mode-specific summary for replication - * - * @param {ClusterModel} cluster - the cluster ember-data model - * @param {string} initialReplicationMode - mode for replication details we want to see, either "dr" or "performance" - */ export default Component.extend(ReplicationActions, { 'data-test-replication-summary': true, attributeBindings: ['data-test-replication-summary'], @@ -29,6 +22,7 @@ export default Component.extend(ReplicationActions, { this.set('replicationMode', initialReplicationMode); } }, + showModeSummary: false, initialReplicationMode: null, cluster: null, diff --git a/ui/lib/replication/addon/templates/components/replication-summary.hbs b/ui/lib/replication/addon/templates/components/replication-summary.hbs index 138b364581c4..eaa1bfebe085 100644 --- a/ui/lib/replication/addon/templates/components/replication-summary.hbs +++ b/ui/lib/replication/addon/templates/components/replication-summary.hbs @@ -3,39 +3,88 @@ SPDX-License-Identifier: BUSL-1.1 ~}} -{{#if (eq this.attrsForCurrentMode.mode "initializing")}} - The cluster is initializing replication. This may take some time. -{{else}} -

{{this.cluster.replicationModeStatus.cluster_id}}

-
+{{#if (not (has-feature "DR Replication"))}} + +{{else if this.showModeSummary}} + {{#if (not (and this.cluster.dr.replicationEnabled this.cluster.performance.replicationEnabled))}} + + +

+ Replication +

+
+
+ {{/if}} + + {{#if (and (eq this.cluster.dr.mode "primary") (eq this.cluster.performance.mode "primary"))}} - - {{#if (eq this.attrsForCurrentMode.mode "secondary")}} - - - {{else}} - - - - {{/if}} + + + + -
+ {{else}} +
+

+ + Disaster Recovery (DR) +

+ {{#if this.cluster.dr.replicationEnabled}} + {{#if this.submit.isRunning}} + + {{else}} + + {{/if}} + {{else}} + + {{/if}} +
+ {{#if (not (and this.submit.isRunning (eq this.cluster.dr.mode "bootstrapping")))}} +
+

+ + Performance +

+ +
+ {{/if}} + {{/if}} +{{else}} + {{#if (eq this.attrsForCurrentMode.mode "initializing")}} + The cluster is initializing replication. This may take some time. + {{else}} +

{{this.cluster.replicationModeStatus.cluster_id}}

+
+ + + {{#if (eq this.attrsForCurrentMode.mode "secondary")}} + + + {{else}} + + + + {{/if}} + + +
+ {{/if}} {{/if}} \ No newline at end of file diff --git a/ui/lib/replication/addon/templates/index.hbs b/ui/lib/replication/addon/templates/index.hbs index 9b2c9c326ebc..accee0a95a05 100644 --- a/ui/lib/replication/addon/templates/index.hbs +++ b/ui/lib/replication/addon/templates/index.hbs @@ -6,7 +6,6 @@
{{#if (eq this.model.mode "unsupported")}} - {{! Replication is unsupported in non-enterprise or when using non-transactional storage (eg inmem) }}

@@ -99,36 +98,8 @@ @onSuccess={{this.onEnableSuccess}} @doTransition={{true}} /> - {{else if (not (has-feature "DR Replication"))}} - - {{else if (and (eq this.model.dr.mode "primary") (eq this.model.performance.mode "primary"))}} - {{! Renders when cluster is primary for both replication modes }} - - - - - - - {{else}} - {{! Renders when at least one mode is not enabled }} - - -

- Replication -

-
-
- -
- - -
+ {{/if}}

\ No newline at end of file diff --git a/ui/mirage/handlers/ldap.js b/ui/mirage/handlers/ldap.js index 8627619ba759..0e976241ae8b 100644 --- a/ui/mirage/handlers/ldap.js +++ b/ui/mirage/handlers/ldap.js @@ -35,6 +35,17 @@ export default function (server) { }; }; + // mount + server.post('/sys/mounts/:path', () => new Response(204)); + server.get('/sys/internal/ui/mounts/:path', () => ({ + data: { + accessor: 'ldap_ade94329', + type: 'ldap', + path: 'ldap-test/', + uuid: '35e9119d-5708-4b6b-58d2-f913e27f242d', + config: {}, + }, + })); // config server.post('/:backend/config', (schema, req) => createOrUpdateRecord(schema, req, 'ldapConfigs')); server.get('/:backend/config', (schema, req) => getRecord(schema, req, 'ldapConfigs')); @@ -56,60 +67,8 @@ export default function (server) { server.post('/:backend/library/:name', (schema, req) => createOrUpdateRecord(schema, req, 'ldapLibraries')); server.get('/:backend/library/:name', (schema, req) => getRecord(schema, req, 'ldapLibraries')); server.get('/:backend/library', (schema) => listRecords(schema, 'ldapLibraries')); - server.get('/:backend/library/:name/status', (schema) => { - const data = schema.db['ldapAccountStatuses'].reduce((prev, curr) => { - prev[curr.account] = { - available: curr.available, - borrower_client_token: curr.borrower_client_token, - }; - return prev; - }, {}); - return { data }; - }); - // check-out / check-in - server.post('/:backend/library/:set_name/check-in', (schema, req) => { - // Check-in makes an unavailable account available again - const { service_account_names } = JSON.parse(req.requestBody); - const dbCollection = schema.db['ldapAccountStatuses']; - const updated = dbCollection.find(service_account_names).map((f) => ({ - ...f, - available: true, - borrower_client_token: undefined, - })); - updated.forEach((u) => { - dbCollection.update(u.id, u); - }); - return { - data: { - check_ins: service_account_names, - }, - }; - }); - server.post('/:backend/library/:set_name/check-out', (schema, req) => { - const { set_name, backend } = req.params; - const dbCollection = schema.db['ldapAccountStatuses']; - const available = dbCollection.where({ available: true }); - if (available) { - return Response(404, {}, { errors: ['no accounts available to check out'] }); - } - const checkOut = { - ...available[0], - available: false, - borrower_client_token: crypto.randomUUID(), - }; - dbCollection.update(checkOut.id, checkOut); - return { - request_id: '364a17d4-e5ab-998b-ceee-b49929229e0c', - lease_id: `${backend}/library/${set_name}/check-out/aoBsaBEI4PK96VnukubvYDlZ`, - renewable: true, - lease_duration: 36000, - data: { - password: crypto.randomUUID(), - service_account_name: checkOut.account, - }, - wrap_info: null, - warnings: null, - auth: null, - }; - }); + server.get('/:backend/library/:name/status', () => ({ + 'bob.johnson': { available: false, borrower_client_token: '8b80c305eb3a7dbd161ef98f10ea60a116ce0910' }, + 'mary.smith': { available: true }, + })); } diff --git a/ui/mirage/models/ldap-account-status.js b/ui/mirage/models/ldap-account-status.js deleted file mode 100644 index 557261f6cd78..000000000000 --- a/ui/mirage/models/ldap-account-status.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { Model } from 'miragejs'; - -export default Model.extend({ - account: '', // should match ID - library: '', - available: false, - borrower_client_token: undefined, -}); diff --git a/ui/mirage/scenarios/ldap.js b/ui/mirage/scenarios/ldap.js index d946dc68529d..19b6b61f4c58 100644 --- a/ui/mirage/scenarios/ldap.js +++ b/ui/mirage/scenarios/ldap.js @@ -4,19 +4,8 @@ */ export default function (server) { - server.create('ldap-config', { path: 'kubernetes', backend: 'ldap-test' }); + server.create('ldap-config', { path: 'kubernetes' }); server.create('ldap-role', 'static', { name: 'static-role' }); server.create('ldap-role', 'dynamic', { name: 'dynamic-role' }); server.create('ldap-library', { name: 'test-library' }); - server.create('ldap-account-status', { - id: 'bob.johnson', - account: 'bob.johnson', - available: false, - borrower_client_token: '8b80c305eb3a7dbd161ef98f10ea60a116ce0910', - }); - server.create('ldap-account-status', { - id: 'mary.smith', - account: 'mary.smith', - available: true, - }); } diff --git a/ui/tests/acceptance/enterprise-kmip-test.js b/ui/tests/acceptance/enterprise-kmip-test.js index 916cc31da051..a2f743227640 100644 --- a/ui/tests/acceptance/enterprise-kmip-test.js +++ b/ui/tests/acceptance/enterprise-kmip-test.js @@ -16,7 +16,7 @@ import { import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; -import { login } from 'vault/tests/helpers/auth/auth-helpers'; +import authPage from 'vault/tests/pages/auth'; import scopesPage from 'vault/tests/pages/secrets/backend/kmip/scopes'; import rolesPage from 'vault/tests/pages/secrets/backend/kmip/roles'; import credentialsPage from 'vault/tests/pages/secrets/backend/kmip/credentials'; @@ -91,7 +91,7 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) { hooks.beforeEach(async function () { this.backend = `kmip-${uuidv4()}`; - await login(); + await authPage.login(); return; }); @@ -221,11 +221,16 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) { test('it can create a role', async function (assert) { // moving create scope here to help with flaky test - const scope = `scope-for-can-create-role`; - const role = `role-new-role`; const backend = await mountWithConfig(this.backend); await settled(); - await runCmd([`write ${backend}/scope/${scope} -force`], true); + const scope = `scope-for-can-create-role`; + await settled(); + const res = await runCmd([`write ${backend}/scope/${scope} -force`]); + await settled(); + if (res.includes('Error')) { + throw new Error(`Error creating scope: ${res}`); + } + const role = `role-new-role`; await rolesPage.visit({ backend, scope }); await settled(); assert.ok(rolesPage.isEmpty, 'renders the empty role page'); @@ -236,15 +241,11 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) { `/vault/secrets/${backend}/kmip/scopes/${scope}/roles/create`, 'links to the role create form' ); - // check that the role form looks right - assert.dom(GENERAL.inputByAttr('operationNone')).isChecked('allows role to perform roles by default'); - assert.dom(GENERAL.inputByAttr('operationAll')).isChecked('operationAll is checked by default'); - assert.dom('[data-test-kmip-section]').exists({ count: 2 }); - assert.dom('[data-test-kmip-operations]').exists({ count: 4 }); await rolesPage.roleName(role); await settled(); - await click(GENERAL.saveButton); + await rolesPage.submit(); + await settled(); assert.strictEqual( currentURL(), `/vault/secrets/${backend}/kmip/scopes/${scope}/roles`, @@ -252,13 +253,6 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) { ); assert.strictEqual(rolesPage.listItemLinks.length, 1, 'renders a single role'); - await rolesPage.visitDetail({ backend, scope, role }); - // check that the role details looks right - assert.dom('h2').exists({ count: 2 }, 'renders correct section headings'); - assert.dom('[data-test-inline-error-message]').hasText('This role allows all KMIP operations'); - ['Managed Cryptographic Objects', 'Object Attributes', 'Server', 'Other'].forEach((title) => { - assert.dom(`[data-test-row-label="${title}"]`).exists(`Renders allowed operations row for: ${title}`); - }); }); test('it navigates to kmip roles view using breadcrumbs', async function (assert) { @@ -310,7 +304,8 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) { `/vault/secrets/${backend}/kmip/scopes/${scope}/roles/${role}/edit`, 'navigates to role edit' ); - await click(GENERAL.cancelButton); + await rolesPage.cancelLink(); + await settled(); assert.strictEqual( currentURL(), `/vault/secrets/${backend}/kmip/scopes/${scope}/roles/${role}`, @@ -369,12 +364,12 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) { this.store = this.owner.lookup('service:store'); this.scope = 'my-scope'; this.name = 'my-role'; - await login(); + await authPage.login(); await runCmd(mountEngineCmd('kmip', this.backend), false); await runCmd([`write ${this.backend}/scope/${this.scope} -force`]); await rolesPage.visit({ backend: this.backend, scope: this.scope }); this.setModel = async () => { - await click(GENERAL.saveButton); + await click('[data-test-edit-form-submit]'); await visit(`/vault/secrets/${this.backend}/kmip/scopes/${this.scope}/roles/${this.name}`); this.model = this.store.peekRecord('kmip/role', this.name); }; @@ -387,7 +382,7 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) { assert.expect(3); await click('[data-test-role-create]'); - await fillIn(GENERAL.inputByAttr('role'), this.name); + await fillIn(GENERAL.inputByAttr('name'), this.name); assert.dom(GENERAL.inputByAttr('operationAll')).isChecked('operationAll is checked by default'); await this.setModel(); assert.true(this.model.operationAll, 'operationAll is true'); @@ -398,7 +393,7 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) { assert.expect(4); await click('[data-test-role-create]'); - await fillIn(GENERAL.inputByAttr('role'), this.name); + await fillIn(GENERAL.inputByAttr('name'), this.name); await click(GENERAL.inputByAttr('operationNone')); assert .dom(GENERAL.inputByAttr('operationNone')) @@ -415,10 +410,9 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) { assert.expect(2); await click('[data-test-role-create]'); - await fillIn(GENERAL.inputByAttr('role'), this.name); + await fillIn(GENERAL.inputByAttr('name'), this.name); await click(GENERAL.inputByAttr('operationAll')); await this.setModel(); - assert.strictEqual(this.model.operationAll, undefined, 'operationAll is unset'); assert.true(this.model.operationNone, 'operationNone is true'); }); @@ -427,7 +421,7 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) { assert.expect(6); await click('[data-test-role-create]'); - await fillIn(GENERAL.inputByAttr('role'), this.name); + await fillIn(GENERAL.inputByAttr('name'), this.name); await click(GENERAL.inputByAttr('operationAll')); await click(GENERAL.inputByAttr('operationGet')); await click(GENERAL.inputByAttr('operationGetAttributes')); diff --git a/ui/tests/acceptance/secrets/backend/ldap/libraries-test.js b/ui/tests/acceptance/secrets/backend/ldap/libraries-test.js index 4e9cc5a57059..dd3d0e0725a6 100644 --- a/ui/tests/acceptance/secrets/backend/ldap/libraries-test.js +++ b/ui/tests/acceptance/secrets/backend/ldap/libraries-test.js @@ -6,13 +6,11 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { setupMirage } from 'ember-cli-mirage/test-support'; -import { v4 as uuidv4 } from 'uuid'; import ldapMirageScenario from 'vault/mirage/scenarios/ldap'; import ldapHandlers from 'vault/mirage/handlers/ldap'; import authPage from 'vault/tests/pages/auth'; import { click } from '@ember/test-helpers'; import { isURL, visitURL } from 'vault/tests/helpers/ldap/ldap-helpers'; -import { deleteEngineCmd, mountEngineCmd, runCmd } from 'vault/tests/helpers/commands'; module('Acceptance | ldap | libraries', function (hooks) { setupApplicationTest(hooks); @@ -21,41 +19,21 @@ module('Acceptance | ldap | libraries', function (hooks) { hooks.beforeEach(async function () { ldapHandlers(this.server); ldapMirageScenario(this.server); - this.backend = `ldap-test-${uuidv4()}`; await authPage.login(); - // mount & configure - await runCmd([ - mountEngineCmd('ldap', this.backend), - `write ${this.backend}/config binddn=foo bindpass=bar url=http://localhost:8208`, - ]); - return visitURL('libraries', this.backend); - }); - - hooks.afterEach(async function () { - await runCmd(deleteEngineCmd(this.backend)); - }); - - test('it should show libraries on overview page', async function (assert) { - await visitURL('overview', this.backend); - assert.dom('[data-test-libraries-count]').hasText('1'); + return visitURL('libraries'); }); test('it should transition to create library route on toolbar link click', async function (assert) { await click('[data-test-toolbar-action="library"]'); - assert.true( - isURL('libraries/create', this.backend), - 'Transitions to library create route on toolbar link click' - ); + assert.true(isURL('libraries/create'), 'Transitions to library create route on toolbar link click'); }); test('it should transition to library details route on list item click', async function (assert) { await click('[data-test-list-item-link] a'); assert.true( - isURL('libraries/test-library/details/accounts', this.backend), + isURL('libraries/test-library/details/accounts'), 'Transitions to library details accounts route on list item click' ); - assert.dom('[data-test-account-name]').exists({ count: 2 }, 'lists the accounts'); - assert.dom('[data-test-checked-out-account]').exists({ count: 1 }, 'lists the checked out accounts'); }); test('it should transition to routes from list item action menu', async function (assert) { @@ -66,7 +44,7 @@ module('Acceptance | ldap | libraries', function (hooks) { await click(`[data-test-${action}]`); const uri = action === 'details' ? 'details/accounts' : action; assert.true( - isURL(`libraries/test-library/${uri}`, this.backend), + isURL(`libraries/test-library/${uri}`), `Transitions to ${action} route on list item action menu click` ); await click('[data-test-breadcrumb="libraries"] a'); @@ -77,13 +55,13 @@ module('Acceptance | ldap | libraries', function (hooks) { await click('[data-test-list-item-link] a'); await click('[data-test-tab="config"]'); assert.true( - isURL('libraries/test-library/details/configuration', this.backend), + isURL('libraries/test-library/details/configuration'), 'Transitions to configuration route on tab click' ); await click('[data-test-tab="accounts"]'); assert.true( - isURL('libraries/test-library/details/accounts', this.backend), + isURL('libraries/test-library/details/accounts'), 'Transitions to accounts route on tab click' ); }); @@ -91,9 +69,6 @@ module('Acceptance | ldap | libraries', function (hooks) { test('it should transition to routes from library details toolbar links', async function (assert) { await click('[data-test-list-item-link] a'); await click('[data-test-edit]'); - assert.true( - isURL('libraries/test-library/edit', this.backend), - 'Transitions to credentials route from toolbar link' - ); + assert.true(isURL('libraries/test-library/edit'), 'Transitions to credentials route from toolbar link'); }); }); diff --git a/ui/tests/acceptance/secrets/backend/ldap/overview-test.js b/ui/tests/acceptance/secrets/backend/ldap/overview-test.js index 1490e40e00f3..8fe90cccf2e1 100644 --- a/ui/tests/acceptance/secrets/backend/ldap/overview-test.js +++ b/ui/tests/acceptance/secrets/backend/ldap/overview-test.js @@ -6,14 +6,12 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { setupMirage } from 'ember-cli-mirage/test-support'; -import { v4 as uuidv4 } from 'uuid'; import ldapMirageScenario from 'vault/mirage/scenarios/ldap'; import ldapHandlers from 'vault/mirage/handlers/ldap'; import authPage from 'vault/tests/pages/auth'; import { click, fillIn, visit } from '@ember/test-helpers'; import { selectChoose } from 'ember-power-select/test-support'; import { isURL, visitURL } from 'vault/tests/helpers/ldap/ldap-helpers'; -import { deleteEngineCmd, mountEngineCmd, runCmd } from 'vault/tests/helpers/commands'; module('Acceptance | ldap | overview', function (hooks) { setupApplicationTest(hooks); @@ -21,101 +19,77 @@ module('Acceptance | ldap | overview', function (hooks) { hooks.beforeEach(async function () { ldapHandlers(this.server); - this.backend = `ldap-test-${uuidv4()}`; - this.mountAndConfig = (backend) => { - return runCmd([ - mountEngineCmd('ldap', backend), - `write ${backend}/config binddn=foo bindpass=bar url=http://localhost:8208`, - ]); - }; return authPage.login(); }); test('it should transition to ldap overview on mount success', async function (assert) { - const backend = 'ldap-test-mount'; await visit('/vault/secrets'); await click('[data-test-enable-engine]'); await click('[data-test-mount-type="ldap"]'); - await fillIn('[data-test-input="path"]', backend); + await fillIn('[data-test-input="path"]', 'ldap-test'); await click('[data-test-mount-submit]'); - assert.true(isURL('overview', backend), 'Transitions to ldap overview route on mount success'); - assert.dom('[data-test-header-title]').hasText(backend); - // cleanup mounted engine - await visit('/vault/secrets'); - await runCmd(deleteEngineCmd(backend)); + assert.true(isURL('overview'), 'Transitions to ldap overview route on mount success'); }); test('it should transition to routes on tab link click', async function (assert) { assert.expect(4); - await this.mountAndConfig(this.backend); - await visitURL('overview', this.backend); + await visitURL('overview'); for (const tab of ['roles', 'libraries', 'config', 'overview']) { await click(`[data-test-tab="${tab}"]`); const route = tab === 'config' ? 'configuration' : tab; - assert.true(isURL(route, this.backend), `Transitions to ${route} route on tab link click`); + assert.true(isURL(route), `Transitions to ${route} route on tab link click`); } }); test('it should transition to configuration route when engine is not configured', async function (assert) { - await runCmd(mountEngineCmd('ldap', this.backend)); - await visitURL('overview', this.backend); + await visitURL('overview'); await click('[data-test-config-cta] a'); - assert.true(isURL('configure', this.backend), 'Transitions to configure route on cta link click'); + assert.true(isURL('configure'), 'Transitions to configure route on cta link click'); - await click(`[data-test-breadcrumb="${this.backend}"] a`); + await click('[data-test-breadcrumb="ldap-test"] a'); await click('[data-test-toolbar-action="config"]'); - assert.true(isURL('configure', this.backend), 'Transitions to configure route on toolbar link click'); + assert.true(isURL('configure'), 'Transitions to configure route on toolbar link click'); }); // including a test for the configuration route here since it is the only one needed test('it should transition to configuration edit on toolbar link click', async function (assert) { ldapMirageScenario(this.server); - await this.mountAndConfig(this.backend); - await visitURL('overview', this.backend); + await visitURL('overview'); await click('[data-test-tab="config"]'); await click('[data-test-toolbar-config-action]'); - assert.true(isURL('configure', this.backend), 'Transitions to configure route on toolbar link click'); + assert.true(isURL('configure'), 'Transitions to configure route on toolbar link click'); }); test('it should transition to create role route on card action link click', async function (assert) { ldapMirageScenario(this.server); - await this.mountAndConfig(this.backend); - await visitURL('overview', this.backend); + await visitURL('overview'); await click('[data-test-overview-card="Roles"] a'); - assert.true( - isURL('roles/create', this.backend), - 'Transitions to role create route on card action link click' - ); + assert.true(isURL('roles/create'), 'Transitions to role create route on card action link click'); }); test('it should transition to create library route on card action link click', async function (assert) { ldapMirageScenario(this.server); - await this.mountAndConfig(this.backend); - await visitURL('overview', this.backend); + await visitURL('overview'); await click('[data-test-overview-card="Libraries"] a'); - assert.true( - isURL('libraries/create', this.backend), - 'Transitions to library create route on card action link click' - ); + assert.true(isURL('libraries/create'), 'Transitions to library create route on card action link click'); }); test('it should transition to role credentials route on generate credentials action', async function (assert) { ldapMirageScenario(this.server); - await this.mountAndConfig(this.backend); - await visitURL('overview', this.backend); + await visitURL('overview'); await selectChoose('.search-select', 'static-role'); await click('[data-test-generate-credential-button]'); assert.true( - isURL('roles/static/static-role/credentials', this.backend), + isURL('roles/static/static-role/credentials'), 'Transitions to role credentials route on generate credentials action' ); - await click(`[data-test-breadcrumb="${this.backend}"] a`); + await click('[data-test-breadcrumb="ldap-test"] a'); await selectChoose('.search-select', 'dynamic-role'); await click('[data-test-generate-credential-button]'); assert.true( - isURL('roles/dynamic/dynamic-role/credentials', this.backend), + isURL('roles/dynamic/dynamic-role/credentials'), 'Transitions to role credentials route on generate credentials action' ); }); diff --git a/ui/tests/acceptance/secrets/backend/ldap/roles-test.js b/ui/tests/acceptance/secrets/backend/ldap/roles-test.js index 739b604dfc33..15d1df74df2a 100644 --- a/ui/tests/acceptance/secrets/backend/ldap/roles-test.js +++ b/ui/tests/acceptance/secrets/backend/ldap/roles-test.js @@ -6,14 +6,11 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { setupMirage } from 'ember-cli-mirage/test-support'; -import { v4 as uuidv4 } from 'uuid'; import ldapMirageScenario from 'vault/mirage/scenarios/ldap'; import ldapHandlers from 'vault/mirage/handlers/ldap'; import authPage from 'vault/tests/pages/auth'; -import { click, fillIn, waitFor } from '@ember/test-helpers'; +import { click, fillIn } from '@ember/test-helpers'; import { isURL, visitURL } from 'vault/tests/helpers/ldap/ldap-helpers'; -import { GENERAL } from 'vault/tests/helpers/general-selectors'; -import { deleteEngineCmd, mountEngineCmd, runCmd } from 'vault/tests/helpers/commands'; module('Acceptance | ldap | roles', function (hooks) { setupApplicationTest(hooks); @@ -22,39 +19,26 @@ module('Acceptance | ldap | roles', function (hooks) { hooks.beforeEach(async function () { ldapHandlers(this.server); ldapMirageScenario(this.server); - this.backend = `ldap-test-${uuidv4()}`; await authPage.login(); - // mount & configure - await runCmd([ - mountEngineCmd('ldap', this.backend), - `write ${this.backend}/config binddn=foo bindpass=bar url=http://localhost:8208`, - ]); - return visitURL('roles', this.backend); - }); - - hooks.afterEach(async function () { - await runCmd(deleteEngineCmd(this.backend)); + return visitURL('roles'); }); test('it should transition to create role route on toolbar link click', async function (assert) { await click('[data-test-toolbar-action="role"]'); - assert.true( - isURL('roles/create', this.backend), - 'Transitions to role create route on toolbar link click' - ); + assert.true(isURL('roles/create'), 'Transitions to role create route on toolbar link click'); }); test('it should transition to role details route on list item click', async function (assert) { await click('[data-test-list-item-link]:nth-of-type(1) a'); assert.true( - isURL('roles/dynamic/dynamic-role/details', this.backend), + isURL('roles/dynamic/dynamic-role/details'), 'Transitions to role details route on list item click' ); await click('[data-test-breadcrumb="roles"] a'); await click('[data-test-list-item-link]:nth-of-type(2) a'); assert.true( - isURL('roles/static/static-role/details', this.backend), + isURL('roles/static/static-role/details'), 'Transitions to role details route on list item click' ); }); @@ -67,7 +51,7 @@ module('Acceptance | ldap | roles', function (hooks) { await click(`[data-test-${action}]`); const uri = action === 'get-creds' ? 'credentials' : action; assert.true( - isURL(`roles/dynamic/dynamic-role/${uri}`, this.backend), + isURL(`roles/dynamic/dynamic-role/${uri}`), `Transitions to ${uri} route on list item action menu click` ); await click('[data-test-breadcrumb="roles"] a'); @@ -78,16 +62,13 @@ module('Acceptance | ldap | roles', function (hooks) { await click('[data-test-list-item-link]:nth-of-type(1) a'); await click('[data-test-get-credentials]'); assert.true( - isURL('roles/dynamic/dynamic-role/credentials', this.backend), + isURL('roles/dynamic/dynamic-role/credentials'), 'Transitions to credentials route from toolbar link' ); await click('[data-test-breadcrumb="dynamic-role"] a'); await click('[data-test-edit]'); - assert.true( - isURL('roles/dynamic/dynamic-role/edit', this.backend), - 'Transitions to edit route from toolbar link' - ); + assert.true(isURL('roles/dynamic/dynamic-role/edit'), 'Transitions to edit route from toolbar link'); }); test('it should clear roles page filter value on route exit', async function (assert) { @@ -95,7 +76,6 @@ module('Acceptance | ldap | roles', function (hooks) { assert .dom('[data-test-filter-input]') .hasValue('foo', 'Roles page filter value set after model refresh and rerender'); - await waitFor(GENERAL.emptyStateTitle); await click('[data-test-tab="libraries"]'); await click('[data-test-tab="roles"]'); assert.dom('[data-test-filter-input]').hasNoValue('Roles page filter value cleared on route exit'); diff --git a/ui/tests/helpers/ldap/ldap-helpers.js b/ui/tests/helpers/ldap/ldap-helpers.js index b184878a0ff6..a08e02275361 100644 --- a/ui/tests/helpers/ldap/ldap-helpers.js +++ b/ui/tests/helpers/ldap/ldap-helpers.js @@ -28,13 +28,13 @@ export const generateBreadcrumbs = (backend, childRoute) => { return breadcrumbs; }; -const baseURL = (backend) => `/vault/secrets/${backend}/ldap/`; +const baseURL = '/vault/secrets/ldap-test/ldap/'; const stripLeadingSlash = (uri) => (uri.charAt(0) === '/' ? uri.slice(1) : uri); -export const isURL = (uri, backend = 'ldap-test') => { - return currentURL() === `${baseURL(backend)}${stripLeadingSlash(uri)}`; +export const isURL = (uri) => { + return currentURL() === `${baseURL}${stripLeadingSlash(uri)}`; }; -export const visitURL = (uri, backend = 'ldap-test') => { - return visit(`${baseURL(backend)}${stripLeadingSlash(uri)}`); +export const visitURL = (uri) => { + return visit(`${baseURL}${stripLeadingSlash(uri)}`); }; diff --git a/ui/tests/integration/components/ldap/page/library/details/accounts-test.js b/ui/tests/integration/components/ldap/page/library/details/accounts-test.js index 8bb1ab696921..94b0048cc512 100644 --- a/ui/tests/integration/components/ldap/page/library/details/accounts-test.js +++ b/ui/tests/integration/components/ldap/page/library/details/accounts-test.js @@ -77,9 +77,6 @@ module('Integration | Component | ldap | Page::Library::Details::Accounts', func assert .dom('[data-test-accounts-code-block] code') - .hasText( - 'vault lease renew ldap-test/library/test-library/check-out/:lease_id', - 'Renew cli command renders with backend path' - ); + .hasText('vault lease renew ad/library/test-library/check-out/:lease_id', 'Renew cli command renders'); }); }); diff --git a/ui/tests/integration/components/page/mode-index-test.js b/ui/tests/integration/components/page/mode-index-test.js index 9af014881efb..506fc725cd12 100644 --- a/ui/tests/integration/components/page/mode-index-test.js +++ b/ui/tests/integration/components/page/mode-index-test.js @@ -24,7 +24,7 @@ module('Integration | Component | replication page/mode-index', function (hooks) hooks.beforeEach(function () { this.store = this.owner.lookup('service:store'); this.onEnable = () => {}; - this.clusterModel = { replicationAttrs: {} }; + this.clusterModel = {}; this.replicationMode = ''; this.replicationDisabled = true; diff --git a/ui/tests/integration/components/replication-overview-mode-test.js b/ui/tests/integration/components/replication-overview-mode-test.js deleted file mode 100644 index dc8729dc6dd6..000000000000 --- a/ui/tests/integration/components/replication-overview-mode-test.js +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'vault/tests/helpers'; -import { render, settled } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; -import { setupEngine } from 'ember-engines/test-support'; - -const OVERVIEW_MODE = { - title: '[data-test-overview-mode-title]', - body: '[data-test-overview-mode-body]', - detailsLink: '[data-test-replication-details-link]', -}; -module('Integration | Component | replication-overview-mode', function (hooks) { - setupRenderingTest(hooks); - setupEngine(hooks, 'replication'); - - hooks.beforeEach(function () { - this.versionService = this.owner.lookup('service:version'); - this.versionService.features = []; - this.mode = 'dr'; - this.clusterName = 'foobar'; - this.modeDetails = { mode: 'disabled' }; - - this.renderComponent = async () => { - return render( - hbs` - `, - { owner: this.engine } - ); - }; - }); - - test('without features', async function (assert) { - await this.renderComponent(); - assert.dom(OVERVIEW_MODE.title).hasText('Disaster Recovery (DR)'); - assert - .dom(OVERVIEW_MODE.body) - .includesText('Disaster Recovery is a feature of Vault Enterprise Premium. Upgrade'); - assert.dom(OVERVIEW_MODE.detailsLink).doesNotExist('does not show link to replication (dr)'); - - this.set('mode', 'performance'); - await settled(); - assert.dom(OVERVIEW_MODE.title).hasText('Performance'); - assert - .dom(OVERVIEW_MODE.body) - .includesText('Performance Replication is a feature of Vault Enterprise Premium. Upgrade'); - assert.dom(OVERVIEW_MODE.detailsLink).doesNotExist('does not show link to replication (perf)'); - }); - - module('with features', function (hooks) { - hooks.beforeEach(function () { - this.versionService.features = ['DR Replication', 'Performance Replication']; - }); - - test('it renders when replication disabled', async function (assert) { - await this.renderComponent(); - assert.dom(OVERVIEW_MODE.title).hasText('Disaster Recovery (DR)'); - assert - .dom(OVERVIEW_MODE.body) - .hasText( - 'Disaster Recovery Replication is designed to protect against catastrophic failure of entire clusters. Secondaries do not forward service requests until they are elected and become a new primary.' - ); - assert.dom(OVERVIEW_MODE.detailsLink).hasText('Enable'); - - this.set('mode', 'performance'); - await settled(); - assert.dom(OVERVIEW_MODE.title).hasText('Performance'); - assert - .dom(OVERVIEW_MODE.body) - .hasText( - 'Performance Replication scales workloads horizontally across clusters to make requests faster. Local secondaries handle read requests but forward writes to the primary to be handled.' - ); - assert.dom(OVERVIEW_MODE.detailsLink).hasText('Enable'); - }); - - test('it renders when replication enabled', async function (assert) { - this.mode = 'performance'; - this.modeDetails = { - replicationEnabled: true, - mode: 'primary', - modeForUrl: 'primary', - clusterIdDisplay: 'foobar12', - }; - await this.renderComponent(); - assert.dom(OVERVIEW_MODE.title).hasText('Performance'); - assert - .dom(OVERVIEW_MODE.body) - .includesText('ENABLED Primary foobar12', 'renders mode type and cluster ID if passed'); - assert.dom(OVERVIEW_MODE.detailsLink).hasText('Details'); - - this.set('modeDetails', { - replicationEnabled: true, - mode: 'secondary', - modeForUrl: 'secondary', - clusterIdDisplay: 'foobar12', - secondaryId: 'some-secondary', - }); - await settled(); - assert.dom(OVERVIEW_MODE.title).hasText('Performance'); - assert.dom(OVERVIEW_MODE.body).includesText('ENABLED Secondary some-secondary foobar12'); - assert.dom(OVERVIEW_MODE.detailsLink).hasText('Details'); - }); - - test('it renders when replication bootstrapping', async function (assert) { - this.modeDetails = { - replicationEnabled: true, - mode: 'bootstrapping', - modeForUrl: 'bootstrapping', - }; - await this.renderComponent(); - assert.dom(OVERVIEW_MODE.title).hasText('Disaster Recovery (DR)'); - assert.dom(OVERVIEW_MODE.body).includesText('ENABLED Bootstrapping'); - assert.dom(OVERVIEW_MODE.detailsLink).hasText('Details'); - }); - }); -}); diff --git a/ui/tests/pages/secrets/backend/kmip/roles.js b/ui/tests/pages/secrets/backend/kmip/roles.js index bc06acb4a584..aafaeba988a0 100644 --- a/ui/tests/pages/secrets/backend/kmip/roles.js +++ b/ui/tests/pages/secrets/backend/kmip/roles.js @@ -11,7 +11,7 @@ export default create({ visit: visitable('/vault/secrets/:backend/kmip/scopes/:scope/roles'), visitDetail: visitable('/vault/secrets/:backend/kmip/scopes/:scope/roles/:role'), create: clickable('[data-test-role-create]'), - roleName: fillable('[data-test-input="role"]'), + roleName: fillable('[data-test-input="name"]'), submit: clickable('[data-test-edit-form-submit]'), detailEditLink: clickable('[data-test-kmip-link-edit-role]'), cancelLink: clickable('[data-test-edit-form-cancel]'), diff --git a/ui/tests/unit/adapters/kmip/role-test.js b/ui/tests/unit/adapters/kmip/role-test.js index 36dcf9a883de..a9d20d900459 100644 --- a/ui/tests/unit/adapters/kmip/role-test.js +++ b/ui/tests/unit/adapters/kmip/role-test.js @@ -9,8 +9,6 @@ import { setupTest } from 'ember-qunit'; module('Unit | Adapter | kmip/role', function (hooks) { setupTest(hooks); - // these are only some of the actual editable fields - const editableFields = ['tlsTtl', 'operationAll', 'operationNone', 'operationGet', 'operationCreate']; const serializeTests = [ [ 'operation_all is the only operation item present after serialization', @@ -19,7 +17,7 @@ module('Unit | Adapter | kmip/role', function (hooks) { return { operation_all: true, operation_get: true, operation_create: true, tls_ttl: '10s' }; }, record: { - editableFields, + nonOperationFields: ['tlsTtl'], }, }, { @@ -34,7 +32,7 @@ module('Unit | Adapter | kmip/role', function (hooks) { return { operation_all: true, operation_get: true, operation_create: true }; }, record: { - editableFields, + nonOperationFields: ['tlsTtl'], }, }, { @@ -48,7 +46,7 @@ module('Unit | Adapter | kmip/role', function (hooks) { return { operation_none: true, operation_get: true, operation_add_attribute: true, tls_ttl: '10s' }; }, record: { - editableFields, + nonOperationFields: ['tlsTtl'], }, }, { @@ -69,7 +67,7 @@ module('Unit | Adapter | kmip/role', function (hooks) { }; }, record: { - editableFields, + nonOperationFields: ['tlsTtl'], }, }, { diff --git a/ui/tests/unit/utils/kmip-role-fields-test.js b/ui/tests/unit/utils/kmip-role-fields-test.js deleted file mode 100644 index effbda02fb5c..000000000000 --- a/ui/tests/unit/utils/kmip-role-fields-test.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import { - nonOperationFields, - operationFields, - operationFieldsWithoutSpecial, -} from 'vault/utils/kmip-role-fields'; - -module('Unit | Util | kmip role fields', function (hooks) { - setupTest(hooks); - - [ - { - name: 'when fields is empty', - fields: [], - opFields: [], - nonOpFields: [], - opWithoutSpecial: [], - }, - { - name: 'when no op fields', - fields: ['foo', 'bar'], - opFields: [], - nonOpFields: ['foo', 'bar'], - opWithoutSpecial: [], - }, - { - name: 'when op fields', - fields: ['foo', 'bar', 'operationFoo', 'operationBar', 'operationAll'], - opFields: ['operationFoo', 'operationBar', 'operationAll'], - nonOpFields: ['foo', 'bar'], - opWithoutSpecial: ['operationFoo', 'operationBar'], - }, - ].forEach(({ name, fields, opFields, nonOpFields, opWithoutSpecial }) => { - test(`${name}`, function (assert) { - const originalFields = JSON.parse(JSON.stringify(fields)); - assert.deepEqual(operationFields(fields), opFields, 'operation fields correct'); - assert.deepEqual(nonOperationFields(fields), nonOpFields, 'non operation fields'); - assert.deepEqual( - operationFieldsWithoutSpecial(fields), - opWithoutSpecial, - 'operation fields without special' - ); - assert.deepEqual(fields, originalFields, 'does not mutate the original'); - }); - }); -}); diff --git a/vault/external_tests/audit/audit_test.go b/vault/external_tests/audit/audit_test.go deleted file mode 100644 index dbe56fd73151..000000000000 --- a/vault/external_tests/audit/audit_test.go +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package audit - -import ( - "bufio" - "context" - "encoding/json" - "fmt" - "os" - "strings" - "testing" - - "github.com/hashicorp/vault/api" - "github.com/hashicorp/vault/api/auth/userpass" - "github.com/hashicorp/vault/helper/testhelpers/minimal" - "github.com/stretchr/testify/require" -) - -// TestAudit_HMACFields verifies that all appropriate fields in audit -// request and response entries are HMACed properly. The fields in question are: -// - request.headers.x-correlation-id -// - request.data: all sub-fields -// - respnse.auth.client_token -// - response.auth.accessor -// - response.data: all sub-fields -// - response.wrap_info.token -// - response.wrap_info.accessor -func TestAudit_HMACFields(t *testing.T) { - const hmacPrefix = "hmac-sha256:" - - cluster := minimal.NewTestSoloCluster(t, nil) - client := cluster.Cores[0].Client - - tempDir := t.TempDir() - logFile, err := os.CreateTemp(tempDir, "") - require.NoError(t, err) - devicePath := "file" - deviceData := map[string]any{ - "type": "file", - "description": "", - "local": false, - "options": map[string]any{ - "file_path": logFile.Name(), - }, - } - - _, err = client.Logical().Write("sys/config/auditing/request-headers/x-correlation-id", map[string]interface{}{ - "hmac": true, - }) - require.NoError(t, err) - - // Request 1 - // Enable the audit device. A test probe request will audited along with the associated - // to the creation response - _, err = client.Logical().Write("sys/audit/"+devicePath, deviceData) - require.NoError(t, err) - - // Request 2 - // Ensure the device has been created. - devices, err := client.Sys().ListAudit() - require.NoError(t, err) - require.Len(t, devices, 1) - - // Request 3 - // Enable the userpass auth method (this will be an audited action) - err = client.Sys().EnableAuthWithOptions("userpass", &api.EnableAuthOptions{ - Type: "userpass", - }) - require.NoError(t, err) - - username := "jdoe" - password := "abc123" - - // Request 4 - // Create a user with a password (another audited action) - _, err = client.Logical().Write(fmt.Sprintf("auth/userpass/users/%s", username), map[string]interface{}{ - "password": password, - }) - require.NoError(t, err) - - authInput, err := userpass.NewUserpassAuth(username, &userpass.Password{FromString: password}) - require.NoError(t, err) - - newClient, err := client.Clone() - require.NoError(t, err) - - correlationID := "correlation-id-foo" - newClient.AddHeader("x-correlation-id", correlationID) - - // Request 5 - authOutput, err := newClient.Auth().Login(context.Background(), authInput) - require.NoError(t, err) - - // Request 6 - hashedPassword, err := client.Sys().AuditHash(devicePath, password) - require.NoError(t, err) - - // Request 7 - hashedClientToken, err := client.Sys().AuditHash(devicePath, authOutput.Auth.ClientToken) - require.NoError(t, err) - - // Request 8 - hashedAccessor, err := client.Sys().AuditHash(devicePath, authOutput.Auth.Accessor) - require.NoError(t, err) - - // Request 9 - wrapResp, err := client.Logical().Write("sys/wrapping/wrap", map[string]interface{}{ - "foo": "bar", - }) - require.NoError(t, err) - - // Request 10 - hashedBar, err := client.Sys().AuditHash(devicePath, "bar") - require.NoError(t, err) - - // Request 11 - hashedWrapAccessor, err := client.Sys().AuditHash(devicePath, wrapResp.WrapInfo.Accessor) - require.NoError(t, err) - - // Request 12 - hashedWrapToken, err := client.Sys().AuditHash(devicePath, wrapResp.WrapInfo.Token) - require.NoError(t, err) - - // Request 13 - hashedCorrelationID, err := client.Sys().AuditHash(devicePath, correlationID) - require.NoError(t, err) - - // Request 14 - // Disable the audit device. The request will be audited but not the response. - _, err = client.Logical().Delete("sys/audit/" + devicePath) - require.NoError(t, err) - - // Request 15 - // Ensure the device has been deleted. This will not be audited. - devices, err = client.Sys().ListAudit() - require.NoError(t, err) - require.Len(t, devices, 0) - - entries := make([]map[string]interface{}, 0) - scanner := bufio.NewScanner(logFile) - - for scanner.Scan() { - entry := make(map[string]interface{}) - - err := json.Unmarshal(scanner.Bytes(), &entry) - require.NoError(t, err) - - entries = append(entries, entry) - } - - // This count includes the initial test probe upon creation of the audit device - require.Equal(t, 27, len(entries)) - - loginReqEntry := entries[8] - loginRespEntry := entries[9] - - loginRequestFromReq := loginReqEntry["request"].(map[string]interface{}) - loginRequestDataFromReq := loginRequestFromReq["data"].(map[string]interface{}) - loginHeadersFromReq := loginRequestFromReq["headers"].(map[string]interface{}) - - loginRequestFromResp := loginRespEntry["request"].(map[string]interface{}) - loginRequestDataFromResp := loginRequestFromResp["data"].(map[string]interface{}) - loginHeadersFromResp := loginRequestFromResp["headers"].(map[string]interface{}) - - loginAuth := loginRespEntry["auth"].(map[string]interface{}) - - require.True(t, strings.HasPrefix(loginRequestDataFromReq["password"].(string), hmacPrefix)) - require.Equal(t, loginRequestDataFromReq["password"].(string), hashedPassword) - - require.True(t, strings.HasPrefix(loginRequestDataFromResp["password"].(string), hmacPrefix)) - require.Equal(t, loginRequestDataFromResp["password"].(string), hashedPassword) - - require.True(t, strings.HasPrefix(loginAuth["client_token"].(string), hmacPrefix)) - require.Equal(t, loginAuth["client_token"].(string), hashedClientToken) - - require.True(t, strings.HasPrefix(loginAuth["accessor"].(string), hmacPrefix)) - require.Equal(t, loginAuth["accessor"].(string), hashedAccessor) - - xCorrelationIDFromReq := loginHeadersFromReq["x-correlation-id"].([]interface{}) - require.Equal(t, len(xCorrelationIDFromReq), 1) - require.True(t, strings.HasPrefix(xCorrelationIDFromReq[0].(string), hmacPrefix)) - require.Equal(t, xCorrelationIDFromReq[0].(string), hashedCorrelationID) - - xCorrelationIDFromResp := loginHeadersFromResp["x-correlation-id"].([]interface{}) - require.Equal(t, len(xCorrelationIDFromResp), 1) - require.True(t, strings.HasPrefix(xCorrelationIDFromReq[0].(string), hmacPrefix)) - require.Equal(t, xCorrelationIDFromResp[0].(string), hashedCorrelationID) - - wrapReqEntry := entries[16] - wrapRespEntry := entries[17] - - wrapRequestFromReq := wrapReqEntry["request"].(map[string]interface{}) - wrapRequestDataFromReq := wrapRequestFromReq["data"].(map[string]interface{}) - - wrapRequestFromResp := wrapRespEntry["request"].(map[string]interface{}) - wrapRequestDataFromResp := wrapRequestFromResp["data"].(map[string]interface{}) - - require.True(t, strings.HasPrefix(wrapRequestDataFromReq["foo"].(string), hmacPrefix)) - require.Equal(t, wrapRequestDataFromReq["foo"].(string), hashedBar) - - require.True(t, strings.HasPrefix(wrapRequestDataFromResp["foo"].(string), hmacPrefix)) - require.Equal(t, wrapRequestDataFromResp["foo"].(string), hashedBar) - - wrapResponseData := wrapRespEntry["response"].(map[string]interface{}) - wrapInfo := wrapResponseData["wrap_info"].(map[string]interface{}) - - require.True(t, strings.HasPrefix(wrapInfo["accessor"].(string), hmacPrefix)) - require.Equal(t, wrapInfo["accessor"].(string), hashedWrapAccessor) - - require.True(t, strings.HasPrefix(wrapInfo["token"].(string), hmacPrefix)) - require.Equal(t, wrapInfo["token"].(string), hashedWrapToken) -} diff --git a/version/VERSION b/version/VERSION index a90d62be3124..744068368fba 100644 --- a/version/VERSION +++ b/version/VERSION @@ -1 +1 @@ -1.19.0-beta1 +1.18.0 \ No newline at end of file diff --git a/website/content/api-docs/system/storage/raftautopilot.mdx b/website/content/api-docs/system/storage/raftautopilot.mdx index ea02f25539cb..19b453b8144d 100644 --- a/website/content/api-docs/system/storage/raftautopilot.mdx +++ b/website/content/api-docs/system/storage/raftautopilot.mdx @@ -35,69 +35,54 @@ $ curl \ ```json { - "failure_tolerance": 1, "healthy": true, - "leader": "vault_1", + "failure_tolerance": 1, "servers": { - "vault_1": { + "raft1": { + "id": "raft1", + "name": "raft1", "address": "127.0.0.1:8201", - "healthy": true, - "id": "vault_1", + "node_status": "alive", "last_contact": "0s", - "last_index": 63, "last_term": 3, - "name": "vault_1", - "node_status": "alive", - "node_type": "voter", - "stable_since": "2024-08-29T16:02:45.639829+02:00", + "last_index": 459, + "healthy": true, + "stable_since": "2021-03-19T20:14:11.831678-04:00", "status": "leader", - "version": "1.17.3" + "meta": null }, - "vault_2": { - "address": "127.0.0.1:8203", - "healthy": true, - "id": "vault_2", - "last_contact": "678.62575ms", - "last_index": 63, - "last_term": 3, - "name": "vault_2", + "raft2": { + "id": "raft2", + "name": "raft2", + "address": "127.0.0.2:8201", "node_status": "alive", - "node_type": "voter", - "stable_since": "2024-08-29T16:02:47.640976+02:00", + "last_contact": "516.49595ms", + "last_term": 3, + "last_index": 459, + "healthy": true, + "stable_since": "2021-03-19T20:14:19.831931-04:00", "status": "voter", - "version": "1.17.3" + "meta": null }, - "vault_3": { - "address": "127.0.0.1:8205", - "healthy": true, - "id": "vault_3", - "last_contact": "3.969159375s", - "last_index": 63, - "last_term": 3, - "name": "vault_3", + "raft3": { + "id": "raft3", + "name": "raft3", + "address": "127.0.0.3:8201", "node_status": "alive", - "node_type": "voter", - "stable_since": "2024-08-29T16:02:49.640905+02:00", + "last_contact": "196.706591ms", + "last_term": 3, + "last_index": 459, + "healthy": true, + "stable_since": "2021-03-19T20:14:25.83565-04:00", "status": "voter", - "version": "1.17.3" + "meta": null } }, - "voters": [ - "vault_1", - "vault_2", - "vault_3" - ] + "leader": "raft1", + "voters": ["raft1", "raft2", "raft3"], + "non_voters": null } ``` -The `failure_tolerance` of a cluster is the number of nodes in the cluster that could -fail gradually without causing an outage. - -When verifying the health of your cluster, check the following fields of each server: -- `healthy`: whether Autopilot considers this node healthy or not -- `status`: the voting status of the node. This will be `voter`, `leader`, or [`non-voter`](/vault/docs/concepts/integrated-storage#non-voting-nodes-enterprise-only)") -- `last_index`: the index of the last applied Raft log. This should be close to the `last_index` value of the leader. -- `version`: the version of Vault running on the server -- `node_type`: the type of node. On CE, this will always be `voter`. See below for an explanation of Enterprise node types. ### Enterprise only Vault Enterprise will include additional output in its API response to indicate the current state of redundancy zones, @@ -164,7 +149,7 @@ automated upgrade progress (if any), and optimistic failure tolerance. } }, "status": "await-new-voters", - "target_version": "1.17.5", + "target_version": "1.12.0", "target_version_non_voters": [ "vault_5" ] @@ -176,11 +161,6 @@ automated upgrade progress (if any), and optimistic failure tolerance. } ``` -`optimistic_failure_tolerance` describes the number of healthy active and -back-up voting servers that can fail gradually without causing an outage. - -@include 'autopilot/node-types.mdx' - ## Get configuration This endpoint is used to get the configuration of the autopilot subsystem of Integrated Storage. @@ -223,7 +203,31 @@ This endpoint is used to modify the configuration of the autopilot subsystem of ### Parameters -@include 'autopilot/config.mdx' +- `cleanup_dead_servers` `(bool: false)` - Controls whether to remove dead servers from + the Raft peer list periodically or when a new server joins. This requires that + `min_quorum` is also set. + +- `last_contact_threshold` `(string: "10s")` - Limit on the amount of time a server can + go without leader contact before being considered unhealthy. + +- `dead_server_last_contact_threshold` `(string: "24h")` - Limit on the amount of time + a server can go without leader contact before being considered failed. This + takes effect only when `cleanup_dead_servers` is `true`. This can not be set to a value + smaller than 1m. **We strongly recommend that this is kept at a high duration, such as a day, + as it being too low could result in removal of nodes that aren't actually dead.** + +- `max_trailing_logs` `(int: 1000)` - Amount of entries in the Raft Log that a server + can be behind before being considered unhealthy. + +- `min_quorum` `(int: 3)` - Minimum number of servers allowed in a cluster before + autopilot can prune dead servers. This should at least be 3. Applicable only for + voting nodes. + +- `server_stabilization_time` `(string: "10s")` - Minimum amount of time a server must + be in a stable, healthy state before it can be added to the cluster. + +- `disable_upgrade_migration` `(bool: false)` - Disables automatically upgrading Vault using + autopilot. (Enterprise-only) ### Sample request diff --git a/website/content/docs/commands/operator/raft.mdx b/website/content/docs/commands/operator/raft.mdx index 59fa0ead31e6..9b7f91da94ef 100644 --- a/website/content/docs/commands/operator/raft.mdx +++ b/website/content/docs/commands/operator/raft.mdx @@ -128,13 +128,6 @@ Usage: vault operator raft list-peers } ``` -Use the output of `list-peers` to ensure that your cluster is in an expected state. -If you've removed a server using `remove-peer`, the server should no longer be -listed in the `list-peers` output. If you've added a server using `add-peer` or -through `retry_join`, check the `list-peers` output to see that it has been added -to the cluster and (if the node has not been added as a non-voter) -it has been promoted to a voter. - ## remove-peer This command is used to remove a node from being a peer to the Raft cluster. In @@ -236,9 +229,14 @@ Subcommands: ### autopilot state Displays the state of the raft cluster under integrated storage as seen by -autopilot. It shows whether autopilot thinks the cluster is healthy or not. +autopilot. It shows whether autopilot thinks the cluster is healthy or not, +and how many nodes could fail before the cluster becomes unhealthy ("Failure Tolerance"). + +State includes a list of all servers by nodeID and IP address. Last Index +indicates how close the state on each node is to the leader's. -State includes a list of all servers by nodeID and IP address. +A node can have a status of "leader", "voter", and +"[non-voter](/vault/docs/concepts/integrated-storage#non-voting-nodes-enterprise-only)". ```text Usage: vault operator raft autopilot state @@ -251,60 +249,34 @@ Usage: vault operator raft autopilot state #### Example output ```text -Healthy: true -Failure Tolerance: 1 -Leader: vault_1 +Healthy: true +Failure Tolerance: 1 +Leader: raft1 Voters: - vault_1 - vault_2 - vault_3 + raft1 + raft2 + raft3 Servers: - vault_1 - Name: vault_1 - Address: 127.0.0.1:8201 - Status: leader - Node Status: alive - Healthy: true - Last Contact: 0s - Last Term: 3 - Last Index: 61 - Version: 1.17.3 - Node Type: voter - vault_2 - Name: vault_2 - Address: 127.0.0.1:8203 - Status: voter - Node Status: alive - Healthy: true - Last Contact: 564.765375ms - Last Term: 3 - Last Index: 61 - Version: 1.17.3 - Node Type: voter - vault_3 - Name: vault_3 - Address: 127.0.0.1:8205 - Status: voter - Node Status: alive - Healthy: true - Last Contact: 3.814017875s - Last Term: 3 - Last Index: 61 - Version: 1.17.3 - Node Type: voter + raft1 + Name: raft1 + Address: 127.0.0.1:8201 + Status: leader + Node Status: alive + Healthy: true + Last Contact: 0s + Last Term: 3 + Last Index: 38 + raft2 + Name: raft2 + Address: 127.0.0.2:8201 + Status: voter + Node Status: alive + Healthy: true + Last Contact: 2.514176729s + Last Term: 3 + Last Index: 38 ``` - -The "Failure Tolerance" of a cluster is the number of nodes in the cluster that could -fail gradually without causing an outage. - -When verifying the health of your cluster, check the following fields of each server: -- Healthy: whether Autopilot considers this node healthy or not -- Status: the voting status of the node. This will be `voter`, `leader`, or [`non-voter`](/vault/docs/concepts/integrated-storage#non-voting-nodes-enterprise-only). -- Last Index: the index of the last applied Raft log. This should be close to the "Last Index" value of the leader. -- Version: the version of Vault running on the server -- Node Type: the type of node. On CE, this will always be `voter`. See below for an explanation of Enterprise node types. - -Vault Enterprise will include additional output related to automated upgrades, optimistic failure tolerance, and redundancy zones. +Vault Enterprise will include additional output related to automated upgrades and redundancy zones. #### Example Vault enterprise output @@ -320,7 +292,7 @@ Redundancy Zones: Failure Tolerance: 1 Upgrade Info: Status: await-new-voters - Target Version: 1.17.5 + Target Version: 1.12.0 Target Version Voters: Target Version Non-Voters: vault_5 Other Version Voters: vault_1, vault_3 @@ -338,11 +310,6 @@ Upgrade Info: Other Version Non-Voters: vault_4 ``` -"Optimistic Failure Tolerance" describes the number of healthy active and -back-up voting servers that can fail gradually without causing an outage. - -@include 'autopilot/node-types.mdx' - ### autopilot get-config Returns the configuration of the autopilot subsystem under integrated storage. @@ -370,49 +337,29 @@ Usage: vault operator raft autopilot set-config [options] Flags applicable to this command are the following: -- `cleanup-dead-servers` `(bool: false)` - Controls whether to remove dead servers from +- `cleanup-dead-servers` `(bool)` - Controls whether to remove dead servers from the Raft peer list periodically or when a new server joins. This requires that - `min-quorum` is also set. - -- `last-contact-threshold` `(string: "10s")` - Limit on the amount of time a server can - go without leader contact before being considered unhealthy. - -- `dead-server-last-contact-threshold` `(string: "24h")` - Limit on the amount of time -a server can go without leader contact before being considered failed. This -takes effect only when `cleanup_dead_servers` is set. When adding new nodes -to your cluster, the `dead_server_last_contact_threshold` needs to be larger -than the amount of time that it takes to load a Raft snapshot, otherwise the -newly added nodes will be removed from your cluster before they have finished -loading the snapshot and starting up. If you are using an [HSM](/vault/docs/enterprise/hsm), your -`dead_server_last_contact_threshold` needs to be larger than the response -time of the HSM. - - - - We strongly recommend keeping `dead_server_last_contact_threshold` at a high - duration, such as a day, as it being too low could result in removal of nodes - that aren't actually dead - - - -- `max-trailing-logs` `(int: 1000)` - Amount of entries in the Raft Log that a server - can be behind before being considered unhealthy. If this value is too low, - it can cause the cluster to lose quorum if a follower falls behind. This - value only needs to be increased from the default if you have a very high - write load on Vault and you see that it takes a long time to promote new - servers to becoming voters. This is an unlikely scenario and most users - should not modify this value. - -- `min-quorum` `(int)` - The minimum number of servers that should always be -present in a cluster. Autopilot will not prune servers below this number. -**There is no default for this value** and it should be set to the expected -number of voters in your cluster when `cleanup_dead_servers` is set as `true`. -Use the [quorum size guidance](/vault/docs/internals/integrated-storage#quorum-size-and-failure-tolerance) -to determine the proper minimum quorum size for your cluster. - -- `server-stabilization-time` `(string: "10s")` - Minimum amount of time a server must be in a healthy state before it + `min-quorum` is also set. Defaults to `false`. + +- `last-contact-threshold` `(string)` - Limit on the amount of time a server can + go without leader contact before being considered unhealthy. Defaults to `10s`. + +- `dead-server-last-contact-threshold` `(string)` - Limit on the amount of time + a server can go without leader contact before being considered failed. + This takes effect only when `cleanup_dead_servers` is set as `true`. Defaults to `24h`. + + -> **Note:** A failed server that autopilot has removed from the raft configuration cannot rejoin the cluster without being reinitialized. + +- `max-trailing-logs` `(int)` - Amount of entries in the Raft Log that a server + can be behind before being considered unhealthy. Defaults to `1000`. + +- `min-quorum` `(int)` - Minimum number of servers that should always be present in a cluster. + Autopilot will not prune servers below this number. This should be set to the expected number + of voters in your cluster. There is no default. + +- `server-stabilization-time` `(string)` - Minimum amount of time a server must be in a healthy state before it can become a voter. Until that happens, it will be visible as a peer in the cluster, but as a non-voter, meaning it - won't contribute to quorum. + won't contribute to quorum. Defaults to `10s`. -- `disable-upgrade-migration` `(bool: false)` - Controls whether to disable automated - upgrade migrations, an Enterprise-only feature. +- `disable-upgrade-migration` `(bool)` - Controls whether to disable automated + upgrade migrations, an Enterprise-only feature. Defaults to `false`. diff --git a/website/content/docs/commands/server.mdx b/website/content/docs/commands/server.mdx index de61d671ef75..ce0b987534ad 100644 --- a/website/content/docs/commands/server.mdx +++ b/website/content/docs/commands/server.mdx @@ -92,11 +92,6 @@ flags](/vault/docs/commands) included on all commands. `VAULT_EXPERIMENTS` environment variable as a comma-separated list, or via the [`experiments`](/vault/docs/configuration#experiments) config key. -- `-pprof-dump-dir` `(string: "")` - Directory where the generated profiles are - created. Vault does not generate profiles when `pprof-dump-dir` is unset. - Use `pprof-dump-dir` temporarily during debugging sessions. Do not use - `pprof-dump-dir` in regular production processes. - - `VAULT_ALLOW_PENDING_REMOVAL_MOUNTS` `(bool: false)` - (environment variable) Allow Vault to be started with builtin engines which have the `Pending Removal` deprecation state. This is a temporary stopgap in place in order to perform an diff --git a/website/content/docs/concepts/integrated-storage/autopilot.mdx b/website/content/docs/concepts/integrated-storage/autopilot.mdx index 497d336fd790..327c87d47bcf 100644 --- a/website/content/docs/concepts/integrated-storage/autopilot.mdx +++ b/website/content/docs/concepts/integrated-storage/autopilot.mdx @@ -17,7 +17,7 @@ These two features were introduced in Vault 1.11. Server stabilization helps to retain the stability of the Raft cluster by safely joining new voting nodes to the cluster. When a new voter node is joined to an existing cluster, autopilot adds it as a non-voter instead, and waits for a -pre-configured amount of time to monitor its health. If the node remains +pre-configured amount of time to monitor it's health. If the node remains to be healthy for the entire duration of stabilization, then that node will be promoted as a voter. The server stabilization period can be tuned using `server_stabilization_time` (see below). @@ -31,7 +31,7 @@ and `min_quorum` (see below). ## State API -The [State API](/vault/api-docs/system/storage/raftautopilot#get-cluster-state) provides detailed information about all the nodes in the Raft cluster +State API provides detailed information about all the nodes in the Raft cluster in a single call. This API can be used for monitoring for cluster health. ### Follower health @@ -50,7 +50,40 @@ although dead server cleanup is not enabled by default. Upgrade of Raft clusters deployed with older versions of Vault will also transition to use Autopilot automatically. -@include 'autopilot/config.mdx' +Autopilot exposes a [configuration +API](/vault/api-docs/system/storage/raftautopilot#set-configuration) to manage its +behavior. Autopilot gets initialized with the following default values. If these default values do not meet your expected autopilot behavior, don't forget to set them to your desired values. + +- `cleanup_dead_servers` - `false` + - This controls whether to remove dead servers from + the Raft peer list periodically or when a new server joins. This requires that + `min-quorum` is also set. + +- `dead_server_last_contact_threshold` - `24h` + - Limit on the amount of time + a server can go without leader contact before being considered failed. This + takes effect only when `cleanup_dead_servers` is set. **We strongly recommend + that this is kept at a high duration, such as a day, as it being too low could + result in removal of nodes that aren't actually dead.** + +- `min_quorum` - This doesn't default to anything and should be set to the expected + number of voters in your cluster when `cleanup_dead_servers` is set as `true`. + - Minimum number of servers that should always be present in a cluster. + Autopilot will not prune servers below this number. + +- `max_trailing_logs` - `1000` + - Amount of entries in the Raft Log that a server + can be behind before being considered unhealthy. + +- `last_contact_threshold` - `10s` + - Limit on the amount of time a server can go without leader contact before being considered unhealthy. + +- `server_stabilization_time` - `10s` + - Minimum amount of time a server must be in a healthy state before it can become a voter. Until that happens, + it will be visible as a peer in the cluster, but as a non-voter, meaning it won't contribute to quorum. + +- `disable_upgrade_migration` - `false` + - Controls whether to disable automated upgrade migrations, an Enterprise-only feature. ~> **Note**: Autopilot in Vault does similar things to what autopilot does in [Consul](https://www.consul.io/). However, the configuration in these 2 systems @@ -61,7 +94,7 @@ provide the autopilot functionality. ## Automated upgrades -[Automated Upgrades](/vault/docs/enterprise/automated-upgrades) lets you automatically upgrade a cluster of Vault nodes to a new version as +Automated Upgrades lets you automatically upgrade a cluster of Vault nodes to a new version as updated server nodes join the cluster. Once the number of nodes on the new version is equal to or greater than the number of nodes on the old version, Autopilot will promote the newer versioned nodes to voters, demote the older versioned nodes to non-voters, @@ -71,7 +104,7 @@ nodes can be removed from the cluster. ## Redundancy zones -[Redundancy Zones](/vault/docs/enterprise/redundancy-zones) provide both scaling and resiliency benefits by deploying non-voting +Redundancy Zones provide both scaling and resiliency benefits by deploying non-voting nodes alongside voting nodes on a per availability zone basis. When using redundancy zones, each zone will have exactly one voting node and as many additional non-voting nodes as desired. If the voting node in a zone fails, a non-voting node will be automatically promoted to diff --git a/website/content/docs/concepts/integrated-storage/index.mdx b/website/content/docs/concepts/integrated-storage/index.mdx index 5fe51e63aca0..6e1d8c79ca0b 100644 --- a/website/content/docs/concepts/integrated-storage/index.mdx +++ b/website/content/docs/concepts/integrated-storage/index.mdx @@ -60,11 +60,6 @@ API (both methods described below). When joining a node, the API address of the recommend setting the [`api_addr`](/vault/docs/concepts/ha#direct-access) configuration option on all nodes to make joining simpler. -Always join nodes to a cluster one at a time and wait for the node to become -healthy and (if applicable) a voter before continuing to add more nodes. The -status of a node can be verified by performing a [`list-peers`](/vault/docs/commands/operator/raft#list-peers) -command or by checking the [`autopilot state`](/vault/docs/commands/operator/raft#autopilot-state). - #### `retry_join` configuration This method enables setting one, or more, target leader nodes in the config file. @@ -100,10 +95,9 @@ provided, Vault will use [go-discover](https://github.com/hashicorp/go-discover) to automatically attempt to discover and resolve potential Raft leader addresses. -Check the go-discover +See the go-discover [README](https://github.com/hashicorp/go-discover/blob/master/README.md) for -details on the format of the [`auto_join`](/vault/docs/configuration/storage/raft#auto_join) -value per cloud provider. +details on the format of the [`auto_join`](/vault/docs/configuration/storage/raft#auto_join) value. ```hcl storage "raft" { @@ -173,14 +167,6 @@ $ vault operator raft remove-peer node1 Peer removed successfully! ``` -#### Re-joining after removal - -If you have used `remove-peer` to remove a node from the Raft cluster, but you -later want to have this same node re-join the cluster, you will need to delete -any existing Raft data on the removed node before adding it back to the cluster. -This will involve stopping the Vault process, deleting the data directory containing -Raft data, and then restarting the Vault process. - ### Listing peers To see the current peer set for the cluster you can issue a diff --git a/website/content/docs/enterprise/lts.mdx b/website/content/docs/enterprise/lts.mdx index 1940abb883df..33d4bae8b0d6 100644 --- a/website/content/docs/enterprise/lts.mdx +++ b/website/content/docs/enterprise/lts.mdx @@ -91,7 +91,7 @@ upgrade frequently, quickly, or easily. Vault upgrades are challenging, especially for sensitive or critical workflows, extensive integrations, and large-scale deployments. Strict upgrade policies also require significant planning, testing, and employee hours to execute -successfully. +successfully. Customers who need assurances that their current installation will receive critical bug fixes and security patches with minimal service disruptions should @@ -111,8 +111,8 @@ the current version ("N") and the two previous versions ("N−2"). Vault versions typically update 3 times per calendar year (CY), which means that **standard maintenance** for a given Vault version lasts approximately 1 year. After the first year, LTS Vault versions move from standard maintenance to -**extended maintenance** for three additional major version releases (approximately one additional year) -with patches for bugs that may cause outages and critical vulnerabilities and exposures (CVEs). +**extended maintenance** for an additional year with patches for bugs that +may cause outages and critical vulnerabilities and exposures (CVEs). Maintenance updates | Standard maintenance | Extended maintenance --------------------------------- | -------------------- | -------------------- @@ -152,10 +152,10 @@ The goal is to establish a predictable upgrade path with a longer timeline rather than extending the lifetime for every Vault version. Long-term support ensures your Vault Enterprise version continues to receive -critical patches for an additional three major version releases (approximately one additional year). -If you upgrade to a non-LTS version,you are moving your Vault instance to a version -that lacks extended support. Non-LTS versions stop receiving updates once they leave -the standard maintenance window. +critical patches for an additional year. If you upgrade to a non-LTS version, +you are moving your Vault instance to a version that lacks extended support. +Non-LTS versions stop receiving updates once they leave the standard maintenance +window. @include 'assets/lts-upgrade-path.mdx' @@ -164,7 +164,7 @@ Version | Expected release | Standard maintenance ends | Extended maintenance e 1.19 | CY25 Q1 | CY26 Q1 (1.22 release) | CY27 Q1 (1.25 release) 1.18 | CY24 Q3 | CY25 Q3 (1.21 release) | Not provided 1.17 | CY24 Q2 | CY25 Q2 (1.20 release) | Not provided -1.16 | CY24 Q1 | CY25 Q1 (1.19 release) | CY26 Q1 (1.22 release) +1.16 | CY24 Q1 | CY25 Q1 (1.19 release) | CY26 Q1 (1.22 release) If a newer version of Vault Enterprise includes features you want to take advantage of, you have two options: @@ -180,4 +180,4 @@ advantage of, you have two options: You should follow your existing upgrade process for major version upgrades but allow additional time. Upgrading from version LTS to LTS+1 translates to jumping 3 major Vault Enterprise versions, which **may** require transitional upgrades -to move through the intermediate Vault versions. +to move through the intermediate Vault versions. \ No newline at end of file diff --git a/website/content/docs/enterprise/redundancy-zones.mdx b/website/content/docs/enterprise/redundancy-zones.mdx index c4cb9f903a8e..f905ff20b524 100644 --- a/website/content/docs/enterprise/redundancy-zones.mdx +++ b/website/content/docs/enterprise/redundancy-zones.mdx @@ -36,7 +36,3 @@ wait to begin leadership transfer until it can ensure that there will be as much new Vault version as there was on the old Vault version. The status of redundancy zones can be monitored by consulting the [Autopilot state API endpoint](/vault/api-docs/system/storage/raftautopilot#get-cluster-state). - -## Optimistic Failure Tolerance - -@include 'autopilot/redundancy-zones.mdx' diff --git a/website/content/docs/internals/integrated-storage.mdx b/website/content/docs/internals/integrated-storage.mdx index 5fa5e0756e6e..1679575f7b5f 100644 --- a/website/content/docs/internals/integrated-storage.mdx +++ b/website/content/docs/internals/integrated-storage.mdx @@ -271,28 +271,6 @@ For example, if you start with a 5-node cluster: You should always maintain quorum to limit the impact on failure tolerance when changing or scaling your Vault instance. -### Redundancy Zones - -If you are using autopilot with [redundancy zones](/vault/docs/enterprise/redundancy-zones), -the total number of servers will be different from the above, and is dependent -on how many redundancy zones and servers per redundancy zone that you choose. - -@include 'autopilot/redundancy-zones.mdx' - - - - If you choose to use redundancy zones, we **strongly recommend** using at least 3 - zones to ensure failure tolerance. - - - -Redundancy zones | Servers per zone | Quorum size | Failure tolerance | Optimistic failure tolerance -:--------------: | :--------------: | :---------: | :---------------: | :--------------------------: -2 | 2 | 2 | 0 | 2 -3 | 2 | 2 | 1 | 3 -3 | 3 | 2 | 1 | 5 -5 | 2 | 3 | 2 | 6 - [consensus protocol]: https://en.wikipedia.org/wiki/Consensus_(computer_science) [consistency]: https://en.wikipedia.org/wiki/CAP_theorem ["Raft: In search of an Understandable Consensus Algorithm"]: https://raft.github.io/raft.pdf diff --git a/website/content/docs/platform/aws/lambda-extension.mdx b/website/content/docs/platform/aws/lambda-extension.mdx index 1df42ecf0cee..61e9718de65b 100644 --- a/website/content/docs/platform/aws/lambda-extension.mdx +++ b/website/content/docs/platform/aws/lambda-extension.mdx @@ -284,8 +284,7 @@ processing with returned secrets such as automatic lease renewal. The proxy serv own Vault auth token is the only thing that gets automatically refreshed. It will synchronously refresh its own token before proxying requests if the token is expired (including a grace window), and it will attempt to renew its token if the -token is nearly expired but renewable. The proxy will also immediately refresh its token -if the incoming request header `X-Vault-Token-Options: revoke` is present. +token is nearly expired but renewable. diff --git a/website/content/docs/platform/k8s/helm/index.mdx b/website/content/docs/platform/k8s/helm/index.mdx index 6cc6c6bb1ed9..53f410103b93 100644 --- a/website/content/docs/platform/k8s/helm/index.mdx +++ b/website/content/docs/platform/k8s/helm/index.mdx @@ -59,15 +59,6 @@ cluster](https://kubernetes.io/docs/tasks/administer-cluster/securing-a-cluster/ options](/vault/docs/platform/k8s/helm/configuration), and read the [production deployment checklist](/vault/docs/platform/k8s/helm/run#architecture). - - - - If you use AWS features (e.g, AWS PrivateLink) that require a network load - balancer (NLB), you must provision your NLB **before** your application load - balancer (ALB). - - - ## Tutorial Refer to the [Kubernetes](/vault/tutorials/kubernetes) diff --git a/website/content/docs/platform/k8s/helm/run.mdx b/website/content/docs/platform/k8s/helm/run.mdx index 261d227eda9e..95184caaf18b 100644 --- a/website/content/docs/platform/k8s/helm/run.mdx +++ b/website/content/docs/platform/k8s/helm/run.mdx @@ -436,25 +436,7 @@ running: $ kubectl delete pod ``` - -If you deployed Vault in high availability (`ha`) mode, you must upgrade your -standby pods before upgrading the active pod: - -1. Before deleting the standby pod, remove the associated node from the raft - with `vault operator raft remove-peer `. -1. Confirm Vault removed the node successfully from Raft with - `vault operator raft list-peers`. -1. Once you confirm the removal, delete the pod. - - - -Removing a pod without first deleting the node from its cluster means that -Raft will not be aware of the correct number of nodes in the cluster. Not knowing -the correct number of nodes can trigger a leader election, which can potentially -cause unneeded downtime. - - - +If Vault is deployed using `ha` mode, the standby pods must be upgraded first. Vault has K8s service discovery built in (when enabled in the server configuration) and will automatically change the labels of the pod with its current leader status. These labels can be used to filter the pods. diff --git a/website/content/docs/release-notes/1.17.0.mdx b/website/content/docs/release-notes/1.17.0.mdx index 96b23723917c..ead4d7efde6f 100644 --- a/website/content/docs/release-notes/1.17.0.mdx +++ b/website/content/docs/release-notes/1.17.0.mdx @@ -22,7 +22,6 @@ description: |- | Known issue (1.17.0) | [Vault Agent and Vault Proxy consume excessive amounts of CPU](/vault/docs/upgrading/upgrade-to-1.17.x#agent-proxy-cpu-1-17) | | Known issue (1.15.8 - 1.15.9, 1.16.0 - 1.16.3) | [Autopilot upgrade for Vault Enterprise fails](/vault/docs/upgrading/upgrade-to-1.16.x#new-nodes-added-by-autopilot-upgrades-provisioned-with-the-wrong-version) | | Known issue (1.17.0 - 1.17.2) | [Vault standby nodes not deleting removed entity-aliases from in-memory database](/vault/docs/upgrading/upgrade-to-1.17.x#dangling-entity-alias-in-memory) | -| Known issue (1.17.0 - 1.17.3) | [AWS Auth AssumeRole requires an external ID even if none is set](/vault/docs/upgrading/upgrade-to-1.17.x#aws-auth-role-configuration-requires-an-external_id) | | Known Issue (0.7.0+) | [Duplicate identity groups created](/vault/docs/upgrading/upgrade-to-1.17.x#duplicate-identity-groups-created-when-concurrent-requests-sent-to-the-primary-and-pr-secondary-cluster) | Known Issue (0.7.0+) | [Manual entity merges fail](/vault/docs/upgrading/upgrade-to-1.17.x#manual-entity-merges-sent-to-a-pr-secondary-cluster-are-not-persisted-to-storage) | Known Issue (1.17.3-1.17.4) | [Some values in the audit logs not hmac'd properly](/vault/docs/upgrading/upgrade-to-1.17.x#client-tokens-and-token-accessors-audited-in-plaintext) diff --git a/website/content/docs/secrets/databases/mongodbatlas.mdx b/website/content/docs/secrets/databases/mongodbatlas.mdx index 62313bb859c3..25741aff20d0 100644 --- a/website/content/docs/secrets/databases/mongodbatlas.mdx +++ b/website/content/docs/secrets/databases/mongodbatlas.mdx @@ -19,8 +19,8 @@ more information about setting up the database secrets engine. The information below relates to the MongoDB Altas database plugin for the Vault database secrets engine. - Refer to the
MongoDB Atlas secrets engine for - information about using the MongoDB Atlas secrets engine for the Vault. + Refer to the MongoDB Atlas secrets engine + for information about using the MongoDB Atlas secrets engine for the Vault. ## Capabilities diff --git a/website/content/docs/upgrading/upgrade-to-1.17.x.mdx b/website/content/docs/upgrading/upgrade-to-1.17.x.mdx index 0fb20e75a6c6..7b94b6646ed1 100644 --- a/website/content/docs/upgrading/upgrade-to-1.17.x.mdx +++ b/website/content/docs/upgrading/upgrade-to-1.17.x.mdx @@ -146,7 +146,3 @@ kubectl exec -ti -- wget https://github.com/moparisthebest/static-curl/re @include 'known-issues/duplicate-identity-groups.mdx' @include 'known-issues/manual-entity-merge-does-not-persist.mdx' - -@include 'known-issues/aws-auth-external-id.mdx' - -@include 'known-issues/sync-activation-flags-cache-not-updated.mdx' diff --git a/website/content/partials/autopilot/config.mdx b/website/content/partials/autopilot/config.mdx deleted file mode 100644 index cede26434b8d..000000000000 --- a/website/content/partials/autopilot/config.mdx +++ /dev/null @@ -1,53 +0,0 @@ -Autopilot exposes a [configuration -API](/vault/api-docs/system/storage/raftautopilot#set-configuration) to manage its -behavior. These items cannot be set in Vault server configuration files. -Autopilot gets initialized with the following default values. If these default -values do not meet your expected autopilot behavior, don't forget to set them to your desired values. - -- `cleanup_dead_servers` `(bool: false)` - This controls whether to remove dead servers from -the Raft peer list periodically or when a new server joins. This requires that -`min-quorum` is also set. - -- `dead_server_last_contact_threshold` `(string: "24h")` - Limit on the amount of time -a server can go without leader contact before being considered failed. This -takes effect only when `cleanup_dead_servers` is set. When adding new nodes -to your cluster, the `dead_server_last_contact_threshold` needs to be larger -than the amount of time that it takes to load a Raft snapshot, otherwise the -newly added nodes will be removed from your cluster before they have finished -loading the snapshot and starting up. If you are using an [HSM](/vault/docs/enterprise/hsm), your -`dead_server_last_contact_threshold` needs to be larger than the response -time of the HSM. - - - - We strongly recommend keeping `dead_server_last_contact_threshold` at a high - duration, such as a day, as it being too low could result in removal of nodes - that aren't actually dead - - - -- `min_quorum` `(int)` - The minimum number of servers that should always be -present in a cluster. Autopilot will not prune servers below this number. -**There is no default for this value** and it should be set to the expected -number of voters in your cluster when `cleanup_dead_servers` is set as `true`. -Use the [quorum size guidance](/vault/docs/internals/integrated-storage#quorum-size-and-failure-tolerance) -to determine the proper minimum quorum size for your cluster. - -- `max_trailing_logs` `(int: 1000)` - Amount of entries in the Raft Log that a -server can be behind before being considered unhealthy. If this value is too low, -it can cause the cluster to lose quorum if a follower falls behind. This -value only needs to be increased from the default if you have a very high -write load on Vault and you see that it takes a long time to promote new -servers to becoming voters. This is an unlikely scenario and most users -should not modify this value. - -- `last_contact_threshold` `(string "10s")` - Limit on the amount of time a -server can go without leader contact before being considered unhealthy. - -- `server_stabilization_time` `(string "10s")` - Minimum amount of time a server -must be in a healthy state before it can become a voter. Until that happens, -it will be visible as a peer in the cluster, but as a non-voter, meaning it -won't contribute to quorum. - -- `disable_upgrade_migration` `(bool: false)` - Disables automatically upgrading -Vault using autopilot (Enterprise-only) diff --git a/website/content/partials/autopilot/node-types.mdx b/website/content/partials/autopilot/node-types.mdx deleted file mode 100644 index 8ee7c5b9f498..000000000000 --- a/website/content/partials/autopilot/node-types.mdx +++ /dev/null @@ -1,6 +0,0 @@ -#### Enterprise Node Types -- `voter`: The server is a Raft voter and contributing to quorum. -- `read-replica`: The server is not a Raft voter, but receives a replica of all data. -- `zone-voter`: The main Raft voter in a redundancy zone. -- `zone-extra-voter`: An additional Raft voter in a redundancy zone. -- `zone-standby`: A non-voter in a redundancy zone that can be promoted to a voter, if needed. diff --git a/website/content/partials/autopilot/redundancy-zones.mdx b/website/content/partials/autopilot/redundancy-zones.mdx deleted file mode 100644 index e681b1e26baa..000000000000 --- a/website/content/partials/autopilot/redundancy-zones.mdx +++ /dev/null @@ -1,25 +0,0 @@ -The majority of the voting servers in a cluster need to be available to agree on -changes in configuration. If a voting node becomes unavailable and that causes -the cluster to have fewer voting nodes than the quorum size, then Autopilot will not -be able to promote a non-voter to become a voter. This is the **failure tolerance** of -the cluster. Redundancy zones are not able to improve the failure tolerance of a -cluster. - -Say that you have a cluster configured to have 2 redundancy zones and each zone -has 2 servers within it (for total of 4 nodes in the cluster). The quorum size -is 2. If the zone voter in either of the redundancy zones becomes unavailable, -the cluster does not have quorum and is not able to agree on the configuration -change needed to promote the non-voter in the zone into a voter. - -Redundancy zones do improve the **optimistic failure tolerance** of a cluster. -The optimistic failure tolerance is the number of healthy active and back-up -voting servers that can fail gradually without causing an outage. If the Vault -cluster is able to maintain a quorum of voting nodes, then the cluster has the -capability to lose nodes gradually and promote the standby redundancy zone nodes -to take the place of voters. - -For example, consider a cluster that is configured to have 3 redundancy zones -with 2 nodes in each zone. If a voting node becomes unreachable, the zone standby -in that zone is promoted. The cluster then maintains 3 voting nodes with 2 remaining -standbys. The cluster can handle an additional 2 gradual failures before it loses -quorum. diff --git a/website/content/partials/known-issues/aws-auth-external-id.mdx b/website/content/partials/known-issues/aws-auth-external-id.mdx deleted file mode 100644 index e4774161255d..000000000000 --- a/website/content/partials/known-issues/aws-auth-external-id.mdx +++ /dev/null @@ -1,19 +0,0 @@ -### AWS Auth Role configuration requires an external_id - -#### Affected Versions - -- 1.17.0 - 1.17.3 - -#### Issue - -You must set the `external_id` parameter during role configuration, or the Vault -AWS authentication plugin returns a validation error. - -#### Workaround - -To avoid the error during configuration: - -1. Set the `external_id` parameter when configuring AWS authentication plugin - with a valid ID or any string longer than two characters. -1. Configure any desired roles. -1. If you used an arbitrary string, remove the external ID. diff --git a/website/content/partials/known-issues/sync-activation-flags-cache-not-updated.mdx b/website/content/partials/known-issues/sync-activation-flags-cache-not-updated.mdx deleted file mode 100644 index 942146ba08f7..000000000000 --- a/website/content/partials/known-issues/sync-activation-flags-cache-not-updated.mdx +++ /dev/null @@ -1,23 +0,0 @@ -### Cached activation flags for secrets sync on follower nodes are not updated - -#### Affected versions - -- 1.16.0 - 1.16.2 -- 1.17.0 - 1.17.5 - -#### Issue - -Vault 1.16 introduced secrets sync with a one-time flag required to activate the -feature before use. Writing the activation flag to enable secrets sync is forwarded -to leader nodes for storage and distributed to follower nodes, but the in-memory -cache for this flag is not updated on the followers. - -This prevents any secrets sync endpoints (those starting with `sys/sync/`) from -being usable on follower nodes in a cluster. - -#### Workaround - -The cache is force-updated on all nodes when the leader node steps down and the -cluster promotes a new leader. First, activate the secrets sync feature as described -in the [documentation](/vault/docs/sync#activating-the-feature). Then, have the leader node -step down.