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)); + }); +});