Icu 13880 create manage scopes route and view with keyword values (#2342)
* feat: 🎸 manage scopes route and page ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13880 * Update addons/core/translations/resources/en-us.yaml Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com> * Update ui/admin/app/abilities/role.js Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com> * Update ui/admin/app/components/form/role/manage-scopes/index.hbs Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com> --------- Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
This commit is contained in:
1 parent
85bce84356
commit
89e072401c
18 files changed
+536
-16
No files matched your search
@@ -23,6 +23,7 @@ export default class GeneratedRoleModel extends BaseModel {
|
||||
description;
|
||||
|
||||
@attr('array', {
|
||||
emptyArrayIfMissing: true,
|
||||
description:
|
||||
"The scopes to which this role applies. Defaults to the role's scope.",
|
||||
})
|
||||
|
||||
@@ -97,6 +97,8 @@ module('Unit | Serializer | role', function (hooks) {
|
||||
id: '1',
|
||||
name: 'Role 1',
|
||||
grant_strings: ['*', '*'],
|
||||
grant_scope_ids: ['*', '*'],
|
||||
principals: ['*'],
|
||||
};
|
||||
const normalized = serializer.normalizeSingleResponse(
|
||||
store,
|
||||
@@ -112,14 +114,15 @@ module('Unit | Serializer | role', function (hooks) {
|
||||
authorized_actions: [],
|
||||
name: 'Role 1',
|
||||
grant_strings: ['*', '*'],
|
||||
principals: [],
|
||||
grant_scope_ids: ['*', '*'],
|
||||
principals: ['*'],
|
||||
},
|
||||
relationships: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('it normalizes missing principals to empty array', function (assert) {
|
||||
test('it normalizes missing arrays to empty array', function (assert) {
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('role');
|
||||
const role = store.createRecord('role').constructor;
|
||||
@@ -140,6 +143,7 @@ module('Unit | Serializer | role', function (hooks) {
|
||||
scope: { id: 'o_123', scope_id: 'o_123' },
|
||||
principals: [],
|
||||
grant_strings: [],
|
||||
grant_scope_ids: [],
|
||||
},
|
||||
relationships: {},
|
||||
},
|
||||
|
||||
@@ -571,9 +571,16 @@ role:
|
||||
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
|
||||
none:
|
||||
title: No scopes added
|
||||
description: You haven't added any scopes to this role's grant sets.
|
||||
action: Add scopes to this role
|
||||
manage-scopes:
|
||||
title: Manage Scopes
|
||||
description: Select which scopes you want to add to this role.
|
||||
keywords-selected:
|
||||
description: Just a heads up, you can't select both add all descendants and add all children. You can only choose one or the other.
|
||||
link: You can read more about it here
|
||||
keywords:
|
||||
this:
|
||||
sub-title: This scope
|
||||
@@ -583,6 +590,23 @@ role:
|
||||
descendants:
|
||||
title: All Descendants
|
||||
sub-title: Add all of the orgs and the projects in the orgs that are underneath this scope
|
||||
actions:
|
||||
manage-scopes: Manage Scopes
|
||||
add-custom-scopes:
|
||||
text: Add custom scopes
|
||||
help: Customize which scopes are added to this role.
|
||||
form:
|
||||
this:
|
||||
label: Add this scope
|
||||
help: Add this scope.
|
||||
children:
|
||||
label: Add all children
|
||||
help:
|
||||
0: Add all of the orgs underneath this scope.
|
||||
1: Add all of the projects underneath this org.
|
||||
descendants:
|
||||
label: Add all descendants
|
||||
help: Add all of the orgs and the projects in the orgs that are underneath this scope.
|
||||
target:
|
||||
title: Target
|
||||
title_plural: Targets
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import RoleAbility from 'api/abilities/role';
|
||||
|
||||
export default class OverrideRoleAbility extends RoleAbility {
|
||||
/**
|
||||
* This override ensures that a user can only set grant scopes
|
||||
* on global and org level roles.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canSetGrantScopes() {
|
||||
return !this.model.scope.isProject && super.canSetGrantScopes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<Rose::Form
|
||||
class='full-width role-manage-scopes-form'
|
||||
@onSubmit={{fn @submit this.selectedItems}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
as |form|
|
||||
>
|
||||
|
||||
<Hds::Form::Toggle::Field
|
||||
@value={{this.keywords.keyThis}}
|
||||
name={{this.keywords.keyThis}}
|
||||
checked={{includes this.keywords.keyThis this.selectedItems}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'change' this.toggleField}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.role.scope.form.this.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'resources.role.scope.form.this.help'}}</F.HelperText>
|
||||
</Hds::Form::Toggle::Field>
|
||||
|
||||
<Hds::Form::Toggle::Field
|
||||
@value={{this.keywords.keyChildren}}
|
||||
name={{this.keywords.keyChildren}}
|
||||
checked={{includes this.keywords.keyChildren this.selectedItems}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'change' this.toggleField}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.role.scope.form.children.label'}}</F.Label>
|
||||
<F.HelperText>{{if
|
||||
@model.scope.isGlobal
|
||||
(t 'resources.role.scope.form.children.help.0')
|
||||
(t 'resources.role.scope.form.children.help.1')
|
||||
}}</F.HelperText>
|
||||
</Hds::Form::Toggle::Field>
|
||||
|
||||
{{#if @model.scope.isGlobal}}
|
||||
<Hds::Form::Toggle::Field
|
||||
@value={{this.keywords.keyDescendants}}
|
||||
name={{this.keywords.keyDescendants}}
|
||||
checked={{includes this.keywords.keyDescendants this.selectedItems}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'change' this.toggleField}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.role.scope.form.descendants.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.role.scope.form.descendants.help'
|
||||
}}</F.HelperText>
|
||||
</Hds::Form::Toggle::Field>
|
||||
{{/if}}
|
||||
|
||||
{{! TODO: Add correct href value inline link}}
|
||||
{{#if this.showAlert}}
|
||||
<Hds::Alert @type='compact' as |A|>
|
||||
<A.Description>
|
||||
{{t 'resources.role.scope.messages.keywords-selected.description'}}
|
||||
<Hds::Link::Inline @color='secondary' @href={{(doc-url 'role')}}>
|
||||
{{t 'resources.role.scope.messages.keywords-selected.link'}}
|
||||
</Hds::Link::Inline>
|
||||
</A.Description>
|
||||
</Hds::Alert>
|
||||
{{/if}}
|
||||
|
||||
{{! TODO: Add correct route string once route is created}}
|
||||
<Hds::Button
|
||||
@text={{t 'resources.role.scope.actions.add-custom-scopes.text'}}
|
||||
@color='secondary'
|
||||
@route='scopes.scope.roles.role'
|
||||
/>
|
||||
|
||||
<Hds::Form::HelperText
|
||||
@controlId='for-add-custom-scopes-button'
|
||||
class='add-custom-scopes-helper-text'
|
||||
>
|
||||
{{t 'resources.role.scope.actions.add-custom-scopes.help'}}
|
||||
</Hds::Form::HelperText>
|
||||
|
||||
<form.actions
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
</Rose::Form>
|
||||
@@ -0,0 +1,58 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import {
|
||||
GRANT_SCOPE_THIS,
|
||||
GRANT_SCOPE_CHILDREN,
|
||||
GRANT_SCOPE_DESCENDANTS,
|
||||
} from 'api/models/role';
|
||||
|
||||
export default class FormRoleManageScopesIndexComponent extends Component {
|
||||
// =attributes
|
||||
|
||||
keywords = {
|
||||
keyThis: GRANT_SCOPE_THIS,
|
||||
keyChildren: GRANT_SCOPE_CHILDREN,
|
||||
keyDescendants: GRANT_SCOPE_DESCENDANTS,
|
||||
};
|
||||
|
||||
@tracked selectedItems = [...this.args.model.grant_scope_ids];
|
||||
|
||||
/**
|
||||
* Returns true if role is global level and either
|
||||
* "children" or "descendants" is toggled on.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get showAlert() {
|
||||
return (
|
||||
this.args.model.scope.isGlobal &&
|
||||
(this.selectedItems.includes(GRANT_SCOPE_CHILDREN) ||
|
||||
this.selectedItems.includes(GRANT_SCOPE_DESCENDANTS))
|
||||
);
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Handles toggle event changes for selectedGrantScopeIDs
|
||||
* @param {object} event
|
||||
*/
|
||||
@action
|
||||
toggleField(event) {
|
||||
const { checked, value } = event.target;
|
||||
const removeValue = (value) => {
|
||||
this.selectedItems = this.selectedItems.filter((item) => item !== value);
|
||||
};
|
||||
if (checked) {
|
||||
this.selectedItems = [...this.selectedItems, value];
|
||||
if (value === GRANT_SCOPE_CHILDREN) {
|
||||
removeValue(GRANT_SCOPE_DESCENDANTS);
|
||||
}
|
||||
if (value === GRANT_SCOPE_DESCENDANTS) {
|
||||
removeValue(GRANT_SCOPE_CHILDREN);
|
||||
}
|
||||
} else {
|
||||
removeValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,30 @@
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
{{!TODO Refactor page to use hds dropdown in ICU-13879}}
|
||||
<Rose::Dropdown
|
||||
@text={{t 'actions.manage'}}
|
||||
@dropdownRight={{true}}
|
||||
as |dropdown|
|
||||
>
|
||||
{{#if (can 'addPrincipals role' @model)}}
|
||||
<dropdown.link @route='scopes.scope.roles.role.add-principals'>
|
||||
<dropdown.link
|
||||
@route='scopes.scope.roles.role.add-principals'
|
||||
data-test-manage-dropdown-principals
|
||||
>
|
||||
{{t 'resources.role.principal.actions.add-principals'}}
|
||||
</dropdown.link>
|
||||
{{/if}}
|
||||
|
||||
{{#if (can 'setGrantScopes role' @model)}}
|
||||
<dropdown.link
|
||||
@route='scopes.scope.roles.role.manage-scopes'
|
||||
data-test-manage-dropdown-scopes
|
||||
>
|
||||
{{t 'resources.role.scope.actions.manage-scopes'}}
|
||||
</dropdown.link>
|
||||
{{/if}}
|
||||
|
||||
{{#if (can 'delete model' @model)}}
|
||||
<dropdown.separator />
|
||||
<dropdown.button
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { loading } from 'ember-loading';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
export default class ScopesScopeRolesRoleManageScopesController extends Controller {
|
||||
// =services
|
||||
|
||||
@service router;
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Save grant scope IDs to current role via the API.
|
||||
* @param {RoleModel} role
|
||||
* @param {[string]} grantScopeIDs
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.add-success')
|
||||
async setGrantScopes(role, grantScopeIDs) {
|
||||
await role.setGrantScopes(grantScopeIDs);
|
||||
this.router.replaceWith('scopes.scope.roles.role.scopes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to role scopes as if nothing ever happened.
|
||||
*/
|
||||
@action
|
||||
async cancel() {
|
||||
await this.router.replaceWith('scopes.scope.roles.role.scopes');
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ Router.map(function () {
|
||||
this.route('principals');
|
||||
this.route('add-principals');
|
||||
this.route('scopes');
|
||||
this.route('manage-scopes');
|
||||
});
|
||||
this.route('new');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeRolesRoleManageScopesRoute extends Route {
|
||||
// =methods
|
||||
|
||||
model() {
|
||||
return this.modelFor('scopes.scope.roles.role');
|
||||
}
|
||||
}
|
||||
@@ -1043,3 +1043,14 @@
|
||||
margin-top: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.role-manage-scopes-form {
|
||||
.add-custom-scopes-helper-text {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hds-alert {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
{{page-title (t 'resources.role.scope.actions.manage-scopes')}}
|
||||
<Breadcrumbs::Item
|
||||
@text={{t 'resources.role.scope.actions.manage-scopes'}}
|
||||
@route='scopes.scope.roles.role.manage-scopes'
|
||||
/>
|
||||
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<page.breadcrumbs>
|
||||
<Breadcrumbs::Container />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>
|
||||
{{t 'resources.role.scope.messages.manage-scopes.title'}}
|
||||
{{! TODO: Add doclink }}
|
||||
</h2>
|
||||
<p>{{t 'resources.role.scope.messages.manage-scopes.description'}}</p>
|
||||
</page.header>
|
||||
|
||||
<page.body>
|
||||
<Form::Role::ManageScopes
|
||||
@model={{@model}}
|
||||
@submit={{fn this.setGrantScopes @model}}
|
||||
@cancel={{this.cancel}}
|
||||
/>
|
||||
</page.body>
|
||||
|
||||
</Rose::Layout::Page>
|
||||
@@ -149,15 +149,14 @@
|
||||
</Hds::Table>
|
||||
{{else}}
|
||||
<Hds::ApplicationState as |A|>
|
||||
<A.Header @title={{t 'resources.role.scope.messages.title'}} />
|
||||
<A.Body @text={{t 'resources.role.scope.messages.description'}} />
|
||||
<A.Header @title={{t 'resources.role.scope.messages.none.title'}} />
|
||||
<A.Body @text={{t 'resources.role.scope.messages.none.description'}} />
|
||||
{{#if (can 'setGrantScopes role' @model)}}
|
||||
<A.Footer as |F|>
|
||||
{{! TODO: Update route to manage-scopes once it is implemented }}
|
||||
<F.Link::Standalone
|
||||
@icon='plus-circle'
|
||||
@text={{t 'resources.role.scope.messages.action'}}
|
||||
@route='scopes.scope.roles.role.scopes'
|
||||
@text={{t 'resources.role.scope.messages.none.action'}}
|
||||
@route='scopes.scope.roles.role.manage-scopes'
|
||||
/>
|
||||
</A.Footer>
|
||||
{{/if}}
|
||||
|
||||
@@ -22,6 +22,8 @@ module('Acceptance | roles | principals', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
const ADD_PRINCIPALS_SELECTOR = '[data-test-manage-dropdown-principals]';
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -111,14 +113,14 @@ module('Acceptance | roles | principals', function (hooks) {
|
||||
);
|
||||
instances.role.update({ authorized_actions });
|
||||
await visit(urls.rolePrincipals);
|
||||
assert.notOk(find('.rose-layout-page-actions a'));
|
||||
assert.dom(ADD_PRINCIPALS_SELECTOR).doesNotExist();
|
||||
});
|
||||
|
||||
test('select and save principals to add', async function (assert) {
|
||||
instances.role.update({ userIds: [], groupIds: [], managedGroupIds: [] });
|
||||
await visit(urls.rolePrincipals);
|
||||
assert.strictEqual(findAll('tbody tr').length, 0);
|
||||
await click('.rose-layout-page-actions a');
|
||||
await click(ADD_PRINCIPALS_SELECTOR);
|
||||
assert.strictEqual(currentURL(), urls.addPrincipals);
|
||||
// Click three times to select, unselect, then reselect (for coverage)
|
||||
await click('tbody label');
|
||||
@@ -136,7 +138,7 @@ module('Acceptance | roles | principals', function (hooks) {
|
||||
await click('.hds-dropdown-toggle-icon');
|
||||
await click('tbody tr .hds-dropdown-list-item button');
|
||||
assert.strictEqual(findAll('tbody tr').length, principalsCount - 1);
|
||||
await click('.rose-layout-page-actions a');
|
||||
await click(ADD_PRINCIPALS_SELECTOR);
|
||||
assert.strictEqual(currentURL(), urls.addPrincipals);
|
||||
await click('tbody label');
|
||||
await click('form [type="button"]');
|
||||
|
||||
@@ -4,18 +4,27 @@
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { visit, currentURL, click } from '@ember/test-helpers';
|
||||
import { visit, currentURL, click, findAll } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import { GRANT_SCOPE_THIS } from 'api/models/role';
|
||||
|
||||
module('Acceptance | roles | scopes', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
const SCOPE_TOGGLE_SELECTOR = (name) =>
|
||||
`.hds-form-toggle input[name="${name}"]`;
|
||||
const TABLE_ROW_SELECTOR = 'tbody tr';
|
||||
const SAVE_BTN_SELECTOR = 'form [type="submit"]';
|
||||
const CANCEL_BTN_SELECTOR = '.rose-form-actions [type="button"]';
|
||||
const MANAGE_SCOPES_SELECTOR = '[data-test-manage-dropdown-scopes]';
|
||||
const TOAST_SELECTOR = '[role="alert"]';
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -28,6 +37,7 @@ module('Acceptance | roles | scopes', function (hooks) {
|
||||
roles: null,
|
||||
role: null,
|
||||
roleScopes: null,
|
||||
manageScopes: null,
|
||||
};
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
@@ -47,6 +57,7 @@ module('Acceptance | roles | scopes', function (hooks) {
|
||||
urls.roles = `/scopes/${instances.scopes.org.id}/roles`;
|
||||
urls.role = `${urls.roles}/${instances.role.id}`;
|
||||
urls.roleScopes = `${urls.role}/scopes`;
|
||||
urls.manageScopes = `${urls.role}/manage-scopes`;
|
||||
});
|
||||
|
||||
test('visiting role scopes', async function (assert) {
|
||||
@@ -54,7 +65,7 @@ module('Acceptance | roles | scopes', function (hooks) {
|
||||
await a11yAudit();
|
||||
assert.strictEqual(currentURL(), urls.roleScopes);
|
||||
assert
|
||||
.dom('tbody tr')
|
||||
.dom(TABLE_ROW_SELECTOR)
|
||||
.exists({ count: instances.role.grant_scope_ids.length });
|
||||
});
|
||||
|
||||
@@ -93,4 +104,71 @@ module('Acceptance | roles | scopes', function (hooks) {
|
||||
await visit(urls.roleScopes);
|
||||
assert.dom('.role-grant-scopes div div:nth-child(3) a').doesNotExist();
|
||||
});
|
||||
|
||||
test('toggle and save scope keywords to add', async function (assert) {
|
||||
instances.role.update({ grant_scope_ids: [] });
|
||||
await visit(urls.role);
|
||||
|
||||
await click(`[href="${urls.roleScopes}"]`);
|
||||
|
||||
assert.strictEqual(findAll(TABLE_ROW_SELECTOR).length, 0);
|
||||
|
||||
await click(MANAGE_SCOPES_SELECTOR);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.manageScopes);
|
||||
|
||||
// Click three times to select, unselect, then reselect (for coverage)
|
||||
await click(SCOPE_TOGGLE_SELECTOR(GRANT_SCOPE_THIS));
|
||||
await click(SCOPE_TOGGLE_SELECTOR(GRANT_SCOPE_THIS));
|
||||
await click(SCOPE_TOGGLE_SELECTOR(GRANT_SCOPE_THIS));
|
||||
await click(SAVE_BTN_SELECTOR);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.roleScopes);
|
||||
assert.strictEqual(findAll(TABLE_ROW_SELECTOR).length, 1);
|
||||
});
|
||||
|
||||
test('toggle and cancel scope keywords to add', async function (assert) {
|
||||
instances.role.update({ grant_scope_ids: [] });
|
||||
await visit(urls.role);
|
||||
|
||||
await click(`[href="${urls.roleScopes}"]`);
|
||||
|
||||
assert.strictEqual(findAll(TABLE_ROW_SELECTOR).length, 0);
|
||||
|
||||
await click(MANAGE_SCOPES_SELECTOR);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.manageScopes);
|
||||
|
||||
// Click three times to select, unselect, then reselect (for coverage)
|
||||
await click(SCOPE_TOGGLE_SELECTOR(GRANT_SCOPE_THIS));
|
||||
await click(SCOPE_TOGGLE_SELECTOR(GRANT_SCOPE_THIS));
|
||||
await click(SCOPE_TOGGLE_SELECTOR(GRANT_SCOPE_THIS));
|
||||
await click(CANCEL_BTN_SELECTOR);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.roleScopes);
|
||||
assert.strictEqual(findAll(TABLE_ROW_SELECTOR).length, 0);
|
||||
});
|
||||
|
||||
test('shows error message on scope save', async function (assert) {
|
||||
this.server.post('/roles/:idMethod', () => {
|
||||
return new Response(
|
||||
400,
|
||||
{},
|
||||
{
|
||||
status: 400,
|
||||
code: 'invalid_argument',
|
||||
message: 'The request was invalid.',
|
||||
details: {},
|
||||
},
|
||||
);
|
||||
});
|
||||
instances.role.update({ grant_scope_ids: [] });
|
||||
await visit(urls.role);
|
||||
|
||||
await click(MANAGE_SCOPES_SELECTOR);
|
||||
await click(SCOPE_TOGGLE_SELECTOR(GRANT_SCOPE_THIS));
|
||||
await click(SAVE_BTN_SELECTOR);
|
||||
|
||||
assert.dom(TOAST_SELECTOR).isVisible();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Abilities | role', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
let canService;
|
||||
let store;
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
canService = this.owner.lookup('service:can');
|
||||
store = this.owner.lookup('service:store');
|
||||
});
|
||||
|
||||
test('can setGrantScopes on role when authorized and scope is global type', function (assert) {
|
||||
const globalScope = store.createRecord('scope', { id: 'global' });
|
||||
const role = store.createRecord('role', {
|
||||
authorized_actions: ['set-grant-scopes'],
|
||||
scope: globalScope,
|
||||
});
|
||||
assert.true(canService.can('setGrantScopes role', role));
|
||||
role.authorized_actions = [];
|
||||
assert.false(canService.can('setGrantScopes role', role));
|
||||
});
|
||||
|
||||
test('can setGrantScopes on role when authorized and scope is org type', function (assert) {
|
||||
const globalScope = store.createRecord('scope', { id: 'global' });
|
||||
const orgScope = store.createRecord('scope', {
|
||||
type: 'org',
|
||||
scope: globalScope,
|
||||
});
|
||||
const role = store.createRecord('role', {
|
||||
authorized_actions: ['set-grant-scopes'],
|
||||
scope: orgScope,
|
||||
});
|
||||
assert.true(canService.can('setGrantScopes role', role));
|
||||
role.authorized_actions = [];
|
||||
assert.false(canService.can('setGrantScopes role', role));
|
||||
});
|
||||
|
||||
test('cannot setGrantScopes on role when authorized and scope is project type', function (assert) {
|
||||
const globalScope = store.createRecord('scope', { id: 'global' });
|
||||
const orgScope = store.createRecord('scope', {
|
||||
type: 'org',
|
||||
scope: globalScope,
|
||||
});
|
||||
const projectScope = store.createRecord('scope', {
|
||||
type: 'project',
|
||||
scope: orgScope,
|
||||
});
|
||||
const role = store.createRecord('role', {
|
||||
authorized_actions: ['set-grant-scopes'],
|
||||
scope: projectScope,
|
||||
});
|
||||
assert.false(canService.can('setGrantScopes role', role));
|
||||
role.authorized_actions = [];
|
||||
assert.false(canService.can('setGrantScopes role', role));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { visit, currentURL } from '@ember/test-helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
|
||||
module(
|
||||
'Unit | Controller | scopes/scope/roles/role/manage-scopes',
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
let store;
|
||||
let controller;
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
global: null,
|
||||
},
|
||||
role: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
globalScope: null,
|
||||
scopes: null,
|
||||
manageScopes: null,
|
||||
};
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
authenticateSession({});
|
||||
store = this.owner.lookup('service:store');
|
||||
controller = this.owner.lookup(
|
||||
'controller:scopes/scope/roles/role/manage-scopes',
|
||||
);
|
||||
|
||||
instances.scopes.global = this.server.create('scope', { id: 'global' });
|
||||
instances.role = this.server.create('role', {
|
||||
scopeId: 'global',
|
||||
grant_scope_ids: [],
|
||||
});
|
||||
|
||||
urls.globalScope = `/scopes/global`;
|
||||
urls.scopes = `${urls.globalScope}/roles/${instances.role.id}/scopes`;
|
||||
urls.manageScopes = `${urls.globalScope}/roles/${instances.role.id}/manage-scopes`;
|
||||
});
|
||||
|
||||
test('it exists', function (assert) {
|
||||
assert.ok(controller);
|
||||
});
|
||||
|
||||
test('setGrantScopes action adds scopes to specified model', async function (assert) {
|
||||
await visit(urls.manageScopes);
|
||||
const role = await store.findRecord('role', instances.role.id);
|
||||
|
||||
assert.deepEqual(role.grant_scope_ids, []);
|
||||
|
||||
await controller.setGrantScopes(role, ['this']);
|
||||
|
||||
assert.deepEqual(role.grant_scope_ids, ['this']);
|
||||
});
|
||||
|
||||
test('cancel action causes transition to expected route', async function (assert) {
|
||||
await visit(urls.manageScopes);
|
||||
|
||||
await controller.cancel();
|
||||
|
||||
assert.strictEqual(currentURL(), urls.scopes);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,16 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module(
|
||||
'Unit | Route | scopes/scope/roles/role/manage-scopes',
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function (assert) {
|
||||
let route = this.owner.lookup(
|
||||
'route:scopes/scope/roles/role/manage-scopes',
|
||||
);
|
||||
assert.ok(route);
|
||||
});
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user