import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { I18n } from '@ngx-translate/i18n-polyfill';
import * as _ from 'lodash';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { AuthService } from '../../../shared/api/auth.service';
import { RoleService } from '../../../shared/api/role.service';
import { UserService } from '../../../shared/api/user.service';
import { ConfirmationModalComponent } from '../../../shared/components/confirmation-modal/confirmation-modal.component';
import { SelectMessages } from '../../../shared/components/select/select-messages.model';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
import { NotificationType } from '../../../shared/enum/notification-type.enum';
import { CdFormGroup } from '../../../shared/forms/cd-form-group';
import { CdValidators } from '../../../shared/forms/cd-validators';
import { AuthStorageService } from '../../../shared/services/auth-storage.service';
import { NotificationService } from '../../../shared/services/notification.service';
import { UserFormMode } from './user-form-mode.enum';
import { UserFormRoleModel } from './user-form-role.model';
import { UserFormModel } from './user-form.model';
@Component({
selector: 'cd-user-form',
templateUrl: './user-form.component.html',
styleUrls: ['./user-form.component.scss']
})
export class UserFormComponent implements OnInit {
@ViewChild('removeSelfUserReadUpdatePermissionTpl')
removeSelfUserReadUpdatePermissionTpl: TemplateRef<any>;
modalRef: BsModalRef;
userForm: CdFormGroup;
response: UserFormModel;
userFormMode = UserFormMode;
mode: UserFormMode;
allRoles: Array<UserFormRoleModel>;
messages = new SelectMessages({ empty: 'There are no roles.' }, this.i18n);
action: string;
resource: string;
constructor(
private authService: AuthService,
private authStorageService: AuthStorageService,
private route: ActivatedRoute,
private router: Router,
private modalService: BsModalService,
private roleService: RoleService,
private userService: UserService,
private notificationService: NotificationService,
private i18n: I18n,
public actionLabels: ActionLabelsI18n
) {
this.resource = this.i18n('user');
this.createForm();
this.messages = new SelectMessages({ empty: 'There are no roles.' }, this.i18n);
}
createForm() {
this.userForm = new CdFormGroup(
{
username: new FormControl('', {
validators: [Validators.required]
}),
name: new FormControl(''),
password: new FormControl('', {
validators: []
}),
confirmpassword: new FormControl('', {
updateOn: 'blur',
validators: []
}),
email: new FormControl('', {
validators: [Validators.email]
}),
roles: new FormControl([])
},
{
validators: [CdValidators.match('password', 'confirmpassword')]
}
);
}
ngOnInit() {
if (this.router.url.startsWith('/user-management/users/edit')) {
this.mode = this.userFormMode.editing;
this.action = this.actionLabels.EDIT;
} else {
this.action = this.actionLabels.CREATE;
}
this.roleService.list().subscribe((roles: Array<UserFormRoleModel>) => {
this.allRoles = roles;
});
if (this.mode === this.userFormMode.editing) {
this.initEdit();
}
}
initEdit() {
this.disableForEdit();
this.route.params.subscribe((params: { username: string }) => {
const username = params.username;
this.userService.get(username).subscribe((userFormModel: UserFormModel) => {
this.response = _.cloneDeep(userFormModel);
this.setResponse(userFormModel);
});
});
}
disableForEdit() {
this.userForm.get('username').disable();
}
setResponse(response: UserFormModel) {
['username', 'name', 'email', 'roles'].forEach((key) =>
this.userForm.get(key).setValue(response[key])
);
}
getRequest(): UserFormModel {
const userFormModel = new UserFormModel();
['username', 'password', 'name', 'email', 'roles'].forEach(
(key) => (userFormModel[key] = this.userForm.get(key).value)
);
return userFormModel;
}
createAction() {
const userFormModel = this.getRequest();
this.userService.create(userFormModel).subscribe(
() => {
this.notificationService.show(
NotificationType.success,
this.i18n('Created user "{{username}}"', { username: userFormModel.username })
);
this.router.navigate(['/user-management/users']);
},
() => {
this.userForm.setErrors({ cdSubmitButton: true });
}
);
}
editAction() {
if (this.isUserRemovingNeededRolePermissions()) {
const initialState = {
titleText: this.i18n('Update user'),
buttonText: this.i18n('Continue'),
bodyTpl: this.removeSelfUserReadUpdatePermissionTpl,
onSubmit: () => {
this.modalRef.hide();
this.doEditAction();
},
onCancel: () => {
this.userForm.setErrors({ cdSubmitButton: true });
this.userForm.get('roles').reset(this.userForm.get('roles').value);
}
};
this.modalRef = this.modalService.show(ConfirmationModalComponent, { initialState });
} else {
this.doEditAction();
}
}
private isCurrentUser(): boolean {
return this.authStorageService.getUsername() === this.userForm.getValue('username');
}
private isUserChangingRoles(): boolean {
const isCurrentUser = this.isCurrentUser();
return (
isCurrentUser &&
this.response &&
!_.isEqual(this.response.roles, this.userForm.getValue('roles'))
);
}
private isUserRemovingNeededRolePermissions(): boolean {
const isCurrentUser = this.isCurrentUser();
return isCurrentUser && !this.hasUserReadUpdatePermissions(this.userForm.getValue('roles'));
}
private hasUserReadUpdatePermissions(roles: Array<string> = []) {
for (const role of this.allRoles) {
if (roles.indexOf(role.name) !== -1 && role.scopes_permissions['user']) {
const userPermissions = role.scopes_permissions['user'];
return ['read', 'update'].every((permission) => {
return userPermissions.indexOf(permission) !== -1;
});
}
}
return false;
}
private doEditAction() {
const userFormModel = this.getRequest();
this.userService.update(userFormModel).subscribe(
() => {
if (this.isUserChangingRoles()) {
this.authService.logout(() => {
this.notificationService.show(
NotificationType.info,
this.i18n('You were automatically logged out because your roles have been changed.')
);
});
} else {
this.notificationService.show(
NotificationType.success,
this.i18n('Updated user "{{username}}"', { username: userFormModel.username })
);
this.router.navigate(['/user-management/users']);
}
},
() => {
this.userForm.setErrors({ cdSubmitButton: true });
}
);
}
submit() {
if (this.mode === this.userFormMode.editing) {
this.editAction();
} else {
this.createAction();
}
}
}
<div class="col-sm-12 col-lg-6">
<form name="userForm"
class="form-horizontal"
#formDir="ngForm"
[formGroup]="userForm"
novalidate>
<div class="panel panel-default">
<div class="panel-heading">
<h3 i18n="form title|Example: Create Pool@@formTitle"
class="panel-title">{{ action | titlecase }} {{ resource | upperFirst }}</h3>
</div>
<div class="panel-body">
<!-- Username -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('username', formDir)}">
<label class="control-label col-sm-3"
for="name">
<ng-container i18n>Username</ng-container>
<span class="required"
*ngIf="mode !== userFormMode.editing"></span>
</label>
<div class="col-sm-9">
<input class="form-control"
type="text"
placeholder="Username..."
id="username"
name="username"
formControlName="username"
autofocus>
<span class="help-block"
*ngIf="userForm.showError('username', formDir, 'required')"
i18n>This field is required.</span>
</div>
</div>
<!-- Password -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('password', formDir)}">
<label i18n
class="control-label col-sm-3"
for="name">Password</label>
<div class="col-sm-9">
<div class="input-group">
<input class="form-control"
type="password"
placeholder="Password..."
id="password"
name="password"
autocomplete="new-password"
formControlName="password">
<span class="input-group-btn">
<button type="button"
class="btn btn-default"
cdPasswordButton="password">
</button>
</span>
</div>
<span class="help-block"
*ngIf="userForm.showError('password', formDir, 'required')"
i18n>This field is required.</span>
</div>
</div>
<!-- Confirm password -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('confirmpassword', formDir)}">
<label i18n
class="control-label col-sm-3"
for="name">Confirm password</label>
<div class="col-sm-9">
<div class="input-group">
<input class="form-control"
type="password"
placeholder="Confirm password..."
id="confirmpassword"
name="confirmpassword"
formControlName="confirmpassword">
<span class="input-group-btn">
<button type="button"
class="btn btn-default"
cdPasswordButton="confirmpassword">
</button>
</span>
</div>
<span class="help-block"
*ngIf="userForm.showError('confirmpassword', formDir, 'required')"
i18n>This field is required.</span>
<span class="help-block"
*ngIf="userForm.showError('confirmpassword', formDir, 'match')"
i18n>Password confirmation doesn't match the password.</span>
</div>
</div>
<!-- Name -->
<div class="form-group">
<label i18n
class="control-label col-sm-3"
for="name">Full name</label>
<div class="col-sm-9">
<input class="form-control"
type="text"
placeholder="Full name..."
id="name"
name="name"
formControlName="name">
</div>
</div>
<!-- Email -->
<div class="form-group"
[ngClass]="{'has-error': userForm.showError('email', formDir)}">
<label i18n
class="control-label col-sm-3"
for="email">Email</label>
<div class="col-sm-9">
<input class="form-control"
type="email"
placeholder="Email..."
id="email"
name="email"
formControlName="email">
<span class="help-block"
*ngIf="userForm.showError('email', formDir, 'email')"
i18n>Invalid email.</span>
</div>
</div>
<!-- Roles -->
<label class="col-sm-3 control-label"
i18n>Roles</label>
<div class="col-sm-9">
<span class="form-control no-border full-height"
*ngIf="allRoles">
<cd-select-badges [data]="userForm.controls.roles.value"
[options]="allRoles"
[messages]="messages"></cd-select-badges>
</span>
</div>
</div>
<div class="panel-footer">
<div class="button-group text-right">
<cd-submit-button
[form]="formDir"
(submitAction)="submit()"
i18n="form action button|Example: Create Pool@@formActionButton"
type="button">{{ action | titlecase }} {{ resource | upperFirst }}</cd-submit-button>
<cd-back-button></cd-back-button>
</div>
</div>
</div>
</form>
</div>
<ng-template #removeSelfUserReadUpdatePermissionTpl>
<p><strong i18n>You are about to remove "user read / update" permissions from your own user.</strong></p>
<br>
<p i18n>If you continue, you will no longer be able to add or remove roles from any user.</p>
<ng-container i18n>Are you sure you want to continue?</ng-container>
</ng-template>