diff --git a/addons/api/addon/abilities/host-set.js b/addons/api/addon/abilities/host-set.js
new file mode 100644
index 000000000..088683178
--- /dev/null
+++ b/addons/api/addon/abilities/host-set.js
@@ -0,0 +1,22 @@
+import ModelAbility from './model';
+
+/**
+ * Provides abilities for host sets.
+ */
+export default class UserAbility extends ModelAbility {
+ // =permissions
+
+ /**
+ * @type {boolean}
+ */
+ get canAddHosts() {
+ return this.hasAuthorizedAction('add-hosts');
+ }
+
+ /**
+ * @type {boolean}
+ */
+ get canRemoveHosts() {
+ return this.hasAuthorizedAction('remove-hosts');
+ }
+}
diff --git a/addons/api/app/abilities/host-set.js b/addons/api/app/abilities/host-set.js
new file mode 100644
index 000000000..37ccd5e81
--- /dev/null
+++ b/addons/api/app/abilities/host-set.js
@@ -0,0 +1 @@
+export { default } from 'api/abilities/host-set';
diff --git a/addons/api/mirage/factories/host-set.js b/addons/api/mirage/factories/host-set.js
index def5ff70f..3b88650b7 100644
--- a/addons/api/mirage/factories/host-set.js
+++ b/addons/api/mirage/factories/host-set.js
@@ -8,6 +8,8 @@ export default factory.extend({
'read',
'update',
'delete',
+ 'add-hosts',
+ 'remove-hosts',
],
id: (i) => `host-set-id-${i}`,
});
diff --git a/addons/api/tests/unit/abilities/host-set-test.js b/addons/api/tests/unit/abilities/host-set-test.js
new file mode 100644
index 000000000..24b0efa15
--- /dev/null
+++ b/addons/api/tests/unit/abilities/host-set-test.js
@@ -0,0 +1,28 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+
+module('Unit | Abilities | Host Set', function (hooks) {
+ setupTest(hooks);
+
+ test('it reflects when a given host set resource may add hosts based on authorized_actions', function (assert) {
+ assert.expect(2);
+ const service = this.owner.lookup('service:can');
+ const model = {
+ authorized_actions: ['add-hosts'],
+ };
+ assert.ok(service.can('addHosts hostSet', model));
+ model.authorized_actions = [];
+ assert.notOk(service.can('addHosts hostSet', model));
+ });
+
+ test('it reflects when a given hostSet resource may remove hosts based on authorized_actions', function (assert) {
+ assert.expect(2);
+ const service = this.owner.lookup('service:can');
+ const model = {
+ authorized_actions: ['remove-hosts'],
+ };
+ assert.ok(service.can('removeHosts hostSet', model));
+ model.authorized_actions = [];
+ assert.notOk(service.can('removeHosts hostSet', model));
+ });
+});
diff --git a/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/-actions.hbs b/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/-actions.hbs
index d78e1b940..d0bae064d 100644
--- a/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/-actions.hbs
+++ b/ui/admin/app/templates/scopes/scope/host-catalogs/host-catalog/host-sets/host-set/-actions.hbs
@@ -3,16 +3,18 @@
@dropdownRight={{true}}
as |dropdown|
>
-
- {{t 'resources.host-set.actions.create-and-add-host'}}
-
-
- {{t 'resources.host-set.actions.add-hosts'}}
-
+ {{#if (can 'addHosts hostSet' @model)}}
+
+ {{t 'resources.host-set.actions.create-and-add-host'}}
+
+
+ {{t 'resources.host-set.actions.add-hosts'}}
+
+ {{/if}}
{{#if (can 'delete model' @model)}}
-
-
- {{t 'actions.remove'}}
-
-
+
+ {{t 'actions.remove'}}
+
+
+ {{/if}}
{{/each}}
diff --git a/ui/admin/tests/acceptance/host-catalogs/host-sets/hosts-test.js b/ui/admin/tests/acceptance/host-catalogs/host-sets/hosts-test.js
index 3140961c7..2423c62ca 100644
--- a/ui/admin/tests/acceptance/host-catalogs/host-sets/hosts-test.js
+++ b/ui/admin/tests/acceptance/host-catalogs/host-sets/hosts-test.js
@@ -99,6 +99,16 @@ module('Acceptance | host-catalogs | host-sets | hosts', function (hooks) {
assert.equal(findAll('tbody tr').length, count - 1);
});
+ test('cannot remove a host without proper authorization', async function (assert) {
+ assert.expect(1);
+ const authorized_actions = instances.hostSet.authorized_actions.filter(
+ (item) => item !== 'remove-hosts'
+ );
+ instances.hostSet.update({ authorized_actions });
+ await visit(urls.hostSetHosts);
+ assert.notOk(find('tbody tr .rose-dropdown-button-danger'));
+ });
+
test('shows error message on host remove error', async function (assert) {
assert.expect(2);
this.server.post('/host-sets/:idMethod', () => {
@@ -126,6 +136,22 @@ module('Acceptance | host-catalogs | host-sets | hosts', function (hooks) {
assert.equal(currentURL(), urls.addHosts);
});
+ test('can navigate to add hosts with proper authorization', async function (assert) {
+ assert.expect(1);
+ await visit(urls.hostSet);
+ assert.ok(find(`[href="${urls.addHosts}"]`));
+ });
+
+ test('cannot navigate to add hosts without proper authorization', async function (assert) {
+ assert.expect(1);
+ const authorized_actions = instances.hostSet.authorized_actions.filter(
+ (item) => item !== 'add-hosts'
+ );
+ instances.hostSet.update({ authorized_actions });
+ await visit(urls.hostSet);
+ assert.notOk(find(`[href="${urls.addHosts}"]`));
+ });
+
test('select and save hosts to add', async function (assert) {
assert.expect(3);
instances.hostSet.update({ hostIds: [] });