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: Update resultant-acl banner into release/1.15.x #25267

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/25256.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
ui: Do not show resultant-acl banner on namespaces a user has access to
```
3 changes: 2 additions & 1 deletion ui/app/adapters/permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
8 changes: 2 additions & 6 deletions ui/app/components/resultant-acl-banner.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions ui/app/components/resultant-acl-banner.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.";
}
}
2 changes: 1 addition & 1 deletion ui/app/controllers/vault/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default Controller.extend({
consoleOpen: alias('console.isOpen'),
activeCluster: alias('auth.activeCluster'),

permissionReadFailed: alias('permissions.readFailed'),
permissionBanner: alias('permissions.permissionsBanner'),

actions: {
toggleConsole() {
Expand Down
35 changes: 28 additions & 7 deletions ui/app/services/permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -65,12 +69,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;
Expand All @@ -83,24 +94,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) {
Expand Down Expand Up @@ -128,9 +151,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 {
Expand Down
4 changes: 2 additions & 2 deletions ui/app/templates/vault/cluster.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,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">
Expand Down
34 changes: 25 additions & 9 deletions ui/tests/integration/components/resultant-acl-banner-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
37 changes: 37 additions & 0 deletions ui/tests/unit/adapters/permissions-test.js
Original file line number Diff line number Diff line change
@@ -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();
});
});
Loading