blob: 868bc156696486bb7a8e59cfbe78bb70a057e8c5 [file] [log] [blame]
Iftekharul Islamcd789502017-04-19 14:37:55 -05001/**
2 * Controller for user Accounts
3 *
miramurali23afc8a792019-06-17 13:07:24 -05004 * @module app/access-control
5 * @exports userController
6 * @name userController
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
miramurali23afc8a792019-06-17 13:07:24 -050012 angular.module('app.accessControl').controller('userController', [
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) {
Patrick Williams339db9a2021-02-22 17:18:14 -060044 const accountStatus = user.Locked ? 'Locked' :
45 user.Enabled ? 'Enabled' :
46 'Disabled';
Yoshie Muranakabb688792019-08-12 09:31:52 -050047 const editAction = {type: 'Edit', enabled: true, file: 'icon-edit.svg'};
48 const deleteAction = {
49 type: 'Delete',
Yoshie Muranaka49001e22019-09-16 10:33:16 -070050 enabled: checkIfRoot(user) ? false : true,
Yoshie Muranakabb688792019-08-12 09:31:52 -050051 file: 'icon-trashcan.svg'
52 };
Yoshie Muranaka49001e22019-09-16 10:33:16 -070053 user.selectable = checkIfRoot(user) ? false : true;
Yoshie Muranakabb688792019-08-12 09:31:52 -050054 user.actions = [editAction, deleteAction];
Yoshie Muranakafa562732019-07-17 11:23:15 -050055 user.uiData = [user.UserName, user.RoleId, accountStatus];
56 return user;
57 }
58
59 /**
Yoshie Muranakab4d9c092019-08-01 16:19:40 -050060 * Returns lockout method based on the lockout duration property
61 * If the lockoutDuration is greater than 0 the lockout method
62 * is automatic otherwise the lockout method is manual
63 * @param {number} lockoutDuration
64 * @returns {number} : returns the account lockout method
65 * 1(automatic) / 0(manual)
66 */
67 function mapLockoutMethod(lockoutDuration) {
68 return lockoutDuration > 0 ? 1 : 0;
69 }
70
71 /**
Yoshie Muranakafa562732019-07-17 11:23:15 -050072 * API call to get all user accounts
73 */
74 function getLocalUsers() {
AppaRao Puli28711a62018-10-17 16:07:55 +053075 $scope.loading = true;
Yoshie Muranakafa562732019-07-17 11:23:15 -050076 APIUtils.getAllUserAccounts()
77 .then((users) => {
78 $scope.localUsers = users;
Yoshie Muranakab1f64242019-09-04 11:40:51 -070079 $scope.tableData = users.map(mapTableData);
Yoshie Muranakafa562732019-07-17 11:23:15 -050080 })
81 .catch((error) => {
82 console.log(JSON.stringify(error));
83 toastService.error('Failed to load users.');
84 })
85 .finally(() => {
86 $scope.loading = false;
87 })
88 }
AppaRao Pulia83cd052019-01-07 23:25:43 +053089
Yoshie Muranakafa562732019-07-17 11:23:15 -050090 /**
91 * API call to get current Account settings
92 */
93 function getAccountSettings() {
94 APIUtils.getAllUserAccountProperties()
95 .then((settings) => {
96 $scope.accountSettings = settings;
97 })
98 .catch((error) => {
99 console.log(JSON.stringify(error));
100 $scope.accountSettings = null;
101 })
102 }
AppaRao Pulib1e7c862019-03-12 14:56:40 +0530103
Yoshie Muranakafa562732019-07-17 11:23:15 -0500104 /**
105 * API call to get local user roles
106 */
107 function getUserRoles() {
108 APIUtils.getAccountServiceRoles()
109 .then((roles) => {
110 $scope.userRoles = roles;
111 })
112 .catch((error) => {
113 console.log(JSON.stringify(error));
114 $scope.userRoles = null;
115 })
116 }
AppaRao Pulib1e7c862019-03-12 14:56:40 +0530117
Yoshie Muranakafa562732019-07-17 11:23:15 -0500118 /**
119 * API call to create new user
120 * @param {*} user
121 */
122 function createUser(username, password, role, enabled) {
AppaRao Pulib1e7c862019-03-12 14:56:40 +0530123 $scope.loading = true;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500124 APIUtils.createUser(username, password, role, enabled)
125 .then(() => {
126 getLocalUsers();
127 toastService.success(`User '${username}' has been created.`);
128 })
129 .catch((error) => {
130 console.log(JSON.stringify(error));
131 toastService.error(`Failed to create new user '${username}'.`);
132 })
133 .finally(() => {
AppaRao Pulib1e7c862019-03-12 14:56:40 +0530134 $scope.loading = false;
135 });
Yoshie Muranakafa562732019-07-17 11:23:15 -0500136 }
AppaRao Pulib1e7c862019-03-12 14:56:40 +0530137
Yoshie Muranakafa562732019-07-17 11:23:15 -0500138 /**
139 * API call to update existing user
140 */
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500141 function updateUser(
142 originalUsername, username, password, role, enabled, locked) {
AppaRao Puli28711a62018-10-17 16:07:55 +0530143 $scope.loading = true;
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500144 APIUtils
145 .updateUser(
146 originalUsername, username, password, role, enabled, locked)
Yoshie Muranakafa562732019-07-17 11:23:15 -0500147 .then(() => {
148 getLocalUsers();
149 toastService.success('User has been updated successfully.')
150 })
151 .catch((error) => {
152 console.log(JSON.stringify(error));
153 toastService.error(`Unable to update user '${originalUsername}'.`)
154 })
155 .finally(() => {
156 $scope.loading = false;
157 })
158 }
159
160 /**
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700161 * API call to delete users
162 * @param {*} users : Array of users to delete
Yoshie Muranakafa562732019-07-17 11:23:15 -0500163 */
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700164 function deleteUsers(users = []) {
Yoshie Muranakafa562732019-07-17 11:23:15 -0500165 $scope.loading = true;
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700166 const promises =
167 users.map((user) => APIUtils.deleteUser(user.UserName));
168 $q.all(promises)
Yoshie Muranakafa562732019-07-17 11:23:15 -0500169 .then(() => {
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700170 let message;
171 if (users.length > 1) {
172 message = 'Users have been removed.'
173 } else {
174 message = `User '${users[0].UserName}' has been removed.`
175 }
176 toastService.success(message);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500177 })
178 .catch((error) => {
179 console.log(JSON.stringify(error));
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700180 let message;
181 if (users.length > 1) {
182 message = 'Failed to remove users.'
183 } else {
184 message = `Failed to remove user '${users[0].UserName}'.`
185 }
186 toastService.error(message);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500187 })
188 .finally(() => {
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700189 getLocalUsers();
190 $scope.loading = false;
191 });
192 }
193
194 /**
195 * API call to update user status enabled/disabled
196 * @param {*} users : Array of users to update
197 * @param {boolean} enabled : status
198 */
199 function updateUserStatus(users = [], enabled = true) {
200 $scope.loading = true;
201 const promises = users.map(
202 (user) => APIUtils.updateUser(
203 user.UserName, null, null, null, enabled, null));
204 $q.all(promises)
205 .then(() => {
206 let message;
207 let statusLabel = enabled ? 'enabled' : 'disabled';
208 if (users.length > 1) {
209 message = `Users ${statusLabel}.`
210 } else {
211 message = `User '${users[0].UserName}' ${statusLabel}.`;
212 }
213 toastService.success(message);
214 })
215 .catch((error) => {
216 console.log(JSON.stringify(error));
217 let message;
218 let statusLabel = enabled ? 'enable' : 'disable';
219 if (users.length > 1) {
220 message = `Failed to ${statusLabel} users.`
221 } else {
222 message =
223 `Failed to ${statusLabel} user '${users[0].UserName}'.`
224 }
225 toastService.error(message);
226 })
227 .finally(() => {
228 getLocalUsers();
AppaRao Puli28711a62018-10-17 16:07:55 +0530229 $scope.loading = false;
Gunnar Millsb3d48d42018-05-25 08:34:35 -0500230 });
Yoshie Muranakafa562732019-07-17 11:23:15 -0500231 }
AppaRao Puli28711a62018-10-17 16:07:55 +0530232
Yoshie Muranakafa562732019-07-17 11:23:15 -0500233 /**
234 * API call to save account policy settings
235 * @param {number} lockoutDuration
236 * @param {number} lockoutThreshold
237 */
238 function updateAccountSettings(lockoutDuration, lockoutThreshold) {
AppaRao Puli28711a62018-10-17 16:07:55 +0530239 $scope.loading = true;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500240 APIUtils.saveUserAccountProperties(lockoutDuration, lockoutThreshold)
241 .then(() => {
242 $scope.accountSettings['AccountLockoutDuration'] =
243 lockoutDuration;
244 $scope.accountSettings['AccountLockoutThreshold'] =
245 lockoutThreshold;
246 toastService.success(
247 'Account policy settings have been updated.');
248 })
249 .catch((error) => {
250 console.log(JSON.stringify(error));
251 toastService.error('Failed to update account policy settings.');
252 })
253 .finally(() => {
AppaRao Puli28711a62018-10-17 16:07:55 +0530254 $scope.loading = false;
255 });
Yoshie Muranakafa562732019-07-17 11:23:15 -0500256 }
AppaRao Pulicf7219c2018-12-27 16:17:46 +0530257
Yoshie Muranakafa562732019-07-17 11:23:15 -0500258 /**
259 * Initiate account settings modal
260 */
261 function initAccountSettingsModal() {
262 const template = require('./user-accounts-modal-settings.html');
263 $uibModal
264 .open({
265 template,
266 windowTopClass: 'uib-modal',
267 ariaLabelledBy: 'dialog_label',
268 controllerAs: 'modalCtrl',
269 controller: function() {
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500270 const lockoutMethod = mapLockoutMethod(
271 $scope.accountSettings.AccountLockoutDuration);
272
Yoshie Muranakafa562732019-07-17 11:23:15 -0500273 this.settings = {};
274 this.settings.maxLogin =
275 $scope.accountSettings.AccountLockoutThreshold;
276 this.settings.lockoutMethod = lockoutMethod;
277 this.settings.timeoutDuration = !lockoutMethod ?
278 null :
279 $scope.accountSettings.AccountLockoutDuration;
280 }
281 })
282 .result
283 .then((form) => {
284 if (form.$valid) {
285 const lockoutDuration = form.lockoutMethod.$modelValue ?
286 form.timeoutDuration.$modelValue :
287 0;
288 const lockoutThreshold = form.maxLogin.$modelValue;
289 updateAccountSettings(lockoutDuration, lockoutThreshold);
290 }
291 })
292 .catch(
293 () => {
294 // do nothing
295 })
296 }
297
298 /**
299 * Initiate user modal
300 * Can be triggered by clicking edit in table or 'Add user' button
301 * If triggered from the table, user parameter will be provided
302 * If triggered by add user button, user parameter will be undefined
303 * @optional @param {*} user
304 */
305 function initUserModal(user) {
306 if ($scope.userRoles === null || $scope.userRoles === undefined) {
307 // If userRoles failed to load, do not allow add/edit
308 // functionality
309 return;
310 }
311 const newUser = user ? false : true;
312 const originalUsername = user ? angular.copy(user.UserName) : null;
313 const template = require('./user-accounts-modal-user.html');
314 $uibModal
315 .open({
316 template,
317 windowTopClass: 'uib-modal',
318 ariaLabelledBy: 'dialog_label',
319 controllerAs: 'modalCtrl',
320 controller: function() {
321 // Set default status to Enabled
322 const status = newUser ? true : user.Enabled;
323 // Check if UserName is root
324 // Some form controls will be disabled for root users:
325 // edit enabled status, edit username, edit role
Patrick Williams339db9a2021-02-22 17:18:14 -0600326 const isRoot = newUser ? false :
327 checkIfRoot(user) ? true :
328 false;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500329 // Array of existing usernames (excluding current user instance)
330 const existingUsernames =
331 $scope.localUsers.reduce((acc, val) => {
332 if (user && (val.UserName === user.UserName)) {
333 return acc;
334 }
335 acc.push(val.UserName);
336 return acc;
337 }, []);
338
339 this.user = {};
340 this.user.isRoot = isRoot;
341 this.user.new = newUser;
342 this.user.accountStatus = status;
343 this.user.username = newUser ? '' : user.UserName;
344 this.user.privilege = newUser ? '' : user.RoleId;
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500345 this.user.locked = newUser ? null : user.Locked;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500346
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500347 this.manualUnlockProperty = false;
348 this.automaticLockout = mapLockoutMethod(
349 $scope.accountSettings.AccountLockoutDuration);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500350 this.privilegeRoles = $scope.userRoles;
351 this.existingUsernames = existingUsernames;
352 this.minPasswordLength = $scope.accountSettings ?
353 $scope.accountSettings.MinPasswordLength :
354 null;
355 this.maxPasswordLength = $scope.accountSettings ?
356 $scope.accountSettings.MaxPasswordLength :
357 null;
358 }
359 })
360 .result
361 .then((form) => {
362 if (form.$valid) {
363 // If form control is pristine set property to null
364 // this will make sure only changed values are updated when
365 // modifying existing users
366 // API utils checks for null values
367 const username =
368 form.username.$pristine ? null : form.username.$modelValue;
369 const password =
370 form.password.$pristine ? null : form.password.$modelValue;
371 const role = form.privilege.$pristine ?
372 null :
373 form.privilege.$modelValue;
374 const enabled = (form.accountStatus.$pristine &&
375 form.accountStatus1.$pristine) ?
376 null :
377 form.accountStatus.$modelValue;
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500378 const locked = (form.lock && form.lock.$dirty) ?
379 form.lock.$modelValue :
380 null;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500381
382 if (!newUser) {
383 updateUser(
Yoshie Muranakab4d9c092019-08-01 16:19:40 -0500384 originalUsername, username, password, role, enabled,
385 locked);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500386 } else {
387 createUser(
388 username, password, role, form.accountStatus.$modelValue);
389 }
390 }
391 })
392 .catch(
393 () => {
394 // do nothing
395 })
396 }
397
398 /**
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700399 * Intiate remove users modal
400 * @param {*} users
Yoshie Muranakafa562732019-07-17 11:23:15 -0500401 */
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700402 function initRemoveModal(users) {
Yoshie Muranakafa562732019-07-17 11:23:15 -0500403 const template = require('./user-accounts-modal-remove.html');
404 $uibModal
405 .open({
406 template,
407 windowTopClass: 'uib-modal',
408 ariaLabelledBy: 'dialog_label',
409 controllerAs: 'modalCtrl',
410 controller: function() {
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700411 this.users = users;
Yoshie Muranakafa562732019-07-17 11:23:15 -0500412 }
413 })
414 .result
415 .then(() => {
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700416 deleteUsers(users);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500417 })
418 .catch(
419 () => {
420 // do nothing
421 })
422 }
423
424 /**
425 * Callback when action emitted from table
426 * @param {*} value
427 */
Yoshie Muranaka5b8cef82019-09-10 08:09:43 -0700428 $scope.onEmitRowAction = (value) => {
Yoshie Muranakafa562732019-07-17 11:23:15 -0500429 switch (value.action) {
430 case 'Edit':
431 initUserModal(value.row);
432 break;
433 case 'Delete':
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700434 initRemoveModal([value.row]);
Yoshie Muranakafa562732019-07-17 11:23:15 -0500435 break;
436 default:
beccabroeka5deeea2019-03-22 15:38:49 -0500437 }
438 };
Yoshie Muranakafa562732019-07-17 11:23:15 -0500439
440 /**
Yoshie Muranaka49001e22019-09-16 10:33:16 -0700441 * Callback when batch action emitted from table
442 */
443 $scope.onEmitBatchAction = (value) => {
444 switch (value.action) {
445 case 'delete':
446 initRemoveModal(value.filteredRows);
447 break;
448 case 'enable':
449 updateUserStatus(value.filteredRows, true)
450 break;
451 case 'disable':
452 updateUserStatus(value.filteredRows, false)
453 break;
454 default:
455 break;
456 }
457 };
458
459 /**
Yoshie Muranakafa562732019-07-17 11:23:15 -0500460 * Callback when 'Account settings policy' button clicked
461 */
462 $scope.onClickAccountSettingsPolicy = () => {
463 initAccountSettingsModal();
464 };
465
466 /**
467 * Callback when 'Add user' button clicked
468 */
469 $scope.onClickAddUser = () => {
470 initUserModal();
471 };
472
473 /**
474 * Callback when controller view initially loaded
475 */
476 $scope.$on('$viewContentLoaded', () => {
477 getLocalUsers();
478 getUserRoles();
479 getAccountSettings();
480 })
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700481 }
482 ]);
Iftekharul Islamcd789502017-04-19 14:37:55 -0500483})(angular);