]>
Commit | Line | Data |
---|---|---|
11fdf7f2 | 1 | import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; |
9f95a23c | 2 | import { Validators } from '@angular/forms'; |
11fdf7f2 TL |
3 | import { ActivatedRoute, Router } from '@angular/router'; |
4 | ||
5 | import { I18n } from '@ngx-translate/i18n-polyfill'; | |
6 | import * as _ from 'lodash'; | |
7 | import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; | |
9f95a23c | 8 | import { forkJoin as observableForkJoin } from 'rxjs'; |
11fdf7f2 TL |
9 | |
10 | import { AuthService } from '../../../shared/api/auth.service'; | |
11 | import { RoleService } from '../../../shared/api/role.service'; | |
9f95a23c | 12 | import { SettingsService } from '../../../shared/api/settings.service'; |
11fdf7f2 TL |
13 | import { UserService } from '../../../shared/api/user.service'; |
14 | import { ConfirmationModalComponent } from '../../../shared/components/confirmation-modal/confirmation-modal.component'; | |
15 | import { SelectMessages } from '../../../shared/components/select/select-messages.model'; | |
16 | import { ActionLabelsI18n } from '../../../shared/constants/app.constants'; | |
9f95a23c | 17 | import { Icons } from '../../../shared/enum/icons.enum'; |
11fdf7f2 | 18 | import { NotificationType } from '../../../shared/enum/notification-type.enum'; |
9f95a23c | 19 | import { CdFormBuilder } from '../../../shared/forms/cd-form-builder'; |
11fdf7f2 TL |
20 | import { CdFormGroup } from '../../../shared/forms/cd-form-group'; |
21 | import { CdValidators } from '../../../shared/forms/cd-validators'; | |
9f95a23c | 22 | import { CdPwdExpirationSettings } from '../../../shared/models/cd-pwd-expiration-settings'; |
11fdf7f2 TL |
23 | import { AuthStorageService } from '../../../shared/services/auth-storage.service'; |
24 | import { NotificationService } from '../../../shared/services/notification.service'; | |
9f95a23c | 25 | import { PasswordPolicyService } from '../../../shared/services/password-policy.service'; |
11fdf7f2 TL |
26 | import { UserFormMode } from './user-form-mode.enum'; |
27 | import { UserFormRoleModel } from './user-form-role.model'; | |
28 | import { UserFormModel } from './user-form.model'; | |
29 | ||
30 | @Component({ | |
31 | selector: 'cd-user-form', | |
32 | templateUrl: './user-form.component.html', | |
33 | styleUrls: ['./user-form.component.scss'] | |
34 | }) | |
35 | export class UserFormComponent implements OnInit { | |
9f95a23c | 36 | @ViewChild('removeSelfUserReadUpdatePermissionTpl', { static: true }) |
11fdf7f2 TL |
37 | removeSelfUserReadUpdatePermissionTpl: TemplateRef<any>; |
38 | ||
39 | modalRef: BsModalRef; | |
40 | ||
41 | userForm: CdFormGroup; | |
42 | response: UserFormModel; | |
43 | ||
44 | userFormMode = UserFormMode; | |
45 | mode: UserFormMode; | |
46 | allRoles: Array<UserFormRoleModel>; | |
9f95a23c | 47 | messages = new SelectMessages({ empty: this.i18n('There are no roles.') }, this.i18n); |
11fdf7f2 TL |
48 | action: string; |
49 | resource: string; | |
9f95a23c TL |
50 | passwordPolicyHelpText = ''; |
51 | passwordStrengthLevelClass: string; | |
52 | passwordValuation: string; | |
53 | icons = Icons; | |
54 | minDate: Date; | |
55 | bsConfig = { | |
56 | dateInputFormat: 'YYYY-MM-DD', | |
57 | containerClass: 'theme-default' | |
58 | }; | |
59 | pwdExpirationSettings: CdPwdExpirationSettings; | |
11fdf7f2 TL |
60 | |
61 | constructor( | |
62 | private authService: AuthService, | |
63 | private authStorageService: AuthStorageService, | |
64 | private route: ActivatedRoute, | |
65 | private router: Router, | |
66 | private modalService: BsModalService, | |
67 | private roleService: RoleService, | |
68 | private userService: UserService, | |
69 | private notificationService: NotificationService, | |
70 | private i18n: I18n, | |
9f95a23c TL |
71 | public actionLabels: ActionLabelsI18n, |
72 | private passwordPolicyService: PasswordPolicyService, | |
73 | private formBuilder: CdFormBuilder, | |
74 | private settingsService: SettingsService | |
11fdf7f2 TL |
75 | ) { |
76 | this.resource = this.i18n('user'); | |
77 | this.createForm(); | |
9f95a23c | 78 | this.messages = new SelectMessages({ empty: this.i18n('There are no roles.') }, this.i18n); |
11fdf7f2 TL |
79 | } |
80 | ||
81 | createForm() { | |
9f95a23c TL |
82 | this.passwordPolicyService.getHelpText().subscribe((helpText: string) => { |
83 | this.passwordPolicyHelpText = helpText; | |
84 | }); | |
85 | this.userForm = this.formBuilder.group( | |
11fdf7f2 | 86 | { |
e306af50 TL |
87 | username: [ |
88 | '', | |
89 | [Validators.required], | |
90 | [CdValidators.unique(this.userService.validateUserName, this.userService)] | |
91 | ], | |
9f95a23c TL |
92 | name: [''], |
93 | password: [ | |
94 | '', | |
95 | [], | |
96 | [ | |
97 | CdValidators.passwordPolicy( | |
98 | this.userService, | |
99 | () => this.userForm.getValue('username'), | |
100 | (_valid: boolean, credits: number, valuation: string) => { | |
101 | this.passwordStrengthLevelClass = this.passwordPolicyService.mapCreditsToCssClass( | |
102 | credits | |
103 | ); | |
104 | this.passwordValuation = _.defaultTo(valuation, ''); | |
105 | } | |
106 | ) | |
107 | ] | |
108 | ], | |
109 | confirmpassword: [''], | |
110 | pwdExpirationDate: [''], | |
111 | email: ['', [CdValidators.email]], | |
112 | roles: [[]], | |
113 | enabled: [true, [Validators.required]], | |
114 | pwdUpdateRequired: [true] | |
11fdf7f2 TL |
115 | }, |
116 | { | |
117 | validators: [CdValidators.match('password', 'confirmpassword')] | |
118 | } | |
119 | ); | |
120 | } | |
121 | ||
122 | ngOnInit() { | |
123 | if (this.router.url.startsWith('/user-management/users/edit')) { | |
124 | this.mode = this.userFormMode.editing; | |
125 | this.action = this.actionLabels.EDIT; | |
126 | } else { | |
127 | this.action = this.actionLabels.CREATE; | |
128 | } | |
9f95a23c | 129 | this.minDate = new Date(); |
11fdf7f2 | 130 | |
9f95a23c TL |
131 | const observables = [this.roleService.list(), this.settingsService.getStandardSettings()]; |
132 | observableForkJoin(observables).subscribe( | |
133 | (result: [UserFormRoleModel[], CdPwdExpirationSettings]) => { | |
134 | this.allRoles = _.map(result[0], (role) => { | |
135 | role.enabled = true; | |
136 | return role; | |
137 | }); | |
138 | this.pwdExpirationSettings = new CdPwdExpirationSettings(result[1]); | |
139 | ||
140 | if (this.mode === this.userFormMode.editing) { | |
141 | this.initEdit(); | |
142 | } else { | |
143 | if (this.pwdExpirationSettings.pwdExpirationSpan > 0) { | |
144 | const pwdExpirationDateField = this.userForm.get('pwdExpirationDate'); | |
145 | const expirationDate = new Date(); | |
146 | expirationDate.setDate( | |
147 | this.minDate.getDate() + this.pwdExpirationSettings.pwdExpirationSpan | |
148 | ); | |
149 | pwdExpirationDateField.setValue(expirationDate); | |
150 | pwdExpirationDateField.setValidators([Validators.required]); | |
151 | } | |
152 | } | |
153 | } | |
154 | ); | |
11fdf7f2 TL |
155 | } |
156 | ||
157 | initEdit() { | |
158 | this.disableForEdit(); | |
159 | this.route.params.subscribe((params: { username: string }) => { | |
160 | const username = params.username; | |
161 | this.userService.get(username).subscribe((userFormModel: UserFormModel) => { | |
162 | this.response = _.cloneDeep(userFormModel); | |
163 | this.setResponse(userFormModel); | |
164 | }); | |
165 | }); | |
166 | } | |
167 | ||
168 | disableForEdit() { | |
169 | this.userForm.get('username').disable(); | |
170 | } | |
171 | ||
172 | setResponse(response: UserFormModel) { | |
9f95a23c | 173 | ['username', 'name', 'email', 'roles', 'enabled', 'pwdUpdateRequired'].forEach((key) => |
11fdf7f2 TL |
174 | this.userForm.get(key).setValue(response[key]) |
175 | ); | |
9f95a23c TL |
176 | const expirationDate = response['pwdExpirationDate']; |
177 | if (expirationDate) { | |
178 | this.userForm.get('pwdExpirationDate').setValue(new Date(expirationDate * 1000)); | |
179 | } | |
11fdf7f2 TL |
180 | } |
181 | ||
182 | getRequest(): UserFormModel { | |
183 | const userFormModel = new UserFormModel(); | |
9f95a23c | 184 | ['username', 'password', 'name', 'email', 'roles', 'enabled', 'pwdUpdateRequired'].forEach( |
11fdf7f2 TL |
185 | (key) => (userFormModel[key] = this.userForm.get(key).value) |
186 | ); | |
9f95a23c TL |
187 | const expirationDate = this.userForm.get('pwdExpirationDate').value; |
188 | if (expirationDate) { | |
189 | if ( | |
190 | this.mode !== this.userFormMode.editing || | |
191 | this.response.pwdExpirationDate !== Number(expirationDate) / 1000 | |
192 | ) { | |
193 | expirationDate.setHours(23, 59, 59); | |
194 | } | |
195 | userFormModel['pwdExpirationDate'] = Number(expirationDate) / 1000; | |
196 | } | |
11fdf7f2 TL |
197 | return userFormModel; |
198 | } | |
199 | ||
200 | createAction() { | |
201 | const userFormModel = this.getRequest(); | |
202 | this.userService.create(userFormModel).subscribe( | |
203 | () => { | |
204 | this.notificationService.show( | |
205 | NotificationType.success, | |
206 | this.i18n('Created user "{{username}}"', { username: userFormModel.username }) | |
207 | ); | |
208 | this.router.navigate(['/user-management/users']); | |
209 | }, | |
210 | () => { | |
211 | this.userForm.setErrors({ cdSubmitButton: true }); | |
212 | } | |
213 | ); | |
214 | } | |
215 | ||
216 | editAction() { | |
217 | if (this.isUserRemovingNeededRolePermissions()) { | |
218 | const initialState = { | |
219 | titleText: this.i18n('Update user'), | |
220 | buttonText: this.i18n('Continue'), | |
221 | bodyTpl: this.removeSelfUserReadUpdatePermissionTpl, | |
222 | onSubmit: () => { | |
223 | this.modalRef.hide(); | |
224 | this.doEditAction(); | |
225 | }, | |
226 | onCancel: () => { | |
227 | this.userForm.setErrors({ cdSubmitButton: true }); | |
228 | this.userForm.get('roles').reset(this.userForm.get('roles').value); | |
229 | } | |
230 | }; | |
231 | this.modalRef = this.modalService.show(ConfirmationModalComponent, { initialState }); | |
232 | } else { | |
233 | this.doEditAction(); | |
234 | } | |
235 | } | |
236 | ||
9f95a23c | 237 | public isCurrentUser(): boolean { |
11fdf7f2 TL |
238 | return this.authStorageService.getUsername() === this.userForm.getValue('username'); |
239 | } | |
240 | ||
241 | private isUserChangingRoles(): boolean { | |
242 | const isCurrentUser = this.isCurrentUser(); | |
243 | return ( | |
244 | isCurrentUser && | |
245 | this.response && | |
246 | !_.isEqual(this.response.roles, this.userForm.getValue('roles')) | |
247 | ); | |
248 | } | |
249 | ||
250 | private isUserRemovingNeededRolePermissions(): boolean { | |
251 | const isCurrentUser = this.isCurrentUser(); | |
252 | return isCurrentUser && !this.hasUserReadUpdatePermissions(this.userForm.getValue('roles')); | |
253 | } | |
254 | ||
255 | private hasUserReadUpdatePermissions(roles: Array<string> = []) { | |
256 | for (const role of this.allRoles) { | |
257 | if (roles.indexOf(role.name) !== -1 && role.scopes_permissions['user']) { | |
258 | const userPermissions = role.scopes_permissions['user']; | |
259 | return ['read', 'update'].every((permission) => { | |
260 | return userPermissions.indexOf(permission) !== -1; | |
261 | }); | |
262 | } | |
263 | } | |
264 | return false; | |
265 | } | |
266 | ||
267 | private doEditAction() { | |
268 | const userFormModel = this.getRequest(); | |
269 | this.userService.update(userFormModel).subscribe( | |
270 | () => { | |
271 | if (this.isUserChangingRoles()) { | |
272 | this.authService.logout(() => { | |
273 | this.notificationService.show( | |
274 | NotificationType.info, | |
275 | this.i18n('You were automatically logged out because your roles have been changed.') | |
276 | ); | |
277 | }); | |
278 | } else { | |
279 | this.notificationService.show( | |
280 | NotificationType.success, | |
281 | this.i18n('Updated user "{{username}}"', { username: userFormModel.username }) | |
282 | ); | |
283 | this.router.navigate(['/user-management/users']); | |
284 | } | |
285 | }, | |
286 | () => { | |
287 | this.userForm.setErrors({ cdSubmitButton: true }); | |
288 | } | |
289 | ); | |
290 | } | |
291 | ||
9f95a23c TL |
292 | clearExpirationDate() { |
293 | this.userForm.get('pwdExpirationDate').setValue(undefined); | |
294 | } | |
295 | ||
11fdf7f2 TL |
296 | submit() { |
297 | if (this.mode === this.userFormMode.editing) { | |
298 | this.editAction(); | |
299 | } else { | |
300 | this.createAction(); | |
301 | } | |
302 | } | |
303 | } |