@fleetbase/iam-engine
Version:
Fleetbase IAM extension provides identity and access management module for managing users, permissions and policies.
363 lines (329 loc) • 11.1 kB
JavaScript
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { isBlank } from '@ember/utils';
import { timeout, task } from 'ember-concurrency';
export default class RolesIndexController extends Controller {
store;
intl;
notifications;
currentUser;
modalsManager;
hostRouter;
filters;
crud;
fetch;
abilities;
iam;
/**
* Queryable parameters for this controller's model
*
* @var {Array}
*/
queryParams = ['page', 'limit', 'sort', 'query', 'service', 'type'];
/**
* The current page of data being viewed
*
* @var {Integer}
*/
page = 1;
/**
* The maximum number of items to show per page
*
* @var {Integer}
*/
limit;
/**
* The search query param
*
* @var {Integer}
*/
query;
/**
* The param to sort the data on, the param with prepended `-` is descending
*
* @var {String}
*/
sort = '-created_at';
/**
* All services for roles.
*
* @memberof RolesIndexController
*/
services = [];
/**
* All types of roles.
*
* @memberof RolesIndexController
*/
types = this.iam.schemeTypes;
/**
* All columns applicable for roles
*
* @var {Array}
*/
columns = [
{
label: this.intl.t('iam.common.name'),
valuePath: 'name',
cellComponent: 'table/cell/anchor',
permission: 'iam view role',
onClick: this.editRole,
width: '20%',
sortable: false,
},
{
label: this.intl.t('iam.common.description'),
valuePath: 'description',
sortable: false,
width: '28%',
},
{
label: this.intl.t('iam.common.service'),
valuePath: 'service',
sortable: false,
width: '12%',
filterable: true,
filterComponent: 'filter/select',
filterOptions: this.services,
},
{
label: this.intl.t('iam.common.type'),
valuePath: 'type',
sortable: false,
width: '13%',
filterable: true,
filterComponent: 'filter/select',
filterOptionLabel: 'name',
filterOptionValue: 'id',
filterOptions: this.types,
},
{
label: this.intl.t('iam.common.create'),
valuePath: 'createdAt',
sortable: false,
width: '12%',
tooltip: true,
cellClassNames: 'overflow-visible',
},
{
label: '',
cellComponent: 'table/cell/dropdown',
ddButtonText: false,
ddButtonIcon: 'ellipsis-h',
ddButtonIconPrefix: 'fas',
ddMenuLabel: this.intl.t('iam.roles.index.role-actions'),
cellClassNames: 'overflow-visible',
wrapperClass: 'flex items-center justify-end mx-2',
width: '15%',
actions: [
{
label: this.intl.t('iam.roles.index.edit-role'),
fn: this.editRole,
permission: 'iam view role',
},
{
label: this.intl.t('iam.roles.index.delete-role'),
fn: this.deleteRole,
className: 'text-red-700 hover:text-red-800',
permission: 'iam delete role',
},
],
},
];
/**
* Creates an instance of RolesIndexController.
* @memberof RolesIndexController
*/
constructor() {
super(...arguments);
this.iam.getServices.perform({
onSuccess: (services) => {
this.services = services;
},
});
}
/**
* The search task.
*
* @void
*/
*search({ target: { value } }) {
// if no query don't search
if (isBlank(value)) {
this.query = null;
return;
}
// timeout for typing
yield timeout(250);
// reset page for results
if (this.page > 1) {
this.page = 1;
}
// update the query param
this.query = value;
}
/**
* Bulk deletes selected `role` via confirm prompt
*
* @param {Array} selected an array of selected models
* @void
*/
bulkDeleteRoles() {
const selected = this.table.selectedRows;
this.crud.bulkDelete(selected, {
modelNamePath: `name`,
acceptButtonText: this.intl.t('iam.roles.index.delete-roles'),
onSuccess: () => {
return this.hostRouter.refresh();
},
});
}
/**
* Toggles dialog to create a new Role
*
* @void
*/
createRole() {
const formPermission = 'iam create role';
const role = this.store.createRecord('role', { is_mutable: true });
this.editRole(role, {
title: this.intl.t('iam.roles.index.new-role'),
acceptButtonText: this.intl.t('common.confirm'),
acceptButtonIcon: 'check',
acceptButtonDisabled: this.abilities.cannot(formPermission),
acceptButtonHelpText: this.abilities.cannot(formPermission) ? this.intl.t('common.unauthorized') : null,
formPermission,
keepOpen: true,
confirm: async (modal) => {
modal.startLoading();
if (!role.name || typeof role.name !== 'string') {
modal.stopLoading();
return this.notifications.warning('Role name is required.');
}
const roleName = role.name.toLowerCase();
if (roleName === 'administrator' || roleName.startsWith('admin')) {
modal.stopLoading();
return this.notifications.error('Creating a role with name "Administrator" or a role name that starts with "Admin" is prohibited, as the name is system reserved.');
}
if (this.abilities.cannot(formPermission)) {
modal.stopLoading();
return this.notifications.warning(this.intl.t('common.permissions-required-for-changes'));
}
try {
await role.save();
this.notifications.success(this.intl.t('iam.roles.index.new-role-create'));
modal.done();
return this.hostRouter.refresh();
} catch (error) {
this.notifications.serverError(error);
modal.stopLoading();
}
},
});
}
/**
* Toggles dialog to edit a Role
*
* @void
*/
editRole(role, options = {}) {
if (!role.is_mutable) {
return this.viewRolePermissions(role);
}
const formPermission = 'iam update role';
this.modalsManager.show('modals/role-form', {
title: this.intl.t('iam.roles.index.edit-role-title'),
acceptButtonText: this.intl.t('common.save-changes'),
acceptButtonIcon: 'save',
acceptButtonDisabled: this.abilities.cannot(formPermission),
acceptButtonHelpText: this.abilities.cannot(formPermission) ? this.intl.t('common.unauthorized') : null,
keepOpen: true,
formPermission,
role,
setPermissions: (permissions) => {
role.permissions = permissions;
},
confirm: async (modal) => {
modal.startLoading();
if (!role.name || typeof role.name !== 'string') {
modal.stopLoading();
return this.notifications.warning('Role name is required.');
}
const roleName = role.name.toLowerCase();
if (roleName === 'administrator' || roleName.startsWith('admin')) {
modal.stopLoading();
return this.notifications.error('Creating a role with name "Administrator" or a role name that starts with "Admin" is prohibited, as the name is system reserved.');
}
if (this.abilities.cannot(formPermission)) {
modal.stopLoading();
return this.notifications.warning(this.intl.t('common.permissions-required-for-changes'));
}
try {
await role.save();
this.notifications.success(this.intl.t('iam.roles.index.changes-role-saved'));
modal.done();
return this.hostRouter.refresh();
} catch (error) {
this.notifications.serverError(error);
modal.stopLoading();
}
},
...options,
});
}
/**
* Toggles dialog to delete Role
*
* @void
*/
deleteRole(role) {
if (!role.is_deletable) {
return this.notifications.warning(this.intl.t('iam.roles.index.unable-delete-role-warning', { roleType: role.type }));
}
this.modalsManager.confirm({
title: `Delete (${role.name || 'Untitled'}) role`,
body: this.intl.t('iam.roles.index.data-assosciated-this-role-deleted'),
confirm: async (modal) => {
modal.startLoading();
try {
await role.destroyRecord();
this.notifications.success(this.intl.t('iam.roles.index.role-deleted', { roleName: role.name }));
return this.hostRouter.refresh();
} catch (error) {
this.notifications.serverError(error);
modal.stopLoading();
}
},
});
}
/**
* View role permissions
*
* @param {RoleModel} role
* @memberof RolesIndexController
*/
viewRolePermissions(role) {
this.modalsManager.show('modals/view-role-permissions', {
title: this.intl.t('iam.components.modals.view-role-permissions.view-permissions', { roleName: role.name }),
hideDeclineButton: true,
acceptButtonText: this.intl.t('common.done'),
role,
});
}
/**
* Toggles dialog to export roles
*
* @void
*/
exportRoles() {
this.crud.export('role');
}
/**
* Reload data.
*/
reload() {
return this.hostRouter.refresh();
}
}