mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
UI: Update resultant-acl banner (#25256)
* Request resultant-acl only from users root namespace * Update permissions adapter to always call resultant-acl at users root, with test * Update resultant-acl to accept failType * Update permissions service to set permissionsBanner based on resultant-acl contents * wire it up * add changelog * cleanup unused adapter changes * use getter for shared namespace logic
This commit is contained in:
3
changelog/25256.txt
Normal file
3
changelog/25256.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
```release-note:bug
|
||||
ui: Do not show resultant-acl banner on namespaces a user has access to
|
||||
```
|
||||
@@ -7,7 +7,8 @@ import ApplicationAdapter from './application';
|
||||
|
||||
export default ApplicationAdapter.extend({
|
||||
query() {
|
||||
return this.ajax(this.urlForQuery(), 'GET');
|
||||
const namespace = this.namespaceService.userRootNamespace || this.namespaceService.path;
|
||||
return this.ajax(this.urlForQuery(), 'GET', { namespace });
|
||||
},
|
||||
|
||||
urlForQuery() {
|
||||
|
||||
@@ -11,13 +11,9 @@
|
||||
data-test-resultant-acl-banner
|
||||
as |A|
|
||||
>
|
||||
<A.Title>Resultant ACL check failed</A.Title>
|
||||
<A.Title>{{this.title}}</A.Title>
|
||||
<A.Description>
|
||||
{{if
|
||||
@isEnterprise
|
||||
"You do not have access to resources in this namespace."
|
||||
"Links might be shown that you don't have access to. Contact your administrator to update your policy."
|
||||
}}
|
||||
{{this.message}}
|
||||
</A.Description>
|
||||
{{#if @isEnterprise}}
|
||||
<A.Link::Standalone
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { service } from '@ember/service';
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { PERMISSIONS_BANNER_STATES } from 'vault/services/permissions';
|
||||
|
||||
export default class ResultantAclBannerComponent extends Component {
|
||||
@service namespace;
|
||||
@@ -20,4 +21,16 @@ export default class ResultantAclBannerComponent extends Component {
|
||||
// Bring user back to current page after login
|
||||
return { redirect_to: this.router.currentURL };
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this.args.failType === PERMISSIONS_BANNER_STATES.noAccess
|
||||
? 'You do not have access to this namespace'
|
||||
: 'Resultant ACL check failed';
|
||||
}
|
||||
|
||||
get message() {
|
||||
return this.args.failType === PERMISSIONS_BANNER_STATES.noAccess
|
||||
? 'Log into the namespace directly, or contact your administrator if you think you should have access.'
|
||||
: "Links might be shown that you don't have access to. Contact your administrator to update your policy.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export default Controller.extend({
|
||||
consoleOpen: alias('console.isOpen'),
|
||||
activeCluster: alias('auth.activeCluster'),
|
||||
|
||||
permissionReadFailed: alias('permissions.readFailed'),
|
||||
permissionBanner: alias('permissions.permissionsBanner'),
|
||||
|
||||
actions: {
|
||||
toggleConsole() {
|
||||
|
||||
@@ -7,6 +7,10 @@ import Service, { inject as service } from '@ember/service';
|
||||
import { sanitizePath, sanitizeStart } from 'core/utils/sanitize-path';
|
||||
import { task } from 'ember-concurrency';
|
||||
|
||||
export const PERMISSIONS_BANNER_STATES = {
|
||||
readFailed: 'read-failed',
|
||||
noAccess: 'no-ns-access',
|
||||
};
|
||||
const API_PATHS = {
|
||||
access: {
|
||||
methods: 'sys/auth',
|
||||
@@ -68,12 +72,19 @@ export default Service.extend({
|
||||
exactPaths: null,
|
||||
globPaths: null,
|
||||
canViewAll: null,
|
||||
readFailed: false,
|
||||
permissionsBanner: null,
|
||||
chrootNamespace: null,
|
||||
store: service(),
|
||||
auth: service(),
|
||||
namespace: service(),
|
||||
|
||||
get baseNs() {
|
||||
const currentNs = this.namespace.path;
|
||||
return this.chrootNamespace
|
||||
? `${sanitizePath(this.chrootNamespace)}/${sanitizePath(currentNs)}`
|
||||
: sanitizePath(currentNs);
|
||||
},
|
||||
|
||||
getPaths: task(function* () {
|
||||
if (this.paths) {
|
||||
return;
|
||||
@@ -86,24 +97,36 @@ export default Service.extend({
|
||||
} catch (err) {
|
||||
// If no policy can be found, default to showing all nav items.
|
||||
this.set('canViewAll', true);
|
||||
this.set('readFailed', true);
|
||||
this.set('permissionsBanner', PERMISSIONS_BANNER_STATES.readFailed);
|
||||
}
|
||||
}),
|
||||
|
||||
calcNsAccess() {
|
||||
if (this.canViewAll) {
|
||||
this.set('permissionsBanner', null);
|
||||
return;
|
||||
}
|
||||
const namespace = this.baseNs;
|
||||
const allowed =
|
||||
Object.keys(this.globPaths).any((k) => k.startsWith(namespace)) ||
|
||||
Object.keys(this.exactPaths).any((k) => k.startsWith(namespace));
|
||||
this.set('permissionsBanner', allowed ? null : PERMISSIONS_BANNER_STATES.noAccess);
|
||||
},
|
||||
|
||||
setPaths(resp) {
|
||||
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);
|
||||
this.calcNsAccess();
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.set('exactPaths', null);
|
||||
this.set('globPaths', null);
|
||||
this.set('canViewAll', null);
|
||||
this.set('readFailed', false);
|
||||
this.set('chrootNamespace', null);
|
||||
this.set('permissionsBanner', null);
|
||||
},
|
||||
|
||||
hasNavPermission(navItem, routeParams, requireAll) {
|
||||
@@ -131,9 +154,7 @@ export default Service.extend({
|
||||
},
|
||||
|
||||
pathNameWithNamespace(pathName) {
|
||||
const namespace = this.chrootNamespace
|
||||
? `${sanitizePath(this.chrootNamespace)}/${sanitizePath(this.namespace.path)}`
|
||||
: sanitizePath(this.namespace.path);
|
||||
const namespace = this.baseNs;
|
||||
if (namespace) {
|
||||
return `${sanitizePath(namespace)}/${sanitizeStart(pathName)}`;
|
||||
} else {
|
||||
|
||||
@@ -68,8 +68,8 @@
|
||||
@autoloaded={{eq this.activeCluster.licenseState "autoloaded"}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{#if this.permissionReadFailed}}
|
||||
<ResultantAclBanner @isEnterprise={{this.activeCluster.version.isEnterprise}} />
|
||||
{{#if this.permissionBanner}}
|
||||
<ResultantAclBanner @isEnterprise={{this.activeCluster.version.isEnterprise}} @failType={{this.permissionBanner}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="global-flash">
|
||||
|
||||
@@ -7,35 +7,51 @@ import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'vault/tests/helpers';
|
||||
import { click, render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { PERMISSIONS_BANNER_STATES } from 'vault/services/permissions';
|
||||
|
||||
const TEXT = {
|
||||
titleReadFail: 'Resultant ACL check failed',
|
||||
titleNoAccess: 'You do not have access to this namespace',
|
||||
messageReadFail:
|
||||
"Links might be shown that you don't have access to. Contact your administrator to update your policy.",
|
||||
messageNoAccess:
|
||||
'Log into the namespace directly, or contact your administrator if you think you should have access.',
|
||||
};
|
||||
module('Integration | Component | resultant-acl-banner', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders correctly by default', async function (assert) {
|
||||
await render(hbs`<ResultantAclBanner />`);
|
||||
|
||||
assert.dom('[data-test-resultant-acl-banner] .hds-alert__title').hasText('Resultant ACL check failed');
|
||||
assert
|
||||
.dom('[data-test-resultant-acl-banner] .hds-alert__description')
|
||||
.hasText(
|
||||
"Links might be shown that you don't have access to. Contact your administrator to update your policy."
|
||||
);
|
||||
assert.dom('[data-test-resultant-acl-banner] .hds-alert__title').hasText(TEXT.titleReadFail);
|
||||
assert.dom('[data-test-resultant-acl-banner] .hds-alert__description').hasText(TEXT.messageReadFail);
|
||||
assert.dom('[data-test-resultant-acl-reauthenticate]').doesNotExist('Does not show reauth link');
|
||||
});
|
||||
|
||||
test('it renders correctly with set namespace', async function (assert) {
|
||||
const nsService = this.owner.lookup('service:namespace');
|
||||
nsService.setNamespace('my-ns');
|
||||
this.set('failType', undefined);
|
||||
|
||||
await render(hbs`<ResultantAclBanner @isEnterprise={{true}} />`);
|
||||
await render(hbs`<ResultantAclBanner @isEnterprise={{true}} @failType={{this.failType}} />`);
|
||||
|
||||
assert.dom('[data-test-resultant-acl-banner] .hds-alert__title').hasText('Resultant ACL check failed');
|
||||
assert
|
||||
.dom('[data-test-resultant-acl-banner] .hds-alert__title')
|
||||
.hasText(TEXT.titleReadFail, 'title correct for default fail type');
|
||||
assert
|
||||
.dom('[data-test-resultant-acl-banner] .hds-alert__description')
|
||||
.hasText('You do not have access to resources in this namespace.');
|
||||
.hasText(TEXT.messageReadFail, 'message correct for default fail type');
|
||||
assert
|
||||
.dom('[data-test-resultant-acl-reauthenticate]')
|
||||
.hasText('Log into my-ns namespace', 'Shows reauth link with given namespace');
|
||||
|
||||
this.set('failType', PERMISSIONS_BANNER_STATES.noAccess);
|
||||
assert
|
||||
.dom('[data-test-resultant-acl-banner] .hds-alert__title')
|
||||
.hasText(TEXT.titleNoAccess, 'title correct for no access failtype');
|
||||
assert
|
||||
.dom('[data-test-resultant-acl-banner] .hds-alert__description')
|
||||
.hasText(TEXT.messageNoAccess, 'message correct for no access failtype');
|
||||
});
|
||||
|
||||
test('it renders correctly with default namespace', async function (assert) {
|
||||
|
||||
37
ui/tests/unit/adapters/permissions-test.js
Normal file
37
ui/tests/unit/adapters/permissions-test.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
|
||||
module('Unit | Adapter | permissions', function (hooks) {
|
||||
setupTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
test('it calls resultant-acl with the users root namespace', async function (assert) {
|
||||
assert.expect(1);
|
||||
const adapter = this.owner.lookup('adapter:permissions');
|
||||
const nsService = this.owner.lookup('service:namespace');
|
||||
nsService.setNamespace('admin/foo');
|
||||
nsService.reopen({
|
||||
userRootNamespace: 'admin/bar',
|
||||
});
|
||||
this.server.get('/sys/internal/ui/resultant-acl', (schema, request) => {
|
||||
assert.strictEqual(
|
||||
request.requestHeaders['X-Vault-Namespace'],
|
||||
'admin/bar',
|
||||
'Namespace is users root not current path'
|
||||
);
|
||||
return {
|
||||
data: {
|
||||
exact_paths: {},
|
||||
glob_paths: {},
|
||||
},
|
||||
};
|
||||
});
|
||||
await adapter.query();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user