From 27170b662de88eac7f92b13b54abd3478280274e Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Fri, 13 Oct 2023 11:01:23 -0600 Subject: [PATCH] Replace KV v2 List route with wildcard instead of dynamic segment (#23620) * initial work on the LIST route. * fix * changelog * add s * add in prepending forward slash --- changelog/23620.txt | 3 ++ ui/lib/kv/addon/routes.js | 5 ++- .../backend/kv/kv-v2-workflow-create-test.js | 14 +++---- .../kv/kv-v2-workflow-edge-cases-test.js | 8 ++-- .../kv/kv-v2-workflow-navigation-test.js | 42 +++++++++---------- 5 files changed, 38 insertions(+), 34 deletions(-) create mode 100644 changelog/23620.txt diff --git a/changelog/23620.txt b/changelog/23620.txt new file mode 100644 index 0000000000..60667c2806 --- /dev/null +++ b/changelog/23620.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixes issue where you could not share the list view URL from the KV v2 secrets engine. +``` \ No newline at end of file diff --git a/ui/lib/kv/addon/routes.js b/ui/lib/kv/addon/routes.js index 529ecbe8d5..507662f04a 100644 --- a/ui/lib/kv/addon/routes.js +++ b/ui/lib/kv/addon/routes.js @@ -7,9 +7,10 @@ import buildRoutes from 'ember-engines/routes'; export default buildRoutes(function () { // There are two list routes because Ember won't let a route param (e.g. *path_to_secret) be blank. - // :path_to_secret is used when we're listing a secret directory. Example { path: '/:beep%2Fboop%2F/directory' }); + // *path_to_secret is used when we're listing a secret directory. + // Must use a wildcard for path-to-secret because the value can contain a forward slash if it's a secret directory. Ember's router decodes encoded forward slashes which leads to beep%2fboop becoming beep/boop and messing up routing after copying and pasting the URL. this.route('list'); - this.route('list-directory', { path: '/:path_to_secret/directory' }); + this.route('list-directory', { path: '/list/*path_to_secret' }); this.route('create'); this.route('secret', { path: '/:name' }, function () { this.route('paths'); diff --git a/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-create-test.js b/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-create-test.js index 82ea955118..a807da07ce 100644 --- a/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-create-test.js +++ b/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-create-test.js @@ -213,7 +213,7 @@ module('Acceptance | kv-v2 workflow | secret and version create', function (hook }); test('creates a secret at a sub-directory (a)', async function (assert) { const backend = this.backend; - await visit(`/vault/secrets/${backend}/kv/app%2F/directory`); + await visit(`/vault/secrets/${backend}/kv/list/app/`); assert.dom(PAGE.list.item('first')).exists('Lists first sub-secret'); assert.dom(PAGE.list.item('new')).doesNotExist('Does not show new secret'); await click(PAGE.list.createSecret); @@ -232,7 +232,7 @@ module('Acceptance | kv-v2 workflow | secret and version create', function (hook 'Redirects to detail after save' ); await click(PAGE.breadcrumbAtIdx(2)); - assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/app%2F/directory`, 'sub-dir page'); + assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list/app/`, 'sub-dir page'); assert.dom(PAGE.list.item('new')).exists('Lists new secret in sub-dir'); }); test('create new version of secret from older version (a)', async function (assert) { @@ -390,7 +390,7 @@ module('Acceptance | kv-v2 workflow | secret and version create', function (hook }); test('creates a secret at a sub-directory (dr)', async function (assert) { const backend = this.backend; - await visit(`/vault/secrets/${backend}/kv/app%2F/directory`); + await visit(`/vault/secrets/${backend}/kv/list/app/`); assert.dom(PAGE.list.item()).doesNotExist('Does not list any secrets'); await click(PAGE.list.createSecret); assert.strictEqual( @@ -535,7 +535,7 @@ module('Acceptance | kv-v2 workflow | secret and version create', function (hook }); test('creates a secret at a sub-directory (dlr)', async function (assert) { const backend = this.backend; - await visit(`/vault/secrets/${backend}/kv/app%2F/directory`); + await visit(`/vault/secrets/${backend}/kv/list/app/`); assert.dom(PAGE.list.item()).doesNotExist('Does not list any secrets'); await click(PAGE.list.createSecret); assert.strictEqual( @@ -704,7 +704,7 @@ module('Acceptance | kv-v2 workflow | secret and version create', function (hook }); test('creates a secret at a sub-directory (mm)', async function (assert) { const backend = this.backend; - await visit(`/vault/secrets/${backend}/kv/app%2F/directory`); + await visit(`/vault/secrets/${backend}/kv/list/app/`); assert.dom(PAGE.list.item('first')).exists('Lists first sub-secret'); assert.dom(PAGE.list.item('new')).doesNotExist('Does not show new secret'); await click(PAGE.list.createSecret); @@ -941,7 +941,7 @@ module('Acceptance | kv-v2 workflow | secret and version create', function (hook }); test('creates a secret at a sub-directory (sc)', async function (assert) { const backend = this.backend; - await visit(`/vault/secrets/${backend}/kv/app%2F/directory`); + await visit(`/vault/secrets/${backend}/kv/list/app/`); assert.dom(PAGE.list.item()).doesNotExist('Does not list any secrets'); await click(PAGE.list.createSecret); assert.strictEqual( @@ -959,7 +959,7 @@ module('Acceptance | kv-v2 workflow | secret and version create', function (hook 'Redirects to detail after save' ); await click(PAGE.breadcrumbAtIdx(2)); - assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/app%2F/directory`, 'sub-dir page'); + assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list/app/`, 'sub-dir page'); assert.dom(PAGE.list.item()).doesNotExist('Does not list any secrets'); }); test('create new version of secret from older version (sc)', async function (assert) { 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 7c9543e7cb..54d01b63f3 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 @@ -84,7 +84,7 @@ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) { // URL correct assert.strictEqual( currentURL(), - `/vault/secrets/${backend}/kv/${root}%2F/directory`, + `/vault/secrets/${backend}/kv/list/${root}/`, 'visits list-directory of root' ); @@ -125,7 +125,7 @@ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) { await click(PAGE.breadcrumbAtIdx(previousCrumb)); assert.strictEqual( currentURL(), - `/vault/secrets/${backend}/kv/${root}%2F${subdirectory}%2F/directory`, + `/vault/secrets/${backend}/kv/list/${root}/${subdirectory}/`, 'goes back to subdirectory list' ); assert.dom(PAGE.list.filter).hasValue(`${root}/${subdirectory}/`); @@ -136,7 +136,7 @@ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) { await click(PAGE.breadcrumbAtIdx(previousCrumb)); assert.strictEqual( currentURL(), - `/vault/secrets/${backend}/kv/${root}%2F/directory`, + `/vault/secrets/${backend}/kv/list/${root}/`, 'goes back to root directory' ); assert.dom(PAGE.list.item(`${subdirectory}/`)).exists('renders linked block for subdirectory'); @@ -253,7 +253,7 @@ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) { }); test('no ghost item after editing metadata', async function (assert) { - await visit(`/vault/secrets/${this.backend}/kv/edge/directory`); + await visit(`/vault/secrets/${this.backend}/kv/list/edge/`); assert.dom(PAGE.list.item()).exists({ count: 2 }, 'two secrets are listed'); await click(PAGE.list.item('two')); await click(PAGE.secretTab('Metadata')); diff --git a/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-navigation-test.js b/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-navigation-test.js index 52a95dba5a..1babf24b28 100644 --- a/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-navigation-test.js +++ b/ui/tests/acceptance/secrets/backend/kv/kv-v2-workflow-navigation-test.js @@ -165,14 +165,14 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) { // Navigate through list items await click(PAGE.list.item('app/')); - assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/app%2F/directory`); + assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list/app/`); assertCorrectBreadcrumbs(assert, ['secret', backend, 'app']); assert.dom(PAGE.title).hasText(`${backend} Version 2`); assert.dom(PAGE.list.filter).hasValue('app/', 'List filter input is prefilled'); assert.dom(PAGE.list.item('nested/')).exists('Shows nested secret'); await click(PAGE.list.item('nested/')); - assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`); + assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list/app/nested/`); assertCorrectBreadcrumbs(assert, ['secret', backend, 'app', 'nested']); assert.dom(PAGE.title).hasText(`${backend} Version 2`); assert.dom(PAGE.list.filter).hasValue('app/nested/', 'List filter input is prefilled'); @@ -189,13 +189,13 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) { await click(PAGE.breadcrumbAtIdx(3)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`), 'links back to list directory' ); await click(PAGE.breadcrumbAtIdx(2)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`), 'links back to list directory' ); @@ -416,13 +416,13 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) { await click(PAGE.breadcrumbAtIdx(3)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`), 'links back to list directory' ); await click(PAGE.breadcrumbAtIdx(2)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`), 'links back to list directory' ); @@ -585,7 +585,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) { // Navigate through list items await click(PAGE.list.item('app/')); - assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/app%2F/directory`); + assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list/app/`); assertCorrectBreadcrumbs(assert, ['secret', backend, 'app']); assert.dom(PAGE.title).hasText(`${backend} Version 2`); assert.dom(PAGE.list.filter).doesNotExist('List filter hidden since no nested list access'); @@ -606,13 +606,13 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) { await click(PAGE.breadcrumbAtIdx(3)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`), 'links back to list directory' ); await click(PAGE.breadcrumbAtIdx(2)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`), 'links back to list directory' ); @@ -771,14 +771,14 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) { // Navigate through list items await click(PAGE.list.item('app/')); - assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/app%2F/directory`); + assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list/app/`); assertCorrectBreadcrumbs(assert, ['secret', backend, 'app']); assert.dom(PAGE.title).hasText(`${backend} Version 2`); assert.dom(PAGE.list.filter).hasValue('app/', 'List filter input is prefilled'); assert.dom(PAGE.list.item('nested/')).exists('Shows nested secret'); await click(PAGE.list.item('nested/')); - assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`); + assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list/app/nested/`); assertCorrectBreadcrumbs(assert, ['secret', backend, 'app', 'nested']); assert.dom(PAGE.title).hasText(`${backend} Version 2`); assert.dom(PAGE.list.filter).hasValue('app/nested/', 'List filter input is prefilled'); @@ -797,13 +797,13 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) { await click(PAGE.breadcrumbAtIdx(3)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`), 'links back to list directory' ); await click(PAGE.breadcrumbAtIdx(2)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`), 'links back to list directory' ); @@ -989,13 +989,13 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) { await click(PAGE.breadcrumbAtIdx(3)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`), 'links back to list directory' ); await click(PAGE.breadcrumbAtIdx(2)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`), 'links back to list directory' ); @@ -1152,14 +1152,14 @@ path "${this.backend}/*" { // Navigate through list items await click(PAGE.list.item('app/')); - assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/app%2F/directory`); + assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list/app/`); assertCorrectBreadcrumbs(assert, ['secret', backend, 'app']); assert.dom(PAGE.title).hasText(`${backend} Version 2`); assert.dom(PAGE.list.filter).hasValue('app/', 'List filter input is prefilled'); assert.dom(PAGE.list.item('nested/')).exists('Shows nested secret'); await click(PAGE.list.item('nested/')); - assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`); + assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list/app/nested/`); assertCorrectBreadcrumbs(assert, ['secret', backend, 'app', 'nested']); assert.dom(PAGE.title).hasText(`${backend} Version 2`); assert.dom(PAGE.list.filter).hasValue('app/nested/', 'List filter input is prefilled'); @@ -1174,12 +1174,12 @@ path "${this.backend}/*" { ); await grantAccess({ apiPath: `${backend}/data/app/nested/secret`, - originUrl: `/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`, + originUrl: `/vault/secrets/${backend}/kv/list/app/nested/`, userToken: this.userToken, }); assert.strictEqual( currentURL(), - `/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`, + `/vault/secrets/${backend}/kv/list/app/nested/`, 'navigates to list url where secret is' ); await click(PAGE.list.item('secret')); @@ -1195,13 +1195,13 @@ path "${this.backend}/*" { await click(PAGE.breadcrumbAtIdx(3)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2Fnested%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`), 'links back to list directory' ); await click(PAGE.breadcrumbAtIdx(2)); assert.ok( - currentURL().startsWith(`/vault/secrets/${backend}/kv/app%2F/directory`), + currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`), 'links back to list directory' );