diff --git a/addons/api/addon/abilities/role.js b/addons/api/addon/abilities/role.js
new file mode 100644
index 000000000..9a8c71a12
--- /dev/null
+++ b/addons/api/addon/abilities/role.js
@@ -0,0 +1,15 @@
+import ModelAbility from './model';
+
+/**
+ * Provides abilities for roles.
+ */
+export default class RoleAbility extends ModelAbility {
+ // =permissions
+
+ /**
+ * @type {boolean}
+ */
+ get canSetGrants() {
+ return this.hasAuthorizedAction('set-grants');
+ }
+}
diff --git a/addons/api/app/abilities/role.js b/addons/api/app/abilities/role.js
new file mode 100644
index 000000000..aefb6fd13
--- /dev/null
+++ b/addons/api/app/abilities/role.js
@@ -0,0 +1 @@
+export { default } from 'api/abilities/role';
diff --git a/addons/api/mirage/factories/role.js b/addons/api/mirage/factories/role.js
index a057fc29f..da42cee79 100644
--- a/addons/api/mirage/factories/role.js
+++ b/addons/api/mirage/factories/role.js
@@ -9,6 +9,7 @@ export default factory.extend({
'read',
'update',
'delete',
+ 'set-grants',
],
// eslint-disable-next-line ember/avoid-leaking-state-in-ember-objects
grant_strings: () => [
diff --git a/addons/api/tests/unit/abilities/role-test.js b/addons/api/tests/unit/abilities/role-test.js
new file mode 100644
index 000000000..f5428d85e
--- /dev/null
+++ b/addons/api/tests/unit/abilities/role-test.js
@@ -0,0 +1,17 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+
+module('Unit | Abilities | Account', function (hooks) {
+ setupTest(hooks);
+
+ test('it reflects when a given account may set grants based on authorized_actions', function (assert) {
+ assert.expect(2);
+ const service = this.owner.lookup('service:can');
+ const model = {
+ authorized_actions: ['set-grants'],
+ };
+ assert.ok(service.can('setGrants role', model));
+ model.authorized_actions = [];
+ assert.notOk(service.can('setGrants role', model));
+ });
+});
diff --git a/ui/admin/app/components/form/role/grants/index.hbs b/ui/admin/app/components/form/role/grants/index.hbs
index 79766a2e1..d6aaad424 100644
--- a/ui/admin/app/components/form/role/grants/index.hbs
+++ b/ui/admin/app/components/form/role/grants/index.hbs
@@ -1,39 +1,43 @@
-
-
- {{! Add grant field (only one per form) }}
-
-
-
- {{t 'resources.role.grant.actions.create'}}
-
-
-
-
-
-
- {{t 'actions.add'}}
-
-
-
-
-
-
+{{#if (can 'setGrants role' @model)}}
+
+
+ {{! Add grant field (only one per form) }}
+
+
+
+ {{t
+ 'resources.role.grant.actions.create'
+ }}
+
+
+
+
+
+
+ {{t 'actions.add'}}
+
+
+
+
+
+
+{{/if}}
@@ -72,23 +77,27 @@
-
- {{t 'actions.remove'}}
-
+ {{#if (can 'setGrants role' @model)}}
+
+ {{t 'actions.remove'}}
+
+ {{/if}}
{{/each}}
-
+ {{#if (can 'setGrants role' @model)}}
+
+ {{/if}}
\ No newline at end of file
diff --git a/ui/admin/tests/acceptance/roles/grants-test.js b/ui/admin/tests/acceptance/roles/grants-test.js
index d13eb4f46..fd169aa46 100644
--- a/ui/admin/tests/acceptance/roles/grants-test.js
+++ b/ui/admin/tests/acceptance/roles/grants-test.js
@@ -63,6 +63,26 @@ module('Acceptance | roles | grants', function (hooks) {
assert.equal(findAll(`${grantsForm} [name="grant"]`).length, grantsCount);
});
+ test('cannot set grants without proper authorization', async function (assert) {
+ assert.expect(4);
+ const authorized_actions = instances.role.authorized_actions.filter(
+ (item) => item !== 'set-grants'
+ );
+ instances.role.update({ authorized_actions });
+ await visit(urls.grants);
+ assert.equal(
+ findAll('main form').length,
+ 1,
+ 'New grant form is not displayed.'
+ );
+ assert.notOk(
+ find('main form button:not([type="submit"])'),
+ 'Grant delete button is not displayed.'
+ );
+ assert.notOk(find('.rose-form-actions'), 'Form actions are not displayed.');
+ assert.ok(find('main form input[disabled]'), 'Grant fields are disabled.');
+ });
+
test('update a grant', async function (assert) {
assert.expect(1);
this.server.post(