diff --git a/ui/app/components/namespace-picker.js b/ui/app/components/namespace-picker.js index 550ab87fa04d..8a0a623d8a15 100644 --- a/ui/app/components/namespace-picker.js +++ b/ui/app/components/namespace-picker.js @@ -126,6 +126,7 @@ export default Component.extend({ // gets set as 'lastMenuLeaves' in the ember concurrency task above menuLeaves: computed('namespacePath', 'namespaceTree', function() { let ns = this.get('namespacePath'); + ns = ns.replace(/^\//, ''); let leaves = ancestorKeysForKey(ns); leaves.push(ns); leaves = this.maybeAddRoot(leaves); diff --git a/ui/app/routes/vault/cluster/logout.js b/ui/app/routes/vault/cluster/logout.js index 81df5af42969..d54b22ad9244 100644 --- a/ui/app/routes/vault/cluster/logout.js +++ b/ui/app/routes/vault/cluster/logout.js @@ -9,19 +9,21 @@ export default Route.extend(ModelBoundaryRoute, { flashMessages: service(), console: service(), permissions: service(), + namespaceService: service('namespace'), modelTypes: computed(function() { return ['secret', 'secret-engine']; }), beforeModel() { - this.get('auth').deleteCurrentToken(); - this.get('controlGroup').deleteTokens(); - this.get('console').set('isOpen', false); - this.get('console').clearLog(true); + this.auth.deleteCurrentToken(); + this.controlGroup.deleteTokens(); + this.namespaceService.reset(); + this.console.set('isOpen', false); + this.console.clearLog(true); this.clearModelCache(); this.replaceWith('vault.cluster'); - this.get('flashMessages').clearMessages(); - this.get('permissions').reset(); + this.flashMessages.clearMessages(); + this.permissions.reset(); }, }); diff --git a/ui/app/services/namespace.js b/ui/app/services/namespace.js index 7e38e57584b2..2f3c6f917500 100644 --- a/ui/app/services/namespace.js +++ b/ui/app/services/namespace.js @@ -33,9 +33,10 @@ export default Service.extend({ namespace: userRoot, }, }); + let keys = ns.data.keys || []; this.set( 'accessibleNamespaces', - ns.data.keys.map(n => { + keys.map(n => { let fullNS = n; // if the user's root isn't '', then we need to construct // the paths so they connect to the user root to the list @@ -51,4 +52,8 @@ export default Service.extend({ //do nothing here } }).drop(), + + reset() { + this.set('accessibleNamespaces', null); + }, }); diff --git a/ui/app/templates/components/namespace-link.hbs b/ui/app/templates/components/namespace-link.hbs index 39532a1b7eb3..693069c83b16 100644 --- a/ui/app/templates/components/namespace-link.hbs +++ b/ui/app/templates/components/namespace-link.hbs @@ -1,11 +1,13 @@ -{{#link-to "vault.cluster.secrets" (query-params namespace=normalizedNamespace) - class=(concat "is-block " class) -}} + {{#if (has-block)}} {{yield}} {{else}}
- {{namespaceDisplay}} + {{this.namespaceDisplay}}
{{/if}} -{{/link-to}} +
diff --git a/ui/app/templates/components/namespace-picker.hbs b/ui/app/templates/components/namespace-picker.hbs index 8b86a5ee9297..facc69139d8e 100644 --- a/ui/app/templates/components/namespace-picker.hbs +++ b/ui/app/templates/components/namespace-picker.hbs @@ -9,6 +9,7 @@ {{yield}} {{#if namespaceDisplay}} @@ -44,7 +45,7 @@
Current namespace
{{/each}} diff --git a/ui/tests/acceptance/enterprise-namespaces-test.js b/ui/tests/acceptance/enterprise-namespaces-test.js new file mode 100644 index 000000000000..0c844d33c7a2 --- /dev/null +++ b/ui/tests/acceptance/enterprise-namespaces-test.js @@ -0,0 +1,63 @@ +import { click, currentRouteName, currentURL, visit } from '@ember/test-helpers'; +import { module, test } from 'qunit'; +import { setupApplicationTest } from 'ember-qunit'; +import { create } from 'ember-cli-page-object'; + +import consoleClass from 'vault/tests/pages/components/console/ui-panel'; +import authPage from 'vault/tests/pages/auth'; +import logout from 'vault/tests/pages/logout'; + +const shell = create(consoleClass); + +const createNS = async name => { + await shell.runCommands(`write sys/namespaces/${name} -force`); +}; + +const switchToNS = async name => { + await click('[data-test-namespace-toggle]'); + await click(`[data-test-namespace-link="${name}"]`); + await click('[data-test-namespace-toggle]'); +}; + +module('Acceptance | Enterprise | namespaces', function(hooks) { + setupApplicationTest(hooks); + + hooks.beforeEach(function() { + return authPage.login(); + }); + + test('it clears namespaces when you log out', async function(assert) { + let ns = 'foo'; + await createNS(ns); + await shell.runCommands(`write -field=client_token auth/token/create policies=default`); + let token = shell.lastLogOutput; + await logout.visit(); + await authPage.login(token); + assert.dom('[data-test-namespace-toggle]').doesNotExist('does not show the namespace picker'); + await logout.visit(); + }); + + test('it shows nested namespaces if you log in with a namspace starting with a /', async function(assert) { + let nses = ['beep', 'boop', 'bop']; + for (let [i, ns] of nses.entries()) { + await createNS(ns); + // this is usually triggered when creating a ns in the form, here we'll trigger a reload of the + // namespaces manually + await this.owner.lookup('service:namespace').findNamespacesForUser.perform(); + if (i === nses.length - 1) { + break; + } + // the namespace path will include all of the namespaces up to this point + let targetNamespace = nses.slice(0, i + 1).join('/'); + await switchToNS(targetNamespace); + } + await logout.visit(); + await authPage.visit({ with: 'token', namespace: '/beep/boop' }); + await authPage.tokenInput('root').submit(); + await click('[data-test-namespace-toggle]'); + assert.dom('[data-test-current-namespace]').hasText('/beep/boop/', 'current namespace begins with a /'); + assert + .dom('[data-test-namespace-link="beep/boop/bop"]') + .exists('renders the link to the nested namespace'); + }); +});