diff --git a/changelog/14422.txt b/changelog/14422.txt new file mode 100644 index 000000000..edfc38067 --- /dev/null +++ b/changelog/14422.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Redirects to managed namespace if incorrect namespace in URL param +``` \ No newline at end of file diff --git a/ui/app/routes/vault/cluster.js b/ui/app/routes/vault/cluster.js index 2d36978ef..9f80e82e7 100644 --- a/ui/app/routes/vault/cluster.js +++ b/ui/app/routes/vault/cluster.js @@ -10,6 +10,16 @@ import ModelBoundaryRoute from 'vault/mixins/model-boundary-route'; const POLL_INTERVAL_MS = 10000; +export const getManagedNamespace = (nsParam, root) => { + if (!nsParam || nsParam.replaceAll('/', '') === root) return root; + // Check if param starts with root and / + if (nsParam.startsWith(`${root}/`)) { + return nsParam; + } + // Otherwise prepend the given param with the root + return `${root}/${nsParam}`; +}; + export default Route.extend(ModelBoundaryRoute, ClusterRoute, { namespaceService: service('namespace'), version: service(), @@ -38,20 +48,25 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, { const params = this.paramsFor(this.routeName); let namespace = params.namespaceQueryParam; const currentTokenName = this.auth.get('currentTokenName'); - // if no namespace queryParam and user authenticated, - // use user's root namespace to redirect to properly param'd url - if (this.featureFlagService.managedNamespaceRoot && this.version.isOSS) { + const managedRoot = this.featureFlagService.managedNamespaceRoot; + if (managedRoot && this.version.isOSS) { + // eslint-disable-next-line no-console console.error('Cannot use Cloud Admin Namespace flag with OSS Vault'); } if (!namespace && currentTokenName && !Ember.testing) { + // if no namespace queryParam and user authenticated, + // use user's root namespace to redirect to properly param'd url const storage = getStorage().getItem(currentTokenName); namespace = storage?.userRootNamespace; // only redirect if something other than nothing if (namespace) { this.transitionTo({ queryParams: { namespace } }); } - } else if (!namespace && !!this.featureFlagService.managedNamespaceRoot) { - this.transitionTo({ queryParams: { namespace: this.featureFlagService.managedNamespaceRoot } }); + } else if (managedRoot !== null) { + let managed = getManagedNamespace(namespace, managedRoot); + if (managed !== namespace) { + this.transitionTo({ queryParams: { namespace: managed } }); + } } this.namespaceService.setNamespace(namespace); const id = this.getClusterId(params); diff --git a/ui/tests/acceptance/managed-namespace-test.js b/ui/tests/acceptance/managed-namespace-test.js index 43a382456..950164ff8 100644 --- a/ui/tests/acceptance/managed-namespace-test.js +++ b/ui/tests/acceptance/managed-namespace-test.js @@ -3,6 +3,7 @@ import { currentURL, visit, fillIn } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import Pretender from 'pretender'; import logout from 'vault/tests/pages/logout'; +import { getManagedNamespace } from 'vault/routes/vault/cluster'; const FEATURE_FLAGS_RESPONSE = { feature_flags: ['VAULT_CLOUD_ADMIN_NAMESPACE'], @@ -37,7 +38,6 @@ module('Acceptance | Enterprise | Managed namespace root', function (hooks) { await visit('/vault/auth'); assert.ok(currentURL().startsWith('/vault/auth'), 'Redirected to auth'); assert.ok(currentURL().includes('?namespace=admin'), 'with base namespace'); - assert.dom('[data-test-namespace-toolbar]').doesNotExist('Normal namespace toolbar does not exist'); assert.dom('[data-test-managed-namespace-toolbar]').exists('Managed namespace toolbar exists'); assert.dom('[data-test-managed-namespace-root]').hasText('/admin', 'Shows /admin namespace prefix'); @@ -50,4 +50,32 @@ module('Acceptance | Enterprise | Managed namespace root', function (hooks) { 'Correctly prepends root to namespace' ); }); + + test('getManagedNamespace helper works as expected', function (assert) { + let managedNs = getManagedNamespace(null, 'admin'); + assert.equal(managedNs, 'admin', 'returns root ns when no namespace present'); + managedNs = getManagedNamespace('admin/', 'admin'); + assert.equal(managedNs, 'admin', 'returns root ns when matches passed ns'); + managedNs = getManagedNamespace('adminfoo/', 'admin'); + assert.equal( + managedNs, + 'admin/adminfoo/', + 'appends passed namespace to root even if it matches without slashes' + ); + managedNs = getManagedNamespace('admin/foo/', 'admin'); + assert.equal(managedNs, 'admin/foo/', 'returns passed namespace if it starts with root and /'); + }); + + test('it redirects to root prefixed ns when non-root passed', async function (assert) { + await logout.visit(); + await visit('/vault/auth?namespace=admindev'); + assert.ok(currentURL().startsWith('/vault/auth'), 'Redirected to auth'); + assert.ok( + currentURL().includes(`?namespace=${encodeURIComponent('admin/admindev')}`), + 'with appended namespace' + ); + + assert.dom('[data-test-managed-namespace-root]').hasText('/admin', 'Shows /admin namespace prefix'); + assert.dom('input#namespace').hasValue('/admindev', 'Input has /dev value'); + }); });