From 8da4386caceb3fdfaa90074bb29c77e8a99c7dad Mon Sep 17 00:00:00 2001 From: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Date: Fri, 27 Jun 2025 15:13:24 -0700 Subject: [PATCH] UI: Fixes kv v2 secret overview for failed subkeys policy check for secrets with underscores (#31136) * default subkeyData to an empty object * add changelog and extra check * m rewrite test stubbing capabilities intead --- changelog/31136.txt | 3 +++ ui/lib/kv/addon/routes/secret.js | 4 +-- .../kv/kv-v2-workflow-edge-cases-test.js | 25 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 changelog/31136.txt diff --git a/changelog/31136.txt b/changelog/31136.txt new file mode 100644 index 0000000000..0d78bb1b42 --- /dev/null +++ b/changelog/31136.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fix kv v2 overview page from erroring if a user does not have access to the /subkeys endpoint and the policy check fails. +``` \ No newline at end of file diff --git a/ui/lib/kv/addon/routes/secret.js b/ui/lib/kv/addon/routes/secret.js index 43ff63a01f..b96c9cd04a 100644 --- a/ui/lib/kv/addon/routes/secret.js +++ b/ui/lib/kv/addon/routes/secret.js @@ -37,11 +37,11 @@ export default class KvSecretRoute extends Route { return null; } - isPatchAllowed({ capabilities, subkeysMeta }) { + isPatchAllowed({ capabilities, subkeysMeta = {} }) { if (!this.version.isEnterprise) return false; const canReadSubkeys = capabilities.subkeys.canRead; const canPatchData = capabilities.data.canPatch; - if (canReadSubkeys && canPatchData) { + if (canReadSubkeys && canPatchData && subkeysMeta) { const { deletion_time, destroyed } = subkeysMeta; const isLatestActive = isDeleted(deletion_time) || destroyed ? false : true; // only the latest secret version can be patched and it must not be deleted or destroyed diff --git a/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-edge-cases-test.js b/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-edge-cases-test.js index 5919abd80f..ebffffd8e3 100644 --- a/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-edge-cases-test.js +++ b/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-edge-cases-test.js @@ -37,12 +37,15 @@ import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors'; import codemirror from 'vault/tests/helpers/codemirror'; import { personas } from 'vault/tests/helpers/kv/policy-generator'; +import { capabilitiesStub } from 'vault/tests/helpers/stubs'; +import { setupMirage } from 'ember-cli-mirage/test-support'; /** * This test set is for testing edge cases, such as specific bug fixes or reported user workflows */ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) { setupApplicationTest(hooks); + setupMirage(hooks); hooks.beforeEach(async function () { const uid = uuidv4(); @@ -195,6 +198,28 @@ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) { assert.dom(PAGE.secretTab('Secrets')).doesNotHaveClass('is-active'); assert.dom(PAGE.secretTab('Configuration')).doesNotHaveClass('is-active'); }); + + // it's rare for a policy check to fail, but if it does we default to "true" and let the API handle gating. + // there was an issue with the new capabilities service incorrectly mapping permissions for secrets with underscores which surfaced this bug. + // The user logged in here does NOT have access to the subkeys endpoint, but we're stubbing capabilities to return true + // to simulate the capabilities map failing and returning a false positive. + test('it navigates to secret if policy check fails for the subkeys endpoint', async function (assert) { + assert.expect(2); + this.server.post( + '/sys/capabilities-self', + capabilitiesStub(`${this.backend}/subkeys/my_secret`, ['read']) + ); + + await visit(`/vault/secrets/${this.backend}/kv/list`); + await typeIn(PAGE.list.overviewInput, 'my_secret'); + await click(GENERAL.submitButton); + assert.strictEqual( + currentURL(), + `/vault/secrets/${this.backend}/kv/my_secret`, + 'it navigates to secret overview' + ); + assert.dom(GENERAL.overviewCard.container('Paths')).exists(); + }); }); module('destruction without read', function (hooks) {