1/**
2 * Controller for user Accounts
3 *
4 * @module app/access-control
5 * @exports userController
6 * @name userController
7 */
8
9window.angular && (function(angular) {
10  'use strict';
11
12  angular.module('app.accessControl').controller('userController', [
13    '$scope', 'APIUtils', 'toastService', '$uibModal', '$q',
14    function($scope, APIUtils, toastService, $uibModal, $q) {
15      $scope.loading;
16      $scope.accountSettings;
17      $scope.userRoles;
18      $scope.localUsers;
19
20      $scope.tableData = [];
21      $scope.tableHeader = [
22        {label: 'Username'}, {label: 'Privilege'}, {label: 'Account status'}
23      ];
24      $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      }
37
38      /**
39       * Data table mapper
40       * @param {*} user
41       * @returns user
42       */
43      function mapTableData(user) {
44        const accountStatus =
45            user.Locked ? 'Locked' : user.Enabled ? 'Enabled' : 'Disabled';
46        const editAction = {type: 'Edit', enabled: true, file: 'icon-edit.svg'};
47        const deleteAction = {
48          type: 'Delete',
49          enabled: checkIfRoot(user) ? false : true,
50          file: 'icon-trashcan.svg'
51        };
52        user.selectable = checkIfRoot(user) ? false : true;
53        user.actions = [editAction, deleteAction];
54        user.uiData = [user.UserName, user.RoleId, accountStatus];
55        return user;
56      }
57
58      /**
59       * 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      /**
71       * API call to get all user accounts
72       */
73      function getLocalUsers() {
74        $scope.loading = true;
75        APIUtils.getAllUserAccounts()
76            .then((users) => {
77              $scope.localUsers = users;
78              $scope.tableData = users.map(mapTableData);
79            })
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      }
88
89      /**
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      }
102
103      /**
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      }
116
117      /**
118       * API call to create new user
119       * @param {*} user
120       */
121      function createUser(username, password, role, enabled) {
122        $scope.loading = true;
123        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(() => {
133              $scope.loading = false;
134            });
135      }
136
137      /**
138       * API call to update existing user
139       */
140      function updateUser(
141          originalUsername, username, password, role, enabled, locked) {
142        $scope.loading = true;
143        APIUtils
144            .updateUser(
145                originalUsername, username, password, role, enabled, locked)
146            .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      /**
160       * API call to delete users
161       * @param {*} users : Array of users to delete
162       */
163      function deleteUsers(users = []) {
164        $scope.loading = true;
165        const promises =
166            users.map((user) => APIUtils.deleteUser(user.UserName));
167        $q.all(promises)
168            .then(() => {
169              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);
176            })
177            .catch((error) => {
178              console.log(JSON.stringify(error));
179              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);
186            })
187            .finally(() => {
188              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();
228              $scope.loading = false;
229            });
230      }
231
232      /**
233       * API call to save account policy settings
234       * @param {number} lockoutDuration
235       * @param {number} lockoutThreshold
236       */
237      function updateAccountSettings(lockoutDuration, lockoutThreshold) {
238        $scope.loading = true;
239        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(() => {
253              $scope.loading = false;
254            });
255      }
256
257      /**
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() {
269                const lockoutMethod = mapLockoutMethod(
270                    $scope.accountSettings.AccountLockoutDuration);
271
272                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 =
326                    newUser ? false : checkIfRoot(user) ? true : false;
327                // 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;
343                this.user.locked = newUser ? null : user.Locked;
344
345                this.manualUnlockProperty = false;
346                this.automaticLockout = mapLockoutMethod(
347                    $scope.accountSettings.AccountLockoutDuration);
348                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;
376                const locked = (form.lock && form.lock.$dirty) ?
377                    form.lock.$modelValue :
378                    null;
379
380                if (!newUser) {
381                  updateUser(
382                      originalUsername, username, password, role, enabled,
383                      locked);
384                } else {
385                  createUser(
386                      username, password, role, form.accountStatus.$modelValue);
387                }
388              }
389            })
390            .catch(
391                () => {
392                    // do nothing
393                })
394      }
395
396      /**
397       * Intiate remove users modal
398       * @param {*} users
399       */
400      function initRemoveModal(users) {
401        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() {
409                this.users = users;
410              }
411            })
412            .result
413            .then(() => {
414              deleteUsers(users);
415            })
416            .catch(
417                () => {
418                    // do nothing
419                })
420      }
421
422      /**
423       * Callback when action emitted from table
424       * @param {*} value
425       */
426      $scope.onEmitRowAction = (value) => {
427        switch (value.action) {
428          case 'Edit':
429            initUserModal(value.row);
430            break;
431          case 'Delete':
432            initRemoveModal([value.row]);
433            break;
434          default:
435        }
436      };
437
438      /**
439       * 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      /**
458       * 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      })
479    }
480  ]);
481})(angular);
482