From c7a224f8c1ed48e3a23dec609e36b83931f13ec6 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 12 Dec 2023 10:17:27 -0600 Subject: [PATCH 1/6] Add sanitizeStart helper and tests --- ui/lib/core/addon/utils/sanitize-path.js | 6 ++++++ ui/tests/unit/utils/sanitize-path-test.js | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/ui/lib/core/addon/utils/sanitize-path.js b/ui/lib/core/addon/utils/sanitize-path.js index 7ca66c651de..88a7bbc9fe3 100644 --- a/ui/lib/core/addon/utils/sanitize-path.js +++ b/ui/lib/core/addon/utils/sanitize-path.js @@ -9,6 +9,12 @@ export function sanitizePath(path) { return path.trim().replace(/^\/+|\/+$/g, ''); } +export function sanitizeStart(path) { + if (!path) return ''; + //remove leading slashes + return path.trim().replace(/^\/+/, ''); +} + export function ensureTrailingSlash(path) { return path.replace(/(\w+[^/]$)/g, '$1/'); } diff --git a/ui/tests/unit/utils/sanitize-path-test.js b/ui/tests/unit/utils/sanitize-path-test.js index 7107d737f2c..fe4fa998403 100644 --- a/ui/tests/unit/utils/sanitize-path-test.js +++ b/ui/tests/unit/utils/sanitize-path-test.js @@ -4,7 +4,7 @@ */ import { module, test } from 'qunit'; -import { ensureTrailingSlash, getRelativePath, sanitizePath } from 'core/utils/sanitize-path'; +import { ensureTrailingSlash, getRelativePath, sanitizePath, sanitizeStart } from 'core/utils/sanitize-path'; module('Unit | Utility | sanitize-path', function () { test('it removes spaces and slashes from either side', function (assert) { @@ -29,4 +29,18 @@ module('Unit | Utility | sanitize-path', function () { assert.strictEqual(getRelativePath('/recipes/cookies/choc-chip/', 'recipes/cookies'), 'choc-chip'); assert.strictEqual(getRelativePath('/admin/bop/boop/admin_foo/baz/', 'admin'), 'bop/boop/admin_foo/baz'); }); + + test('#sanitizeStart', function (assert) { + assert.strictEqual( + sanitizeStart(' /foo/bar/baz/ '), + 'foo/bar/baz/', + 'trims spaces and removes slashes only from beginning' + ); + assert.strictEqual( + sanitizeStart('//foo/bar/baz/'), + 'foo/bar/baz/', + 'removes more than one slash from start' + ); + assert.strictEqual(sanitizeStart(undefined), '', 'handles falsey values'); + }); }); From 033c1684a50fce7f3ef6e11354d16872dadbf286 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 12 Dec 2023 10:26:50 -0600 Subject: [PATCH 2/6] Update permission service to handle chroot_namespace; reorganize and add tests --- ui/app/services/permissions.js | 13 +- ui/tests/unit/services/permissions-test.js | 267 ++++++++++++--------- 2 files changed, 157 insertions(+), 123 deletions(-) diff --git a/ui/app/services/permissions.js b/ui/app/services/permissions.js index db23cfa809e..72240f5238b 100644 --- a/ui/app/services/permissions.js +++ b/ui/app/services/permissions.js @@ -4,6 +4,7 @@ */ import Service, { inject as service } from '@ember/service'; +import { sanitizePath, sanitizeStart } from 'core/utils/sanitize-path'; import { task } from 'ember-concurrency'; const API_PATHS = { @@ -65,6 +66,7 @@ export default Service.extend({ globPaths: null, canViewAll: null, readFailed: false, + chrootNamespace: null, store: service(), auth: service(), namespace: service(), @@ -89,6 +91,7 @@ export default Service.extend({ this.set('exactPaths', resp.data.exact_paths); this.set('globPaths', resp.data.glob_paths); this.set('canViewAll', resp.data.root); + this.set('chrootNamespace', resp.data.chroot_namespace); this.set('readFailed', false); }, @@ -97,6 +100,7 @@ export default Service.extend({ this.set('globPaths', null); this.set('canViewAll', null); this.set('readFailed', false); + this.set('chrootNamespace', null); }, hasNavPermission(navItem, routeParams, requireAll) { @@ -124,20 +128,21 @@ export default Service.extend({ }, pathNameWithNamespace(pathName) { - const namespace = this.namespace.path; + const namespace = this.chrootNamespace + ? `${sanitizePath(this.chrootNamespace)}/${sanitizePath(this.namespace.path)}` + : sanitizePath(this.namespace.path); if (namespace) { - return `${namespace}/${pathName}`; + return `${sanitizePath(namespace)}/${sanitizeStart(pathName)}`; } else { return pathName; } }, hasPermission(pathName, capabilities = [null]) { - const path = this.pathNameWithNamespace(pathName); - if (this.canViewAll) { return true; } + const path = this.pathNameWithNamespace(pathName); return capabilities.every( (capability) => diff --git a/ui/tests/unit/services/permissions-test.js b/ui/tests/unit/services/permissions-test.js index ceb7cf89429..c9483fc365a 100644 --- a/ui/tests/unit/services/permissions-test.js +++ b/ui/tests/unit/services/permissions-test.js @@ -53,66 +53,11 @@ module('Unit | Service | permissions', function (hooks) { assert.deepEqual(this.service.get('globPaths'), PERMISSIONS_RESPONSE.data.glob_paths); }); - test('returns true if a policy includes access to an exact path', function (assert) { - this.service.set('exactPaths', PERMISSIONS_RESPONSE.data.exact_paths); - assert.true(this.service.hasPermission('foo')); - }); - - test('returns true if a paths prefix is included in the policys exact paths', function (assert) { - this.service.set('exactPaths', PERMISSIONS_RESPONSE.data.exact_paths); - assert.true(this.service.hasPermission('bar')); - }); - - test('it returns true if a policy includes access to a glob path', function (assert) { - this.service.set('globPaths', PERMISSIONS_RESPONSE.data.glob_paths); - assert.true(this.service.hasPermission('baz/biz/hi')); - }); - - test('it returns true if a policy includes access to the * glob path', function (assert) { - const splatPath = { '': {} }; - this.service.set('globPaths', splatPath); - assert.true(this.service.hasPermission('hi')); - }); - - test('it returns false if the matched path includes the deny capability', function (assert) { - this.service.set('globPaths', PERMISSIONS_RESPONSE.data.glob_paths); - assert.false(this.service.hasPermission('boo')); - }); - - test('it returns true if passed path does not end in a slash but globPath does', function (assert) { - this.service.set('globPaths', PERMISSIONS_RESPONSE.data.glob_paths); - assert.true(this.service.hasPermission('ends/in/slash'), 'matches without slash'); - assert.true(this.service.hasPermission('ends/in/slash/'), 'matches with slash'); - }); - - test('it returns false if a policy does not includes access to a path', function (assert) { - assert.false(this.service.hasPermission('danger')); - }); - test('sets the root token', function (assert) { this.service.setPaths({ data: { root: true } }); assert.true(this.service.canViewAll); }); - test('returns true with the root token', function (assert) { - this.service.set('canViewAll', true); - assert.true(this.service.hasPermission('hi')); - }); - - test('it returns true if a policy has the specified capabilities on a path', function (assert) { - this.service.set('exactPaths', PERMISSIONS_RESPONSE.data.exact_paths); - this.service.set('globPaths', PERMISSIONS_RESPONSE.data.glob_paths); - assert.true(this.service.hasPermission('bar/bee', ['create', 'list'])); - assert.true(this.service.hasPermission('baz/biz', ['read'])); - }); - - test('it returns false if a policy does not have the specified capabilities on a path', function (assert) { - this.service.set('exactPaths', PERMISSIONS_RESPONSE.data.exact_paths); - this.service.set('globPaths', PERMISSIONS_RESPONSE.data.glob_paths); - assert.false(this.service.hasPermission('bar/bee', ['create', 'delete'])); - assert.false(this.service.hasPermission('foo', ['create'])); - }); - test('defaults to show all items when policy cannot be found', async function (assert) { this.server.get('/v1/sys/internal/ui/resultant-acl', () => { return [403, { 'Content-Type': 'application/json' }]; @@ -148,77 +93,161 @@ module('Unit | Service | permissions', function (hooks) { assert.deepEqual(this.service.navPathParams('access'), expected); }); - test('hasNavPermission returns true if a policy includes the required capabilities for at least one path', function (assert) { - const accessPaths = { - 'sys/auth': { - capabilities: ['deny'], - }, - 'identity/group/id': { - capabilities: ['list', 'read'], - }, - }; - this.service.set('exactPaths', accessPaths); - assert.true(this.service.hasNavPermission('access', 'groups')); - }); + module('hasPermission', function () { + test('returns true if a policy includes access to an exact path', function (assert) { + this.service.set('exactPaths', PERMISSIONS_RESPONSE.data.exact_paths); + assert.true(this.service.hasPermission('foo')); + }); - test('hasNavPermission returns false if a policy does not include the required capabilities for at least one path', function (assert) { - const accessPaths = { - 'sys/auth': { - capabilities: ['deny'], - }, - 'identity/group/id': { - capabilities: ['read'], - }, - }; - this.service.set('exactPaths', accessPaths); - assert.false(this.service.hasNavPermission('access', 'groups')); + test('returns true if a paths prefix is included in the policys exact paths', function (assert) { + this.service.set('exactPaths', PERMISSIONS_RESPONSE.data.exact_paths); + assert.true(this.service.hasPermission('bar')); + }); + + test('it returns true if a policy includes access to a glob path', function (assert) { + this.service.set('globPaths', PERMISSIONS_RESPONSE.data.glob_paths); + assert.true(this.service.hasPermission('baz/biz/hi')); + }); + + test('it returns true if a policy includes access to the * glob path', function (assert) { + const splatPath = { '': {} }; + this.service.set('globPaths', splatPath); + assert.true(this.service.hasPermission('hi')); + }); + + test('it returns false if the matched path includes the deny capability', function (assert) { + this.service.set('globPaths', PERMISSIONS_RESPONSE.data.glob_paths); + assert.false(this.service.hasPermission('boo')); + }); + + test('it returns true if passed path does not end in a slash but globPath does', function (assert) { + this.service.set('globPaths', PERMISSIONS_RESPONSE.data.glob_paths); + assert.true(this.service.hasPermission('ends/in/slash'), 'matches without slash'); + assert.true(this.service.hasPermission('ends/in/slash/'), 'matches with slash'); + }); + + test('it returns false if a policy does not includes access to a path', function (assert) { + assert.false(this.service.hasPermission('danger')); + }); + test('returns true with the root token', function (assert) { + this.service.set('canViewAll', true); + assert.true(this.service.hasPermission('hi')); + }); + test('it returns true if a policy has the specified capabilities on a path', function (assert) { + this.service.set('exactPaths', PERMISSIONS_RESPONSE.data.exact_paths); + this.service.set('globPaths', PERMISSIONS_RESPONSE.data.glob_paths); + assert.true(this.service.hasPermission('bar/bee', ['create', 'list'])); + assert.true(this.service.hasPermission('baz/biz', ['read'])); + }); + + test('it returns false if a policy does not have the specified capabilities on a path', function (assert) { + this.service.set('exactPaths', PERMISSIONS_RESPONSE.data.exact_paths); + this.service.set('globPaths', PERMISSIONS_RESPONSE.data.glob_paths); + assert.false(this.service.hasPermission('bar/bee', ['create', 'delete'])); + assert.false(this.service.hasPermission('foo', ['create'])); + }); }); - test('hasNavPermission should handle routeParams as array', function (assert) { - const getPaths = (override) => ({ - 'sys/auth': { - capabilities: [override || 'read'], - }, - 'identity/mfa/method': { - capabilities: [override || 'read'], - }, - 'identity/oidc/client': { - capabilities: [override || 'deny'], - }, + module('hasNavPermission', function () { + test('returns true if a policy includes the required capabilities for at least one path', function (assert) { + const accessPaths = { + 'sys/auth': { + capabilities: ['deny'], + }, + 'identity/group/id': { + capabilities: ['list', 'read'], + }, + }; + this.service.set('exactPaths', accessPaths); + assert.true(this.service.hasNavPermission('access', 'groups')); }); - this.service.set('exactPaths', getPaths()); - assert.true( - this.service.hasNavPermission('access', ['methods', 'mfa', 'oidc']), - 'hasNavPermission returns true for array of route params when any route is permitted' - ); - assert.false( - this.service.hasNavPermission('access', ['methods', 'mfa', 'oidc'], true), - 'hasNavPermission returns false for array of route params when any route is not permitted and requireAll is passed' - ); - - this.service.set('exactPaths', getPaths('read')); - assert.true( - this.service.hasNavPermission('access', ['methods', 'mfa', 'oidc'], true), - 'hasNavPermission returns true for array of route params when all routes are permitted and requireAll is passed' - ); - - this.service.set('exactPaths', getPaths('deny')); - assert.false( - this.service.hasNavPermission('access', ['methods', 'mfa', 'oidc']), - 'hasNavPermission returns false for array of route params when no routes are permitted' - ); - assert.false( - this.service.hasNavPermission('access', ['methods', 'mfa', 'oidc'], true), - 'hasNavPermission returns false for array of route params when no routes are permitted and requireAll is passed' - ); + test('returns false if a policy does not include the required capabilities for at least one path', function (assert) { + const accessPaths = { + 'sys/auth': { + capabilities: ['deny'], + }, + 'identity/group/id': { + capabilities: ['read'], + }, + }; + this.service.set('exactPaths', accessPaths); + assert.false(this.service.hasNavPermission('access', 'groups')); + }); + + test('should handle routeParams as array', function (assert) { + const getPaths = (override) => ({ + 'sys/auth': { + capabilities: [override || 'read'], + }, + 'identity/mfa/method': { + capabilities: [override || 'read'], + }, + 'identity/oidc/client': { + capabilities: [override || 'deny'], + }, + }); + + this.service.set('exactPaths', getPaths()); + assert.true( + this.service.hasNavPermission('access', ['methods', 'mfa', 'oidc']), + 'hasNavPermission returns true for array of route params when any route is permitted' + ); + assert.false( + this.service.hasNavPermission('access', ['methods', 'mfa', 'oidc'], true), + 'hasNavPermission returns false for array of route params when any route is not permitted and requireAll is passed' + ); + + this.service.set('exactPaths', getPaths('read')); + assert.true( + this.service.hasNavPermission('access', ['methods', 'mfa', 'oidc'], true), + 'hasNavPermission returns true for array of route params when all routes are permitted and requireAll is passed' + ); + + this.service.set('exactPaths', getPaths('deny')); + assert.false( + this.service.hasNavPermission('access', ['methods', 'mfa', 'oidc']), + 'hasNavPermission returns false for array of route params when no routes are permitted' + ); + assert.false( + this.service.hasNavPermission('access', ['methods', 'mfa', 'oidc'], true), + 'hasNavPermission returns false for array of route params when no routes are permitted and requireAll is passed' + ); + }); }); - test('appends the namespace to the path if there is one', function (assert) { - const namespaceService = Service.extend({ - path: 'marketing', + module('pathWithNamespace', function () { + test('appends the namespace to the path if there is one', function (assert) { + const namespaceService = Service.extend({ + path: 'marketing', + }); + this.owner.register('service:namespace', namespaceService); + assert.strictEqual(this.service.pathNameWithNamespace('sys/auth'), 'marketing/sys/auth'); + }); + + test('appends the chroot and namespace when both present', function (assert) { + const namespaceService = Service.extend({ + path: 'marketing', + }); + this.owner.register('service:namespace', namespaceService); + this.service.set('chrootNamespace', 'admin/'); + assert.strictEqual(this.service.pathNameWithNamespace('sys/auth'), 'admin/marketing/sys/auth'); + }); + test('appends the chroot when no namespace', function (assert) { + this.service.set('chrootNamespace', 'admin'); + assert.strictEqual(this.service.pathNameWithNamespace('sys/auth'), 'admin/sys/auth'); + }); + test('handles superfluous slashes', function (assert) { + const namespaceService = Service.extend({ + path: '/marketing', + }); + this.owner.register('service:namespace', namespaceService); + this.service.set('chrootNamespace', '/admin/'); + assert.strictEqual(this.service.pathNameWithNamespace('/sys/auth'), 'admin/marketing/sys/auth'); + assert.strictEqual( + this.service.pathNameWithNamespace('/sys/policies/'), + 'admin/marketing/sys/policies/' + ); }); - this.owner.register('service:namespace', namespaceService); - assert.strictEqual(this.service.pathNameWithNamespace('sys/auth'), 'marketing/sys/auth'); }); }); From 721ae85263b75e1410dc673b097a41d767c66eed Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 12 Dec 2023 10:27:47 -0600 Subject: [PATCH 3/6] hide nav items for root namespace only when in chroot --- ui/app/components/sidebar/nav/cluster.hbs | 12 +++++------- ui/app/components/sidebar/nav/cluster.js | 5 +++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ui/app/components/sidebar/nav/cluster.hbs b/ui/app/components/sidebar/nav/cluster.hbs index bb3c142de08..2eb24b9af03 100644 --- a/ui/app/components/sidebar/nav/cluster.hbs +++ b/ui/app/components/sidebar/nav/cluster.hbs @@ -44,9 +44,7 @@ {{#if (or - (and - this.namespace.inRootNamespace (has-permission "status" routeParams=(array "replication" "raft" "license" "seal")) - ) + (and this.isRootNamespace (has-permission "status" routeParams=(array "replication" "raft" "license" "seal"))) (has-permission "clients" routeParams="activity") ) }} @@ -55,7 +53,7 @@ {{#if (and this.version.isEnterprise - this.namespace.inRootNamespace + this.isRootNamespace (not this.cluster.replicationRedacted) (has-permission "status" routeParams="replication") ) @@ -67,7 +65,7 @@ @hasSubItems={{true}} /> {{/if}} - {{#if (and this.cluster.usingRaft this.namespace.inRootNamespace (has-permission "status" routeParams="raft"))}} + {{#if (and this.cluster.usingRaft this.isRootNamespace (has-permission "status" routeParams="raft"))}} {{/if}} - {{#if (and this.namespace.inRootNamespace (has-permission "status" routeParams="seal") (not this.cluster.dr.isSecondary))}} + {{#if (and this.isRootNamespace (has-permission "status" routeParams="seal") (not this.cluster.dr.isSecondary))}} Date: Tue, 12 Dec 2023 10:28:07 -0600 Subject: [PATCH 4/6] chroot nav test coverage --- ui/mirage/handlers/chroot-namespace.js | 4 + ui/tests/acceptance/chroot-namespace-test.js | 100 ++++++++++++++++++- 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/ui/mirage/handlers/chroot-namespace.js b/ui/mirage/handlers/chroot-namespace.js index 97a17e10074..6a6fb5d7eca 100644 --- a/ui/mirage/handlers/chroot-namespace.js +++ b/ui/mirage/handlers/chroot-namespace.js @@ -4,6 +4,7 @@ */ import { Response } from 'miragejs'; +import modifyPassthroughResponse from '../helpers/modify-passthrough-response'; /* These are mocked responses to mimic what we get from the server @@ -12,4 +13,7 @@ import { Response } from 'miragejs'; export default function (server) { server.get('sys/health', () => new Response(400, {}, { errors: ['unsupported path'] })); server.get('sys/replication/status', () => new Response(400, {}, { errors: ['unsupported path'] })); + server.get('sys/internal/ui/resultant-acl', (schema, req) => + modifyPassthroughResponse(req, { chroot_namespace: 'my-ns' }) + ); } diff --git a/ui/tests/acceptance/chroot-namespace-test.js b/ui/tests/acceptance/chroot-namespace-test.js index 5bca573f31c..2389ddba598 100644 --- a/ui/tests/acceptance/chroot-namespace-test.js +++ b/ui/tests/acceptance/chroot-namespace-test.js @@ -5,10 +5,15 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; -import { currentRouteName } from '@ember/test-helpers'; +import { currentRouteName, visit } from '@ember/test-helpers'; import authPage from 'vault/tests/pages/auth'; import { setupMirage } from 'ember-cli-mirage/test-support'; import ENV from 'vault/config/environment'; +import { createTokenCmd, runCmd, tokenWithPolicyCmd } from '../helpers/commands'; + +const navLink = (title) => `[data-test-sidebar-nav-link="${title}"]`; +// Matches the chroot namespace on the mirage handler +const namespace = 'my-ns'; module('Acceptance | chroot-namespace enterprise ui', function (hooks) { setupApplicationTest(hooks); @@ -26,4 +31,97 @@ module('Acceptance | chroot-namespace enterprise ui', function (hooks) { assert.strictEqual(currentRouteName(), 'vault.cluster.dashboard', 'goes to dashboard page'); assert.dom('[data-test-badge-namespace]').includesText('root', 'Shows root namespace badge'); }); + + test('a user with default policy should see nav items', async function (assert) { + await authPage.login(); + // Create namespace + await runCmd(`write sys/namespaces/${namespace} -f`, false); + // Create user within the namespace + await authPage.loginNs(namespace); + const userDefault = await runCmd(createTokenCmd()); + + await authPage.loginNs(namespace, userDefault); + ['Dashboard', 'Secrets Engines', 'Access', 'Tools'].forEach((nav) => { + assert.dom(navLink(nav)).exists(`Shows ${nav} nav item for user with default policy`); + }); + ['Policies', 'Client Count', 'Replication', 'Raft Storage', 'License', 'Seal Vault'].forEach((nav) => { + assert.dom(navLink(nav)).doesNotExist(`Does not show ${nav} nav item for user with default policy`); + }); + + // cleanup namespace + await authPage.login(); + await runCmd(`delete sys/namespaces/${namespace}`); + }); + + test('a user with read policy should see nav items', async function (assert) { + await authPage.login(); + // Create namespace + await runCmd(`write sys/namespaces/${namespace} -f`, false); + // Create user within the namespace + await authPage.loginNs(namespace); + const reader = await runCmd( + tokenWithPolicyCmd( + 'read-all', + ` + path "sys/*" { + capabilities = ["read"] + } + ` + ) + ); + + await authPage.loginNs(namespace, reader); + ['Dashboard', 'Secrets Engines', 'Access', 'Policies', 'Tools', 'Client Count'].forEach((nav) => { + assert.dom(navLink(nav)).exists(`Shows ${nav} nav item for user with read access policy`); + }); + ['Replication', 'Raft Storage', 'License', 'Seal Vault'].forEach((nav) => { + assert.dom(navLink(nav)).doesNotExist(`Does not show ${nav} nav item for user with read access policy`); + }); + + // cleanup namespace + await authPage.login(); + await runCmd(`delete sys/namespaces/${namespace}`); + }); + + test('it works within a child namespace', async function (assert) { + await authPage.login(); + // Create namespace + await runCmd(`write sys/namespaces/${namespace} -f`, false); + // Create user within the namespace + await authPage.loginNs(namespace); + const childReader = await runCmd( + tokenWithPolicyCmd( + 'read-child', + ` + path "child/sys/*" { + capabilities = ["read"] + } + ` + ) + ); + // Create child namespace + await runCmd(`write sys/namespaces/child -f`, false); + + await authPage.loginNs(namespace, childReader); + ['Dashboard', 'Secrets Engines', 'Access', 'Tools'].forEach((nav) => { + assert.dom(navLink(nav)).exists(`Shows ${nav} nav item`); + }); + ['Policies', 'Client Count', 'Replication', 'Raft Storage', 'License', 'Seal Vault'].forEach((nav) => { + assert.dom(navLink(nav)).doesNotExist(`Does not show ${nav} nav item`); + }); + + visit(`/vault/dashboard?namespace=${namespace}/child`); + ['Dashboard', 'Secrets Engines', 'Access', 'Policies', 'Tools', 'Client Count'].forEach((nav) => { + assert.dom(navLink(nav)).exists(`Shows ${nav} nav item within child namespace`); + }); + ['Replication', 'Raft Storage', 'License', 'Seal Vault'].forEach((nav) => { + assert.dom(navLink(nav)).doesNotExist(`Does not show ${nav} nav item within child namespace`); + }); + + // cleanup namespaces + await authPage.loginNs(namespace); + await runCmd(`delete sys/namespaces/child`); + await authPage.login(); + await runCmd(`delete sys/namespaces/${namespace}`); + }); }); From bd96b1318c17a935427af7fd535747c72a5f44e5 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 12 Dec 2023 10:56:07 -0600 Subject: [PATCH 5/6] fix test --- ui/tests/acceptance/chroot-namespace-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/tests/acceptance/chroot-namespace-test.js b/ui/tests/acceptance/chroot-namespace-test.js index 2389ddba598..48fe749ef8e 100644 --- a/ui/tests/acceptance/chroot-namespace-test.js +++ b/ui/tests/acceptance/chroot-namespace-test.js @@ -5,7 +5,7 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; -import { currentRouteName, visit } from '@ember/test-helpers'; +import { currentRouteName } from '@ember/test-helpers'; import authPage from 'vault/tests/pages/auth'; import { setupMirage } from 'ember-cli-mirage/test-support'; import ENV from 'vault/config/environment'; @@ -110,7 +110,7 @@ module('Acceptance | chroot-namespace enterprise ui', function (hooks) { assert.dom(navLink(nav)).doesNotExist(`Does not show ${nav} nav item`); }); - visit(`/vault/dashboard?namespace=${namespace}/child`); + await authPage.loginNs(`${namespace}/child`, childReader); ['Dashboard', 'Secrets Engines', 'Access', 'Policies', 'Tools', 'Client Count'].forEach((nav) => { assert.dom(navLink(nav)).exists(`Shows ${nav} nav item within child namespace`); }); From 93d0040c9d83ee3cfa0706570fb5ab1a8bcedd53 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 12 Dec 2023 12:44:06 -0600 Subject: [PATCH 6/6] Add changelog --- changelog/24492.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/24492.txt diff --git a/changelog/24492.txt b/changelog/24492.txt new file mode 100644 index 00000000000..d61c901a2c1 --- /dev/null +++ b/changelog/24492.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: fix navigation items shown to user when chroot_namespace configured +```