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 82c8f74c0c42..3556b87346ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,224 +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.6 -### September 25, 2024 - -CHANGES: - -* core: Bump Go version to 1.22.7 -* secrets/ldap: Update vault-plugin-secrets-openldap to v0.13.1 [[GH-28478](https://github.com/hashicorp/vault/pull/28478)] -* secrets/ssh: Add a flag, `allow_empty_principals` to allow keys or certs to apply to any user/principal. [[GH-28466](https://github.com/hashicorp/vault/pull/28466)] - -IMPROVEMENTS: - -* audit: Internal implementation changes to the audit subsystem which improve relability. [[GH-28286](https://github.com/hashicorp/vault/pull/28286)] -* ui: Remove deprecated `current_billing_period` from dashboard activity log request [[GH-27559](https://github.com/hashicorp/vault/pull/27559)] - -BUG FIXES: - -* auth/aws: Fixed potential panic after step-down and the queue has not repopulated. [[GH-28330](https://github.com/hashicorp/vault/pull/28330)] -* auth/cert: During certificate validation, OCSP requests are debug logged even if Vault's log level is above DEBUG. [[GH-28450](https://github.com/hashicorp/vault/pull/28450)] -* 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: Updated error handling for missing login credentials in AppRole and UserPass auth methods to return a 400 error instead of a 500 error. [[GH-28441](https://github.com/hashicorp/vault/pull/28441)] -* 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)] -* proxy/cache (enterprise): Fixed a data race that could occur while tracking capabilities in Proxy's static secret cache. [[GH-28494](https://github.com/hashicorp/vault/pull/28494)] -* secrets-sync (enterprise): Secondary nodes in a cluster now properly check activation-flags values. -* secrets-sync (enterprise): Validate corresponding GitHub app parameters `app_name` and `installation_id` are set - ## 1.17.5 ## August 30, 2024 @@ -551,31 +333,6 @@ autopilot to fail to discover new server versions and so not trigger an upgrade. * ui: fixed a bug where the replication pages did not update display when navigating between DR and performance [[GH-26325](https://github.com/hashicorp/vault/pull/26325)] * ui: fixes undefined start time in filename for downloaded client count attribution csv [[GH-26485](https://github.com/hashicorp/vault/pull/26485)] -## 1.16.10 Enterprise -### September 25, 2024 - -**Enterprise LTS:** Vault Enterprise 1.16 is a [Long-Term Support (LTS)](https://developer.hashicorp.com/vault/docs/enterprise/lts) release. - -CHANGES: - -* core: Bump Go version to 1.22.7. -* secrets/ssh: Add a flag, `allow_empty_principals` to allow keys or certs to apply to any user/principal. [[GH-28466](https://github.com/hashicorp/vault/pull/28466)] - -IMPROVEMENTS: - -* audit: Internal implementation changes to the audit subsystem which improve relability. [[GH-28286](https://github.com/hashicorp/vault/pull/28286)] -* ui: Remove deprecated `current_billing_period` from dashboard activity log request [[GH-27559](https://github.com/hashicorp/vault/pull/27559)] - -BUG FIXES: - -* auth/aws: Fixed potential panic after step-down and the queue has not repopulated. [[GH-28330](https://github.com/hashicorp/vault/pull/28330)] -* auth/cert: During certificate validation, OCSP requests are debug logged even if Vault's log level is above DEBUG. [[GH-28450](https://github.com/hashicorp/vault/pull/28450)] -* 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: Updated error handling for missing login credentials in AppRole and UserPass auth methods to return a 400 error instead of a 500 error. [[GH-28441](https://github.com/hashicorp/vault/pull/28441)] -* 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)] -* proxy/cache (enterprise): Fixed a data race that could occur while tracking capabilities in Proxy's static secret cache. [[GH-28494](https://github.com/hashicorp/vault/pull/28494)] -* secrets-sync (enterprise): Validate corresponding GitHub app parameters `app_name` and `installation_id` are set - ## 1.16.9 Enterprise ### August 30, 2024 @@ -1178,22 +935,6 @@ leading to failure to complete merkle sync without a full re-index. [[GH-23013]( * ui: remove user_lockout_config settings for unsupported methods [[GH-25867](https://github.com/hashicorp/vault/pull/25867)] * ui: show error from API when seal fails [[GH-23921](https://github.com/hashicorp/vault/pull/23921)] -## 1.15.15 Enterprise -### September 25, 2024 - -CHANGES: - -* core: Bump Go version to 1.22.7. -* secrets/ssh: Add a flag, `allow_empty_principals` to allow keys or certs to apply to any user/principal. [[GH-28466](https://github.com/hashicorp/vault/pull/28466)] - -BUG FIXES: - -* auth/aws: Fixed potential panic after step-down and the queue has not repopulated. [[GH-28330](https://github.com/hashicorp/vault/pull/28330)] -* auth/cert: During certificate validation, OCSP requests are debug logged even if Vault's log level is above DEBUG. [[GH-28450](https://github.com/hashicorp/vault/pull/28450)] -* 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: Updated error handling for missing login credentials in AppRole and UserPass auth methods to return a 400 error instead of a 500 error. [[GH-28441](https://github.com/hashicorp/vault/pull/28441)] -* 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)] - ## 1.15.14 Enterprise ### August 29, 2024 @@ -2594,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/api/auth/approle/LICENSE b/api/auth/approle/LICENSE deleted file mode 100644 index f4f97ee5853a..000000000000 --- a/api/auth/approle/LICENSE +++ /dev/null @@ -1,365 +0,0 @@ -Copyright (c) 2015 HashiCorp, Inc. - -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. "Contributor" - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. "Contributor Version" - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the terms of - a Secondary License. - -1.6. "Executable Form" - - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - - means a work that combines Covered Software with other material, in a - separate file or files, that is not Covered Software. - -1.8. "License" - - means this document. - -1.9. "Licensable" - - means having the right to grant, to the maximum extent possible, whether - at the time of the initial grant or subsequently, any and all of the - rights conveyed by this License. - -1.10. "Modifications" - - means any of the following: - - a. any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. "Patent Claims" of a Contributor - - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the License, - by the making, using, selling, offering for sale, having made, import, - or transfer of either its Contributions or its Contributor Version. - -1.12. "Secondary License" - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. "Source Code Form" - - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, "control" means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution - become effective for each Contribution on the date the Contributor first - distributes such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under - this License. No additional rights or licenses will be implied from the - distribution or licensing of Covered Software under this License. - Notwithstanding Section 2.1(b) above, no patent license is granted by a - Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of - its Contributions. - - This License does not grant any rights in the trademarks, service marks, - or logos of any Contributor (except as may be necessary to comply with - the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this - License (see Section 10.2) or under the terms of a Secondary License (if - permitted under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its - Contributions are its original creation(s) or it has sufficient rights to - grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under - applicable copyright doctrines of fair use, fair dealing, or other - equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under - the terms of this License. You must inform recipients that the Source - Code Form of the Covered Software is governed by the terms of this - License, and how they can obtain a copy of this License. You may not - attempt to alter or restrict the recipients' rights in the Source Code - Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter the - recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for - the Covered Software. If the Larger Work is a combination of Covered - Software with a work governed by one or more Secondary Licenses, and the - Covered Software is not Incompatible With Secondary Licenses, this - License permits You to additionally distribute such Covered Software - under the terms of such Secondary License(s), so that the recipient of - the Larger Work may, at their option, further distribute the Covered - Software under the terms of either this License or such Secondary - License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices - (including copyright notices, patent notices, disclaimers of warranty, or - limitations of liability) contained within the Source Code Form of the - Covered Software, except that You may alter any license notices to the - extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on - behalf of any Contributor. You must make it absolutely clear that any - such warranty, support, indemnity, or liability obligation is offered by - You alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, - judicial order, or regulation then You must: (a) comply with the terms of - this License to the maximum extent possible; and (b) describe the - limitations and the code they affect. Such description must be placed in a - text file included with all distributions of the Covered Software under - this License. Except to the extent prohibited by statute or regulation, - such description must be sufficiently detailed for a recipient of ordinary - skill to be able to understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing - basis, if such Contributor fails to notify You of the non-compliance by - some reasonable means prior to 60 days after You have come back into - compliance. Moreover, Your grants from a particular Contributor are - reinstated on an ongoing basis if such Contributor notifies You of the - non-compliance by some reasonable means, this is the first time You have - received notice of non-compliance with this License from such - Contributor, and You become compliant prior to 30 days after Your receipt - of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, - counter-claims, and cross-claims) alleging that a Contributor Version - directly or indirectly infringes any patent, then the rights granted to - You by any and all Contributors for the Covered Software under Section - 2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an "as is" basis, - without warranty of any kind, either expressed, implied, or statutory, - including, without limitation, warranties that the Covered Software is free - of defects, merchantable, fit for a particular purpose or non-infringing. - The entire risk as to the quality and performance of the Covered Software - is with You. Should any Covered Software prove defective in any respect, - You (not any Contributor) assume the cost of any necessary servicing, - repair, or correction. This disclaimer of warranty constitutes an essential - part of this License. No use of any Covered Software is authorized under - this License except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from - such party's negligence to the extent applicable law prohibits such - limitation. Some jurisdictions do not allow the exclusion or limitation of - incidental or consequential damages, so this exclusion and limitation may - not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts - of a jurisdiction where the defendant maintains its principal place of - business and such litigation shall be governed by laws of that - jurisdiction, without reference to its conflict-of-law provisions. Nothing - in this Section shall prevent a party's ability to bring cross-claims or - counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. Any law or regulation which provides that - the language of a contract shall be construed against the drafter shall not - be used to construe this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version - of the License under which You originally received the Covered Software, - or under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a - modified version of this License if you rename the license and remove - any references to the name of the license steward (except to note that - such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary - Licenses If You choose to distribute Source Code Form that is - Incompatible With Secondary Licenses under the terms of this version of - the License, the notice described in Exhibit B of this License must be - attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, -then You may include the notice in a location (such as a LICENSE file in a -relevant directory) where a recipient would be likely to look for such a -notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice - - This Source Code Form is "Incompatible - With Secondary Licenses", as defined by - the Mozilla Public License, v. 2.0. - diff --git a/api/auth/aws/LICENSE b/api/auth/aws/LICENSE deleted file mode 100644 index f4f97ee5853a..000000000000 --- a/api/auth/aws/LICENSE +++ /dev/null @@ -1,365 +0,0 @@ -Copyright (c) 2015 HashiCorp, Inc. - -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. "Contributor" - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. "Contributor Version" - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the terms of - a Secondary License. - -1.6. "Executable Form" - - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - - means a work that combines Covered Software with other material, in a - separate file or files, that is not Covered Software. - -1.8. "License" - - means this document. - -1.9. "Licensable" - - means having the right to grant, to the maximum extent possible, whether - at the time of the initial grant or subsequently, any and all of the - rights conveyed by this License. - -1.10. "Modifications" - - means any of the following: - - a. any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. "Patent Claims" of a Contributor - - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the License, - by the making, using, selling, offering for sale, having made, import, - or transfer of either its Contributions or its Contributor Version. - -1.12. "Secondary License" - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. "Source Code Form" - - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, "control" means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution - become effective for each Contribution on the date the Contributor first - distributes such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under - this License. No additional rights or licenses will be implied from the - distribution or licensing of Covered Software under this License. - Notwithstanding Section 2.1(b) above, no patent license is granted by a - Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of - its Contributions. - - This License does not grant any rights in the trademarks, service marks, - or logos of any Contributor (except as may be necessary to comply with - the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this - License (see Section 10.2) or under the terms of a Secondary License (if - permitted under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its - Contributions are its original creation(s) or it has sufficient rights to - grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under - applicable copyright doctrines of fair use, fair dealing, or other - equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under - the terms of this License. You must inform recipients that the Source - Code Form of the Covered Software is governed by the terms of this - License, and how they can obtain a copy of this License. You may not - attempt to alter or restrict the recipients' rights in the Source Code - Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter the - recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for - the Covered Software. If the Larger Work is a combination of Covered - Software with a work governed by one or more Secondary Licenses, and the - Covered Software is not Incompatible With Secondary Licenses, this - License permits You to additionally distribute such Covered Software - under the terms of such Secondary License(s), so that the recipient of - the Larger Work may, at their option, further distribute the Covered - Software under the terms of either this License or such Secondary - License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices - (including copyright notices, patent notices, disclaimers of warranty, or - limitations of liability) contained within the Source Code Form of the - Covered Software, except that You may alter any license notices to the - extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on - behalf of any Contributor. You must make it absolutely clear that any - such warranty, support, indemnity, or liability obligation is offered by - You alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, - judicial order, or regulation then You must: (a) comply with the terms of - this License to the maximum extent possible; and (b) describe the - limitations and the code they affect. Such description must be placed in a - text file included with all distributions of the Covered Software under - this License. Except to the extent prohibited by statute or regulation, - such description must be sufficiently detailed for a recipient of ordinary - skill to be able to understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing - basis, if such Contributor fails to notify You of the non-compliance by - some reasonable means prior to 60 days after You have come back into - compliance. Moreover, Your grants from a particular Contributor are - reinstated on an ongoing basis if such Contributor notifies You of the - non-compliance by some reasonable means, this is the first time You have - received notice of non-compliance with this License from such - Contributor, and You become compliant prior to 30 days after Your receipt - of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, - counter-claims, and cross-claims) alleging that a Contributor Version - directly or indirectly infringes any patent, then the rights granted to - You by any and all Contributors for the Covered Software under Section - 2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an "as is" basis, - without warranty of any kind, either expressed, implied, or statutory, - including, without limitation, warranties that the Covered Software is free - of defects, merchantable, fit for a particular purpose or non-infringing. - The entire risk as to the quality and performance of the Covered Software - is with You. Should any Covered Software prove defective in any respect, - You (not any Contributor) assume the cost of any necessary servicing, - repair, or correction. This disclaimer of warranty constitutes an essential - part of this License. No use of any Covered Software is authorized under - this License except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from - such party's negligence to the extent applicable law prohibits such - limitation. Some jurisdictions do not allow the exclusion or limitation of - incidental or consequential damages, so this exclusion and limitation may - not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts - of a jurisdiction where the defendant maintains its principal place of - business and such litigation shall be governed by laws of that - jurisdiction, without reference to its conflict-of-law provisions. Nothing - in this Section shall prevent a party's ability to bring cross-claims or - counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. Any law or regulation which provides that - the language of a contract shall be construed against the drafter shall not - be used to construe this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version - of the License under which You originally received the Covered Software, - or under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a - modified version of this License if you rename the license and remove - any references to the name of the license steward (except to note that - such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary - Licenses If You choose to distribute Source Code Form that is - Incompatible With Secondary Licenses under the terms of this version of - the License, the notice described in Exhibit B of this License must be - attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, -then You may include the notice in a location (such as a LICENSE file in a -relevant directory) where a recipient would be likely to look for such a -notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice - - This Source Code Form is "Incompatible - With Secondary Licenses", as defined by - the Mozilla Public License, v. 2.0. - diff --git a/api/auth/azure/LICENSE b/api/auth/azure/LICENSE deleted file mode 100644 index f4f97ee5853a..000000000000 --- a/api/auth/azure/LICENSE +++ /dev/null @@ -1,365 +0,0 @@ -Copyright (c) 2015 HashiCorp, Inc. - -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. "Contributor" - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. "Contributor Version" - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the terms of - a Secondary License. - -1.6. "Executable Form" - - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - - means a work that combines Covered Software with other material, in a - separate file or files, that is not Covered Software. - -1.8. "License" - - means this document. - -1.9. "Licensable" - - means having the right to grant, to the maximum extent possible, whether - at the time of the initial grant or subsequently, any and all of the - rights conveyed by this License. - -1.10. "Modifications" - - means any of the following: - - a. any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. "Patent Claims" of a Contributor - - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the License, - by the making, using, selling, offering for sale, having made, import, - or transfer of either its Contributions or its Contributor Version. - -1.12. "Secondary License" - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. "Source Code Form" - - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, "control" means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution - become effective for each Contribution on the date the Contributor first - distributes such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under - this License. No additional rights or licenses will be implied from the - distribution or licensing of Covered Software under this License. - Notwithstanding Section 2.1(b) above, no patent license is granted by a - Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of - its Contributions. - - This License does not grant any rights in the trademarks, service marks, - or logos of any Contributor (except as may be necessary to comply with - the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this - License (see Section 10.2) or under the terms of a Secondary License (if - permitted under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its - Contributions are its original creation(s) or it has sufficient rights to - grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under - applicable copyright doctrines of fair use, fair dealing, or other - equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under - the terms of this License. You must inform recipients that the Source - Code Form of the Covered Software is governed by the terms of this - License, and how they can obtain a copy of this License. You may not - attempt to alter or restrict the recipients' rights in the Source Code - Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter the - recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for - the Covered Software. If the Larger Work is a combination of Covered - Software with a work governed by one or more Secondary Licenses, and the - Covered Software is not Incompatible With Secondary Licenses, this - License permits You to additionally distribute such Covered Software - under the terms of such Secondary License(s), so that the recipient of - the Larger Work may, at their option, further distribute the Covered - Software under the terms of either this License or such Secondary - License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices - (including copyright notices, patent notices, disclaimers of warranty, or - limitations of liability) contained within the Source Code Form of the - Covered Software, except that You may alter any license notices to the - extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on - behalf of any Contributor. You must make it absolutely clear that any - such warranty, support, indemnity, or liability obligation is offered by - You alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, - judicial order, or regulation then You must: (a) comply with the terms of - this License to the maximum extent possible; and (b) describe the - limitations and the code they affect. Such description must be placed in a - text file included with all distributions of the Covered Software under - this License. Except to the extent prohibited by statute or regulation, - such description must be sufficiently detailed for a recipient of ordinary - skill to be able to understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing - basis, if such Contributor fails to notify You of the non-compliance by - some reasonable means prior to 60 days after You have come back into - compliance. Moreover, Your grants from a particular Contributor are - reinstated on an ongoing basis if such Contributor notifies You of the - non-compliance by some reasonable means, this is the first time You have - received notice of non-compliance with this License from such - Contributor, and You become compliant prior to 30 days after Your receipt - of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, - counter-claims, and cross-claims) alleging that a Contributor Version - directly or indirectly infringes any patent, then the rights granted to - You by any and all Contributors for the Covered Software under Section - 2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an "as is" basis, - without warranty of any kind, either expressed, implied, or statutory, - including, without limitation, warranties that the Covered Software is free - of defects, merchantable, fit for a particular purpose or non-infringing. - The entire risk as to the quality and performance of the Covered Software - is with You. Should any Covered Software prove defective in any respect, - You (not any Contributor) assume the cost of any necessary servicing, - repair, or correction. This disclaimer of warranty constitutes an essential - part of this License. No use of any Covered Software is authorized under - this License except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from - such party's negligence to the extent applicable law prohibits such - limitation. Some jurisdictions do not allow the exclusion or limitation of - incidental or consequential damages, so this exclusion and limitation may - not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts - of a jurisdiction where the defendant maintains its principal place of - business and such litigation shall be governed by laws of that - jurisdiction, without reference to its conflict-of-law provisions. Nothing - in this Section shall prevent a party's ability to bring cross-claims or - counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. Any law or regulation which provides that - the language of a contract shall be construed against the drafter shall not - be used to construe this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version - of the License under which You originally received the Covered Software, - or under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a - modified version of this License if you rename the license and remove - any references to the name of the license steward (except to note that - such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary - Licenses If You choose to distribute Source Code Form that is - Incompatible With Secondary Licenses under the terms of this version of - the License, the notice described in Exhibit B of this License must be - attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, -then You may include the notice in a location (such as a LICENSE file in a -relevant directory) where a recipient would be likely to look for such a -notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice - - This Source Code Form is "Incompatible - With Secondary Licenses", as defined by - the Mozilla Public License, v. 2.0. - diff --git a/api/auth/gcp/LICENSE b/api/auth/gcp/LICENSE deleted file mode 100644 index f4f97ee5853a..000000000000 --- a/api/auth/gcp/LICENSE +++ /dev/null @@ -1,365 +0,0 @@ -Copyright (c) 2015 HashiCorp, Inc. - -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. "Contributor" - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. "Contributor Version" - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the terms of - a Secondary License. - -1.6. "Executable Form" - - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - - means a work that combines Covered Software with other material, in a - separate file or files, that is not Covered Software. - -1.8. "License" - - means this document. - -1.9. "Licensable" - - means having the right to grant, to the maximum extent possible, whether - at the time of the initial grant or subsequently, any and all of the - rights conveyed by this License. - -1.10. "Modifications" - - means any of the following: - - a. any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. "Patent Claims" of a Contributor - - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the License, - by the making, using, selling, offering for sale, having made, import, - or transfer of either its Contributions or its Contributor Version. - -1.12. "Secondary License" - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. "Source Code Form" - - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, "control" means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution - become effective for each Contribution on the date the Contributor first - distributes such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under - this License. No additional rights or licenses will be implied from the - distribution or licensing of Covered Software under this License. - Notwithstanding Section 2.1(b) above, no patent license is granted by a - Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of - its Contributions. - - This License does not grant any rights in the trademarks, service marks, - or logos of any Contributor (except as may be necessary to comply with - the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this - License (see Section 10.2) or under the terms of a Secondary License (if - permitted under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its - Contributions are its original creation(s) or it has sufficient rights to - grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under - applicable copyright doctrines of fair use, fair dealing, or other - equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under - the terms of this License. You must inform recipients that the Source - Code Form of the Covered Software is governed by the terms of this - License, and how they can obtain a copy of this License. You may not - attempt to alter or restrict the recipients' rights in the Source Code - Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter the - recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for - the Covered Software. If the Larger Work is a combination of Covered - Software with a work governed by one or more Secondary Licenses, and the - Covered Software is not Incompatible With Secondary Licenses, this - License permits You to additionally distribute such Covered Software - under the terms of such Secondary License(s), so that the recipient of - the Larger Work may, at their option, further distribute the Covered - Software under the terms of either this License or such Secondary - License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices - (including copyright notices, patent notices, disclaimers of warranty, or - limitations of liability) contained within the Source Code Form of the - Covered Software, except that You may alter any license notices to the - extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on - behalf of any Contributor. You must make it absolutely clear that any - such warranty, support, indemnity, or liability obligation is offered by - You alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, - judicial order, or regulation then You must: (a) comply with the terms of - this License to the maximum extent possible; and (b) describe the - limitations and the code they affect. Such description must be placed in a - text file included with all distributions of the Covered Software under - this License. Except to the extent prohibited by statute or regulation, - such description must be sufficiently detailed for a recipient of ordinary - skill to be able to understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing - basis, if such Contributor fails to notify You of the non-compliance by - some reasonable means prior to 60 days after You have come back into - compliance. Moreover, Your grants from a particular Contributor are - reinstated on an ongoing basis if such Contributor notifies You of the - non-compliance by some reasonable means, this is the first time You have - received notice of non-compliance with this License from such - Contributor, and You become compliant prior to 30 days after Your receipt - of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, - counter-claims, and cross-claims) alleging that a Contributor Version - directly or indirectly infringes any patent, then the rights granted to - You by any and all Contributors for the Covered Software under Section - 2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an "as is" basis, - without warranty of any kind, either expressed, implied, or statutory, - including, without limitation, warranties that the Covered Software is free - of defects, merchantable, fit for a particular purpose or non-infringing. - The entire risk as to the quality and performance of the Covered Software - is with You. Should any Covered Software prove defective in any respect, - You (not any Contributor) assume the cost of any necessary servicing, - repair, or correction. This disclaimer of warranty constitutes an essential - part of this License. No use of any Covered Software is authorized under - this License except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from - such party's negligence to the extent applicable law prohibits such - limitation. Some jurisdictions do not allow the exclusion or limitation of - incidental or consequential damages, so this exclusion and limitation may - not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts - of a jurisdiction where the defendant maintains its principal place of - business and such litigation shall be governed by laws of that - jurisdiction, without reference to its conflict-of-law provisions. Nothing - in this Section shall prevent a party's ability to bring cross-claims or - counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. Any law or regulation which provides that - the language of a contract shall be construed against the drafter shall not - be used to construe this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version - of the License under which You originally received the Covered Software, - or under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a - modified version of this License if you rename the license and remove - any references to the name of the license steward (except to note that - such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary - Licenses If You choose to distribute Source Code Form that is - Incompatible With Secondary Licenses under the terms of this version of - the License, the notice described in Exhibit B of this License must be - attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, -then You may include the notice in a location (such as a LICENSE file in a -relevant directory) where a recipient would be likely to look for such a -notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice - - This Source Code Form is "Incompatible - With Secondary Licenses", as defined by - the Mozilla Public License, v. 2.0. - diff --git a/api/auth/kubernetes/LICENSE b/api/auth/kubernetes/LICENSE deleted file mode 100644 index f4f97ee5853a..000000000000 --- a/api/auth/kubernetes/LICENSE +++ /dev/null @@ -1,365 +0,0 @@ -Copyright (c) 2015 HashiCorp, Inc. - -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. "Contributor" - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. "Contributor Version" - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the terms of - a Secondary License. - -1.6. "Executable Form" - - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - - means a work that combines Covered Software with other material, in a - separate file or files, that is not Covered Software. - -1.8. "License" - - means this document. - -1.9. "Licensable" - - means having the right to grant, to the maximum extent possible, whether - at the time of the initial grant or subsequently, any and all of the - rights conveyed by this License. - -1.10. "Modifications" - - means any of the following: - - a. any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. "Patent Claims" of a Contributor - - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the License, - by the making, using, selling, offering for sale, having made, import, - or transfer of either its Contributions or its Contributor Version. - -1.12. "Secondary License" - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. "Source Code Form" - - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, "control" means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution - become effective for each Contribution on the date the Contributor first - distributes such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under - this License. No additional rights or licenses will be implied from the - distribution or licensing of Covered Software under this License. - Notwithstanding Section 2.1(b) above, no patent license is granted by a - Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of - its Contributions. - - This License does not grant any rights in the trademarks, service marks, - or logos of any Contributor (except as may be necessary to comply with - the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this - License (see Section 10.2) or under the terms of a Secondary License (if - permitted under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its - Contributions are its original creation(s) or it has sufficient rights to - grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under - applicable copyright doctrines of fair use, fair dealing, or other - equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under - the terms of this License. You must inform recipients that the Source - Code Form of the Covered Software is governed by the terms of this - License, and how they can obtain a copy of this License. You may not - attempt to alter or restrict the recipients' rights in the Source Code - Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter the - recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for - the Covered Software. If the Larger Work is a combination of Covered - Software with a work governed by one or more Secondary Licenses, and the - Covered Software is not Incompatible With Secondary Licenses, this - License permits You to additionally distribute such Covered Software - under the terms of such Secondary License(s), so that the recipient of - the Larger Work may, at their option, further distribute the Covered - Software under the terms of either this License or such Secondary - License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices - (including copyright notices, patent notices, disclaimers of warranty, or - limitations of liability) contained within the Source Code Form of the - Covered Software, except that You may alter any license notices to the - extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on - behalf of any Contributor. You must make it absolutely clear that any - such warranty, support, indemnity, or liability obligation is offered by - You alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, - judicial order, or regulation then You must: (a) comply with the terms of - this License to the maximum extent possible; and (b) describe the - limitations and the code they affect. Such description must be placed in a - text file included with all distributions of the Covered Software under - this License. Except to the extent prohibited by statute or regulation, - such description must be sufficiently detailed for a recipient of ordinary - skill to be able to understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing - basis, if such Contributor fails to notify You of the non-compliance by - some reasonable means prior to 60 days after You have come back into - compliance. Moreover, Your grants from a particular Contributor are - reinstated on an ongoing basis if such Contributor notifies You of the - non-compliance by some reasonable means, this is the first time You have - received notice of non-compliance with this License from such - Contributor, and You become compliant prior to 30 days after Your receipt - of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, - counter-claims, and cross-claims) alleging that a Contributor Version - directly or indirectly infringes any patent, then the rights granted to - You by any and all Contributors for the Covered Software under Section - 2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an "as is" basis, - without warranty of any kind, either expressed, implied, or statutory, - including, without limitation, warranties that the Covered Software is free - of defects, merchantable, fit for a particular purpose or non-infringing. - The entire risk as to the quality and performance of the Covered Software - is with You. Should any Covered Software prove defective in any respect, - You (not any Contributor) assume the cost of any necessary servicing, - repair, or correction. This disclaimer of warranty constitutes an essential - part of this License. No use of any Covered Software is authorized under - this License except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from - such party's negligence to the extent applicable law prohibits such - limitation. Some jurisdictions do not allow the exclusion or limitation of - incidental or consequential damages, so this exclusion and limitation may - not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts - of a jurisdiction where the defendant maintains its principal place of - business and such litigation shall be governed by laws of that - jurisdiction, without reference to its conflict-of-law provisions. Nothing - in this Section shall prevent a party's ability to bring cross-claims or - counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. Any law or regulation which provides that - the language of a contract shall be construed against the drafter shall not - be used to construe this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version - of the License under which You originally received the Covered Software, - or under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a - modified version of this License if you rename the license and remove - any references to the name of the license steward (except to note that - such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary - Licenses If You choose to distribute Source Code Form that is - Incompatible With Secondary Licenses under the terms of this version of - the License, the notice described in Exhibit B of this License must be - attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, -then You may include the notice in a location (such as a LICENSE file in a -relevant directory) where a recipient would be likely to look for such a -notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice - - This Source Code Form is "Incompatible - With Secondary Licenses", as defined by - the Mozilla Public License, v. 2.0. - diff --git a/api/auth/ldap/LICENSE b/api/auth/ldap/LICENSE deleted file mode 100644 index f4f97ee5853a..000000000000 --- a/api/auth/ldap/LICENSE +++ /dev/null @@ -1,365 +0,0 @@ -Copyright (c) 2015 HashiCorp, Inc. - -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. "Contributor" - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. "Contributor Version" - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the terms of - a Secondary License. - -1.6. "Executable Form" - - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - - means a work that combines Covered Software with other material, in a - separate file or files, that is not Covered Software. - -1.8. "License" - - means this document. - -1.9. "Licensable" - - means having the right to grant, to the maximum extent possible, whether - at the time of the initial grant or subsequently, any and all of the - rights conveyed by this License. - -1.10. "Modifications" - - means any of the following: - - a. any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. "Patent Claims" of a Contributor - - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the License, - by the making, using, selling, offering for sale, having made, import, - or transfer of either its Contributions or its Contributor Version. - -1.12. "Secondary License" - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. "Source Code Form" - - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, "control" means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution - become effective for each Contribution on the date the Contributor first - distributes such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under - this License. No additional rights or licenses will be implied from the - distribution or licensing of Covered Software under this License. - Notwithstanding Section 2.1(b) above, no patent license is granted by a - Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of - its Contributions. - - This License does not grant any rights in the trademarks, service marks, - or logos of any Contributor (except as may be necessary to comply with - the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this - License (see Section 10.2) or under the terms of a Secondary License (if - permitted under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its - Contributions are its original creation(s) or it has sufficient rights to - grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under - applicable copyright doctrines of fair use, fair dealing, or other - equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under - the terms of this License. You must inform recipients that the Source - Code Form of the Covered Software is governed by the terms of this - License, and how they can obtain a copy of this License. You may not - attempt to alter or restrict the recipients' rights in the Source Code - Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter the - recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for - the Covered Software. If the Larger Work is a combination of Covered - Software with a work governed by one or more Secondary Licenses, and the - Covered Software is not Incompatible With Secondary Licenses, this - License permits You to additionally distribute such Covered Software - under the terms of such Secondary License(s), so that the recipient of - the Larger Work may, at their option, further distribute the Covered - Software under the terms of either this License or such Secondary - License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices - (including copyright notices, patent notices, disclaimers of warranty, or - limitations of liability) contained within the Source Code Form of the - Covered Software, except that You may alter any license notices to the - extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on - behalf of any Contributor. You must make it absolutely clear that any - such warranty, support, indemnity, or liability obligation is offered by - You alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, - judicial order, or regulation then You must: (a) comply with the terms of - this License to the maximum extent possible; and (b) describe the - limitations and the code they affect. Such description must be placed in a - text file included with all distributions of the Covered Software under - this License. Except to the extent prohibited by statute or regulation, - such description must be sufficiently detailed for a recipient of ordinary - skill to be able to understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing - basis, if such Contributor fails to notify You of the non-compliance by - some reasonable means prior to 60 days after You have come back into - compliance. Moreover, Your grants from a particular Contributor are - reinstated on an ongoing basis if such Contributor notifies You of the - non-compliance by some reasonable means, this is the first time You have - received notice of non-compliance with this License from such - Contributor, and You become compliant prior to 30 days after Your receipt - of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, - counter-claims, and cross-claims) alleging that a Contributor Version - directly or indirectly infringes any patent, then the rights granted to - You by any and all Contributors for the Covered Software under Section - 2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an "as is" basis, - without warranty of any kind, either expressed, implied, or statutory, - including, without limitation, warranties that the Covered Software is free - of defects, merchantable, fit for a particular purpose or non-infringing. - The entire risk as to the quality and performance of the Covered Software - is with You. Should any Covered Software prove defective in any respect, - You (not any Contributor) assume the cost of any necessary servicing, - repair, or correction. This disclaimer of warranty constitutes an essential - part of this License. No use of any Covered Software is authorized under - this License except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from - such party's negligence to the extent applicable law prohibits such - limitation. Some jurisdictions do not allow the exclusion or limitation of - incidental or consequential damages, so this exclusion and limitation may - not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts - of a jurisdiction where the defendant maintains its principal place of - business and such litigation shall be governed by laws of that - jurisdiction, without reference to its conflict-of-law provisions. Nothing - in this Section shall prevent a party's ability to bring cross-claims or - counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. Any law or regulation which provides that - the language of a contract shall be construed against the drafter shall not - be used to construe this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version - of the License under which You originally received the Covered Software, - or under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a - modified version of this License if you rename the license and remove - any references to the name of the license steward (except to note that - such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary - Licenses If You choose to distribute Source Code Form that is - Incompatible With Secondary Licenses under the terms of this version of - the License, the notice described in Exhibit B of this License must be - attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, -then You may include the notice in a location (such as a LICENSE file in a -relevant directory) where a recipient would be likely to look for such a -notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice - - This Source Code Form is "Incompatible - With Secondary Licenses", as defined by - the Mozilla Public License, v. 2.0. - diff --git a/api/auth/userpass/LICENSE b/api/auth/userpass/LICENSE deleted file mode 100644 index f4f97ee5853a..000000000000 --- a/api/auth/userpass/LICENSE +++ /dev/null @@ -1,365 +0,0 @@ -Copyright (c) 2015 HashiCorp, Inc. - -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. "Contributor" - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. "Contributor Version" - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the terms of - a Secondary License. - -1.6. "Executable Form" - - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - - means a work that combines Covered Software with other material, in a - separate file or files, that is not Covered Software. - -1.8. "License" - - means this document. - -1.9. "Licensable" - - means having the right to grant, to the maximum extent possible, whether - at the time of the initial grant or subsequently, any and all of the - rights conveyed by this License. - -1.10. "Modifications" - - means any of the following: - - a. any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. "Patent Claims" of a Contributor - - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the License, - by the making, using, selling, offering for sale, having made, import, - or transfer of either its Contributions or its Contributor Version. - -1.12. "Secondary License" - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. "Source Code Form" - - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, "control" means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution - become effective for each Contribution on the date the Contributor first - distributes such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under - this License. No additional rights or licenses will be implied from the - distribution or licensing of Covered Software under this License. - Notwithstanding Section 2.1(b) above, no patent license is granted by a - Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of - its Contributions. - - This License does not grant any rights in the trademarks, service marks, - or logos of any Contributor (except as may be necessary to comply with - the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this - License (see Section 10.2) or under the terms of a Secondary License (if - permitted under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its - Contributions are its original creation(s) or it has sufficient rights to - grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under - applicable copyright doctrines of fair use, fair dealing, or other - equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under - the terms of this License. You must inform recipients that the Source - Code Form of the Covered Software is governed by the terms of this - License, and how they can obtain a copy of this License. You may not - attempt to alter or restrict the recipients' rights in the Source Code - Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter the - recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for - the Covered Software. If the Larger Work is a combination of Covered - Software with a work governed by one or more Secondary Licenses, and the - Covered Software is not Incompatible With Secondary Licenses, this - License permits You to additionally distribute such Covered Software - under the terms of such Secondary License(s), so that the recipient of - the Larger Work may, at their option, further distribute the Covered - Software under the terms of either this License or such Secondary - License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices - (including copyright notices, patent notices, disclaimers of warranty, or - limitations of liability) contained within the Source Code Form of the - Covered Software, except that You may alter any license notices to the - extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on - behalf of any Contributor. You must make it absolutely clear that any - such warranty, support, indemnity, or liability obligation is offered by - You alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, - judicial order, or regulation then You must: (a) comply with the terms of - this License to the maximum extent possible; and (b) describe the - limitations and the code they affect. Such description must be placed in a - text file included with all distributions of the Covered Software under - this License. Except to the extent prohibited by statute or regulation, - such description must be sufficiently detailed for a recipient of ordinary - skill to be able to understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing - basis, if such Contributor fails to notify You of the non-compliance by - some reasonable means prior to 60 days after You have come back into - compliance. Moreover, Your grants from a particular Contributor are - reinstated on an ongoing basis if such Contributor notifies You of the - non-compliance by some reasonable means, this is the first time You have - received notice of non-compliance with this License from such - Contributor, and You become compliant prior to 30 days after Your receipt - of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, - counter-claims, and cross-claims) alleging that a Contributor Version - directly or indirectly infringes any patent, then the rights granted to - You by any and all Contributors for the Covered Software under Section - 2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an "as is" basis, - without warranty of any kind, either expressed, implied, or statutory, - including, without limitation, warranties that the Covered Software is free - of defects, merchantable, fit for a particular purpose or non-infringing. - The entire risk as to the quality and performance of the Covered Software - is with You. Should any Covered Software prove defective in any respect, - You (not any Contributor) assume the cost of any necessary servicing, - repair, or correction. This disclaimer of warranty constitutes an essential - part of this License. No use of any Covered Software is authorized under - this License except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from - such party's negligence to the extent applicable law prohibits such - limitation. Some jurisdictions do not allow the exclusion or limitation of - incidental or consequential damages, so this exclusion and limitation may - not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts - of a jurisdiction where the defendant maintains its principal place of - business and such litigation shall be governed by laws of that - jurisdiction, without reference to its conflict-of-law provisions. Nothing - in this Section shall prevent a party's ability to bring cross-claims or - counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. Any law or regulation which provides that - the language of a contract shall be construed against the drafter shall not - be used to construe this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version - of the License under which You originally received the Covered Software, - or under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a - modified version of this License if you rename the license and remove - any references to the name of the license steward (except to note that - such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary - Licenses If You choose to distribute Source Code Form that is - Incompatible With Secondary Licenses under the terms of this version of - the License, the notice described in Exhibit B of this License must be - attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, -then You may include the notice in a location (such as a LICENSE file in a -relevant directory) where a recipient would be likely to look for such a -notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice - - This Source Code Form is "Incompatible - With Secondary Licenses", as defined by - the Mozilla Public License, v. 2.0. - 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/18615.txt b/changelog/18615.txt deleted file mode 100644 index 2aa4b32a4cb0..000000000000 --- a/changelog/18615.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:bug -core: fix issue when attempting to re-bootstrap HA when using Raft as HA but not storage -``` \ No newline at end of file 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/27920.txt b/changelog/27920.txt deleted file mode 100644 index 6cb687731630..000000000000 --- a/changelog/27920.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:bug -core/api: Added missing LICENSE files to API sub-modules to ensure Go module tooling recognizes MPL-2.0 license. -``` 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/28126.txt b/changelog/28126.txt deleted file mode 100644 index 5dfb7a864955..000000000000 --- a/changelog/28126.txt +++ /dev/null @@ -1,6 +0,0 @@ -```release-note:improvement -auto-auth/cert: support watching changes on certificate/key files and notifying the auth handler when `enable_reauth_on_new_credentials` is enabled. -``` -```release-note:improvement -auto-auth: support new config option `enable_reauth_on_new_credentials`, supporting re-authentication when receiving new credential on certain auto-auth types -``` 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/changelog/28479.txt b/changelog/28479.txt deleted file mode 100644 index 4045d48837ec..000000000000 --- a/changelog/28479.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:change -secrets/openldap: Update plugin to v0.14.1 -``` diff --git a/command/agent/config/config.go b/command/agent/config/config.go index 7b9a942824ec..d1597cece8fa 100644 --- a/command/agent/config/config.go +++ b/command/agent/config/config.go @@ -129,6 +129,8 @@ type AutoAuth struct { Method *Method `hcl:"-"` Sinks []*Sink `hcl:"sinks"` + // NOTE: This is unsupported outside of testing and may disappear at any + // time. EnableReauthOnNewCredentials bool `hcl:"enable_reauth_on_new_credentials"` } diff --git a/command/agentproxyshared/auth/cert/cert.go b/command/agentproxyshared/auth/cert/cert.go index b9410c10b9bd..fabe9a6365fb 100644 --- a/command/agentproxyshared/auth/cert/cert.go +++ b/command/agentproxyshared/auth/cert/cert.go @@ -5,21 +5,14 @@ package cert import ( "context" - "crypto/tls" - "encoding/hex" "errors" "fmt" "net/http" - "os" - "sync" - "time" "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/command/agentproxyshared/auth" "github.com/hashicorp/vault/sdk/helper/consts" - "github.com/hashicorp/vault/sdk/helper/parseutil" - "golang.org/x/crypto/blake2b" ) type certMethod struct { @@ -34,14 +27,6 @@ type certMethod struct { // Client is the cached client to use if cert info was provided. client *api.Client - - stopCh chan struct{} - doneCh chan struct{} - credSuccessGate chan struct{} - ticker *time.Ticker - once *sync.Once - credsFound chan struct{} - latestHash *string } var _ auth.AuthMethodWithClient = &certMethod{} @@ -53,17 +38,10 @@ func NewCertAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) { // Not concerned if the conf.Config is empty as the 'name' // parameter is optional when using TLS Auth - lastHash := "" + c := &certMethod{ logger: conf.Logger, mountPath: conf.MountPath, - - stopCh: make(chan struct{}), - doneCh: make(chan struct{}), - credSuccessGate: make(chan struct{}), - once: new(sync.Once), - credsFound: make(chan struct{}), - latestHash: &lastHash, } if conf.Config != nil { @@ -109,20 +87,6 @@ func NewCertAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) { } } - if c.isCertConfigured() && c.reload { - reloadPeriod := time.Minute - if reloadPeriodRaw, ok := conf.Config["reload_period"]; ok { - period, err := parseutil.ParseDurationSecond(reloadPeriodRaw) - if err != nil { - return nil, fmt.Errorf("error parsing 'reload_period' value: %w", err) - } - reloadPeriod = period - } - c.ticker = time.NewTicker(reloadPeriod) - - go c.runWatcher() - } - return c, nil } @@ -139,26 +103,12 @@ func (c *certMethod) Authenticate(_ context.Context, client *api.Client) (string } func (c *certMethod) NewCreds() chan struct{} { - return c.credsFound + return nil } -func (c *certMethod) CredSuccess() { - c.once.Do(func() { - close(c.credSuccessGate) - }) -} +func (c *certMethod) CredSuccess() {} -func (c *certMethod) Shutdown() { - if c.isCertConfigured() && c.reload { - c.ticker.Stop() - close(c.stopCh) - <-c.doneCh - } -} - -func (c *certMethod) isCertConfigured() bool { - return c.caCert != "" || (c.clientKey != "" && c.clientCert != "") -} +func (c *certMethod) Shutdown() {} // AuthClient uses the existing client's address and returns a new client with // the auto-auth method's certificate information if that's provided in its @@ -168,7 +118,7 @@ func (c *certMethod) AuthClient(client *api.Client) (*api.Client, error) { clientToAuth := client - if c.isCertConfigured() { + if c.caCert != "" || (c.clientKey != "" && c.clientCert != "") { // Return cached client if present if c.client != nil && !c.reload { return c.client, nil @@ -191,13 +141,6 @@ func (c *certMethod) AuthClient(client *api.Client) (*api.Client, error) { return nil, err } - // set last hash if load it successfully - if hash, err := c.hashCert(c.clientCert, c.clientKey, c.caCert); err != nil { - return nil, err - } else { - c.latestHash = &hash - } - var err error clientToAuth, err = api.NewClient(config) if err != nil { @@ -213,95 +156,3 @@ func (c *certMethod) AuthClient(client *api.Client) (*api.Client, error) { return clientToAuth, nil } - -// hashCert returns reads and verifies the given cert/key pair and return the hashing result -// in string representation. Otherwise, returns an error. -// As the pair of cert/key and ca cert are optional because they may be configured externally -// or use system default ca bundle, empty paths are simply skipped. -// A valid hashing result means: -// 1. All presented files are readable. -// 2. The client cert/key pair is valid if presented. -// 3. Any presented file in this bundle changed, the hash changes. -func (c *certMethod) hashCert(certFile, keyFile, caFile string) (string, error) { - var buf []byte - if certFile != "" && keyFile != "" { - certPEMBlock, err := os.ReadFile(certFile) - if err != nil { - return "", err - } - c.logger.Debug("Loaded cert file", "file", certFile, "length", len(certPEMBlock)) - - keyPEMBlock, err := os.ReadFile(keyFile) - if err != nil { - return "", err - } - c.logger.Debug("Loaded key file", "file", keyFile, "length", len(keyPEMBlock)) - - // verify - _, err = tls.X509KeyPair(certPEMBlock, keyPEMBlock) - if err != nil { - return "", err - } - c.logger.Debug("The cert/key are valid") - buf = append(certPEMBlock, keyPEMBlock...) - } - - if caFile != "" { - data, err := os.ReadFile(caFile) - if err != nil { - return "", err - } - c.logger.Debug("Loaded ca file", "file", caFile, "length", len(data)) - buf = append(buf, data...) - } - - sum := blake2b.Sum256(buf) - return hex.EncodeToString(sum[:]), nil -} - -// runWatcher uses polling instead of inotify to sense the changes on the cert/key/ca files. -// The reason not to use inotify: -// 1. To not miss any changes, we need to watch the directory instead of files when using inotify. -// 2. These files are not frequently changed/renewed, and they don't need to be reloaded immediately after renewal. -// 3. Some network based filesystem and FUSE don't support inotify. -func (c *certMethod) runWatcher() { - defer close(c.doneCh) - - select { - case <-c.stopCh: - return - - case <-c.credSuccessGate: - // We only start the next loop once we're initially successful, - // since at startup Authenticate will be called, and we don't want - // to end up immediately re-authenticating by having found a new - // value - } - - for { - changed := false - select { - case <-c.stopCh: - return - - case <-c.ticker.C: - c.logger.Debug("Checking if files changed", "cert", c.clientCert, "key", c.clientKey) - hash, err := c.hashCert(c.clientCert, c.clientKey, c.caCert) - // ignore errors in watcher - if err == nil { - c.logger.Debug("hash before/after", "new", hash, "old", *c.latestHash) - changed = *c.latestHash != hash - } else { - c.logger.Warn("hash failed for cert/key files", "err", err) - } - } - - if changed { - c.logger.Info("The cert/key files changed") - select { - case c.credsFound <- struct{}{}: - case <-c.stopCh: - } - } - } -} diff --git a/command/agentproxyshared/auth/cert/cert_test.go b/command/agentproxyshared/auth/cert/cert_test.go index 7abf856e8426..6a7e4f779e9c 100644 --- a/command/agentproxyshared/auth/cert/cert_test.go +++ b/command/agentproxyshared/auth/cert/cert_test.go @@ -5,13 +5,10 @@ package cert import ( "context" - "fmt" "os" "path" - "path/filepath" "reflect" "testing" - "time" "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/api" @@ -31,7 +28,6 @@ func TestCertAuthMethod_Authenticate(t *testing.T) { if err != nil { t.Fatal(err) } - defer method.Shutdown() client, err := api.NewClient(nil) if err != nil { @@ -69,7 +65,6 @@ func TestCertAuthMethod_AuthClient_withoutCerts(t *testing.T) { if err != nil { t.Fatal(err) } - defer method.Shutdown() client, err := api.NewClient(api.DefaultConfig()) if err != nil { @@ -113,7 +108,6 @@ func TestCertAuthMethod_AuthClient_withCerts(t *testing.T) { if err != nil { t.Fatal(err) } - defer method.Shutdown() client, err := api.NewClient(nil) if err != nil { @@ -140,38 +134,29 @@ func TestCertAuthMethod_AuthClient_withCerts(t *testing.T) { } } -func copyFile(from, to string) error { - data, err := os.ReadFile(from) +func TestCertAuthMethod_AuthClient_withCertsReload(t *testing.T) { + clientCert, err := os.Open("./test-fixtures/keys/cert.pem") if err != nil { - return err + t.Fatal(err) } - return os.WriteFile(to, data, 0o600) -} + defer clientCert.Close() -// TestCertAuthMethod_AuthClient_withCertsReload makes the file change and ensures the cert auth method deliver the event. -func TestCertAuthMethod_AuthClient_withCertsReload(t *testing.T) { - // Initial the cert/key pair to temp path - certPath := filepath.Join(os.TempDir(), "app.crt") - keyPath := filepath.Join(os.TempDir(), "app.key") - if err := copyFile("./test-fixtures/keys/cert.pem", certPath); err != nil { - t.Fatal("copy cert file failed", err) - } - defer os.Remove(certPath) - if err := copyFile("./test-fixtures/keys/key.pem", keyPath); err != nil { - t.Fatal("copy key file failed", err) + clientKey, err := os.Open("./test-fixtures/keys/key.pem") + if err != nil { + t.Fatal(err) } - defer os.Remove(keyPath) + + defer clientKey.Close() config := &auth.AuthConfig{ Logger: hclog.NewNullLogger(), MountPath: "cert-test", Config: map[string]interface{}{ - "name": "with-certs-reloaded", - "client_cert": certPath, - "client_key": keyPath, - "reload": true, - "reload_period": 1, + "name": "with-certs-reloaded", + "client_cert": clientCert.Name(), + "client_key": clientKey.Name(), + "reload": true, }, } @@ -179,7 +164,6 @@ func TestCertAuthMethod_AuthClient_withCertsReload(t *testing.T) { if err != nil { t.Fatal(err) } - defer method.Shutdown() client, err := api.NewClient(nil) if err != nil { @@ -204,113 +188,4 @@ func TestCertAuthMethod_AuthClient_withCertsReload(t *testing.T) { if reloadedClient == clientToUse { t.Fatal("expected client from AuthClient to return back a new client") } - - method.CredSuccess() - // Only make a change to the cert file, it doesn't match the key file so the client won't pick and load them. - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - if err = copyFile("./test-fixtures/keys/cert1.pem", certPath); err != nil { - t.Fatal("update cert file failed", err) - } - - select { - case <-ctx.Done(): - case <-method.NewCreds(): - cancel() - t.Fatal("malformed cert should not be observed as a change") - } - - // Make a change to the key file and now they are good to be picked. - if err = copyFile("./test-fixtures/keys/key1.pem", keyPath); err != nil { - t.Fatal("update key file failed", err) - } - ctx, cancel = context.WithTimeout(context.Background(), 3*time.Second) - select { - case <-ctx.Done(): - t.Fatal("failed to watch the cert change: timeout") - case <-method.NewCreds(): - cancel() - } -} - -// TestCertAuthMethod_hashCert_withEmptyPaths tests hashCert() if it works well with optional options. -func TestCertAuthMethod_hashCert_withEmptyPaths(t *testing.T) { - c := &certMethod{ - logger: hclog.NewNullLogger(), - } - - // It skips empty file paths - sum, err := c.hashCert("", "", "") - if sum == "" || err != nil { - t.Fatal("hashCert() should skip empty file paths and succeed.") - } - emptySum := sum - - // Only present ca cert - sum, err = c.hashCert("", "", "./test-fixtures/root/rootcacert.pem") - if sum == "" || err != nil { - t.Fatal("hashCert() should succeed when only present ca cert.") - } - - // Only present client cert/key - sum, err = c.hashCert("./test-fixtures/keys/cert.pem", "./test-fixtures/keys/key.pem", "") - if sum == "" || err != nil { - fmt.Println(sum, err) - t.Fatal("hashCert() should succeed when only present client cert/key.") - } - - // The client cert/key should be presented together or will be skipped - sum, err = c.hashCert("./test-fixtures/keys/cert.pem", "", "") - if sum == "" || err != nil { - t.Fatal("hashCert() should succeed when only present client cert.") - } else if sum != emptySum { - t.Fatal("hashCert() should skip the client cert/key when only present client cert.") - } -} - -// TestCertAuthMethod_hashCert_withInvalidClientCert adds test cases for invalid input for hashCert(). -func TestCertAuthMethod_hashCert_withInvalidClientCert(t *testing.T) { - c := &certMethod{ - logger: hclog.NewNullLogger(), - } - - // With mismatched cert/key pair - sum, err := c.hashCert("./test-fixtures/keys/cert1.pem", "./test-fixtures/keys/key.pem", "") - if sum != "" || err == nil { - t.Fatal("hashCert() should fail with invalid client cert.") - } - - // With non-existed paths - sum, err = c.hashCert("./test-fixtures/keys/cert2.pem", "./test-fixtures/keys/key.pem", "") - if sum != "" || err == nil { - t.Fatal("hashCert() should fail with non-existed client cert path.") - } -} - -// TestCertAuthMethod_hashCert_withChange tests hashCert() if it detects changes from both client cert/key and ca cert. -func TestCertAuthMethod_hashCert_withChange(t *testing.T) { - c := &certMethod{ - logger: hclog.NewNullLogger(), - } - - // A good first case. - sum, err := c.hashCert("./test-fixtures/keys/cert.pem", "./test-fixtures/keys/key.pem", "./test-fixtures/root/rootcacert.pem") - if sum == "" || err != nil { - t.Fatal("hashCert() shouldn't fail with a valid pair of cert/key.") - } - - // Only change the ca cert from the first case. - sum1, err := c.hashCert("./test-fixtures/keys/cert.pem", "./test-fixtures/keys/key.pem", "./test-fixtures/keys/cert.pem") - if sum1 == "" || err != nil { - t.Fatal("hashCert() shouldn't fail with valid pair of cert/key.") - } else if sum == sum1 { - t.Fatal("The hash should be different with a different ca cert.") - } - - // Only change the cert/key pair from the first case. - sum2, err := c.hashCert("./test-fixtures/keys/cert1.pem", "./test-fixtures/keys/key1.pem", "./test-fixtures/root/rootcacert.pem") - if sum2 == "" || err != nil { - t.Fatal("hashCert() shouldn't fail with a valid cert/key pair") - } else if sum == sum2 || sum1 == sum2 { - t.Fatal("The hash should be different with a different pair of cert/key.") - } } diff --git a/command/agentproxyshared/auth/cert/test-fixtures/keys/cert1.pem b/command/agentproxyshared/auth/cert/test-fixtures/keys/cert1.pem deleted file mode 100644 index 01afb2157e37..000000000000 --- a/command/agentproxyshared/auth/cert/test-fixtures/keys/cert1.pem +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICrTCCAZUCCQDDXho7UXdaIjANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDEwtl -eGFtcGxlLmNvbTAeFw0yNDA4MTcwOTE0MDRaFw0zNDA4MTUwOTE0MDRaMBsxGTAX -BgNVBAMMEGNlcnQuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDDSMAi8aL1XLCRrPl8KjJcH/pJe9QJtzUIU3T9tfj+Eq8yMUbFu+so -ec+knsxTi5zN7wq1/t9B9tIvDVG0C9T7BbhX2dYPNC1oY7DtdI3KqA76Z78v533Y -p/WFMHn9X1v0g7qOHm9Y7V6oHg7m+ICq84fORbmfgNW/tPNqTJRU4wyzlIPw1Toi -9awHMZHZmbjUwFgSQ8TOXgZfWo1ZmbOFY2epBIRCapsYpJgwKXy1UjIfQIQ6e6xm -KbKQ/IIeuufo5U8vYV91nGNOVkieeGQ8vmVa1f/oyFfChCRR+aLCqbUGfJWzdicm -eqyQVmPqJxTFuh7WMq+cOX5A068sYj0FAgMBAAEwDQYJKoZIhvcNAQELBQADggEB -AFtUgRS+OZXmDmhIiaw4OrMruz3N2PCjWo/y+rK5gECuApGv7may3k9E65yRUvBb -Ch68y1TMr+7J0MDl1CIbJUnLJkmcID+IvLVS3hVJ9H0raP6epDRvfkM3Xc/RwNgS -PS1H1K8oxDPoo4an1yc6UoKng5KCAUYN+8dR9iVpCIPzRm0LSDIqMyamxoeNLfrO -Nta+sKu1iS/MHy/MVLqyRDwTP2DnfYJTvhQDK5Y5bi7Chkv7g3ug/o2RZ38rRiRd -Os90dDmTCgnYBSJtfKWF5gSnzP+OTs6Yb6KOIY7gLY/r1PBPabSuAnRMS/iTi6tq -l91Cs+vnv6HNcZsGphoQJq8= ------END CERTIFICATE----- diff --git a/command/agentproxyshared/auth/cert/test-fixtures/keys/key1.pem b/command/agentproxyshared/auth/cert/test-fixtures/keys/key1.pem deleted file mode 100644 index ea07f86416f2..000000000000 --- a/command/agentproxyshared/auth/cert/test-fixtures/keys/key1.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDSMAi8aL1XLCR -rPl8KjJcH/pJe9QJtzUIU3T9tfj+Eq8yMUbFu+soec+knsxTi5zN7wq1/t9B9tIv -DVG0C9T7BbhX2dYPNC1oY7DtdI3KqA76Z78v533Yp/WFMHn9X1v0g7qOHm9Y7V6o -Hg7m+ICq84fORbmfgNW/tPNqTJRU4wyzlIPw1Toi9awHMZHZmbjUwFgSQ8TOXgZf -Wo1ZmbOFY2epBIRCapsYpJgwKXy1UjIfQIQ6e6xmKbKQ/IIeuufo5U8vYV91nGNO -VkieeGQ8vmVa1f/oyFfChCRR+aLCqbUGfJWzdicmeqyQVmPqJxTFuh7WMq+cOX5A -068sYj0FAgMBAAECggEAP1vIMs4xL+g9xVXYsAdExYz+eH77gZd2Vlg1eedjfJN1 -UhSYwKjCmCRFUUTQSD7gxhPLZtblepJpCSkKHB9Gn5bwg1hC0jX8kYTer3wEUP8L -tQSaDCHQO83qo6bhvWoF/KQMj/Wh7Lk+3864yQlRPaW7pxoKKozzTLqZyyBDc/KR -YaUco+9NFqClHd/TRehoykYa7OvNVJjBDxTnnxijE0d5w83rP+wDJczhe/Xn/0f1 -Q7JFa4NpKmLEXj93GZiteloE80AbVnMiIemGB8ZZGHcySiib3wzuLk32dLS8zguU -gp3E1FhL5xI7gsS7ClA/S6+tK1c46FzQYuIA105tAQKBgQDhnoxCW0N8xV5I1tje -Q1AW+tMVyO6yaREFuoz6tE6AZ1drywyHdqia/QNbXkdllYVQDMlszQ3tmZ8RT/+5 -NdJ+LnNag8T6PaN3AtXAf0uveCL1et5ffWuRicesJCCJ10ESFQaVccZEqhJhtnQk -giqICNHV0dWIEVsZGi5R4sA0wQKBgQDdlICpZud2SLrSx00Fb6TfZumxWjDwDb9D -avoQJb376pg1qpAh53hUJbHWPlspeG/k24J0oRrnb3aln8QS21qVov90YllEWwnO -xebYgdjvfOIZ1b8vJ2/UkfLX9Xa9KuzvGpv4BSNOZ8UNHI6Dj/eFmWP+q/a3vzJT -rEgoC1xFRQKBgQCGkZtUxLxnAg1vYn3ta7asTiRyvOrqDNKzWQZXTg34dirlRzGM -5pBACSLkb0Ika98c1NObCl8BVXxTxiRfoqOO0UPKPAfTvcnu5Qj7DLHm0cAALK3P -xK3RG521pcKmlHXiRBouLrM0J0BZeYqib+TQSHpnjwVOaBOu0DfKbXV4wQKBgAaU -VEWzcogGnNWJaXYR3Jltmt7TSMS4A8fis04rcLq8OozNZb47+0y0WdV8wIQ4uUnY -YsVHy1635pQAbHgK32O2FVPFX9UxxtbG9ZXUNTbXRHdz61thFmb/dnCHL2FqluJ6 -rcrtjCDV3/oFsQ2jBryG03tKa+cE3F+zq+jUfYbpAoGAauV0h6kqS5mF+sa3F+I1 -zIZ7k81r5csZXkgQ6HphIAvo5NSv7H1jeSkGbZmg29irReggZLsy6nU4B4ZVt1p9 -GIsLgJfkCkHT+Vf0ipygAwFnbEUKqs6A/D0EUtAF2Oc7nVl0NIX+9LmEx7Dwl34i -bTTPVgw5bid08eiN46NN9J4= ------END PRIVATE KEY----- diff --git a/command/proxy/config/config.go b/command/proxy/config/config.go index e0a9080cc38e..2f5f5b320181 100644 --- a/command/proxy/config/config.go +++ b/command/proxy/config/config.go @@ -117,6 +117,8 @@ type AutoAuth struct { Method *Method `hcl:"-"` Sinks []*Sink `hcl:"sinks"` + // NOTE: This is unsupported outside of testing and may disappear at any + // time. EnableReauthOnNewCredentials bool `hcl:"enable_reauth_on_new_credentials"` } diff --git a/command/server.go b/command/server.go index 52013aab2bbc..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,8 +1707,8 @@ func (c *ServerCommand) Run(args []string) int { // Notify systemd that the server has completed reloading config c.notifySystemd(systemd.SdNotifyReady) + case <-c.SigUSR2Ch: - c.logger.Info("Received SIGUSR2, dumping goroutines. This is expected behavior. Vault continues to run normally.") logWriter := c.logger.StandardWriter(&hclog.StandardLoggerOptions{}) pprof.Lookup("goroutine").WriteTo(logWriter, 2) @@ -1772,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/go.mod b/go.mod index 069716b0ece7..7fdf614a7e33 100644 --- a/go.mod +++ b/go.mod @@ -150,7 +150,7 @@ require ( github.com/hashicorp/vault-plugin-secrets-kubernetes v0.9.0 github.com/hashicorp/vault-plugin-secrets-kv v0.20.0 github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.13.0 - github.com/hashicorp/vault-plugin-secrets-openldap v0.14.1 + github.com/hashicorp/vault-plugin-secrets-openldap v0.14.0 github.com/hashicorp/vault-plugin-secrets-terraform v0.10.0 github.com/hashicorp/vault-testing-stepwise v0.3.1 github.com/hashicorp/vault/api v1.15.0 diff --git a/go.sum b/go.sum index 23962f2bba1f..61d64936cf07 100644 --- a/go.sum +++ b/go.sum @@ -1593,8 +1593,8 @@ github.com/hashicorp/vault-plugin-secrets-kv v0.20.0 h1:p1RVmd4x1rgGK0tN8DDu21J2 github.com/hashicorp/vault-plugin-secrets-kv v0.20.0/go.mod h1:bCpMggD3Z0+H+3dOmTCoQjBHC53jA08lPqOLmFrHBi8= github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.13.0 h1:BeDS7luTeOW0braIbtuyairFF8SEz7k3nvi9e+mJ2Ok= github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.13.0/go.mod h1:sprde+S70PBIbgOLUAKDxR+xNF714ksBBVh77O3hnWc= -github.com/hashicorp/vault-plugin-secrets-openldap v0.14.1 h1:5l7/83OCZsHL1PYkFJd8xjLtKQEz3vpbIbaEzHL5qeU= -github.com/hashicorp/vault-plugin-secrets-openldap v0.14.1/go.mod h1:wqOf/QJqrrNXjnm0eLUnm5Ju9s/LIZUl6wEKmnFL9Uo= +github.com/hashicorp/vault-plugin-secrets-openldap v0.14.0 h1:hhuh8FwP2jJ6dlOKOO/wDmwt1eEmhy0Hw0OjdkioP5c= +github.com/hashicorp/vault-plugin-secrets-openldap v0.14.0/go.mod h1:wqOf/QJqrrNXjnm0eLUnm5Ju9s/LIZUl6wEKmnFL9Uo= github.com/hashicorp/vault-plugin-secrets-terraform v0.10.0 h1:YzOJrpuDRNrw5SQ4i7IEjedF40I/7ejupQy+gAyQ6Zg= github.com/hashicorp/vault-plugin-secrets-terraform v0.10.0/go.mod h1:j2nbB//xAQMD+5JivVDalwDEyzJY3AWzKIkw6k65xJQ= github.com/hashicorp/vault-testing-stepwise v0.3.1 h1:SqItnMWOOknQfJJR49Fps34ZfBMWSqBFFTx6NoTHzNw= 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/generated-item-list.js b/ui/app/adapters/generated-item-list.js index 2d2efd8a9ea7..5c5d3f2e9145 100644 --- a/ui/app/adapters/generated-item-list.js +++ b/ui/app/adapters/generated-item-list.js @@ -4,112 +4,44 @@ */ import ApplicationAdapter from './application'; +import { task } from 'ember-concurrency'; import { service } from '@ember/service'; -import { sanitizePath } from 'core/utils/sanitize-path'; -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import { tracked } from '@glimmer/tracking'; -export default class GeneratedItemListAdapter extends ApplicationAdapter { - @service store; - namespace = 'v1'; +export default ApplicationAdapter.extend({ + store: service(), + namespace: 'v1', + urlForItem() {}, + dynamicApiPath: '', - // these items are set by calling getNewAdapter in the path-help service. - @tracked apiPath = ''; - paths = {}; + getDynamicApiPath: task(function* (id) { + // TODO: remove yield at some point. + const result = yield this.store.peekRecord('auth-method', id); + this.dynamicApiPath = result.apiPath; + return; + }), - // These are the paths used for the adapter actions - get getPath() { - return this.paths.getPath || ''; - } - get createPath() { - return this.paths.createPath || ''; - } - get deletePath() { - return this.paths.deletePath || ''; - } - - getDynamicApiPath(id) { - const result = this.store.peekRecord('auth-method', id); - this.apiPath = result.apiPath; - return result.apiPath; - } - - async fetchByQuery(store, query, isList) { + fetchByQuery: task(function* (store, query, isList) { const { id } = query; - const payload = {}; + const data = {}; if (isList) { - payload.list = true; + data.list = true; + yield this.getDynamicApiPath.perform(id); } - const path = isList ? this.getDynamicApiPath(id) : ''; - const resp = await this.ajax(this.urlForItem(id, isList, path), 'GET', { data: payload }); - const data = { - id, - method: id, - }; - return { ...resp, ...data }; - } + return this.ajax(this.urlForItem(id, isList, this.dynamicApiPath), 'GET', { data }).then((resp) => { + const data = { + id, + method: id, + }; + return { ...resp, ...data }; + }); + }), query(store, type, query) { - return this.fetchByQuery(store, query, true); - } + return this.fetchByQuery.perform(store, query, true); + }, queryRecord(store, type, query) { - return this.fetchByQuery(store, query); - } - - urlForItem(id, isList, dynamicApiPath) { - const itemType = sanitizePath(this.getPath); - let url; - id = encodePath(id); - // the apiPath changes when you switch between routes but the apiPath variable does not unless the model is reloaded - // overwrite apiPath if dynamicApiPath exist. - // dynamicApiPath comes from the model->adapter - let apiPath = this.apiPath; - if (dynamicApiPath) { - apiPath = dynamicApiPath; - } - // isList indicates whether we are viewing the list page - // of a top-level item such as userpass - if (isList) { - url = `${this.buildURL()}/${apiPath}${itemType}/`; - } else { - // build the URL for the show page of a nested item - // such as a userpass group - url = `${this.buildURL()}/${apiPath}${itemType}/${id}`; - } - - return url; - } - - urlForQueryRecord(id, modelName) { - return this.urlForItem(id, modelName); - } - - urlForUpdateRecord(id) { - const itemType = this.createPath.slice(1, this.createPath.indexOf('{') - 1); - return `${this.buildURL()}/${this.apiPath}${itemType}/${id}`; - } - - urlForCreateRecord(modelType, snapshot) { - const id = snapshot.record.mutableId; // computed property that returns either id or private settable _id value - const path = this.createPath.slice(1, this.createPath.indexOf('{') - 1); - return `${this.buildURL()}/${this.apiPath}${path}/${id}`; - } - - urlForDeleteRecord(id) { - const path = this.deletePath.slice(1, this.deletePath.indexOf('{') - 1); - return `${this.buildURL()}/${this.apiPath}${path}/${id}`; - } - - createRecord(store, type, snapshot) { - return super.createRecord(...arguments).then((response) => { - // if the server does not return an id and one has not been set on the model we need to set it manually from the mutableId value - if (!response?.id && !snapshot.record.id) { - snapshot.record.id = snapshot.record.mutableId; - snapshot.id = snapshot.record.id; - } - return response; - }); - } -} + return this.fetchByQuery.perform(store, query); + }, +}); diff --git a/ui/app/adapters/kmip/role.js b/ui/app/adapters/kmip/role.js index 4023ed0f7627..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/model-helpers/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/components/database-role-setting-form.js b/ui/app/components/database-role-setting-form.js index 4bad5e0921f5..de49188ff45b 100644 --- a/ui/app/components/database-role-setting-form.js +++ b/ui/app/components/database-role-setting-form.js @@ -4,7 +4,7 @@ */ import Component from '@glimmer/component'; -import { getStatementFields, getRoleFields } from '../utils/model-helpers/database-helpers'; +import { getStatementFields, getRoleFields } from '../utils/database-helpers'; /** * @module DatabaseRoleSettingForm diff --git a/ui/app/components/secret-create-or-update.js b/ui/app/components/secret-create-or-update.js index 2aa0a7a09d6f..53e3593c2ed2 100644 --- a/ui/app/components/secret-create-or-update.js +++ b/ui/app/components/secret-create-or-update.js @@ -35,7 +35,7 @@ import { service } from '@ember/service'; import { tracked } from '@glimmer/tracking'; import { isBlank, isNone } from '@ember/utils'; import { task, waitForEvent } from 'ember-concurrency'; -import { WHITESPACE_WARNING } from 'vault/utils/model-helpers/validators'; +import { WHITESPACE_WARNING } from 'vault/utils/validators'; const LIST_ROUTE = 'vault.cluster.secrets.backend.list'; const LIST_ROOT_ROUTE = 'vault.cluster.secrets.backend.list-root'; 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/decorators/model-validations.js b/ui/app/decorators/model-validations.js index c28e0620ea78..6be78a132c9c 100644 --- a/ui/app/decorators/model-validations.js +++ b/ui/app/decorators/model-validations.js @@ -4,7 +4,7 @@ */ /* eslint-disable no-console */ -import validators from 'vault/utils/model-helpers/validators'; +import validators from 'vault/utils/validators'; import { get } from '@ember/object'; // see documentation at ui/docs/model-validations.md for detailed usage information 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/auth-method.js b/ui/app/models/auth-method.js index cf11df86434b..1d3ad89dba17 100644 --- a/ui/app/models/auth-method.js +++ b/ui/app/models/auth-method.js @@ -12,7 +12,7 @@ import { allMethods } from 'vault/helpers/mountable-auth-methods'; import lazyCapabilities from 'vault/macros/lazy-capabilities'; import { action } from '@ember/object'; import { camelize } from '@ember/string'; -import { WHITESPACE_WARNING } from 'vault/utils/model-helpers/validators'; +import { WHITESPACE_WARNING } from 'vault/utils/validators'; const validations = { path: [ diff --git a/ui/app/models/database/connection.js b/ui/app/models/database/connection.js index e19585f2b84d..fc1a595bcc46 100644 --- a/ui/app/models/database/connection.js +++ b/ui/app/models/database/connection.js @@ -8,7 +8,7 @@ import { computed } from '@ember/object'; import { alias, or } from '@ember/object/computed'; import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; import fieldToAttrs, { expandAttributeMeta } from 'vault/utils/field-to-attrs'; -import { AVAILABLE_PLUGIN_TYPES } from '../../utils/model-helpers/database-helpers'; +import { AVAILABLE_PLUGIN_TYPES } from '../../utils/database-helpers'; /** * fieldsToGroups helper fn diff --git a/ui/app/models/database/role.js b/ui/app/models/database/role.js index 79f6fbd727a7..cbf1facfdb7c 100644 --- a/ui/app/models/database/role.js +++ b/ui/app/models/database/role.js @@ -8,7 +8,7 @@ import { computed } from '@ember/object'; import { alias } from '@ember/object/computed'; import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; import { expandAttributeMeta } from 'vault/utils/field-to-attrs'; -import { getRoleFields } from 'vault/utils/model-helpers/database-helpers'; +import { getRoleFields } from 'vault/utils/database-helpers'; export default Model.extend({ idPrefix: 'role/', diff --git a/ui/app/models/generated-item.js b/ui/app/models/generated-item.js deleted file mode 100644 index 81aa161a7343..000000000000 --- a/ui/app/models/generated-item.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model from '@ember-data/model'; -import { tracked } from '@glimmer/tracking'; - -// This model is used for OpenApi-generated models in path-help service's getNewModel method -export default class GeneratedItemModel extends Model { - allFields = []; - - @tracked _id; - get mutableId() { - return this._id || this.id; - } - set mutableId(value) { - this._id = value; - } - - get fieldGroups() { - const groups = { - default: [], - }; - const fieldGroups = []; - this.constructor.eachAttribute((name, attr) => { - // if the attr comes in with a fieldGroup from OpenAPI, - if (attr.options.fieldGroup) { - if (groups[attr.options.fieldGroup]) { - groups[attr.options.fieldGroup].push(attr); - } else { - groups[attr.options.fieldGroup] = [attr]; - } - } else { - // otherwise just add that attr to the default group - groups.default.push(attr); - } - }); - for (const group in groups) { - fieldGroups.push({ [group]: groups[group] }); - } - return fieldGroups; - } -} diff --git a/ui/app/models/kmip/role.js b/ui/app/models/kmip/role.js index 2fbace484fa3..8083acd7b331 100644 --- a/ui/app/models/kmip/role.js +++ b/ui/app/models/kmip/role.js @@ -4,39 +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/model-helpers/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', @@ -49,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, @@ -64,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/models/kv/data.js b/ui/app/models/kv/data.js index 19b07fac9554..2617847eef20 100644 --- a/ui/app/models/kv/data.js +++ b/ui/app/models/kv/data.js @@ -8,7 +8,7 @@ import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; import { withModelValidations } from 'vault/decorators/model-validations'; import { withFormFields } from 'vault/decorators/model-form-fields'; import { isDeleted } from 'kv/utils/kv-deleted'; -import { WHITESPACE_WARNING } from 'vault/utils/model-helpers/validators'; +import { WHITESPACE_WARNING } from 'vault/utils/validators'; /* sample response { diff --git a/ui/app/models/secret-engine.js b/ui/app/models/secret-engine.js index 86d497281e57..efd58261e4b5 100644 --- a/ui/app/models/secret-engine.js +++ b/ui/app/models/secret-engine.js @@ -10,7 +10,7 @@ import { withModelValidations } from 'vault/decorators/model-validations'; import { withExpandedAttributes } from 'vault/decorators/model-expanded-attributes'; import { supportedSecretBackends } from 'vault/helpers/supported-secret-backends'; import { isAddonEngine, allEngines } from 'vault/helpers/mountable-secret-engines'; -import { WHITESPACE_WARNING } from 'vault/utils/model-helpers/validators'; +import { WHITESPACE_WARNING } from 'vault/utils/validators'; const LINKED_BACKENDS = supportedSecretBackends(); diff --git a/ui/app/serializers/database/connection.js b/ui/app/serializers/database/connection.js index 92951ccf5da8..96aaae8a0de9 100644 --- a/ui/app/serializers/database/connection.js +++ b/ui/app/serializers/database/connection.js @@ -4,7 +4,7 @@ */ import RESTSerializer from '@ember-data/serializer/rest'; -import { AVAILABLE_PLUGIN_TYPES } from '../../utils/model-helpers/database-helpers'; +import { AVAILABLE_PLUGIN_TYPES } from '../../utils/database-helpers'; export default RESTSerializer.extend({ primaryKey: 'name', diff --git a/ui/app/services/path-help.js b/ui/app/services/path-help.js index 3d05344a93de..07fc64416b01 100644 --- a/ui/app/services/path-help.js +++ b/ui/app/services/path-help.js @@ -8,112 +8,63 @@ shape of data at a specific path to hydrate a model with attrs it has less (or no) information about. */ -import Model, { attr } from '@ember-data/model'; +import Model from '@ember-data/model'; import Service from '@ember/service'; +import { encodePath } from 'vault/utils/path-encoding-helpers'; import { getOwner } from '@ember/owner'; +import { expandOpenApiProps, combineAttributes } from 'vault/utils/openapi-to-attrs'; +import fieldToAttrs from 'vault/utils/field-to-attrs'; import { resolve, reject } from 'rsvp'; import { debug } from '@ember/debug'; import { capitalize } from '@ember/string'; import { computed } from '@ember/object'; // eslint-disable-line +import { withModelValidations } from 'vault/decorators/model-validations'; +import generatedItemAdapter from 'vault/adapters/generated-item-list'; +import { sanitizePath } from 'core/utils/sanitize-path'; import { filterPathsByItemType, pathToHelpUrlSegment, reducePathsByPathName, getHelpUrlForModel, - combineOpenApiAttrs, - expandOpenApiProps, } from 'vault/utils/openapi-helpers'; -import GeneratedItemModel from 'vault/models/generated-item'; -import GeneratedItemListAdapter from 'vault/adapters/generated-item-list'; +import { isPresent } from '@ember/utils'; -export default class PathHelpService extends Service { +export default Service.extend({ + attrs: null, + dynamicApiPath: '', ajax(url, options = {}) { const appAdapter = getOwner(this).lookup(`adapter:application`); const { data } = options; return appAdapter.ajax(url, 'GET', { data, }); - } - - /** - * Registers new ModelClass at specified model type, and busts cache - */ - _registerModel(owner, NewKlass, modelType, isNew = false) { - const store = owner.lookup('service:store'); - // bust cache in ember's registry - if (!isNew) { - owner.unregister('model:' + modelType); - } - owner.register('model:' + modelType, NewKlass); - - // bust cache in EmberData's model lookup - delete store._modelFactoryCache[modelType]; - - // bust cache in schema service - const schemas = store.getSchemaDefinitionService?.(); - if (schemas) { - delete schemas._relationshipsDefCache[modelType]; - delete schemas._attributesDefCache[modelType]; - } - } - - /** - * upgradeModelSchema takes an existing ModelClass and hydrates it with the passed attributes - * @param {ModelClass} Klass model class retrieved with store.modelFor(modelType) - * @param {Attribute[]} attrs array of attributes {name, type, options} - * @returns new ModelClass extended from passed one, with the passed attributes added - */ - _upgradeModelSchema(Klass, attrs, newFields) { - // extending the class will ensure that static schema lookups regenerate - const NewKlass = class extends Klass {}; - - for (const { name, type, options } of attrs) { - const decorator = attr(type, options); - const descriptor = decorator(NewKlass.prototype, name, {}); - Object.defineProperty(NewKlass.prototype, name, descriptor); - } - - // newFields is used in combineFieldGroups within various models - if (newFields) { - NewKlass.prototype.newFields = newFields; - } - - // Ensure this class doesn't get re-hydrated - NewKlass.merged = true; - - return NewKlass; - } + }, /** * hydrateModel instantiates models which use OpenAPI partially * @param {string} modelType path for model, eg pki/role * @param {string} backend path, which will be used for the generated helpUrl - * @returns void - as side effect, re-registers model via upgradeModelSchema + * @returns void - as side effect, registers model via registerNewModelWithProps */ - async hydrateModel(modelType, backend) { + hydrateModel(modelType, backend) { const owner = getOwner(this); + const modelName = `model:${modelType}`; + + const modelFactory = owner.factoryFor(modelName); const helpUrl = getHelpUrlForModel(modelType, backend); - const store = owner.lookup('service:store'); - const Klass = store.modelFor(modelType); - if (Klass?.merged || !helpUrl) { - // if the model is already merged, we don't need to do anything - return resolve(); + if (!modelFactory) { + throw new Error(`modelFactory for ${modelType} not found -- use getNewModel instead.`); } - debug(`Hydrating model ${modelType} at backend ${backend}`); - - // fetch props from openAPI - const props = await this.getProps(helpUrl); - // combine existing attributes with openAPI data - const { attrs, newFields } = combineOpenApiAttrs(Klass.attributes, props); - debug(`${modelType} has ${newFields.length} new fields: ${newFields.join(', ')}`); - // hydrate model - const HydratedKlass = this._upgradeModelSchema(Klass, attrs, newFields); - - this._registerModel(owner, HydratedKlass, modelType); - } + debug(`Model factory found for ${modelType}`); + const newModel = modelFactory.class; + if (newModel.merged || !helpUrl) { + return resolve(); + } + return this.registerNewModelWithProps(helpUrl, backend, newModel, modelName); + }, /** * getNewModel instantiates models which use OpenAPI to generate the model fully @@ -121,7 +72,7 @@ export default class PathHelpService extends Service { * @param {string} backend * @param {string} apiPath this method will call getPaths and build submodels for item types * @param {*} itemType (optional) used in getPaths for additional models - * @returns void - as side effect, registers model via registerNewModelWithAttrs + * @returns void - as side effect, registers model via registerNewModelWithProps */ getNewModel(modelType, backend, apiPath, itemType) { const owner = getOwner(this); @@ -166,13 +117,13 @@ export default class PathHelpService extends Service { const helpUrl = `/v1/${apiPath}${path.slice(1)}?help=true`; pathInfo.paths = paths; newModel = newModel.extend({ paths: pathInfo }); - return this.registerNewModelWithAttrs(helpUrl, modelType); + return this.registerNewModelWithProps(helpUrl, backend, newModel, modelName); }) .catch((err) => { // TODO: we should handle the error better here console.error(err); // eslint-disable-line }); - } + }, /** * getPaths is used to fetch all the openAPI paths available for an auth method, @@ -201,16 +152,16 @@ export default class PathHelpService extends Service { itemID, }); }); - } + }, // Makes a call to grab the OpenAPI document. // Returns relevant information from OpenAPI // as determined by the expandOpenApiProps util - getProps(helpUrl) { + getProps(helpUrl, backend) { // add name of thing you want - debug(`Fetching schema properties from ${helpUrl}`); + debug(`Fetching schema properties for ${backend} from ${helpUrl}`); - return this.ajax(helpUrl).then((help) => { + return this.ajax(helpUrl, backend).then((help) => { // paths is an array but it will have a single entry // for the scope we're in const path = Object.keys(help.openapi.paths)[0]; // do this or look at name @@ -246,16 +197,17 @@ export default class PathHelpService extends Service { } else if (schema.properties) { props = schema.properties; } - // put url params (e.g. {name}, {role}) at the front of the props list + // put url params (e.g. {name}, {role}) + // at the front of the props list const newProps = { ...paramProp, ...props }; return expandOpenApiProps(newProps); }); - } + }, getNewAdapter(pathInfo, itemType) { // we need list and create paths to set the correct urls for actions const paths = filterPathsByItemType(pathInfo, itemType); - const { apiPath } = pathInfo; + let { apiPath } = pathInfo; const getPath = paths.find((path) => path.operations.includes('get')); // the action might be "Generate" or something like that so we'll grab the first post endpoint if there @@ -264,28 +216,140 @@ export default class PathHelpService extends Service { const createPath = paths.find((path) => path.action === 'Create' || path.operations.includes('post')); const deletePath = paths.find((path) => path.operations.includes('delete')); - return class NewAdapter extends GeneratedItemListAdapter { - apiPath = apiPath; + return generatedItemAdapter.extend({ + urlForItem(id, isList, dynamicApiPath) { + const itemType = sanitizePath(getPath.path); + let url; + id = encodePath(id); + // the apiPath changes when you switch between routes but the apiPath variable does not unless the model is reloaded + // overwrite apiPath if dynamicApiPath exist. + // dynamicApiPath comes from the model->adapter + if (dynamicApiPath) { + apiPath = dynamicApiPath; + } + // isList indicates whether we are viewing the list page + // of a top-level item such as userpass + if (isList) { + url = `${this.buildURL()}/${apiPath}${itemType}/`; + } else { + // build the URL for the show page of a nested item + // such as a userpass group + url = `${this.buildURL()}/${apiPath}${itemType}/${id}`; + } - paths = { - createPath: createPath?.path, - deletePath: deletePath?.path, - getPath: getPath?.path, - }; + return url; + }, + + urlForQueryRecord(id, modelName) { + return this.urlForItem(id, modelName); + }, + + urlForUpdateRecord(id) { + const itemType = createPath.path.slice(1, createPath.path.indexOf('{') - 1); + return `${this.buildURL()}/${apiPath}${itemType}/${id}`; + }, + + urlForCreateRecord(modelType, snapshot) { + const id = snapshot.record.mutableId; // computed property that returns either id or private settable _id value + const path = createPath.path.slice(1, createPath.path.indexOf('{') - 1); + return `${this.buildURL()}/${apiPath}${path}/${id}`; + }, + + urlForDeleteRecord(id) { + const path = deletePath.path.slice(1, deletePath.path.indexOf('{') - 1); + return `${this.buildURL()}/${apiPath}${path}/${id}`; + }, + + createRecord(store, type, snapshot) { + return this._super(...arguments).then((response) => { + // if the server does not return an id and one has not been set on the model we need to set it manually from the mutableId value + if (!response?.id && !snapshot.record.id) { + snapshot.record.id = snapshot.record.mutableId; + snapshot.id = snapshot.record.id; + } + return response; + }); + }, + }); + }, + + registerNewModelWithProps(helpUrl, backend, newModel, modelName) { + return this.getProps(helpUrl, backend).then((props) => { + const { attrs, newFields } = combineAttributes(newModel.attributes, props); + const owner = getOwner(this); + newModel = newModel.extend(attrs, { newFields }); + // if our newModel doesn't have fieldGroups already + // we need to create them + try { + // Initialize prototype to access field groups + let fieldGroups = newModel.proto().fieldGroups; + if (!fieldGroups) { + debug(`Constructing fieldGroups for ${backend}`); + fieldGroups = this.getFieldGroups(newModel); + newModel = newModel.extend({ fieldGroups }); + // Build and add validations on model + // NOTE: For initial phase, initialize validations only for user pass auth + if (backend === 'userpass') { + const validations = { + password: [ + { + validator(model) { + return ( + !(isPresent(model.password) && isPresent(model.passwordHash)) && + (isPresent(model.password) || isPresent(model.passwordHash)) + ); + }, + message: 'You must provide either password or password hash, but not both.', + }, + ], + }; + @withModelValidations(validations) + class GeneratedItemModel extends newModel {} + newModel = GeneratedItemModel; + } + } + } catch (err) { + // eat the error, fieldGroups is computed in the model definition + } + // attempting to set the id prop on a model will trigger an error + // this computed will be used in place of the the id fieldValue -- see openapi-to-attrs + newModel.reopen({ + mutableId: computed('id', '_id', { + get() { + return this._id || this.id; + }, + set(key, value) { + return (this._id = value); + }, + }), + }); + newModel.merged = true; + owner.unregister(modelName); + owner.register(modelName, newModel); + }); + }, + getFieldGroups(newModel) { + const groups = { + default: [], }; - } - - /** - * registerNewModelWithAttrs takes the helpUrl of the given model type, - * fetches props, and registers the model hydrated with the provided attrs - * @param {string} helpUrl like /v1/auth/userpass2/users/example?help=true - * @param {string} modelType like generated-user-userpass - */ - async registerNewModelWithAttrs(helpUrl, modelType) { - const owner = getOwner(this); - const props = await this.getProps(helpUrl); - const { attrs, newFields } = combineOpenApiAttrs(new Map(), props); - const NewKlass = this._upgradeModelSchema(GeneratedItemModel, attrs, newFields); - this._registerModel(owner, NewKlass, modelType, true); - } -} + const fieldGroups = []; + newModel.attributes.forEach((attr) => { + // if the attr comes in with a fieldGroup from OpenAPI, + // add it to that group + if (attr.options.fieldGroup) { + if (groups[attr.options.fieldGroup]) { + groups[attr.options.fieldGroup].push(attr.name); + } else { + groups[attr.options.fieldGroup] = [attr.name]; + } + } else { + // otherwise just add that attr to the default group + groups.default.push(attr.name); + } + }); + for (const group in groups) { + fieldGroups.push({ [group]: groups[group] }); + } + return fieldToAttrs(newModel, fieldGroups); + }, +}); 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/templates/vault/cluster/access/mfa/enforcements/enforcement/index.hbs b/ui/app/templates/vault/cluster/access/mfa/enforcements/enforcement/index.hbs index 46900af36feb..f84bd9eb9860 100644 --- a/ui/app/templates/vault/cluster/access/mfa/enforcements/enforcement/index.hbs +++ b/ui/app/templates/vault/cluster/access/mfa/enforcements/enforcement/index.hbs @@ -24,7 +24,6 @@ @route="vault.cluster.access.mfa.enforcements.enforcement" @query={{hash tab="targets"}} data-test-tab="targets" - @model={{this.model}} > Targets @@ -32,7 +31,6 @@ @route="vault.cluster.access.mfa.enforcements.enforcement" @query={{hash tab="methods"}} data-test-tab="methods" - @model={{this.model}} > Methods diff --git a/ui/app/utils/model-helpers/database-helpers.js b/ui/app/utils/database-helpers.js similarity index 100% rename from ui/app/utils/model-helpers/database-helpers.js rename to ui/app/utils/database-helpers.js diff --git a/ui/app/utils/model-helpers/kmip-role-fields.js b/ui/app/utils/model-helpers/kmip-role-fields.js deleted file mode 100644 index 3503ad58f449..000000000000 --- a/ui/app/utils/model-helpers/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/app/utils/openapi-helpers.ts b/ui/app/utils/openapi-helpers.ts index 91675bf580da..115548c394b5 100644 --- a/ui/app/utils/openapi-helpers.ts +++ b/ui/app/utils/openapi-helpers.ts @@ -3,10 +3,11 @@ * SPDX-License-Identifier: BUSL-1.1 */ -import { debug } from '@ember/debug'; -import { camelize, capitalize, dasherize } from '@ember/string'; +import { dasherize } from '@ember/string'; import { singularize } from 'ember-inflector'; +// TODO: Consolidate with openapi-to-attrs once it's typescript + interface Path { path: string; itemType: string; @@ -166,200 +167,3 @@ export function getHelpUrlForModel(modelType: string, backend: string) { if (!urlFn) return null; return urlFn(backend); } - -interface Attribute { - name: string; - type: string | undefined; - options: { - editType?: string; - fieldGroup?: string; - fieldValue?: string; - label?: string; - readonly?: boolean; - }; -} - -interface OpenApiProp { - description: string; - type: string; - 'x-vault-displayAttrs': { - name: string; - value: string | number; - group: string; - sensitive: boolean; - editType?: string; - description?: string; - }; - items?: { type: string }; - format?: string; - isId?: boolean; - deprecated?: boolean; - enum?: string[]; -} -interface MixedAttr { - type?: string; - helpText?: string; - editType?: string; - fieldGroup: string; - fieldValue?: string; - label?: string; - readonly?: boolean; - possibleValues?: string[]; - defaultValue?: string | number | (() => string | number); - sensitive?: boolean; - readOnly?: boolean; - [key: string]: unknown; -} - -export const expandOpenApiProps = function (props: Record): Record { - const attrs: Record = {}; - // expand all attributes - for (const propName in props) { - const prop = props[propName]; - if (!prop) continue; - let { description, items, type, format, isId, deprecated } = prop; - if (deprecated === true) { - continue; - } - let { - name, - value, - group, - sensitive, - editType, - description: displayDescription, - } = prop['x-vault-displayAttrs'] || {}; - - if (type === 'integer') { - type = 'number'; - } - - if (displayDescription) { - description = displayDescription; - } - - editType = editType || type; - - if (format === 'seconds' || format === 'duration') { - editType = 'ttl'; - } else if (items) { - editType = items.type + capitalize(type); - } - - const attrDefn: MixedAttr = { - editType, - helpText: description, - possibleValues: prop['enum'], - fieldValue: isId ? 'mutableId' : undefined, - fieldGroup: group || 'default', - readOnly: isId, - defaultValue: value || undefined, - }; - - if (type === 'object' && !!value) { - attrDefn.defaultValue = () => { - return value; - }; - } - - if (sensitive) { - attrDefn.sensitive = true; - } - - // only set a label if we have one from OpenAPI - // otherwise the propName will be humanized by the form-field component - if (name) { - attrDefn.label = name; - } - - // ttls write as a string and read as a number - // so setting type on them runs the wrong transform - if (editType !== 'ttl' && type !== 'array') { - attrDefn.type = type; - } - - // loop to remove empty vals - for (const attrProp in attrDefn) { - if (attrDefn[attrProp] == null) { - delete attrDefn[attrProp]; - } - } - attrs[camelize(propName)] = attrDefn; - } - return attrs; -}; - -/** - * combineOpenApiAttrs takes attributes defined on an existing models - * and adds in the attributes found on an OpenAPI response. The values - * defined on the model should take precedence so we can overwrite - * attributes from OpenAPI. - */ -export const combineOpenApiAttrs = function ( - oldAttrs: Map, - openApiProps: Record -) { - const allAttrs: Record = {}; - const attrsArray: Attribute[] = []; - const newFields: string[] = []; - - // First iterate over all the existing attrs and combine with recieved props, if they exist - oldAttrs.forEach(function (oldAttr, name) { - const attr: Attribute = { name, type: oldAttr.type, options: oldAttr.options }; - const openApiProp = openApiProps[name]; - if (openApiProp) { - const { type, ...options } = openApiProp; - // TODO: previous behavior took the openApi type no matter what - attr.type = oldAttr.type ?? type; - if (oldAttr.type && type && type !== oldAttr.type) { - debug(`mismatched type for ${name} -- ${type} vs ${oldAttr.type}`); - } - attr.options = { ...options, ...oldAttr.options }; - } - attrsArray.push(attr); - // add to all attrs so we skip in the next part - allAttrs[name] = true; - }); - - // then iterate over all the new props and add them if they haven't already been accounted for - for (const name in openApiProps) { - // iterate over each - if (allAttrs[name]) { - continue; - } else { - const prop = openApiProps[name]; - if (prop) { - const { type, ...options } = prop; - newFields.push(name); - attrsArray.push({ name, type, options }); - } - } - } - return { attrs: attrsArray, newFields }; -}; - -// interface FieldGroups { -// default: string[]; -// [key: string]: string[]; -// } - -// export const combineFieldGroups = function ( -// currentGroups: Array>, -// newFields: string[], -// excludedFields: string[] -// ) { -// console.log({ currentGroups, newFields, excludedFields }); -// let allFields: string[] = []; -// for (const group of currentGroups) { -// const fields = Object.values(group)[0] || []; -// allFields = allFields.concat(fields); -// } -// const otherFields = newFields.filter((field) => { -// return !allFields.includes(field) && !excludedFields.includes(field); -// }); -// if (otherFields.length) { -// currentGroups[0].default = currentGroups[0].default.concat(otherFields); -// } - -// return currentGroups; -// }; diff --git a/ui/app/utils/openapi-to-attrs.js b/ui/app/utils/openapi-to-attrs.js index 899a8946d18e..31406b47e117 100644 --- a/ui/app/utils/openapi-to-attrs.js +++ b/ui/app/utils/openapi-to-attrs.js @@ -3,14 +3,119 @@ * SPDX-License-Identifier: BUSL-1.1 */ -/** - * combineFieldGroups takes the newFields returned from OpenAPI and adds them to the default field group - * if they are not already accounted for in other field groups - * @param {Record[]} currentGroups Field groups, as an array of objects like: [{ default: [] }, { 'TLS options': [] }] - * @param {string[]} newFields - * @param {string[]} excludedFields - * @returns modified currentGroups - */ +import { attr } from '@ember-data/model'; +import { camelize, capitalize } from '@ember/string'; + +export const expandOpenApiProps = function (props) { + const attrs = {}; + // expand all attributes + for (const propName in props) { + const prop = props[propName]; + let { description, items, type, format, isId, deprecated } = prop; + if (deprecated === true) { + continue; + } + let { + name, + value, + group, + sensitive, + editType, + description: displayDescription, + } = prop['x-vault-displayAttrs'] || {}; + + if (type === 'integer') { + type = 'number'; + } + + if (displayDescription) { + description = displayDescription; + } + + editType = editType || type; + + if (format === 'seconds' || format === 'duration') { + editType = 'ttl'; + } else if (items) { + editType = items.type + capitalize(type); + } + + const attrDefn = { + editType, + helpText: description, + possibleValues: prop['enum'], + fieldValue: isId ? 'mutableId' : null, + fieldGroup: group || 'default', + readOnly: isId, + defaultValue: value || null, + }; + + if (type === 'object' && !!value) { + attrDefn.defaultValue = () => { + return value; + }; + } + + if (sensitive) { + attrDefn.sensitive = true; + } + + // only set a label if we have one from OpenAPI + // otherwise the propName will be humanized by the form-field component + if (name) { + attrDefn.label = name; + } + + // ttls write as a string and read as a number + // so setting type on them runs the wrong transform + if (editType !== 'ttl' && type !== 'array') { + attrDefn.type = type; + } + + // loop to remove empty vals + for (const attrProp in attrDefn) { + if (attrDefn[attrProp] == null) { + delete attrDefn[attrProp]; + } + } + attrs[camelize(propName)] = attrDefn; + } + return attrs; +}; + +export const combineAttributes = function (oldAttrs, newProps) { + const newAttrs = {}; + const newFields = []; + if (oldAttrs) { + oldAttrs.forEach(function (value, name) { + if (newProps[name]) { + newAttrs[name] = attr(newProps[name].type, { ...newProps[name], ...value.options }); + } else { + newAttrs[name] = attr(value.type, value.options); + } + }); + } + for (const prop in newProps) { + if (newAttrs[prop]) { + continue; + } else { + newAttrs[prop] = attr(newProps[prop].type, newProps[prop]); + newFields.push(prop); + } + } + return { attrs: newAttrs, newFields }; +}; + +export const combineFields = function (currentFields, newFields, excludedFields) { + const otherFields = newFields.filter((field) => { + return !currentFields.includes(field) && !excludedFields.includes(field); + }); + if (otherFields.length) { + currentFields = currentFields.concat(otherFields); + } + return currentFields; +}; + export const combineFieldGroups = function (currentGroups, newFields, excludedFields) { let allFields = []; for (const group of currentGroups) { diff --git a/ui/app/utils/model-helpers/validators.js b/ui/app/utils/validators.js similarity index 100% rename from ui/app/utils/model-helpers/validators.js rename to ui/app/utils/validators.js diff --git a/ui/config/deprecation-workflow.js b/ui/config/deprecation-workflow.js index 945f70b95fa1..cf343738e691 100644 --- a/ui/config/deprecation-workflow.js +++ b/ui/config/deprecation-workflow.js @@ -5,17 +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-promise-proxies' }, // Transform secrets - { handler: 'silence', matchId: 'ember-data:no-a-with-array-like' }, // MFA - { handler: 'silence', matchId: 'ember-data:deprecate-promise-many-array-behaviors' }, // MFA + { 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/docs/model-validations.md b/ui/docs/model-validations.md index 622aa7debb75..ad0ce0dace6a 100644 --- a/ui/docs/model-validations.md +++ b/ui/docs/model-validations.md @@ -1,33 +1,34 @@ # Model Validations Decorator - + The model-validations decorator provides a method on a model class which may be used for validating properties based on a provided rule set. ## API The decorator expects a validations object as the only argument with the following shape: -```js +``` js const validations = { - [propertyKeyName]: [{ type, options, message, level, validator }], + [propertyKeyName]: [ + { type, options, message, level, validator } + ] }; ``` - **propertyKeyName** [string] - each key in the validations object should refer to the property on the class to apply the validation to. - -**type** [string] - the type of validation to apply. These must be exported from the [validators util](../app/utils/model-helpers/validators.js) for lookup. Type is required if a _validator_ function is not provided. + +**type** [string] - the type of validation to apply. These must be exported from the [validators util](../app/utils/validators.js) for lookup. Type is required if a *validator* function is not provided. **options** [object] - an optional object for the given validator -- min, max, nullable etc. **message** [string | function] - string added to the errors array and returned in the state object from the validate method if validation fails. A function may also be provided with the model as the lone argument that returns a string. Since this value is typically displayed to the user it should be a complete sentence with proper punctuation. -**level** [string] _optional_ - string that defaults to 'error'. Currently the only other accepted value is 'warn'. +**level** [string] *optional* - string that defaults to 'error'. Currently the only other accepted value is 'warn'. -**validator** [function] _optional_ - a function that may be used in place of type that is invoked in the validate method. This is useful when specific validations are needed which may be dependent on other class properties. +**validator** [function] *optional* - a function that may be used in place of type that is invoked in the validate method. This is useful when specific validations are needed which may be dependent on other class properties. This function takes the class context (this) as the only argument and returns true or false. ## Usage -Each property defined in the validations object supports multiple validations provided as an array. For example, _presence_ and _containsWhiteSpace_ can both be added as validations for a string property. +Each property defined in the validations object supports multiple validations provided as an array. For example, *presence* and *containsWhiteSpace* can both be added as validations for a string property. ```js const validations = { @@ -40,7 +41,6 @@ const validations = { ], }; ``` - Decorate the model class and pass the validations object as the argument ```js @@ -48,7 +48,9 @@ import Model, { attr } from '@ember-data/model'; import withModelValidations from 'vault/decorators/model-validations'; const validations = { - name: [{ type: 'presence', message: 'Name is required.' }], + name: [ + { type: 'presence', message: 'Name is required.' }, + ], }; @withModelValidations(validations) @@ -70,13 +72,12 @@ if (isValid) { this.errors = state; } ``` - **isValid** [boolean] - the validity of the full class. If no properties provided in the validations object are invalid this will be true. -**state** [object] - the error state of the properties defined in the validations object. This object is keyed by the property names from the validations object and each property contains an _isValid_ and _errors_ value. The _errors_ array will be populated with messages defined in the validations object when validations fail. Since a property can have multiple validations, errors is always returned as an array. +**state** [object] - the error state of the properties defined in the validations object. This object is keyed by the property names from the validations object and each property contains an *isValid* and *errors* value. The *errors* array will be populated with messages defined in the validations object when validations fail. Since a property can have multiple validations, errors is always returned as an array. **invalidFormMessage** [string] - message describing the number of errors currently present on the model class. - + ```js const { state } = model.validate(); const { isValid, errors } = state[propertyKeyName]; @@ -86,18 +87,17 @@ if (!isValid) { ``` ## Examples - ### Basic ```js const validations = { - foo: [{ type: 'presence', message: 'foo is a required field.' }], + foo: [ + { type: 'presence', message: 'foo is a required field.' } + ], }; @withModelValidations(validations) -class SomeModel extends Model { - foo = null; -} +class SomeModel extends Model { foo = null; } const model = new SomeModel(); const { isValid, state } = model.validate(); @@ -106,17 +106,14 @@ console.log(isValid); // false console.log(state.foo.isValid); // false console.log(state.foo.errors); // ['foo is a required field'] ``` - ### Custom validator ```js const validations = { - foo: [ - { - validator: (model) => (model.bar.includes('test') ? model.foo : false), - message: 'foo is required if bar includes test.', - }, - ], + foo: [{ + validator: (model) => model.bar.includes('test') ? model.foo : false, + message: 'foo is required if bar includes test.' + }], }; @withModelValidations(validations) @@ -142,7 +139,7 @@ console.log(state.foo.errors); // [] ### Adding class in template based on validation state -All form validation errors must have a red border around them. Add this by adding a conditional class _has-error-border_ to the element. +All form validation errors must have a red border around them. Add this by adding a conditional class *has-error-border* to the element. ```js @action @@ -158,5 +155,5 @@ async save() { ``` ```hbs - -``` + +``` \ No newline at end of file diff --git a/ui/docs/models.md b/ui/docs/models.md index e6c94f74bef8..a13a56cbcf2c 100644 --- a/ui/docs/models.md +++ b/ui/docs/models.md @@ -4,252 +4,17 @@ - [Models](#models) - - [Intro](#intro) - - [Model patterns overview](#model-patterns-overview) - - [Patterns](#patterns) - - [Attributes \& field groups](#attributes--field-groups) - - [Attributes \& field groups example](#attributes--field-groups-example) - - [Validations](#validations) - - [@withModelValidations()](#withmodelvalidations) - - [Capabilities](#capabilities) - - [Examples](#examples) - - [Models hydrated by OpenAPI](#models-hydrated-by-openapi) - - [Using Decorators](#using-decorators) + - [Capabilities](#capabilities) + - [Decorators](#decorators) - [@withFormFields()](#withformfields) + - [@withModelValidations()](#withmodelvalidations) -## Intro +## Capabilities -We use models primarily as the backing data layer for our forms and for our list/show views. As Ember-Data has matured, our patterns of usage have become outdated. This document serves to outline our current best-practices, since examples within the codebase are often out of date and do not always reflect our best practices or ambitions. - -## Model patterns overview - -Models can be thought of as the shape of data that an instance of that Model -- a Record -- will have. Models should be as "thin" as possible, holding only data directly relevant to the Record itself. For example, if we have a Model `user` with attributes `firstName` and `lastName`, it _is_ appropriate to have a getter on the Model called `fullName`, because its attributes can be calculated directly from the record's values, and is relevant to the Record itself. However it is _not_ appropriate to store data like which fields are shown on the edit form, because that has no bearing on the Record itself. Field values are a display concern, not related to the values of the record. - -Other patterns and where they belong in relation to the Model: - -- **Attribute metadata** - this is referring to information defined on a Model's attributes, such as label, edit type, and other information relevant to both forms and the given attribute. We use these heavily in the `FormField` component to show the correct label, help text, and input type. Conceptually, this does not belong on a Model (because the information is not directly related to the data in a Record) but, since we leverage OpenAPI heavily to populate both attributes and their metadata, we are going to keep attribute metadata defined on the attribute in the Model. **TL;DR: Lives on Model** - -- **Form and show fields** - the grouping and order of fields that should display on both show routes and create/edit forms, while conceptually related to the Model, is not related to an individual record. Therefore, this information should not be defined on the Model (which has been our previous pattern). To support migration, we have a few helpful decorators and patterns. **TL;DR: Lives in component or model-helper util files** - -- **Validations** - While an argument can go either way about this one, we are going to continue defining these on the Model using our handy [withModelValidation decorator](#withmodelvalidations). The state of validation is directly correlated to a given Record which is a strong reason to keep it on the Model. **TL;DR: Lives on Model** - -- **[Capabilities](#capabilities)** - Capabilities are calculated by fetching permissions by path -- often multiple paths, based on the same information we need to fetch the Record data (eg. backend, ID). When using `lazyCapabilities` on the model we kick off one API request for each of the paths we need, while using the capabilities service `fetchMultiplePaths` method we can make one request with all the required paths included. Our best practice is to fetch capabilities outside of the Model (perhaps as part of a route model, or on a user action such as dropdown click open). A downside to this approach is that the API may get re-requested per page we check the capability (eg. list view dropdown and then detail view) -- but we can optimize this in the future by first checking the store for `capabilities` of matching path/ID before sending the API request. **TL;DR: Lives in route or component where they are used** - -## Patterns - -### Attributes & field groups - -We use attributes defined on the Model to determine input concerns (label, input type, help text) and field groups to determine the order of the attribute data on the form and detail pages, and are defined in the component they are used in or in a `utils/model-helpers/*` file. - -#### Attributes & field groups example - -In this example, we have a Model `simple-timer` with a few attributes defined. The `withExpandedAttributes` helper adds a couple items to the Model it's applied to: - -- allByKey - a getter which returns all the attributes as keys of an object, and the value is the metadata of the attribute including anything returned from OpenAPI if the model is included in `OPENAPI_POWERED_MODELS`. -- \_expandGroups - takes an array of group objects and expands the attribute keys into the metadata - -In the component where we pass a Record of this Model, we can see how we use it to populate either a flat array of attributes for use in the show view, or to populate groups of fields for rendering on a form. - -```js -// models/simple-timer.js -@withExpandedAttributes() -export default class SimpleTimer extends Model { - @attr('string', { - editType: 'ttl', - defaultValue: '3600s', - label: 'TTL', - helpText: 'Here is some help text', - }) - ttl; - - @attr('string') name; - @attr('boolean') restartable; // enterprise only -} -``` - -```js -// components/simple-timer-display.ts -export default class SimpleTimerDisplay extends Component { - @service declare readonly version: VersionService; - - // these fields are shown flat in the show mode, iterated over - // and used in InfoTableRow - get showFields() { - let fields = ['name', 'ttl']; - if (this.version.isEnterprise) { - fields.push('restartable'); - } - return fields.map((field) => this.args.model.allByKey[field]); - } - - // these fields are shown grouped in edit mode and is formatted - // to be used in something like FormFieldGroups - get fieldGroups() { - let groups = [{ default: ['name', 'ttl'] }]; - if (this.version.isEnterprise) { - groups[{ 'Custom options': ['restartable'] }]; - } - return this.args.model._expandGroups(groups); - } -} -``` - -### Validations - -Validations on used on forms, to present the user with feedback about their form answers before sending the payload to the API. Our best practices are: - -- define the validations using the `withModelValidations` decorator -- trigger the `validate()` method added by the decorator on form submit -- if there are validation errors: - - show a message at the bottom of the form saying there were errors with the form - - add inline-alert next to the inputs that have incorrect data - - exit the form submit function early - - do not disable the submit button -- if there are no validation errors, continue saving as normal - -#### [@withModelValidations()](../app/decorators/model-validations.js) - -This decorator: - -- Adds `validate()` method on model to check attributes are valid before making an API request -- Provides option to write a custom validation, or use validation method from the [validators util](../app/utils/model-helpers/validators.js) which is referenced by the `type` key -- Option to add `level: 'warn'` to draw user attention to the input, without preventing form submission -- Component example [here](../lib/pki/addon/components/pki-generate-root.ts) - -```js -import { withModelValidations } from 'vault/decorators/model-validations'; - -const validations = { - // object key is the model's attribute name - password: [{ type: 'presence', message: 'Password is required' }], - keyName: [ - { - validator(model) { - return model.keyName === 'default' ? false : true; - }, - message: `Key name cannot be the reserved value 'default'`, - }, - ], -}; - -@withModelValidations(validations) -export default class FooModel extends Model { - @attr() password; - @attr() keyName; -} -``` - -```js -// form-component.js -export default class FormComponent extends Component { - @tracked modelValidations = null; - @tracked invalidFormAlert = ''; - - checkFormValidity() { - interface Validity { - // only true if all of the state's isValid are also true - isValid: boolean; - state: { - // state is keyed by the attribute names - [key: string]: { - errors: string[]; - warnings: string[]; - isValid: boolean; - } - } - invalidFormMessage: string; // eg "There are 2 errors with this form" - } - // calling validate() returns Validity - const { isValid, state, invalidFormMessage } = this.args.model.validate(); - this.modelValidations = state; - this.invalidFormAlert = invalidFormMessage; - return isValid; - } - - @action - submit() { - // clear errors - this.modelValidations = null; - this.invalidFormAlert = null; - - // check validity - const continueSave = this.checkFormValidity(); - if (!continueSave) return; - - // continue save ... - } -} -``` - -### Capabilities - -- The API will prevent users from performing disallowed actions, so adding capabilities is purely to improve UX by hiding actions we know the user cannot take. Because of this, we default to showing items if we cannot determine the capabilities for an endpoint. -- Always test the capability works as expected (never assume the API path 🙂) -- the extra string interpolation can lead to sneaky typos and incorrect returns from the getters -- Capabilities are checked via the `capabilities-self` endpoint, and registered in the store as a [capabilities Model](../app/models/capabilities.js), with the path as the Record's ID. -- The path IDs on the capabilities Records should never include the namespace, but when operating within a namespace the paths in the API request payload must be prepended with the namespace so the API will return the proper capabilities (eg. for `kv/data/foo` in the `admin` namespace instead of root) -- In general we want to check capabilities outside of the Model, but we have a patterns for both ways. - -#### Examples - -**Single capability check within a component** -In [this example](../app/components/clients/page-header.js), we have an action that some users can take within the page header. Honestly this capability check could just have easily lived in the route's Model (since the PageHeader always renders on the relevant routes), but here it provides a good example of a check happening on component instantiation, using the args passed to the component: - -```js -// clients/page-header.js -constructor() { - super(...arguments); - this.getExportCapabilities(this.args.namespace); -} - -async getExportCapabilities(ns = '') { - try { - const url = ns - ? `${sanitizePath(ns)}/sys/internal/counters/activity/export` - : 'sys/internal/counters/activity/export'; - const cap = await this.store.findRecord('capabilities', url); - this.canDownload = cap.canSudo; - } catch (e) { - // if we can't read capabilities, default to show - this.canDownload = true; - } -} -``` - -**Multiple capabilities checked at once** -When there are multiple capabilities paths to check, the recommended approach is to use the [capabilities service's](../app/services/capabilities.ts) `fetchMultiplePaths` method. It will pass all the paths in a single API request instead of making a capabilities-self call for each path as the other techniques do. In [this example](../lib/kv/addon/routes/secret.js), we get the capabilities as part of the route's model hook and then return the relevant `can*` values: - -```js -async fetchCapabilities(backend, path) { - const metadataPath = `${backend}/metadata/${path}`; - const dataPath = `${backend}/data/${path}`; - const subkeysPath = `${backend}/subkeys/${path}`; - const perms = await this.capabilities.fetchMultiplePaths([metadataPath, dataPath, subkeysPath]); - // returns values keyed at the path - return { - metadata: perms[metadataPath], - data: perms[dataPath], - subkeys: perms[subkeysPath], - }; -} - -async model() { - const backend = this.secretMountPath.currentPath; - const { name: path } = this.paramsFor('secret'); - const capabilities = await this.fetchCapabilities(backend, path); - return hash({ - // ... - canUpdateData: capabilities.data.canUpdate, - canReadData: capabilities.data.canRead, - canReadMetadata: capabilities.metadata.canRead, - canDeleteMetadata: capabilities.metadata.canDelete, - canUpdateMetadata: capabilities.metadata.canUpdate, - }); -} -``` - -Lastly, we have an example that is common but a pattern that we want to move away from: using `lazyCapabilities` on a Model. The `lazyCapabilities` macro only fetches the capabilities when the attribute is invoked -- so in the example below, only when `canRead` is rendered on the template will the capablities-self call be kicked off. +- The API will prevent users from performing disallowed actions, adding capabilities is purely to improve UX +- Always test the capability works as expected (never assume the API path 🙂), the extra string interpolation can lead to sneaky typos and incorrect returns from the getters ```js import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; @@ -272,20 +37,13 @@ export default class FooModel extends Model { } ``` -### Models hydrated by OpenAPI - -In a Model which is hydrated by OpenAPI, it can be cumbersome to keep up with all the changes made by the backend. One pattern available to us is the [`combineFieldGroups`](../app/utils/openapi-to-attrs.js) method, which - ---- - -## Using Decorators +## Decorators ### [@withFormFields()](../app/decorators/model-form-fields.js) - Sets `allFields`, `formFields` and/or `formFieldGroups` properties on a model class - `allFields` includes every model attribute (regardless of args passed to decorator) - `formFields` and `formFieldGroups` only exist if the relevant arg is passed to the decorator -- `type` of validator should match the keys in [model-helpers/validators.js](../app/utils/model-helpers/validators.js) ```js import { withFormFields } from 'vault/decorators/model-form-fields'; @@ -348,3 +106,48 @@ model.formFieldGroups = [ }, ]; ``` + +### [@withModelValidations()](../app/decorators/model-validations.js) + +- Adds `validate()` method on model to check attributes are valid before making an API request +- Option to write a custom validation, or use validation method from the [validators util](../app/utils/validators.js) which is referenced by the `type` key +- Option to add `level: 'warn'` to draw user attention to the input, without preventing form submission +- Component example [here](../lib/pki/addon/components/pki-generate-root.ts) + +```js +import { withModelValidations } from 'vault/decorators/model-validations'; + +const validations = { + // object key is the model's attribute name + password: [{ type: 'presence', message: 'Password is required' }], + keyName: [ + { + validator(model) { + return model.keyName === 'default' ? false : true; + }, + message: `Key name cannot be the reserved value 'default'`, + }, + ], +}; + +@withModelValidations(validations) +export default class FooModel extends Model {} + +// calling validate() returns an object: +model.validate() = { + isValid: false, + state: { + password: { + errors: ['Password is required.'], + warnings: [], + isValid: false, + }, + keyName: { + errors: ["Key name cannot be the reserved value 'default'"], + warnings: [], + isValid: true, + }, + }, + invalidFormMessage: 'There are 2 errors with this form.', +}; +``` diff --git a/ui/lib/core/addon/components/kv-object-editor.js b/ui/lib/core/addon/components/kv-object-editor.js index a44b7aacf96f..c96a68c2874e 100644 --- a/ui/lib/core/addon/components/kv-object-editor.js +++ b/ui/lib/core/addon/components/kv-object-editor.js @@ -10,12 +10,7 @@ import { assert } from '@ember/debug'; import { action } from '@ember/object'; import { guidFor } from '@ember/object/internals'; import KVObject from 'vault/lib/kv-object'; -import { - hasWhitespace, - isNonString, - NON_STRING_WARNING, - WHITESPACE_WARNING, -} from 'vault/utils/model-helpers/validators'; +import { hasWhitespace, isNonString, NON_STRING_WARNING, WHITESPACE_WARNING } from 'vault/utils/validators'; /** * @module KvObjectEditor 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 98d3034ca66f..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/model-helpers/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/kv/addon/components/kv-patch/editor/form.js b/ui/lib/kv/addon/components/kv-patch/editor/form.js index f2e7bde775e8..45dd546d655d 100644 --- a/ui/lib/kv/addon/components/kv-patch/editor/form.js +++ b/ui/lib/kv/addon/components/kv-patch/editor/form.js @@ -7,12 +7,7 @@ import Component from '@glimmer/component'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import { A } from '@ember/array'; -import { - hasWhitespace, - isNonString, - WHITESPACE_WARNING, - NON_STRING_WARNING, -} from 'vault/utils/model-helpers/validators'; +import { hasWhitespace, isNonString, WHITESPACE_WARNING, NON_STRING_WARNING } from 'vault/utils/validators'; /** * @module KvPatch::Editor::Form 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/auth-list-test.js b/ui/tests/acceptance/auth-list-test.js index 38584f6b063e..eb28ba685451 100644 --- a/ui/tests/acceptance/auth-list-test.js +++ b/ui/tests/acceptance/auth-list-test.js @@ -8,7 +8,7 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { v4 as uuidv4 } from 'uuid'; -import { login, loginNs } from 'vault/tests/helpers/auth/auth-helpers'; +import authPage from 'vault/tests/pages/auth'; import enablePage from 'vault/tests/pages/settings/auth/enable'; import { supportedManagedAuthBackends } from 'vault/helpers/supported-managed-auth-backends'; import { deleteAuthCmd, mountAuthCmd, runCmd, createNS } from 'vault/tests/helpers/commands'; @@ -26,7 +26,7 @@ module('Acceptance | auth backend list', function (hooks) { setupApplicationTest(hooks); hooks.beforeEach(async function () { - await login(); + await authPage.login(); this.path1 = `userpass-${uuidv4()}`; this.path2 = `userpass-${uuidv4()}`; this.user1 = 'user1'; @@ -36,16 +36,16 @@ module('Acceptance | auth backend list', function (hooks) { }); hooks.afterEach(async function () { - await login(); + await authPage.login(); await runCmd([deleteAuthCmd(this.path1), deleteAuthCmd(this.path2)], false); return; }); test('userpass secret backend', async function (assert) { + assert.expect(5); // enable a user in first userpass backend await visit('/vault/access'); await click(SELECTORS.backendLink(this.path1)); - assert.dom(GENERAL.emptyStateTitle).exists('shows empty state'); await click(SELECTORS.createUser); await fillIn(GENERAL.inputByAttr('username'), this.user1); await fillIn(GENERAL.inputByAttr('password'), this.user1); @@ -57,12 +57,12 @@ module('Acceptance | auth backend list', function (hooks) { // enable a user in second userpass backend await click(SELECTORS.backendLink(this.path2)); - assert.dom(GENERAL.emptyStateTitle).exists('shows empty state'); await click(SELECTORS.createUser); await fillIn(GENERAL.inputByAttr('username'), this.user2); await fillIn(GENERAL.inputByAttr('password'), this.user2); await click(SELECTORS.saveBtn); assert.strictEqual(currentURL(), `/vault/access/${this.path2}/item/user`); + // Confirm that the user was created. There was a bug where the apiPath was not being updated when toggling between auth routes. assert.dom(SELECTORS.listItem).hasText(this.user2, 'user2 exists in the list'); @@ -132,7 +132,7 @@ module('Acceptance | auth backend list', function (hooks) { // Only SAML is enterprise-only for now const type = 'saml'; const path = `auth-list-${type}-${uid}`; - await runCmd([mountAuthCmd(type, path), 'refresh']); + await enablePage.enable(type, path); await settled(); await visit('/vault/access'); @@ -150,7 +150,7 @@ module('Acceptance | auth backend list', function (hooks) { const ns = 'ns-wxyz'; await runCmd(createNS(ns), false); await settled(); - await loginNs(ns); + await authPage.loginNs(ns); // go directly to token configure route await visit('/vault/settings/auth/configure/token/options'); await fillIn('[data-test-input="description"]', 'My custom description'); 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/auth/auth-form-selectors.ts b/ui/tests/helpers/auth/auth-form-selectors.ts index b5a3730ea922..eb8c77ce9dd1 100644 --- a/ui/tests/helpers/auth/auth-form-selectors.ts +++ b/ui/tests/helpers/auth/auth-form-selectors.ts @@ -12,5 +12,4 @@ export const AUTH_FORM = { input: (item: string) => `[data-test-${item}]`, // i.e. jwt, role, token, password or username mountPathInput: '[data-test-auth-form-mount-path]', moreOptions: '[data-test-auth-form-options-toggle]', - namespaceInput: '[data-test-auth-form-ns-input]', }; diff --git a/ui/tests/helpers/auth/auth-helpers.ts b/ui/tests/helpers/auth/auth-helpers.ts index 544bdcb2a063..65632c066861 100644 --- a/ui/tests/helpers/auth/auth-helpers.ts +++ b/ui/tests/helpers/auth/auth-helpers.ts @@ -14,16 +14,7 @@ export const login = async (token = rootToken) => { await logout(); await visit('/vault/auth?with=token'); await fillIn(AUTH_FORM.input('token'), token); - return click(AUTH_FORM.login); -}; - -export const loginNs = async (ns: string, token = rootToken) => { - // make sure we're always logged out and logged back in - await logout(); - await visit('/vault/auth?with=token'); - await fillIn(AUTH_FORM.namespaceInput, ns); - await fillIn(AUTH_FORM.input('token'), token); - return click(AUTH_FORM.login); + return await click(AUTH_FORM.login); }; export const logout = async () => { 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/kv/kv-patch/editor/form-test.js b/ui/tests/integration/components/kv/kv-patch/editor/form-test.js index dca034ddd673..4096feb945be 100644 --- a/ui/tests/integration/components/kv/kv-patch/editor/form-test.js +++ b/ui/tests/integration/components/kv/kv-patch/editor/form-test.js @@ -11,7 +11,7 @@ import { hbs } from 'ember-cli-htmlbars'; import sinon from 'sinon'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { FORM } from 'vault/tests/helpers/kv/kv-selectors'; -import { NON_STRING_WARNING, WHITESPACE_WARNING } from 'vault/utils/model-helpers/validators'; +import { NON_STRING_WARNING, WHITESPACE_WARNING } from 'vault/utils/validators'; module('Integration | Component | kv | kv-patch/editor/form', function (hooks) { setupRenderingTest(hooks); 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/decorators/model-expanded-attributes-test.js b/ui/tests/unit/decorators/model-expanded-attributes-test.js index 9e3abe771be2..26c077fe9544 100644 --- a/ui/tests/unit/decorators/model-expanded-attributes-test.js +++ b/ui/tests/unit/decorators/model-expanded-attributes-test.js @@ -4,16 +4,58 @@ */ import { module, test } from 'qunit'; -import { setupApplicationTest } from 'ember-qunit'; +import { setupTest } from 'ember-qunit'; import sinon from 'sinon'; +import Model, { attr } from '@ember-data/model'; import { withExpandedAttributes } from 'vault/decorators/model-expanded-attributes'; +// create class using decorator +const createClass = () => { + @withExpandedAttributes() + class Foo extends Model { + @attr('string', { + label: 'Foo', + subText: 'A form field', + }) + foo; + @attr('boolean', { + label: 'Bar', + subText: 'Maybe a checkbox', + }) + bar; + @attr('number', { + label: 'Baz', + subText: 'A number field', + }) + baz; + + get fieldGroups() { + return [{ default: ['baz'] }, { 'Other options': ['foo', 'bar'] }]; + } + } + return new Foo(); +}; + module('Unit | Decorators | model-expanded-attributes', function (hooks) { - setupApplicationTest(hooks); + setupTest(hooks); hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); this.spy = sinon.spy(console, 'error'); + this.fooField = { + name: 'foo', + options: { label: 'Foo', subText: 'A form field' }, + type: 'string', + }; + this.barField = { + name: 'bar', + options: { label: 'Bar', subText: 'Maybe a checkbox' }, + type: 'boolean', + }; + this.bazField = { + name: 'baz', + options: { label: 'Baz', subText: 'A number field' }, + type: 'number', + }; }); hooks.afterEach(function () { this.spy.restore(); @@ -29,70 +71,28 @@ module('Unit | Decorators | model-expanded-attributes', function (hooks) { test('it adds allByKey value to model', function (assert) { assert.expect(1); - const model = this.store.modelFor('namespace'); + const model = createClass(); assert.deepEqual( - model.prototype.allByKey, - { - path: { - name: 'path', - options: {}, - type: 'string', - }, - }, + { foo: this.fooField, bar: this.barField, baz: this.bazField }, + model.allByKey, 'allByKey set on Model class' ); }); test('_expandGroups helper works correctly', function (assert) { - const model = this.store.modelFor('aws-credential'); - const result = model.prototype._expandGroups([ - { default: ['roleArn'] }, - { 'Other options': ['ttl', 'leaseId'] }, - ]); + const model = createClass(); + const result = model._expandGroups(model.fieldGroups); assert.deepEqual(result, [ - { - default: [ - { - name: 'roleArn', - options: { - helpText: - 'The ARN of the role to assume if credential_type on the Vault role is assumed_role. Optional if the role has a single role ARN; required otherwise.', - label: 'Role ARN', - }, - type: 'string', - }, - ], - }, - { - 'Other options': [ - { - name: 'ttl', - options: { - defaultValue: '3600s', - editType: 'ttl', - helpText: - 'Specifies the TTL for the use of the STS token. Valid only when credential_type is assumed_role, federation_token, or session_token.', - label: 'TTL', - setDefault: true, - ttlOffValue: '', - }, - type: undefined, - }, - { - name: 'leaseId', - options: {}, - type: 'string', - }, - ], - }, + { default: [this.bazField] }, + { 'Other options': [this.fooField, this.barField] }, ]); }); test('_expandGroups throws assertion when incorrect inputs', function (assert) { assert.expect(1); - const model = this.store.modelFor('aws-credential'); + const model = createClass(); try { - model.prototype._expandGroups({ foo: ['bar'] }); + model._expandGroups({ foo: ['bar'] }); } catch (e) { assert.strictEqual(e.message, '_expandGroups expects an array of objects'); } diff --git a/ui/tests/unit/decorators/model-form-fields-test.js b/ui/tests/unit/decorators/model-form-fields-test.js index 6d824ce9d232..330736432568 100644 --- a/ui/tests/unit/decorators/model-form-fields-test.js +++ b/ui/tests/unit/decorators/model-form-fields-test.js @@ -4,16 +4,54 @@ */ import { module, test } from 'qunit'; -import { setupApplicationTest } from 'ember-qunit'; +import { setupTest } from 'ember-qunit'; import { withFormFields } from 'vault/decorators/model-form-fields'; import sinon from 'sinon'; +import Model, { attr } from '@ember-data/model'; + +// create class using decorator +const createClass = (propertyNames, groups) => { + @withFormFields(propertyNames, groups) + class Foo extends Model { + @attr('string', { + label: 'Foo', + subText: 'A form field', + }) + foo; + @attr('boolean', { + label: 'Bar', + subText: 'Maybe a checkbox', + }) + bar; + @attr('number', { + label: 'Baz', + subText: 'A number field', + }) + baz; + } + return new Foo(); +}; module('Unit | Decorators | ModelFormFields', function (hooks) { - setupApplicationTest(hooks); + setupTest(hooks); hooks.beforeEach(function () { this.spy = sinon.spy(console, 'error'); - this.store = this.owner.lookup('service:store'); + this.fooField = { + name: 'foo', + options: { label: 'Foo', subText: 'A form field' }, + type: 'string', + }; + this.barField = { + name: 'bar', + options: { label: 'Bar', subText: 'Maybe a checkbox' }, + type: 'boolean', + }; + this.bazField = { + name: 'baz', + options: { label: 'Baz', subText: 'A number field' }, + type: 'number', + }; }); hooks.afterEach(function () { this.spy.restore(); @@ -29,100 +67,23 @@ module('Unit | Decorators | ModelFormFields', function (hooks) { test('it return allFields when arguments not provided', function (assert) { assert.expect(1); - // test by instantiating a record that uses this decorator - const record = this.store.createRecord('kv/data'); + const model = createClass(); assert.deepEqual( - record.allFields, - [ - { - name: 'backend', - options: {}, - type: 'string', - }, - { - name: 'path', - options: { - label: 'Path for this secret', - }, - type: 'string', - }, - { - name: 'secretData', - options: {}, - type: 'object', - }, - { - name: 'createdTime', - options: {}, - type: 'string', - }, - { - name: 'customMetadata', - options: {}, - type: 'object', - }, - { - name: 'deletionTime', - options: {}, - type: 'string', - }, - { - name: 'destroyed', - options: {}, - type: 'boolean', - }, - { - name: 'version', - options: {}, - type: 'number', - }, - { - name: 'failReadErrorCode', - options: {}, - type: 'number', - }, - { - name: 'casVersion', - options: {}, - type: 'number', - }, - ], + [this.fooField, this.barField, this.bazField], + model.allFields, 'allFields set on Model class' ); }); test('it should set formFields prop on Model class', function (assert) { - // this model uses withFormFields - const record = this.store.createRecord('clients/config'); - assert.deepEqual( - record.formFields, - [ - { - name: 'enabled', - options: {}, - type: 'string', - }, - { - name: 'retentionMonths', - options: { - label: 'Retention period', - subText: 'The number of months of activity logs to maintain for client tracking.', - }, - type: 'number', - }, - ], - 'formFields set on Model class' - ); + const model = createClass(['foo']); + assert.deepEqual([this.fooField], model.formFields, 'formFields set on Model class'); }); test('it should set formFieldGroups on Model class', function (assert) { - // this model uses withFormFields with groups - const record = this.store.createRecord('ldap/config'); - const groups = record.formFieldGroups.map((group) => Object.keys(group)[0]); - assert.deepEqual( - groups, - ['default', 'TLS options', 'More options'], - 'formFieldGroups set on Model class with correct group labels' - ); + const groups = [{ default: ['foo'] }, { subgroup: ['bar'] }]; + const model = createClass(null, groups); + const fieldGroups = [{ default: [this.fooField] }, { subgroup: [this.barField] }]; + assert.deepEqual(fieldGroups, model.formFieldGroups, 'formFieldGroups set on Model class'); }); }); diff --git a/ui/tests/unit/decorators/model-validations-test.js b/ui/tests/unit/decorators/model-validations-test.js index 6148cb3d9a44..94ca73b1bda0 100644 --- a/ui/tests/unit/decorators/model-validations-test.js +++ b/ui/tests/unit/decorators/model-validations-test.js @@ -6,7 +6,7 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { withModelValidations } from 'vault/decorators/model-validations'; -import validators from 'vault/utils/model-helpers/validators'; +import validators from 'vault/utils/validators'; import sinon from 'sinon'; import Model from '@ember-data/model'; diff --git a/ui/tests/unit/models/generated-item-test.js b/ui/tests/unit/models/generated-item-test.js deleted file mode 100644 index 1c97de05cfaf..000000000000 --- a/ui/tests/unit/models/generated-item-test.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; - -import { setupTest } from 'vault/tests/helpers'; - -module('Unit | Model | generated item', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - const store = this.owner.lookup('service:store'); - const model = store.createRecord('generated-item', {}); - assert.ok(model, 'generated-item model exists'); - }); -}); 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 ff7e34a62d05..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/model-helpers/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/ui/tests/unit/utils/openapi-helpers-test.js b/ui/tests/unit/utils/openapi-helpers-test.js index dd8dbfb648e3..a0f18a37bcdc 100644 --- a/ui/tests/unit/utils/openapi-helpers-test.js +++ b/ui/tests/unit/utils/openapi-helpers-test.js @@ -4,20 +4,9 @@ */ import { module, test } from 'qunit'; -import { - _getPathParam, - combineOpenApiAttrs, - expandOpenApiProps, - getHelpUrlForModel, - pathToHelpUrlSegment, -} from 'vault/utils/openapi-helpers'; -import Model, { attr } from '@ember-data/model'; -import { setupTest } from 'ember-qunit'; -import { camelize } from '@ember/string'; - -module('Unit | Utility | OpenAPI helper utils', function (hooks) { - setupTest(hooks); +import { _getPathParam, getHelpUrlForModel, pathToHelpUrlSegment } from 'vault/utils/openapi-helpers'; +module('Unit | Utility | OpenAPI helper utils', function () { test(`pathToHelpUrlSegment`, function (assert) { [ { path: '/auth/{username}', result: '/auth/example' }, @@ -59,236 +48,4 @@ module('Unit | Utility | OpenAPI helper utils', function (hooks) { ); }); }); - - test('combineOpenApiAttrs should combine attributes correctly', async function (assert) { - class FooModel extends Model { - @attr('string', { - label: 'Foo', - subText: 'A form field', - }) - foo; - @attr('boolean', { - label: 'Bar', - subText: 'Maybe a checkbox', - }) - bar; - @attr('number', { - label: 'Baz', - subText: 'A number field', - }) - baz; - } - this.owner.register('model:foo', FooModel); - const myModel = this.owner.lookup('service:store').modelFor('foo'); - const newProps = { - foo: { - editType: 'ttl', - }, - baz: { - type: 'number', - editType: 'slider', - label: 'Old label', - }, - foobar: { - type: 'string', - label: 'Foo-bar', - }, - }; - const expected = [ - { - name: 'foo', - type: 'string', - options: { - label: 'Foo', - subText: 'A form field', - editType: 'ttl', - }, - }, - { - name: 'bar', - type: 'boolean', - options: { - label: 'Bar', - subText: 'Maybe a checkbox', - }, - }, - { - name: 'baz', - type: 'number', - options: { - label: 'Baz', // uses the value we set on the model - editType: 'slider', - subText: 'A number field', - }, - }, - { - name: 'foobar', - type: 'string', - options: { - label: 'Foo-bar', - }, - }, - ]; - const { attrs, newFields } = combineOpenApiAttrs(myModel.attributes, newProps); - assert.deepEqual(newFields, ['foobar'], 'correct newFields added'); - - // When combineOpenApiAttrs - assert.strictEqual(attrs.length, 4, 'correct number of attributes returned'); - expected.forEach((exp) => { - const name = exp.name; - const attr = attrs.find((a) => a.name === name); - assert.deepEqual(attr, exp, `${name} combined properly`); - }); - }); - - module('expandopenApiProps', function () { - const OPENAPI_RESPONSE_PROPS = { - ttl: { - type: 'string', - format: 'seconds', - description: 'this is a TTL!', - 'x-vault-displayAttrs': { - name: 'TTL', - }, - }, - 'awesome-people': { - type: 'array', - items: { - type: 'string', - }, - 'x-vault-displayAttrs': { - value: 'Grace Hopper,Lady Ada', - }, - }, - 'favorite-ice-cream': { - type: 'string', - enum: ['vanilla', 'chocolate', 'strawberry'], - }, - 'default-value': { - default: 30, - 'x-vault-displayAttrs': { - value: 300, - }, - type: 'integer', - }, - default: { - 'x-vault-displayAttrs': { - value: 30, - }, - type: 'integer', - }, - 'super-secret': { - type: 'string', - 'x-vault-displayAttrs': { - sensitive: true, - }, - description: 'A really secret thing', - }, - }; - const EXPANDED_PROPS = { - ttl: { - helpText: 'this is a TTL!', - editType: 'ttl', - label: 'TTL', - fieldGroup: 'default', - }, - awesomePeople: { - editType: 'stringArray', - defaultValue: 'Grace Hopper,Lady Ada', - fieldGroup: 'default', - }, - favoriteIceCream: { - editType: 'string', - type: 'string', - possibleValues: ['vanilla', 'chocolate', 'strawberry'], - fieldGroup: 'default', - }, - defaultValue: { - editType: 'number', - type: 'number', - defaultValue: 300, - fieldGroup: 'default', - }, - default: { - editType: 'number', - type: 'number', - defaultValue: 30, - fieldGroup: 'default', - }, - superSecret: { - type: 'string', - editType: 'string', - sensitive: true, - helpText: 'A really secret thing', - fieldGroup: 'default', - }, - }; - const OPENAPI_DESCRIPTIONS = { - token_bound_cidrs: { - type: 'array', - description: - 'Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.', - items: { - type: 'string', - }, - 'x-vault-displayAttrs': { - description: - 'List of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.', - name: "Generated Token's Bound CIDRs", - group: 'Tokens', - }, - }, - blah_blah: { - type: 'array', - description: 'Comma-separated list of policies', - items: { - type: 'string', - }, - 'x-vault-displayAttrs': { - name: "Generated Token's Policies", - group: 'Tokens', - }, - }, - only_display_description: { - type: 'array', - items: { - type: 'string', - }, - 'x-vault-displayAttrs': { - description: 'Hello there, you look nice today', - }, - }, - }; - - const STRING_ARRAY_DESCRIPTIONS = { - token_bound_cidrs: { - helpText: - 'List of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.', - }, - blah_blah: { - helpText: 'Comma-separated list of policies', - }, - only_display_description: { - helpText: 'Hello there, you look nice today', - }, - }; - test('it creates objects from OpenAPI schema props', function (assert) { - assert.expect(6); - const generatedProps = expandOpenApiProps(OPENAPI_RESPONSE_PROPS); - for (const propName in EXPANDED_PROPS) { - assert.deepEqual(EXPANDED_PROPS[propName], generatedProps[propName], `correctly expands ${propName}`); - } - }); - test('it uses the description from the display attrs block if it exists', async function (assert) { - assert.expect(3); - const generatedProps = expandOpenApiProps(OPENAPI_DESCRIPTIONS); - for (const propName in STRING_ARRAY_DESCRIPTIONS) { - assert.strictEqual( - generatedProps[camelize(propName)].helpText, - STRING_ARRAY_DESCRIPTIONS[propName].helpText, - `correctly updates helpText for ${propName}` - ); - } - }); - }); }); diff --git a/ui/tests/unit/utils/openapi-to-attrs-test.js b/ui/tests/unit/utils/openapi-to-attrs-test.js index 560bff9f48da..3d4cd57081e0 100644 --- a/ui/tests/unit/utils/openapi-to-attrs-test.js +++ b/ui/tests/unit/utils/openapi-to-attrs-test.js @@ -3,12 +3,215 @@ * SPDX-License-Identifier: BUSL-1.1 */ -import { combineFieldGroups } from 'vault/utils/openapi-to-attrs'; +import { attr } from '@ember-data/model'; +import { expandOpenApiProps, combineAttributes, combineFieldGroups } from 'vault/utils/openapi-to-attrs'; import { module, test } from 'qunit'; +import { camelize } from '@ember/string'; + +module('Unit | Util | OpenAPI Data Utilities', function () { + const OPENAPI_RESPONSE_PROPS = { + ttl: { + type: 'string', + format: 'seconds', + description: 'this is a TTL!', + 'x-vault-displayAttrs': { + name: 'TTL', + }, + }, + 'awesome-people': { + type: 'array', + items: { + type: 'string', + }, + 'x-vault-displayAttrs': { + value: 'Grace Hopper,Lady Ada', + }, + }, + 'favorite-ice-cream': { + type: 'string', + enum: ['vanilla', 'chocolate', 'strawberry'], + }, + 'default-value': { + default: 30, + 'x-vault-displayAttrs': { + value: 300, + }, + type: 'integer', + }, + default: { + 'x-vault-displayAttrs': { + value: 30, + }, + type: 'integer', + }, + 'super-secret': { + type: 'string', + 'x-vault-displayAttrs': { + sensitive: true, + }, + description: 'A really secret thing', + }, + }; + const EXPANDED_PROPS = { + ttl: { + helpText: 'this is a TTL!', + editType: 'ttl', + label: 'TTL', + fieldGroup: 'default', + }, + awesomePeople: { + editType: 'stringArray', + defaultValue: 'Grace Hopper,Lady Ada', + fieldGroup: 'default', + }, + favoriteIceCream: { + editType: 'string', + type: 'string', + possibleValues: ['vanilla', 'chocolate', 'strawberry'], + fieldGroup: 'default', + }, + defaultValue: { + editType: 'number', + type: 'number', + defaultValue: 300, + fieldGroup: 'default', + }, + default: { + editType: 'number', + type: 'number', + defaultValue: 30, + fieldGroup: 'default', + }, + superSecret: { + type: 'string', + editType: 'string', + sensitive: true, + helpText: 'A really secret thing', + fieldGroup: 'default', + }, + }; + + const EXISTING_MODEL_ATTRS = [ + { + key: 'name', + value: { + isAttribute: true, + name: 'name', + options: { + editType: 'string', + label: 'Role name', + }, + }, + }, + { + key: 'awesomePeople', + value: { + isAttribute: true, + name: 'awesomePeople', + options: { + label: 'People Who Are Awesome', + }, + }, + }, + ]; + + const COMBINED_ATTRS = { + name: attr('string', { + editType: 'string', + type: 'string', + label: 'Role name', + }), + ttl: attr('string', { + editType: 'ttl', + label: 'TTL', + helpText: 'this is a TTL!', + }), + awesomePeople: attr({ + label: 'People Who Are Awesome', + editType: 'stringArray', + defaultValue: 'Grace Hopper,Lady Ada', + }), + favoriteIceCream: attr('string', { + type: 'string', + editType: 'string', + possibleValues: ['vanilla', 'chocolate', 'strawberry'], + }), + superSecret: attr('string', { + type: 'string', + editType: 'string', + sensitive: true, + description: 'A really secret thing', + }), + }; -module('Unit | Util | combineFieldGroups', function () { const NEW_FIELDS = ['one', 'two', 'three']; + const OPENAPI_DESCRIPTIONS = { + token_bound_cidrs: { + type: 'array', + description: + 'Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.', + items: { + type: 'string', + }, + 'x-vault-displayAttrs': { + description: + 'List of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.', + name: "Generated Token's Bound CIDRs", + group: 'Tokens', + }, + }, + blah_blah: { + type: 'array', + description: 'Comma-separated list of policies', + items: { + type: 'string', + }, + 'x-vault-displayAttrs': { + name: "Generated Token's Policies", + group: 'Tokens', + }, + }, + only_display_description: { + type: 'array', + items: { + type: 'string', + }, + 'x-vault-displayAttrs': { + description: 'Hello there, you look nice today', + }, + }, + }; + + const STRING_ARRAY_DESCRIPTIONS = { + token_bound_cidrs: { + helpText: + 'List of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.', + }, + blah_blah: { + helpText: 'Comma-separated list of policies', + }, + only_display_description: { + helpText: 'Hello there, you look nice today', + }, + }; + + test('it creates objects from OpenAPI schema props', function (assert) { + assert.expect(6); + const generatedProps = expandOpenApiProps(OPENAPI_RESPONSE_PROPS); + for (const propName in EXPANDED_PROPS) { + assert.deepEqual(EXPANDED_PROPS[propName], generatedProps[propName], `correctly expands ${propName}`); + } + }); + + test('it combines OpenAPI props with existing model attrs', function (assert) { + assert.expect(3); + const combined = combineAttributes(EXISTING_MODEL_ATTRS, EXPANDED_PROPS); + for (const propName in EXISTING_MODEL_ATTRS) { + assert.deepEqual(COMBINED_ATTRS[propName], combined[propName]); + } + }); + test('it adds new fields from OpenAPI to fieldGroups except for exclusions', function (assert) { assert.expect(3); const modelFieldGroups = [ @@ -77,4 +280,16 @@ module('Unit | Util | combineFieldGroups', function () { assert.deepEqual(fieldGroups[groupName], expectedGroups[groupName], 'it incorporates all new fields'); } }); + + test('it uses the description from the display attrs block if it exists', async function (assert) { + assert.expect(3); + const generatedProps = expandOpenApiProps(OPENAPI_DESCRIPTIONS); + for (const propName in STRING_ARRAY_DESCRIPTIONS) { + assert.strictEqual( + generatedProps[camelize(propName)].helpText, + STRING_ARRAY_DESCRIPTIONS[propName].helpText, + `correctly updates helpText for ${propName}` + ); + } + }); }); diff --git a/ui/tests/unit/utils/model-helpers/validators-test.js b/ui/tests/unit/utils/validators-test.js similarity index 98% rename from ui/tests/unit/utils/model-helpers/validators-test.js rename to ui/tests/unit/utils/validators-test.js index 1642236db301..acd0011bf1b3 100644 --- a/ui/tests/unit/utils/model-helpers/validators-test.js +++ b/ui/tests/unit/utils/validators-test.js @@ -5,7 +5,7 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -import validators from 'vault/utils/model-helpers/validators'; +import validators from 'vault/utils/validators'; module('Unit | Util | validators', function (hooks) { setupTest(hooks); 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/vault/external_tests/raftha/raft_ha_test.go b/vault/external_tests/raftha/raft_ha_test.go index bbeb78eb3923..3f8c57d53533 100644 --- a/vault/external_tests/raftha/raft_ha_test.go +++ b/vault/external_tests/raftha/raft_ha_test.go @@ -14,7 +14,6 @@ import ( vaulthttp "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/sdk/helper/logging" "github.com/hashicorp/vault/vault" - "github.com/stretchr/testify/require" ) func TestRaft_HA_NewCluster(t *testing.T) { @@ -55,20 +54,6 @@ func TestRaft_HA_NewCluster(t *testing.T) { }) } -// TestRaftHA_Recover_Cluster test that we can recover data and re-boostrap a cluster -// that was created with raft HA enabled but is not using raft as the storage backend. -func TestRaftHA_Recover_Cluster(t *testing.T) { - logger := logging.NewVaultLogger(hclog.Debug).Named(t.Name()) - t.Run("file", func(t *testing.T) { - physBundle := teststorage.MakeFileBackend(t, logger) - testRaftHARecoverCluster(t, physBundle, logger) - }) - t.Run("inmem", func(t *testing.T) { - physBundle := teststorage.MakeInmemBackend(t, logger) - testRaftHARecoverCluster(t, physBundle, logger) - }) -} - func testRaftHANewCluster(t *testing.T, bundler teststorage.PhysicalBackendBundler, addClientCerts bool) { var conf vault.CoreConfig opts := vault.TestClusterOptions{HandlerFunc: vaulthttp.Handler} @@ -132,95 +117,6 @@ func testRaftHANewCluster(t *testing.T, bundler teststorage.PhysicalBackendBundl } } -// testRaftHARecoverCluster : in this test, we're going to create a raft HA cluster and store a test secret in a KVv2 -// We're going to simulate an outage and destroy the cluster but we'll keep the storage backend. -// We'll recreate a new cluster with the same storage backend and ensure that we can recover using -// sys/storage/raft/bootstrap. We'll check that the new cluster -// is functional and no data was lost: we can get the test secret from the KVv2. -func testRaftHARecoverCluster(t *testing.T, physBundle *vault.PhysicalBackendBundle, logger hclog.Logger) { - opts := vault.TestClusterOptions{ - HandlerFunc: vaulthttp.Handler, - // We're not testing the HA, only that it can be recovered. No need for multiple cores. - NumCores: 1, - } - - haStorage, haCleanup := teststorage.MakeReusableRaftHAStorage(t, logger, opts.NumCores, physBundle) - defer haCleanup() - haStorage.Setup(nil, &opts) - cluster := vault.NewTestCluster(t, nil, &opts) - - var ( - clusterBarrierKeys [][]byte - clusterRootToken string - ) - clusterBarrierKeys = cluster.BarrierKeys - clusterRootToken = cluster.RootToken - leaderCore := cluster.Cores[0] - testhelpers.EnsureCoreUnsealed(t, cluster, leaderCore) - - leaderClient := cluster.Cores[0].Client - leaderClient.SetToken(clusterRootToken) - - // Mount a KVv2 backend to store a test data - err := leaderClient.Sys().Mount("kv", &api.MountInput{ - Type: "kv-v2", - }) - require.NoError(t, err) - - kvData := map[string]interface{}{ - "data": map[string]interface{}{ - "kittens": "awesome", - }, - } - - // Store the test data in the KVv2 backend - _, err = leaderClient.Logical().Write("kv/data/test_known_data", kvData) - require.NoError(t, err) - - // We delete the current cluster. We keep the storage backend so we can recover the cluster - cluster.Cleanup() - - // We now have a raft HA cluster with a KVv2 backend enabled and a test data. - // We're now going to delete the cluster and create a new raft HA cluster with the same backend storage - // and ensure we can recover to a working vault cluster and don't lose the data from the backend storage. - - opts = vault.TestClusterOptions{ - HandlerFunc: vaulthttp.Handler, - // We're not testing the HA, only that it can be recovered. No need for multiple cores. - NumCores: 1, - // It's already initialized as we keep the same storage backend. - SkipInit: true, - } - haStorage, haCleanup = teststorage.MakeReusableRaftHAStorage(t, logger, opts.NumCores, physBundle) - defer haCleanup() - haStorage.Setup(nil, &opts) - clusterRestored := vault.NewTestCluster(t, nil, &opts) - - clusterRestored.BarrierKeys = clusterBarrierKeys - clusterRestored.RootToken = clusterRootToken - leaderCoreRestored := clusterRestored.Cores[0] - - testhelpers.EnsureCoresUnsealed(t, clusterRestored) - - leaderClientRestored := clusterRestored.Cores[0].Client - - // We now reset the TLS keyring and bootstrap the cluster again. - _, err = leaderClientRestored.Logical().Write("sys/storage/raft/bootstrap", nil) - require.NoError(t, err) - - vault.TestWaitActive(t, leaderCoreRestored.Core) - // Core should be active and cluster in a working state. We should be able to - // read the data from the KVv2 backend. - leaderClientRestored.SetToken(clusterRootToken) - secretRaw, err := leaderClientRestored.Logical().Read("kv/data/test_known_data") - require.NoError(t, err) - - data := secretRaw.Data["data"] - dataAsMap := data.(map[string]interface{}) - require.NotNil(t, dataAsMap) - require.Equal(t, "awesome", dataAsMap["kittens"]) -} - func TestRaft_HA_ExistingCluster(t *testing.T) { t.Parallel() conf := vault.CoreConfig{ diff --git a/vault/init.go b/vault/init.go index 4e12261e0041..28ff05743736 100644 --- a/vault/init.go +++ b/vault/init.go @@ -319,6 +319,32 @@ func (c *Core) Initialize(ctx context.Context, initParams *InitParams) (*InitRes SecretShares: [][]byte{}, } + // If we are storing shares, pop them out of the returned results and push + // them through the seal + switch c.seal.StoredKeysSupported() { + case seal.StoredKeysSupportedShamirRoot: + keysToStore := [][]byte{barrierKey} + if err := c.seal.GetAccess().SetShamirSealKey(sealKey); err != nil { + c.logger.Error("failed to set seal key", "error", err) + return nil, fmt.Errorf("failed to set seal key: %w", err) + } + if err := c.seal.SetStoredKeys(ctx, keysToStore); err != nil { + c.logger.Error("failed to store keys", "error", err) + return nil, fmt.Errorf("failed to store keys: %w", err) + } + results.SecretShares = sealKeyShares + case seal.StoredKeysSupportedGeneric: + keysToStore := [][]byte{barrierKey} + if err := c.seal.SetStoredKeys(ctx, keysToStore); err != nil { + c.logger.Error("failed to store keys", "error", err) + return nil, fmt.Errorf("failed to store keys: %w", err) + } + default: + // We don't support initializing an old-style Shamir seal anymore, so + // this case is only reachable by tests. + results.SecretShares = barrierKeyShares + } + // Perform initial setup if err := c.setupCluster(ctx); err != nil { c.logger.Error("cluster setup failed during init", "error", err) @@ -330,12 +356,6 @@ func (c *Core) Initialize(ctx context.Context, initParams *InitParams) (*InitRes initPTCleanup() } - // Save in a variable whether stored keys are supported before calling postUnsea(), as postUnseal() - // clears the barrier config. For a defaultSeal with a "legacy seal" (i.e. barrier config has StoredShares == 0), - // this will cause StoredKeysSupported() to go from StoredKeysNotSupported to StoredKeysSupportedShamirRoot. - // This would be a problem below when we determine whether to call SetStoredKeys. - storedKeysSupported := c.seal.StoredKeysSupported() - activeCtx, ctxCancel := context.WithCancel(namespace.RootContext(nil)) if err := c.postUnseal(activeCtx, ctxCancel, standardUnsealStrategy{}); err != nil { c.logger.Error("post-unseal setup failed during init", "error", err) @@ -393,32 +413,6 @@ func (c *Core) Initialize(ctx context.Context, initParams *InitParams) (*InitRes } } - // If we are storing shares, pop them out of the returned results and push - // them through the seal - switch storedKeysSupported { - case seal.StoredKeysSupportedShamirRoot: - keysToStore := [][]byte{barrierKey} - if err := c.seal.GetAccess().SetShamirSealKey(sealKey); err != nil { - c.logger.Error("failed to set seal key", "error", err) - return nil, fmt.Errorf("failed to set seal key: %w", err) - } - if err := c.seal.SetStoredKeys(ctx, keysToStore); err != nil { - c.logger.Error("failed to store keys", "error", err) - return nil, fmt.Errorf("failed to store keys: %w", err) - } - results.SecretShares = sealKeyShares - case seal.StoredKeysSupportedGeneric: - keysToStore := [][]byte{barrierKey} - if err := c.seal.SetStoredKeys(ctx, keysToStore); err != nil { - c.logger.Error("failed to store keys", "error", err) - return nil, fmt.Errorf("failed to store keys: %w", err) - } - default: - // We don't support initializing an old-style Shamir seal anymore, so - // this case is only reachable by tests. - results.SecretShares = barrierKeyShares - } - // Prepare to re-seal if err := c.preSeal(); err != nil { c.logger.Error("pre-seal teardown failed", "error", err) diff --git a/vault/raft.go b/vault/raft.go index 7d34bef99c63..97ced8f089c6 100644 --- a/vault/raft.go +++ b/vault/raft.go @@ -751,12 +751,7 @@ func (c *Core) raftCreateTLSKeyring(ctx context.Context) (*raft.TLSKeyring, erro } if raftTLSEntry != nil { - // For Raft storage, the keyring should already be there, but - // for situations with non-Raft storage and Raft HA, we can ignore this, - // as it will need to be remade. - if _, usingRaftStorage := c.underlyingPhysical.(*raft.RaftBackend); usingRaftStorage { - return nil, fmt.Errorf("TLS keyring already present") - } + return nil, fmt.Errorf("TLS keyring already present") } raftTLS, err := raft.GenerateTLSKey(c.secureRandomReader) 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/agent-and-proxy/autoauth/index.mdx b/website/content/docs/agent-and-proxy/autoauth/index.mdx index 442f140176a7..3bee8a43df31 100644 --- a/website/content/docs/agent-and-proxy/autoauth/index.mdx +++ b/website/content/docs/agent-and-proxy/autoauth/index.mdx @@ -105,10 +105,6 @@ The top level `auto_auth` block has two configuration entries: - `sinks` `(array of objects: optional)` - Configuration for the sinks -- `enable_reauth_on_new_credentials` `(bool: false)` - If enabled, Auto-auth will - handle new credential events from supported auth methods (AliCloud/AWS/Cert/JWT/LDAP/OCI) - and re-authenticate with the new credential. - ### Configuration (Method) ~> Auto-auth does not support using tokens with a limited number of uses. Auto-auth diff --git a/website/content/docs/agent-and-proxy/autoauth/methods/cert.mdx b/website/content/docs/agent-and-proxy/autoauth/methods/cert.mdx index 41740cd544aa..8a04bac8b5ff 100644 --- a/website/content/docs/agent-and-proxy/autoauth/methods/cert.mdx +++ b/website/content/docs/agent-and-proxy/autoauth/methods/cert.mdx @@ -31,14 +31,5 @@ config stanza, Agent and Proxy will fall back to using TLS settings from their r - `client_key` `(string: optional)` - Path on the local disk to a single PEM-encoded private key matching the client certificate from client_cert. -- `reload` `(bool: optional, default: false)` - If true, causes the local x509 - key-pair to be reloaded from disk on each authentication attempt. This is useful - in situations where client certificates are short-lived and automatically renewed. - Note that `enable_reauth_on_new_credentials` for auto-auth will need to be additionally - enabled for immediate re-auth on a new certificate. - See [Auto-Auth Configuration](/vault/docs/agent-and-proxy/autoauth#configuration). - -- `reload_period` `(duration: "1m", optional)` - The duration after which auto-auth - will check if there are any changes for the files that are configured through - `ca_cert`/`client_cert`/`client_key`. Defaults to `1m`. - Uses [duration format strings](/vault/docs/concepts/duration-format). +- `reload` `(bool: optional, default: false)` - If true, causes the local x509 key-pair to be reloaded from disk on each authentication attempt. + This is useful in situations where client certificates are short-lived and automatically renewed. 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.