From 3022ca5a53d39b7ae0a519cd2cb1d3895ed11299 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 30 Sep 2021 15:21:45 -0400 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20role=20set-gra?= =?UTF-8?q?nts=20ability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/abilities/role.js | 15 +++ addons/api/app/abilities/role.js | 1 + addons/api/mirage/factories/role.js | 1 + addons/api/tests/unit/abilities/role-test.js | 17 +++ .../app/components/form/role/grants/index.hbs | 107 ++++++++++-------- .../tests/acceptance/roles/grants-test.js | 20 ++++ 6 files changed, 112 insertions(+), 49 deletions(-) create mode 100644 addons/api/addon/abilities/role.js create mode 100644 addons/api/app/abilities/role.js create mode 100644 addons/api/tests/unit/abilities/role-test.js 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( From 8da8fddf293b0973d429fd82baf636272fa1e962 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 30 Sep 2021 16:04:02 -0400 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20implement=20add-=20a?= =?UTF-8?q?nd=20remove-principals=20abilities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/abilities/role.js | 14 ++++++++ addons/api/mirage/factories/role.js | 2 ++ addons/api/tests/unit/abilities/role-test.js | 22 +++++++++++++ .../scopes/scope/roles/role/-actions.hbs | 8 +++-- .../scopes/scope/roles/role/principals.hbs | 33 +++++++++++-------- .../tests/acceptance/roles/principals-test.js | 20 +++++++++++ 6 files changed, 82 insertions(+), 17 deletions(-) diff --git a/addons/api/addon/abilities/role.js b/addons/api/addon/abilities/role.js index 9a8c71a12..f0c6d62ec 100644 --- a/addons/api/addon/abilities/role.js +++ b/addons/api/addon/abilities/role.js @@ -12,4 +12,18 @@ export default class RoleAbility extends ModelAbility { get canSetGrants() { return this.hasAuthorizedAction('set-grants'); } + + /** + * @type {boolean} + */ + get canAddPrincipals() { + return this.hasAuthorizedAction('add-principals'); + } + + /** + * @type {boolean} + */ + get canRemovePrincipals() { + return this.hasAuthorizedAction('remove-principals'); + } } diff --git a/addons/api/mirage/factories/role.js b/addons/api/mirage/factories/role.js index da42cee79..be13712b8 100644 --- a/addons/api/mirage/factories/role.js +++ b/addons/api/mirage/factories/role.js @@ -10,6 +10,8 @@ export default factory.extend({ 'update', 'delete', 'set-grants', + 'add-principals', + 'remove-principals', ], // 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 index f5428d85e..ffaf6a11d 100644 --- a/addons/api/tests/unit/abilities/role-test.js +++ b/addons/api/tests/unit/abilities/role-test.js @@ -14,4 +14,26 @@ module('Unit | Abilities | Account', function (hooks) { model.authorized_actions = []; assert.notOk(service.can('setGrants role', model)); }); + + test('it reflects when a given account may add principals based on authorized_actions', function (assert) { + assert.expect(2); + const service = this.owner.lookup('service:can'); + const model = { + authorized_actions: ['add-principals'], + }; + assert.ok(service.can('addPrincipals role', model)); + model.authorized_actions = []; + assert.notOk(service.can('addPrincipals role', model)); + }); + + test('it reflects when a given account may remove principals based on authorized_actions', function (assert) { + assert.expect(2); + const service = this.owner.lookup('service:can'); + const model = { + authorized_actions: ['remove-principals'], + }; + assert.ok(service.can('removePrincipals role', model)); + model.authorized_actions = []; + assert.notOk(service.can('removePrincipals role', model)); + }); }); diff --git a/ui/admin/app/templates/scopes/scope/roles/role/-actions.hbs b/ui/admin/app/templates/scopes/scope/roles/role/-actions.hbs index 88add065c..6c01fcc16 100644 --- a/ui/admin/app/templates/scopes/scope/roles/role/-actions.hbs +++ b/ui/admin/app/templates/scopes/scope/roles/role/-actions.hbs @@ -3,9 +3,11 @@ @dropdownRight={{true}} as |dropdown| > - - {{t 'resources.role.principal.actions.add-principals'}} - + {{#if (can 'addPrincipals role' @model)}} + + {{t 'resources.role.principal.actions.add-principals'}} + + {{/if}} {{#if (can 'delete model' @model)}} - - - {{t 'actions.remove'}} - - + + {{t 'actions.remove'}} + + + {{/if}} {{/each}} diff --git a/ui/admin/tests/acceptance/roles/principals-test.js b/ui/admin/tests/acceptance/roles/principals-test.js index b26cfe398..998c9acc5 100644 --- a/ui/admin/tests/acceptance/roles/principals-test.js +++ b/ui/admin/tests/acceptance/roles/principals-test.js @@ -69,6 +69,16 @@ module('Acceptance | roles | principals', function (hooks) { assert.equal(findAll('tbody tr').length, principalsCount - 1); }); + test('principal cannot be removed from a role without proper authorization', async function (assert) { + assert.expect(1); + const authorized_actions = instances.role.authorized_actions.filter( + (item) => item !== 'remove-principals' + ); + instances.role.update({ authorized_actions }); + await visit(urls.rolePrincipals); + assert.notOk(find('tbody tr .rose-dropdown-button-danger')); + }); + test('shows error message on principal remove', async function (assert) { assert.expect(2); this.server.post('/roles/:idMethod', () => { @@ -89,6 +99,16 @@ module('Acceptance | roles | principals', function (hooks) { assert.ok(find('[role="alert"]')); }); + test('cannot navigate to add principals without proper authorization', async function (assert) { + assert.expect(1); + const authorized_actions = instances.role.authorized_actions.filter( + (item) => item !== 'add-principals' + ); + instances.role.update({ authorized_actions }); + await visit(urls.rolePrincipals); + assert.notOk(find('.rose-layout-page-actions a')); + }); + test('select and save principals to add', async function (assert) { assert.expect(3); instances.role.update({ userIds: [], groupIds: [] });