From 7dd9fb80d5f1c37e83f83f1e6e30f34a480e6b3d Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 20 Dec 2022 16:31:39 -0500 Subject: [PATCH] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20role=20readPrinci?= =?UTF-8?q?pal=20ability=20(#1540)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/abilities/managed-group.js | 8 +++ addons/api/addon/abilities/role.js | 27 +++++++++++ addons/api/app/abilities/managed-group.js | 1 + addons/api/tests/unit/abilities/role-test.js | 51 +++++++++++++++++--- 4 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 addons/api/addon/abilities/managed-group.js create mode 100644 addons/api/app/abilities/managed-group.js diff --git a/addons/api/addon/abilities/managed-group.js b/addons/api/addon/abilities/managed-group.js new file mode 100644 index 000000000..dd02a34e9 --- /dev/null +++ b/addons/api/addon/abilities/managed-group.js @@ -0,0 +1,8 @@ +import ModelAbility from './model'; + +/** + * Provides abilities for managed groups. + */ +export default class ManagedGroupAbility extends ModelAbility { + // no op +} diff --git a/addons/api/addon/abilities/role.js b/addons/api/addon/abilities/role.js index f0c6d62ec..b944fa548 100644 --- a/addons/api/addon/abilities/role.js +++ b/addons/api/addon/abilities/role.js @@ -1,9 +1,18 @@ import ModelAbility from './model'; +import { inject as service } from '@ember/service'; + +class InvalidRolePrincipalTypeError extends Error { + name = 'InvalidRolePrincipalTypeError'; +} /** * Provides abilities for roles. */ export default class RoleAbility extends ModelAbility { + // =services + + @service can; + // =permissions /** @@ -26,4 +35,22 @@ export default class RoleAbility extends ModelAbility { get canRemovePrincipals() { return this.hasAuthorizedAction('remove-principals'); } + + /** + * Returns the ability check for a principal model instance. + * Valid role principal models are: User, Group, and ManagedGroup. + * Throws an error if the principal type is invalid. + * @type {boolean} + * @throws {InvalidRolePrincipalTypeError} + */ + get canReadPrincipal() { + const type = this.model.constructor.modelName; + const typeIsValid = + type === 'user' || type === 'group' || type === 'managed-group'; + if (!typeIsValid) + throw new InvalidRolePrincipalTypeError( + `Expected a role principal of type 'user', 'group', or 'managed-group'. Got '${type}'.` + ); + return this.can.can(`read ${type}`, this.model); + } } diff --git a/addons/api/app/abilities/managed-group.js b/addons/api/app/abilities/managed-group.js new file mode 100644 index 000000000..bf7e304a5 --- /dev/null +++ b/addons/api/app/abilities/managed-group.js @@ -0,0 +1 @@ +export { default } from 'api/abilities/managed-group'; diff --git a/addons/api/tests/unit/abilities/role-test.js b/addons/api/tests/unit/abilities/role-test.js index 462743446..b2e8675e5 100644 --- a/addons/api/tests/unit/abilities/role-test.js +++ b/addons/api/tests/unit/abilities/role-test.js @@ -10,9 +10,9 @@ module('Unit | Abilities | Role', function (hooks) { const model = { authorized_actions: ['set-grants'], }; - assert.ok(service.can('setGrants role', model)); + assert.true(service.can('setGrants role', model)); model.authorized_actions = []; - assert.notOk(service.can('setGrants role', model)); + assert.false(service.can('setGrants role', model)); }); test('it reflects when a given role may add principals based on authorized_actions', function (assert) { @@ -21,9 +21,9 @@ module('Unit | Abilities | Role', function (hooks) { const model = { authorized_actions: ['add-principals'], }; - assert.ok(service.can('addPrincipals role', model)); + assert.true(service.can('addPrincipals role', model)); model.authorized_actions = []; - assert.notOk(service.can('addPrincipals role', model)); + assert.false(service.can('addPrincipals role', model)); }); test('it reflects when a given role may remove principals based on authorized_actions', function (assert) { @@ -32,8 +32,47 @@ module('Unit | Abilities | Role', function (hooks) { const model = { authorized_actions: ['remove-principals'], }; - assert.ok(service.can('removePrincipals role', model)); + assert.true(service.can('removePrincipals role', model)); model.authorized_actions = []; - assert.notOk(service.can('removePrincipals role', model)); + assert.false(service.can('removePrincipals role', model)); + }); + + test('it proxies role principal read ability to the appropriate ability type', function (assert) { + assert.expect(6); + const service = this.owner.lookup('service:can'); + const user = { + constructor: { modelName: 'user' }, + authorized_actions: [], + }; + const group = { + constructor: { modelName: 'group' }, + authorized_actions: [], + }; + const managedGroup = { + constructor: { modelName: 'managed-group' }, + authorized_actions: [], + }; + assert.false(service.can('readPrincipal role', user)); + assert.false(service.can('readPrincipal role', group)); + assert.false(service.can('readPrincipal role', managedGroup)); + user.authorized_actions.push('read'); + group.authorized_actions.push('read'); + managedGroup.authorized_actions.push('read'); + assert.true(service.can('readPrincipal role', user)); + assert.true(service.can('readPrincipal role', group)); + assert.true(service.can('readPrincipal role', managedGroup)); + }); + + test('it throws an error for unknown principal types on principal read check', function (assert) { + assert.expect(2); + const service = this.owner.lookup('service:can'); + const account = { + constructor: { modelName: 'account' }, + authorized_actions: ['read'], + }; + assert.throws(() => { + service.can('readPrincipal role', account); + }); + assert.true(service.can('read account', account)); }); });