Skip to content
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
1 change: 1 addition & 0 deletions addons/core/translations/actions/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ retry: Retry
play: Play
pause: Pause
resume: Resume
export: Export
get-topic-help: Get help for this topic
copy-to-clipboard: Copy to Clipboard
copy-error-detail-to-clipboard: Copy error detail to clipboard
Expand Down
3 changes: 3 additions & 0 deletions addons/core/translations/resources/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,9 @@ role:
read-only-user:
name: Read-Only User
description: Can view but not modify any resources
edit-grants:
title: Edit Grants
description: Modify existing grant strings or add new grant strings to this role.
scope:
title: Scope
title_plural: Scopes
Expand Down
31 changes: 31 additions & 0 deletions ui/admin/app/components/form/role/edit-grants/index.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{{!
Copyright IBM Corp. 2021, 2026
SPDX-License-Identifier: BUSL-1.1
}}

<Rose::Form class='full-width' as |form|>
{{! TODO: Add responsive options to @colspan once we upgrade to HDS v6.0.0}}
<Hds::Layout::Grid @gap='32' as |LG|>
<LG.Item @colspan={{2}}>
{{! TODO: Add grants code editor. }}
</LG.Item>

<LG.Item @colspan={{1}}>
{{! TODO: Add string options and actions. }}
</LG.Item>
</Hds::Layout::Grid>

<form.actions class='margin-top-l'>
<:actions>
<Hds::ButtonSet>
<Hds::Button type='submit' @text={{t 'actions.save'}} />
<Hds::Button @text={{t 'actions.cancel'}} @color='secondary' />
<Hds::Button
@text={{t 'actions.export'}}
@color='tertiary'
@icon='upload'
/>
</Hds::ButtonSet>
</:actions>
</form.actions>
</Rose::Form>
6 changes: 6 additions & 0 deletions ui/admin/app/components/roles/role/actions/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
>
{{t 'resources.role.grant-templates.actions.add-grant-templates'}}
</dd.Interactive>
<dd.Interactive
@route='scopes.scope.roles.role.edit-grants'
data-test-manage-dropdown-grants
>
{{t 'resources.role.edit-grants.title'}}
</dd.Interactive>
{{/if}}
{{#if perms.canDelete}}
{{#if
Expand Down
1 change: 1 addition & 0 deletions ui/admin/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Router.map(function () {
this.route('manage-custom-scopes');
this.route('manage-org-projects', { path: ':org_id' });
});
this.route('edit-grants');
});
this.route('new');
});
Expand Down
8 changes: 8 additions & 0 deletions ui/admin/app/routes/scopes/scope/roles/role/edit-grants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/

import Route from '@ember/routing/route';

export default class ScopesScopeRolesRoleEditGrantsRoute extends Route {}
34 changes: 34 additions & 0 deletions ui/admin/app/templates/scopes/scope/roles/role/edit-grants.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{{!
Copyright IBM Corp. 2021, 2026
SPDX-License-Identifier: BUSL-1.1
}}

{{page-title (t 'resources.role.edit-grants.title')}}
<Breadcrumbs::Item
@text={{t 'resources.role.edit-grants.title'}}
@route='scopes.scope.roles.role.edit-grants'
/>

<Rose::Layout::Page as |page|>

<page.header>
<Hds::PageHeader as |PH|>
<PH.Breadcrumb>
<Breadcrumbs::Container />
</PH.Breadcrumb>
<PH.Title>
{{t 'resources.role.edit-grants.title'}}
{{! TODO: Add correct doc link once it's created}}
{{! <DocLink @doc='role.' /> }}
</PH.Title>
<PH.Description>
{{t 'resources.role.edit-grants.description'}}
</PH.Description>
</Hds::PageHeader>
</page.header>

<page.body>
<Form::Role::EditGrants />
</page.body>

</Rose::Layout::Page>
79 changes: 79 additions & 0 deletions ui/admin/tests/acceptance/roles/edit-grants-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/

import { module, test } from 'qunit';
import { visit, click, currentURL } from '@ember/test-helpers';
import { setupApplicationTest } from 'admin/tests/helpers';
import * as selectors from './selectors';
import { setRunOptions } from 'ember-a11y-testing/test-support';

module('Acceptance | roles/edit grants', function (hooks) {
setupApplicationTest(hooks);

const instances = {
scopes: {
global: null,
},
role: null,
};

const urls = {
role: null,
editGrants: null,
};

hooks.beforeEach(async function () {
instances.scopes.global = this.server.schema.scopes.find('global');
instances.role = this.server.create('role', {
scope: instances.scopes.global,
});
urls.role = `/scopes/global/roles/${instances.role.id}`;
urls.editGrants = `${urls.role}/edit-grants`;
});

test('can navigate to edit grants for roles with proper authorization', async function (assert) {
setRunOptions({
rules: {
'color-contrast': {
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2026-02-27
enabled: false,
},
},
});

await visit(urls.role);

assert.true(instances.role.authorized_actions.includes('set-grants'));

await click(selectors.MANAGE_DROPDOWN_ROLES);
await click(selectors.MANAGE_DROPDOWN_EDIT_GRANTS);

assert.strictEqual(currentURL(), urls.editGrants);
});

test('cannot navigate to edit grants for roles without proper authorization', async function (assert) {
setRunOptions({
rules: {
'color-contrast': {
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2026-02-27
enabled: false,
},
},
});

instances.role.authorized_actions =
instances.role.authorized_actions.filter(
(action) => action !== 'set-grants',
);
await visit(urls.role);

assert.false(instances.role.authorized_actions.includes('set-grants'));

await click(selectors.MANAGE_DROPDOWN_ROLES);

assert.dom(selectors.MANAGE_DROPDOWN_EDIT_GRANTS).doesNotExist();
assert.strictEqual(currentURL(), urls.role);
});
});
1 change: 1 addition & 0 deletions ui/admin/tests/acceptance/roles/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const MANAGE_DROPDOWN_ADD_PRINCIPALS =
'[data-test-manage-role-principals]';
export const MANAGE_DROPDOWN_ADD_GRANT_TEMPLATES =
'[data-test-manage-role-grant-templates]';
export const MANAGE_DROPDOWN_EDIT_GRANTS = '[data-test-manage-dropdown-grants]';

export const ROLE_BADGE = (id) =>
`tbody [data-test-role-row="${id}"] td:nth-child(2) .hds-badge__text`;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Route | scopes/scope/roles/role/edit-grants', function (hooks) {
setupTest(hooks);

test('it exists', function (assert) {
let route = this.owner.lookup('route:scopes/scope/roles/role/edit-grants');
assert.ok(route);
});
});