Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport of UI/add managed ns redirect prefix into release/1.9.x #14440

Merged
merged 1 commit into from
Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/14422.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
ui: Redirects to managed namespace if incorrect namespace in URL param
```
32 changes: 25 additions & 7 deletions ui/app/routes/vault/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 /
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍 your comments!

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(),
Expand All @@ -18,7 +28,7 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, {
auth: service(),
featureFlagService: service('featureFlag'),
currentCluster: service(),
modelTypes: computed(function() {
modelTypes: computed(function () {
return ['node', 'secret', 'secret-engine'];
}),

Expand All @@ -38,17 +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
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);
Expand All @@ -66,7 +84,7 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, {
return this.store.findRecord('cluster', id);
},

poll: task(function*() {
poll: task(function* () {
while (true) {
// when testing, the polling loop causes promises to never settle so acceptance tests hang
// to get around that, we just disable the poll in tests
Expand Down Expand Up @@ -118,7 +136,7 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, {
let controller = this.controllerFor('vault.cluster');
controller.set('currentlyLoading', true);

transition.finally(function() {
transition.finally(function () {
controller.set('currentlyLoading', false);
});
},
Expand Down
44 changes: 37 additions & 7 deletions ui/tests/acceptance/managed-namespace-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@ import { module, test } from 'qunit';
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'],
};

module('Acceptance | Enterprise | Managed namespace root', function(hooks) {
module('Acceptance | Enterprise | Managed namespace root', function (hooks) {
setupApplicationTest(hooks);

hooks.beforeEach(function() {
hooks.beforeEach(function () {
/**
* Since the features are fetched on the application load,
* we have to populate them on the beforeEach hook because
* the fetch won't trigger again within the tests
*/
this.server = new Pretender(function() {
this.server = new Pretender(function () {
this.get('/v1/sys/internal/ui/feature-flags', () => {
return [200, { 'Content-Type': 'application/json' }, JSON.stringify(FEATURE_FLAGS_RESPONSE)];
});
Expand All @@ -26,14 +28,14 @@ module('Acceptance | Enterprise | Managed namespace root', function(hooks) {
});
});

hooks.afterEach(function() {
hooks.afterEach(function () {
this.server.shutdown();
});

test('it shows the managed namespace toolbar when feature flag exists', async function(assert) {
test('it shows the managed namespace toolbar when feature flag exists', async function (assert) {
await visit('/vault/auth');
assert.equal(currentURL(), '/vault/auth?namespace=admin&with=token', 'Redirected to base namespace');

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');
Expand All @@ -46,4 +48,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');
});
});