blob: 2e7605cb315dc3db5732675a10b40566cec9d659 [file] [log] [blame]
Iftekharul Islamcd789502017-04-19 14:37:55 -05001/**
2 * Controller for user Accounts
3 *
4 * @module app/users
5 * @exports userAccountsController
6 * @name userAccountsController
Iftekharul Islamcd789502017-04-19 14:37:55 -05007 */
8
Andrew Geisslerba5e3f32018-05-24 10:58:00 -07009window.angular && (function(angular) {
10 'use strict';
Iftekharul Islamcd789502017-04-19 14:37:55 -050011
Andrew Geisslerd27bb132018-05-24 11:07:27 -070012 angular.module('app.users').controller('userAccountsController', [
Yoshie Muranaka49001e22019-09-16 10:33:16 -070013 '$scope', 'APIUtils', 'toastService', '$uibModal', '$q',
14 function($scope, APIUtils, toastService, $uibModal, $q) {
Yoshie Muranakafa562732019-07-17 11:23:15 -050015 $scope.loading;
16 $scope.accountSettings;
17 $scope.userRoles;
18 $scope.localUsers;
Gunnar Mills08744f82018-03-19 16:24:41 -050019
Yoshie Muranakab1f64242019-09-04 11:40:51 -070020 $scope.tableData = [];
21 $scope.tableHeader = [
22 {label: 'Username'}, {label: 'Privilege'}, {label: 'Account status'}
23 ];
Yoshie Muranaka49001e22019-09-16 10:33:16 -070024 $scope.tableBatchActions = [
25 {type: 'delete', label: 'Remove'},
26 {type: 'enable', label: 'Enable'},
27 {type: 'disable', label: 'Disable'},
28 ];
29
30 /**
31 * Returns true if username is 'root'
32 * @param {*} user
33 */
34 function checkIfRoot(user) {
35 return user.UserName === 'root' ? true : false;
36 }
Yoshie Muranakafa562732019-07-17 11:23:15 -050037
38 /**
39 * Data table mapper
40 * @param {*} user
Yoshie Muranakabb688792019-08-12 09:31:52 -050041 * @returns user
Yoshie Muranakafa562732019-07-17 11:23:15 -050042 */
43 function mapTableData(user) {
Yoshie Muranakabb688792019-08-12 09:31:52 -050044 const accountStatus =
Yoshie Muranakafa562732019-07-17 11:23:15 -050045 user.Locked ? 'Locked' : user.Enabled ? 'Enabled' : 'Disabled';
Yoshie Muranakabb688792019-08-12 09:31:52 -050046 const editAction = {type: 'Edit', enabled: true, file: 'icon-edit.svg'};
47 const deleteAction = {
48 type: 'Delete',
Yoshie Muranaka49001e22019-09-16 10:33:16 -070049 enabled: checkIfRoot(user) ? false : true,
Yoshie Muranakabb688792019-08-12 09:31:52 -050050 file: 'icon-trashcan.svg'
51 };
Yoshie Muranaka49001e22019-09-16 10:33:16 -070052 user.selectable = checkIfRoot(user) ? false : true;
Yoshie Muranakabb688792019-08-12 09:31:52 -050053 user.actions = [editAction, deleteAction];
Yoshie Muranakafa562732019-07-17 11:23:15 -050054 user.uiData = [user.UserName, user.RoleId, accountStatus];
55 return user;
56 }
57
58 /**
Yoshie Muranakab4d9c092019-08-01 16:19:40 -050059 * Returns lockout method based on the lockout duration property
60 * If the lockoutDuration is greater than 0 the lockout method
61 * is automatic otherwise the lockout method is manual
62 * @param {number} lockoutDuration
63 * @returns {number} : returns the account lockout method
64 * 1(automatic) / 0(manual)
65 */
66 function mapLockoutMethod(lockoutDuration) {
67 return lockoutDuration > 0 ? 1 : 0;
68 }
69
70 /**
Yoshie Muranakafa562732019-07-17 11:23:15 -050071 * API call to get all user accounts
72 */
73 function getLocalUsers() {
AppaRao Puli28711a62018-10-17 16:07:55 +053074 $scope.loading = true;
Yoshie Muranakafa562732019-07-17 11:23:15 -050075 APIUtils.getAllUserAccounts()
76 .then((users) => {
77 $scope.localUsers = users;
Yoshie Muranakab1f64242019-09-04 11:40:51 -070078 $scope.tableData = users.map(mapTableData);
Yoshie Muranakafa562732019-07-17 11:23:15 -050079 })
80 .catch((error) => {
81 console.log(JSON.stringify(error));
82 toastService.error('Failed to load users.');
83 })
84 .finally(() => {
85 $scope.loading = false;
86 })
87 }
AppaRao Pulia83cd052019-01-07 23:25:43 +053088
Yoshie Muranakafa562732019-07-17 11:23:15 -050089 /**
90 * API call to get current Account settings
91 */
92 function getAccountSettings() {
93 APIUtils.getAllUserAccountProperties()
94 .then((settings) => {
95 $scope.accountSettings = settings;
96 })
97 .catch((error) => {
98 console.log(JSON.stringify(error));
99 $scope.accountSettings = null;
100 })
101 }
AppaRao Pulib1e7c862019-03-12 14:56:40 +0530102
Yoshie Muranakafa562732019-07-17 11:23:15 -0500103 /**
104 * API call to get local user roles
105 */
106 function getUserRoles() {
107 APIUtils.getAccountServiceRoles()
108 .then((roles) => {
109 $scope.userRoles = roles;
110 })
111 .catch((error) => {
112 console.log(JSON.stringify(error));
113 $scope.userRoles = null;
114 })
115 }
AppaRao Pulib1e7c862019-03-12 14:56:40 +0530116
Yoshie Muranakafa562732019-07-17 11:23:15 -0500117 /**
118 * API call to create new user
119 * @param {*} user
120 */
121 function createUser(username, password, role, enabled) {
AppaRao Pulib1e7c862019-03-12 14:56:40 +0530122 $scope.loading = true;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500123 APIUtils.createUser(username, password, role, enabled)
124 .then(() => {
125 getLocalUsers();
126 toastService.success(`User '${username}' has been created.`);
127 })
128 .catch((error) => {
129 console.log(JSON.stringify(error));
130 toastService.error(`Failed to create new user '${username}'.`);
131 })
132 .finally(() => {
AppaRao Pulib1e7c862019-03-12 14:56:40 +0530133 $scope.loading = false;
134 });
Yoshie Muranakafa562732019-07-17 11:23:15 -0500135 }
AppaRao Pulib1e7c862019-03-12 14:56:40 +0530136
Yoshie Muranakafa562732019-07-17 11:23:15 -0500137 /**
138 * API call to update existing user
139 */
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500140 function updateUser(
141 originalUsername, username, password, role, enabled, locked) {
AppaRao Puli28711a62018-10-17 16:07:55 +0530142 $scope.loading = true;
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500143 APIUtils
144 .updateUser(
145 originalUsername, username, password, role, enabled, locked)
Yoshie Muranakafa562732019-07-17 11:23:15 -0500146 .then(() => {
147 getLocalUsers();
148 toastService.success('User has been updated successfully.')
149 })
150 .catch((error) => {
151 console.log(JSON.stringify(error));
152 toastService.error(`Unable to update user '${originalUsername}'.`)
153 })
154 .finally(() => {
155 $scope.loading = false;
156 })
157 }
158
159 /**
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700160 * API call to delete users
161 * @param {*} users : Array of users to delete
Yoshie Muranakafa562732019-07-17 11:23:15 -0500162 */
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700163 function deleteUsers(users = []) {
Yoshie Muranakafa562732019-07-17 11:23:15 -0500164 $scope.loading = true;
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700165 const promises =
166 users.map((user) => APIUtils.deleteUser(user.UserName));
167 $q.all(promises)
Yoshie Muranakafa562732019-07-17 11:23:15 -0500168 .then(() => {
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700169 let message;
170 if (users.length > 1) {
171 message = 'Users have been removed.'
172 } else {
173 message = `User '${users[0].UserName}' has been removed.`
174 }
175 toastService.success(message);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500176 })
177 .catch((error) => {
178 console.log(JSON.stringify(error));
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700179 let message;
180 if (users.length > 1) {
181 message = 'Failed to remove users.'
182 } else {
183 message = `Failed to remove user '${users[0].UserName}'.`
184 }
185 toastService.error(message);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500186 })
187 .finally(() => {
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700188 getLocalUsers();
189 $scope.loading = false;
190 });
191 }
192
193 /**
194 * API call to update user status enabled/disabled
195 * @param {*} users : Array of users to update
196 * @param {boolean} enabled : status
197 */
198 function updateUserStatus(users = [], enabled = true) {
199 $scope.loading = true;
200 const promises = users.map(
201 (user) => APIUtils.updateUser(
202 user.UserName, null, null, null, enabled, null));
203 $q.all(promises)
204 .then(() => {
205 let message;
206 let statusLabel = enabled ? 'enabled' : 'disabled';
207 if (users.length > 1) {
208 message = `Users ${statusLabel}.`
209 } else {
210 message = `User '${users[0].UserName}' ${statusLabel}.`;
211 }
212 toastService.success(message);
213 })
214 .catch((error) => {
215 console.log(JSON.stringify(error));
216 let message;
217 let statusLabel = enabled ? 'enable' : 'disable';
218 if (users.length > 1) {
219 message = `Failed to ${statusLabel} users.`
220 } else {
221 message =
222 `Failed to ${statusLabel} user '${users[0].UserName}'.`
223 }
224 toastService.error(message);
225 })
226 .finally(() => {
227 getLocalUsers();
AppaRao Puli28711a62018-10-17 16:07:55 +0530228 $scope.loading = false;
Gunnar Millsb3d48d42018-05-25 08:34:35 -0500229 });
Yoshie Muranakafa562732019-07-17 11:23:15 -0500230 }
AppaRao Puli28711a62018-10-17 16:07:55 +0530231
Yoshie Muranakafa562732019-07-17 11:23:15 -0500232 /**
233 * API call to save account policy settings
234 * @param {number} lockoutDuration
235 * @param {number} lockoutThreshold
236 */
237 function updateAccountSettings(lockoutDuration, lockoutThreshold) {
AppaRao Puli28711a62018-10-17 16:07:55 +0530238 $scope.loading = true;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500239 APIUtils.saveUserAccountProperties(lockoutDuration, lockoutThreshold)
240 .then(() => {
241 $scope.accountSettings['AccountLockoutDuration'] =
242 lockoutDuration;
243 $scope.accountSettings['AccountLockoutThreshold'] =
244 lockoutThreshold;
245 toastService.success(
246 'Account policy settings have been updated.');
247 })
248 .catch((error) => {
249 console.log(JSON.stringify(error));
250 toastService.error('Failed to update account policy settings.');
251 })
252 .finally(() => {
AppaRao Puli28711a62018-10-17 16:07:55 +0530253 $scope.loading = false;
254 });
Yoshie Muranakafa562732019-07-17 11:23:15 -0500255 }
AppaRao Pulicf7219c2018-12-27 16:17:46 +0530256
Yoshie Muranakafa562732019-07-17 11:23:15 -0500257 /**
258 * Initiate account settings modal
259 */
260 function initAccountSettingsModal() {
261 const template = require('./user-accounts-modal-settings.html');
262 $uibModal
263 .open({
264 template,
265 windowTopClass: 'uib-modal',
266 ariaLabelledBy: 'dialog_label',
267 controllerAs: 'modalCtrl',
268 controller: function() {
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500269 const lockoutMethod = mapLockoutMethod(
270 $scope.accountSettings.AccountLockoutDuration);
271
Yoshie Muranakafa562732019-07-17 11:23:15 -0500272 this.settings = {};
273 this.settings.maxLogin =
274 $scope.accountSettings.AccountLockoutThreshold;
275 this.settings.lockoutMethod = lockoutMethod;
276 this.settings.timeoutDuration = !lockoutMethod ?
277 null :
278 $scope.accountSettings.AccountLockoutDuration;
279 }
280 })
281 .result
282 .then((form) => {
283 if (form.$valid) {
284 const lockoutDuration = form.lockoutMethod.$modelValue ?
285 form.timeoutDuration.$modelValue :
286 0;
287 const lockoutThreshold = form.maxLogin.$modelValue;
288 updateAccountSettings(lockoutDuration, lockoutThreshold);
289 }
290 })
291 .catch(
292 () => {
293 // do nothing
294 })
295 }
296
297 /**
298 * Initiate user modal
299 * Can be triggered by clicking edit in table or 'Add user' button
300 * If triggered from the table, user parameter will be provided
301 * If triggered by add user button, user parameter will be undefined
302 * @optional @param {*} user
303 */
304 function initUserModal(user) {
305 if ($scope.userRoles === null || $scope.userRoles === undefined) {
306 // If userRoles failed to load, do not allow add/edit
307 // functionality
308 return;
309 }
310 const newUser = user ? false : true;
311 const originalUsername = user ? angular.copy(user.UserName) : null;
312 const template = require('./user-accounts-modal-user.html');
313 $uibModal
314 .open({
315 template,
316 windowTopClass: 'uib-modal',
317 ariaLabelledBy: 'dialog_label',
318 controllerAs: 'modalCtrl',
319 controller: function() {
320 // Set default status to Enabled
321 const status = newUser ? true : user.Enabled;
322 // Check if UserName is root
323 // Some form controls will be disabled for root users:
324 // edit enabled status, edit username, edit role
325 const isRoot =
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700326 newUser ? false : checkIfRoot(user) ? true : false;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500327 // Array of existing usernames (excluding current user instance)
328 const existingUsernames =
329 $scope.localUsers.reduce((acc, val) => {
330 if (user && (val.UserName === user.UserName)) {
331 return acc;
332 }
333 acc.push(val.UserName);
334 return acc;
335 }, []);
336
337 this.user = {};
338 this.user.isRoot = isRoot;
339 this.user.new = newUser;
340 this.user.accountStatus = status;
341 this.user.username = newUser ? '' : user.UserName;
342 this.user.privilege = newUser ? '' : user.RoleId;
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500343 this.user.locked = newUser ? null : user.Locked;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500344
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500345 this.manualUnlockProperty = false;
346 this.automaticLockout = mapLockoutMethod(
347 $scope.accountSettings.AccountLockoutDuration);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500348 this.privilegeRoles = $scope.userRoles;
349 this.existingUsernames = existingUsernames;
350 this.minPasswordLength = $scope.accountSettings ?
351 $scope.accountSettings.MinPasswordLength :
352 null;
353 this.maxPasswordLength = $scope.accountSettings ?
354 $scope.accountSettings.MaxPasswordLength :
355 null;
356 }
357 })
358 .result
359 .then((form) => {
360 if (form.$valid) {
361 // If form control is pristine set property to null
362 // this will make sure only changed values are updated when
363 // modifying existing users
364 // API utils checks for null values
365 const username =
366 form.username.$pristine ? null : form.username.$modelValue;
367 const password =
368 form.password.$pristine ? null : form.password.$modelValue;
369 const role = form.privilege.$pristine ?
370 null :
371 form.privilege.$modelValue;
372 const enabled = (form.accountStatus.$pristine &&
373 form.accountStatus1.$pristine) ?
374 null :
375 form.accountStatus.$modelValue;
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500376 const locked = (form.lock && form.lock.$dirty) ?
377 form.lock.$modelValue :
378 null;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500379
380 if (!newUser) {
381 updateUser(
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500382 originalUsername, username, password, role, enabled,
383 locked);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500384 } else {
385 createUser(
386 username, password, role, form.accountStatus.$modelValue);
387 }
388 }
389 })
390 .catch(
391 () => {
392 // do nothing
393 })
394 }
395
396 /**
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700397 * Intiate remove users modal
398 * @param {*} users
Yoshie Muranakafa562732019-07-17 11:23:15 -0500399 */
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700400 function initRemoveModal(users) {
Yoshie Muranakafa562732019-07-17 11:23:15 -0500401 const template = require('./user-accounts-modal-remove.html');
402 $uibModal
403 .open({
404 template,
405 windowTopClass: 'uib-modal',
406 ariaLabelledBy: 'dialog_label',
407 controllerAs: 'modalCtrl',
408 controller: function() {
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700409 this.users = users;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500410 }
411 })
412 .result
413 .then(() => {
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700414 deleteUsers(users);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500415 })
416 .catch(
417 () => {
418 // do nothing
419 })
420 }
421
422 /**
423 * Callback when action emitted from table
424 * @param {*} value
425 */
Yoshie Muranaka5b8cef82019-09-10 08:09:43 -0700426 $scope.onEmitRowAction = (value) => {
Yoshie Muranakafa562732019-07-17 11:23:15 -0500427 switch (value.action) {
428 case 'Edit':
429 initUserModal(value.row);
430 break;
431 case 'Delete':
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700432 initRemoveModal([value.row]);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500433 break;
434 default:
beccabroeka5deeea2019-03-22 15:38:49 -0500435 }
436 };
Yoshie Muranakafa562732019-07-17 11:23:15 -0500437
438 /**
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700439 * Callback when batch action emitted from table
440 */
441 $scope.onEmitBatchAction = (value) => {
442 switch (value.action) {
443 case 'delete':
444 initRemoveModal(value.filteredRows);
445 break;
446 case 'enable':
447 updateUserStatus(value.filteredRows, true)
448 break;
449 case 'disable':
450 updateUserStatus(value.filteredRows, false)
451 break;
452 default:
453 break;
454 }
455 };
456
457 /**
Yoshie Muranakafa562732019-07-17 11:23:15 -0500458 * Callback when 'Account settings policy' button clicked
459 */
460 $scope.onClickAccountSettingsPolicy = () => {
461 initAccountSettingsModal();
462 };
463
464 /**
465 * Callback when 'Add user' button clicked
466 */
467 $scope.onClickAddUser = () => {
468 initUserModal();
469 };
470
471 /**
472 * Callback when controller view initially loaded
473 */
474 $scope.$on('$viewContentLoaded', () => {
475 getLocalUsers();
476 getUserRoles();
477 getAccountSettings();
478 })
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700479 }
480 ]);
Iftekharul Islamcd789502017-04-19 14:37:55 -0500481})(angular);