diff options
Diffstat (limited to 'src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-form/role-form.component.ts')
-rw-r--r-- | src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-form/role-form.component.ts | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-form/role-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-form/role-form.component.ts new file mode 100644 index 000000000..21dff1c85 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-form/role-form.component.ts @@ -0,0 +1,315 @@ +import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { FormControl, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; + +import _ from 'lodash'; +import { forkJoin as observableForkJoin } from 'rxjs'; + +import { RoleService } from '~/app/shared/api/role.service'; +import { ScopeService } from '~/app/shared/api/scope.service'; +import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; +import { NotificationType } from '~/app/shared/enum/notification-type.enum'; +import { CdForm } from '~/app/shared/forms/cd-form'; +import { CdFormGroup } from '~/app/shared/forms/cd-form-group'; +import { CdValidators } from '~/app/shared/forms/cd-validators'; +import { CdTableColumn } from '~/app/shared/models/cd-table-column'; +import { NotificationService } from '~/app/shared/services/notification.service'; +import { RoleFormMode } from './role-form-mode.enum'; +import { RoleFormModel } from './role-form.model'; + +@Component({ + selector: 'cd-role-form', + templateUrl: './role-form.component.html', + styleUrls: ['./role-form.component.scss'] +}) +export class RoleFormComponent extends CdForm implements OnInit { + @ViewChild('headerPermissionCheckboxTpl', { static: true }) + headerPermissionCheckboxTpl: TemplateRef<any>; + @ViewChild('cellScopeCheckboxTpl', { static: true }) + cellScopeCheckboxTpl: TemplateRef<any>; + @ViewChild('cellPermissionCheckboxTpl', { static: true }) + cellPermissionCheckboxTpl: TemplateRef<any>; + + roleForm: CdFormGroup; + response: RoleFormModel; + + columns: CdTableColumn[]; + scopes: Array<string> = []; + scopes_permissions: Array<any> = []; + + roleFormMode = RoleFormMode; + mode: RoleFormMode; + + action: string; + resource: string; + + constructor( + private route: ActivatedRoute, + private router: Router, + private roleService: RoleService, + private scopeService: ScopeService, + private notificationService: NotificationService, + public actionLabels: ActionLabelsI18n + ) { + super(); + this.resource = $localize`role`; + this.createForm(); + this.listenToChanges(); + } + + createForm() { + this.roleForm = new CdFormGroup({ + name: new FormControl('', { + validators: [Validators.required], + asyncValidators: [CdValidators.unique(this.roleService.exists, this.roleService)] + }), + description: new FormControl(''), + scopes_permissions: new FormControl({}) + }); + } + + ngOnInit() { + this.columns = [ + { + prop: 'scope', + name: $localize`All`, + flexGrow: 2, + cellTemplate: this.cellScopeCheckboxTpl, + headerTemplate: this.headerPermissionCheckboxTpl + }, + { + prop: 'read', + name: $localize`Read`, + flexGrow: 1, + cellClass: 'text-center', + cellTemplate: this.cellPermissionCheckboxTpl, + headerTemplate: this.headerPermissionCheckboxTpl + }, + { + prop: 'create', + name: $localize`Create`, + flexGrow: 1, + cellClass: 'text-center', + cellTemplate: this.cellPermissionCheckboxTpl, + headerTemplate: this.headerPermissionCheckboxTpl + }, + { + prop: 'update', + name: $localize`Update`, + flexGrow: 1, + cellClass: 'text-center', + cellTemplate: this.cellPermissionCheckboxTpl, + headerTemplate: this.headerPermissionCheckboxTpl + }, + { + prop: 'delete', + name: $localize`Delete`, + flexGrow: 1, + cellClass: 'text-center', + cellTemplate: this.cellPermissionCheckboxTpl, + headerTemplate: this.headerPermissionCheckboxTpl + } + ]; + if (this.router.url.startsWith('/user-management/roles/edit')) { + this.mode = this.roleFormMode.editing; + this.action = this.actionLabels.EDIT; + } else { + this.action = this.actionLabels.CREATE; + } + if (this.mode === this.roleFormMode.editing) { + this.initEdit(); + } else { + this.initCreate(); + } + } + + initCreate() { + // Load the scopes and initialize the default scopes/permissions data. + this.scopeService.list().subscribe((scopes: Array<string>) => { + this.scopes = scopes; + this.roleForm.get('scopes_permissions').setValue({}); + + this.loadingReady(); + }); + } + + initEdit() { + // Disable the 'Name' input field. + this.roleForm.get('name').disable(); + // Load the scopes and the role data. + this.route.params.subscribe((params: { name: string }) => { + const observables = []; + observables.push(this.scopeService.list()); + observables.push(this.roleService.get(params.name)); + observableForkJoin(observables).subscribe((resp: any[]) => { + this.scopes = resp[0]; + ['name', 'description', 'scopes_permissions'].forEach((key) => + this.roleForm.get(key).setValue(resp[1][key]) + ); + + this.loadingReady(); + }); + }); + } + + listenToChanges() { + // Create/Update the data which is used by the data table to display the + // scopes/permissions every time the form field value has been changed. + this.roleForm.get('scopes_permissions').valueChanges.subscribe((value) => { + const scopes_permissions: any[] = []; + _.each(this.scopes, (scope) => { + // Set the defaults values. + const scope_permission: any = { read: false, create: false, update: false, delete: false }; + scope_permission['scope'] = scope; + // Apply settings from the given value if they exist. + if (scope in value) { + _.each(value[scope], (permission) => { + scope_permission[permission] = true; + }); + } + scopes_permissions.push(scope_permission); + }); + this.scopes_permissions = scopes_permissions; + }); + } + + /** + * Checks if the specified row checkbox needs to be rendered as checked. + * @param {string} scope The scope to be checked, e.g. 'cephfs', 'grafana', + * 'osd', 'pool' ... + * @return Returns true if all permissions (read, create, update, delete) + * are checked for the specified scope, otherwise false. + */ + isRowChecked(scope: string) { + const scope_permission = _.find(this.scopes_permissions, (o) => { + return o['scope'] === scope; + }); + if (_.isUndefined(scope_permission)) { + return false; + } + return ( + scope_permission['read'] && + scope_permission['create'] && + scope_permission['update'] && + scope_permission['delete'] + ); + } + + /** + * Checks if the specified header checkbox needs to be rendered as checked. + * @param {string} property The property/permission (read, create, + * update, delete) to be checked. If 'scope' is given, all permissions + * are checked. + * @return Returns true if specified property/permission is selected + * for all scopes, otherwise false. + */ + isHeaderChecked(property: string) { + let permissions = [property]; + if ('scope' === property) { + permissions = ['read', 'create', 'update', 'delete']; + } + return permissions.every((permission) => { + return this.scopes_permissions.every((scope_permission) => { + return scope_permission[permission]; + }); + }); + } + + onClickCellCheckbox(scope: string, property: string, event: any = null) { + // Use a copy of the form field data to do not trigger the redrawing of the + // data table with every change. + const scopes_permissions = _.cloneDeep(this.roleForm.getValue('scopes_permissions')); + let permissions = [property]; + if ('scope' === property) { + permissions = ['read', 'create', 'update', 'delete']; + } + if (!(scope in scopes_permissions)) { + scopes_permissions[scope] = []; + } + // Add or remove the given permission(s) depending on the click event or if no + // click event is given then add/remove them if they are absent/exist. + if ( + (event && event.target['checked']) || + !_.isEqual(permissions.sort(), _.intersection(scopes_permissions[scope], permissions).sort()) + ) { + scopes_permissions[scope] = _.union(scopes_permissions[scope], permissions); + } else { + scopes_permissions[scope] = _.difference(scopes_permissions[scope], permissions); + if (_.isEmpty(scopes_permissions[scope])) { + _.unset(scopes_permissions, scope); + } + } + this.roleForm.get('scopes_permissions').setValue(scopes_permissions); + } + + onClickHeaderCheckbox(property: 'scope' | 'read' | 'create' | 'update' | 'delete', event: any) { + // Use a copy of the form field data to do not trigger the redrawing of the + // data table with every change. + const scopes_permissions = _.cloneDeep(this.roleForm.getValue('scopes_permissions')); + let permissions = [property]; + if ('scope' === property) { + permissions = ['read', 'create', 'update', 'delete']; + } + _.each(permissions, (permission) => { + _.each(this.scopes, (scope) => { + if (event.target['checked']) { + scopes_permissions[scope] = _.union(scopes_permissions[scope], [permission]); + } else { + scopes_permissions[scope] = _.difference(scopes_permissions[scope], [permission]); + if (_.isEmpty(scopes_permissions[scope])) { + _.unset(scopes_permissions, scope); + } + } + }); + }); + this.roleForm.get('scopes_permissions').setValue(scopes_permissions); + } + + getRequest(): RoleFormModel { + const roleFormModel = new RoleFormModel(); + ['name', 'description', 'scopes_permissions'].forEach( + (key) => (roleFormModel[key] = this.roleForm.get(key).value) + ); + return roleFormModel; + } + + createAction() { + const roleFormModel = this.getRequest(); + this.roleService.create(roleFormModel).subscribe( + () => { + this.notificationService.show( + NotificationType.success, + $localize`Created role '${roleFormModel.name}'` + ); + this.router.navigate(['/user-management/roles']); + }, + () => { + this.roleForm.setErrors({ cdSubmitButton: true }); + } + ); + } + + editAction() { + const roleFormModel = this.getRequest(); + this.roleService.update(roleFormModel).subscribe( + () => { + this.notificationService.show( + NotificationType.success, + $localize`Updated role '${roleFormModel.name}'` + ); + this.router.navigate(['/user-management/roles']); + }, + () => { + this.roleForm.setErrors({ cdSubmitButton: true }); + } + ); + } + + submit() { + if (this.mode === this.roleFormMode.editing) { + this.editAction(); + } else { + this.createAction(); + } + } +} |