diff --git a/addons/api/addon/abilities/role.js b/addons/api/addon/abilities/role.js
index f9a5b26ce..323106252 100644
--- a/addons/api/addon/abilities/role.js
+++ b/addons/api/addon/abilities/role.js
@@ -27,6 +27,13 @@ export default class RoleAbility extends ModelAbility {
return this.hasAuthorizedAction('set-grants');
}
+ /**
+ * @type {boolean}
+ */
+ get canSetGrantScopes() {
+ return this.hasAuthorizedAction('set-grant-scopes');
+ }
+
/**
* @type {boolean}
*/
diff --git a/addons/api/mirage/factories/role.js b/addons/api/mirage/factories/role.js
index fc59bbcaa..7b1536a21 100644
--- a/addons/api/mirage/factories/role.js
+++ b/addons/api/mirage/factories/role.js
@@ -18,6 +18,7 @@ export default factory.extend({
'set-grants',
'add-principals',
'remove-principals',
+ 'set-grant-scopes',
],
// eslint-disable-next-line ember/avoid-leaking-state-in-ember-objects
grant_strings: () => [
diff --git a/addons/api/mirage/serializers/role.js b/addons/api/mirage/serializers/role.js
index b5753dc12..619814bef 100644
--- a/addons/api/mirage/serializers/role.js
+++ b/addons/api/mirage/serializers/role.js
@@ -38,11 +38,6 @@ export default ApplicationSerializer.extend({
if (!json.principals.length) delete json.principals;
- // default grant scopes
- if (json.grant_scope_ids.length === 0) {
- json.grant_scope_ids = ['this'];
- }
-
return json;
},
});
diff --git a/addons/api/tests/unit/abilities/role-test.js b/addons/api/tests/unit/abilities/role-test.js
index de3109552..e601bcc1b 100644
--- a/addons/api/tests/unit/abilities/role-test.js
+++ b/addons/api/tests/unit/abilities/role-test.js
@@ -75,4 +75,14 @@ module('Unit | Abilities | Role', function (hooks) {
});
assert.true(service.can('read account', account));
});
+
+ test('can set grant scopes based on authorized_actions', function (assert) {
+ const service = this.owner.lookup('service:can');
+ const model = {
+ authorized_actions: ['set-grant-scopes'],
+ };
+ assert.true(service.can('setGrantScopes role', model));
+ model.authorized_actions = [];
+ assert.false(service.can('setGrantScopes role', model));
+ });
});
diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml
index 24014971d..c34d84430 100644
--- a/addons/core/translations/resources/en-us.yaml
+++ b/addons/core/translations/resources/en-us.yaml
@@ -570,6 +570,10 @@ role:
titles:
parent_scope: Parent scope
resource_type: Resource type
+ messages:
+ title: No scopes added
+ description: You haven’t added any scopes to this role’s grant sets.
+ action: Add scopes to this role
keywords:
this:
sub-title: This scope
diff --git a/ui/admin/app/routes/scopes/scope/roles/role/scopes.js b/ui/admin/app/routes/scopes/scope/roles/role/scopes.js
index 6ddc733bf..bf43ea083 100644
--- a/ui/admin/app/routes/scopes/scope/roles/role/scopes.js
+++ b/ui/admin/app/routes/scopes/scope/roles/role/scopes.js
@@ -9,7 +9,10 @@ export default class ScopesScopeRolesRoleScopesRoute extends Route {
async model() {
const role = this.modelFor('scopes.scope.roles.role');
- if (!GRANT_SCOPE_KEYWORDS.some((id) => role.grant_scope_ids.includes(id))) {
+ if (
+ role.grant_scope_ids &&
+ !GRANT_SCOPE_KEYWORDS.some((id) => role.grant_scope_ids.includes(id))
+ ) {
await this.store.query('scope', {
scope_id: role.scope.id,
recursive: true,
diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss
index 4d5f2227c..f1aaa6622 100644
--- a/ui/admin/app/styles/app.scss
+++ b/ui/admin/app/styles/app.scss
@@ -1036,3 +1036,10 @@
.loading-indicator {
margin-top: 20%;
}
+
+// Roles
+.role-grant-scopes {
+ .hds-application-state {
+ margin-top: 3rem;
+ }
+}
diff --git a/ui/admin/app/templates/scopes/scope/roles/role/scopes.hbs b/ui/admin/app/templates/scopes/scope/roles/role/scopes.hbs
index 2c2c31d7b..e4227b2a3 100644
--- a/ui/admin/app/templates/scopes/scope/roles/role/scopes.hbs
+++ b/ui/admin/app/templates/scopes/scope/roles/role/scopes.hbs
@@ -40,7 +40,7 @@
-
+
{{#if @model.grant_scope_ids}}
+ {{else}}
+
+
+
+ {{#if (can 'setGrantScopes role' @model)}}
+
+ {{! TODO: Update route to manage-scopes once it is implemented }}
+
+
+ {{/if}}
+
{{/if}}
diff --git a/ui/admin/tests/acceptance/roles/scopes-test.js b/ui/admin/tests/acceptance/roles/scopes-test.js
index 392141004..d2d1c5c0f 100644
--- a/ui/admin/tests/acceptance/roles/scopes-test.js
+++ b/ui/admin/tests/acceptance/roles/scopes-test.js
@@ -75,4 +75,22 @@ module('Acceptance | roles | scopes', function (hooks) {
`/scopes/${instances.scopes.global.id}/scopes`,
);
});
+
+ test('user sees no scopes message and action when role has no grant scopes', async function (assert) {
+ instances.role.update({ grant_scope_ids: [] });
+ await visit(urls.roleScopes);
+ assert.dom('.role-grant-scopes div').includesText('No scopes added');
+ assert.dom('.role-grant-scopes div div:nth-child(3) a').isVisible();
+ });
+
+ test('user does not see action to add scopes when role has no grant scopes without proper permissions', async function (assert) {
+ instances.role.update({
+ grant_scope_ids: [],
+ authorized_actions: instances.role.authorized_actions.filter(
+ (action) => action !== 'set-grant-scopes',
+ ),
+ });
+ await visit(urls.roleScopes);
+ assert.dom('.role-grant-scopes div div:nth-child(3) a').doesNotExist();
+ });
});