From 3f178363d9435a5804d2129c6f6f168fc96fa0b6 Mon Sep 17 00:00:00 2001 From: Dharini Jeeva Date: Tue, 9 Jan 2024 11:39:28 -0800 Subject: [PATCH] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20attach=20and=20de?= =?UTF-8?q?tach=20storage=20policy=20permissions=20(#2080)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 🤖 add attach and detach storage policy permissions --- addons/api/addon/abilities/scope.js | 28 +++++++++++ addons/api/app/abilities/scope.js | 6 +++ addons/api/mirage/factories/scope.js | 2 + addons/api/tests/unit/abilities/scope-test.js | 40 +++++++++++++++ ui/admin/app/abilities/scope.js | 20 ++++++++ ui/admin/tests/unit/abilities/scope-test.js | 49 +++++++++++++++++++ 6 files changed, 145 insertions(+) create mode 100644 addons/api/addon/abilities/scope.js create mode 100644 addons/api/app/abilities/scope.js create mode 100644 addons/api/tests/unit/abilities/scope-test.js create mode 100644 ui/admin/tests/unit/abilities/scope-test.js diff --git a/addons/api/addon/abilities/scope.js b/addons/api/addon/abilities/scope.js new file mode 100644 index 000000000..ee7e56ad9 --- /dev/null +++ b/addons/api/addon/abilities/scope.js @@ -0,0 +1,28 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import ModelAbility from './model'; + +/** + * Provides abilities for scopes. + */ +export default class ScopeAbility extends ModelAbility { + // =permissions + /** + * Permission that checks whether a policy can be attached to a scope + * @type {boolean} + */ + get canAttachStoragePolicy() { + return this.hasAuthorizedAction('attach-storage-policy'); + } + + /** + * Permission that checks whether a policy can be detached to a scope + * @type {boolean} + */ + get canDetachStoragePolicy() { + return this.hasAuthorizedAction('detach-storage-policy'); + } +} diff --git a/addons/api/app/abilities/scope.js b/addons/api/app/abilities/scope.js new file mode 100644 index 000000000..993fc1234 --- /dev/null +++ b/addons/api/app/abilities/scope.js @@ -0,0 +1,6 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +export { default } from 'api/abilities/scope'; diff --git a/addons/api/mirage/factories/scope.js b/addons/api/mirage/factories/scope.js index b1fb50979..79dcbd872 100644 --- a/addons/api/mirage/factories/scope.js +++ b/addons/api/mirage/factories/scope.js @@ -17,6 +17,8 @@ export default factory.extend({ 'read', 'update', 'delete', + 'attach-storage-policy', + 'detach-storage-policy', ], authorized_collection_actions() { diff --git a/addons/api/tests/unit/abilities/scope-test.js b/addons/api/tests/unit/abilities/scope-test.js new file mode 100644 index 000000000..afe8d3a32 --- /dev/null +++ b/addons/api/tests/unit/abilities/scope-test.js @@ -0,0 +1,40 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Abilities | Scope', function (hooks) { + setupTest(hooks); + + let canService; + + hooks.beforeEach(function () { + canService = this.owner.lookup('service:can'); + }); + test('it exists', function (assert) { + const ability = this.owner.lookup('ability:scope'); + assert.ok(ability); + }); + + test('it reflects when a given scope may attach a policy in global scope', function (assert) { + const model = { + authorized_actions: ['attach-storage-policy'], + }; + assert.true(canService.can('attachStoragePolicy scope', model)); + model.authorized_actions = []; + assert.false(canService.can('attachStoragePolicy scope', model)); + }); + + test('it reflects when a given scope may remove a policy in org scope', function (assert) { + const model = { + authorized_actions: ['detach-storage-policy'], + }; + + assert.true(canService.can('detachStoragePolicy scope', model)); + model.authorized_actions = []; + assert.false(canService.can('detachStoragePolicy scope', model)); + }); +}); diff --git a/ui/admin/app/abilities/scope.js b/ui/admin/app/abilities/scope.js index 62d03adf1..aa3432220 100644 --- a/ui/admin/app/abilities/scope.js +++ b/ui/admin/app/abilities/scope.js @@ -47,4 +47,24 @@ export default class OverrideScopeAbility extends OverrideModelAbility { return super.canList; } } + + /** + * Attaching a policy is allowed only if the feature flag is enabled + * @type {boolean} + */ + get canAttachStoragePolicy() { + return this.features.isEnabled('ssh-session-recording') + ? this.hasAuthorizedAction('attach-storage-policy') + : false; + } + + /** + * Deattaching a policy is allowed only if the feature flag is enabled + * @type {boolean} + */ + get canDetachStoragePolicy() { + return this.features.isEnabled('ssh-session-recording') + ? this.hasAuthorizedAction('detach-storage-policy') + : false; + } } diff --git a/ui/admin/tests/unit/abilities/scope-test.js b/ui/admin/tests/unit/abilities/scope-test.js new file mode 100644 index 000000000..475794acf --- /dev/null +++ b/ui/admin/tests/unit/abilities/scope-test.js @@ -0,0 +1,49 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Abilities | Scope', function (hooks) { + setupTest(hooks); + + let features; + let canService; + let store; + + hooks.beforeEach(function () { + store = this.owner.lookup('service:store'); + canService = this.owner.lookup('service:can'); + features = this.owner.lookup('service:features'); + }); + + test('can attach storage policy when authorized', function (assert) { + features.enable('ssh-session-recording'); + + const scopeModel = store.createRecord('scope', { + authorized_actions: ['attach-storage-policy'], + }); + assert.true(canService.can('attachStoragePolicy scope', scopeModel)); + }); + + test('cannot attach storage policy when unauthorized', function (assert) { + features.enable('ssh-session-recording'); + + const scopeModel = store.createRecord('scope', { + authorized_actions: [], + }); + + assert.false(canService.can('attachStoragePolicy scope', scopeModel)); + }); + + test('can detach storage policy when authorized', function (assert) { + features.enable('ssh-session-recording'); + + const scopeModel = store.createRecord('scope', { + authorized_actions: ['detach-storage-policy'], + }); + assert.true(canService.can('detachStoragePolicy scope', scopeModel)); + }); +});