'use strict'; /* global angular:false */ /* global Clipboard:false */ /* global asyncForEachParallel:false */ /* global asyncSeries:false */ /* global $:false */ angular.module('Application').controller('UsersController', ['$scope', '$location', '$timeout', 'Client', function ($scope, $location, $timeout, Client) { Client.onReady(function () { if (!Client.getUserInfo().admin) $location.path('/'); }); $scope.showExternalLdap = true; $scope.ldapProvider = [ { name: 'Active Directory', value: 'ad' }, { name: 'Jumpcloud', value: 'jumpcloud' }, { name: 'Okta', value: 'okta' }, { name: 'Other', value: 'other' }, { name: 'Disabled', value: 'noop' } ]; $scope.ready = false; $scope.users = []; $scope.usersById = []; $scope.groups = []; $scope.groupsById = { }; $scope.config = Client.getConfig(); $scope.userInfo = Client.getUserInfo(); $scope.userSearchString = ''; $scope.currentPage = 1; $scope.pageItemCount = [ { name: 'Show 20 per page', value: 20 }, { name: 'Show 50 per page', value: 50 }, { name: 'Show 100 per page', value: 100 } ]; $scope.pageItems = $scope.pageItemCount[0]; $scope.userRefreshBusy = true; $scope.groupMembers = function (group) { return group.userIds.filter(function (uid) { return !!$scope.usersById[uid]; }).map(function (uid) { return $scope.usersById[uid].username || $scope.usersById[uid].email; }).join(' '); }; $scope.userremove = { busy: false, error: {}, userInfo: {}, show: function (userInfo) { $scope.userremove.error.username = null; $scope.userremove.userInfo = userInfo; $('#userRemoveModal').modal('show'); }, submit: function () { $scope.userremove.busy = true; Client.removeUser($scope.userremove.userInfo.id, function (error) { $scope.userremove.busy = false; if (error) return console.error('Unable to delete user.', error); $scope.userremove.userInfo = {}; refresh(); $('#userRemoveModal').modal('hide'); }); } }; $scope.useradd = { busy: false, alreadyTaken: false, error: {}, email: '', username: '', displayName: '', sendInvite: true, selectedGroups: [], admin: false, show: function () { $scope.useradd.error = {}; $scope.useradd.email = ''; $scope.useradd.username = ''; $scope.useradd.displayName = ''; $scope.useradd.selectedGroups = []; $scope.useradd.admin = false; $scope.useradd_form.$setUntouched(); $scope.useradd_form.$setPristine(); $('#userAddModal').modal('show'); }, submit: function () { $scope.useradd.busy = true; $scope.useradd.alreadyTaken = false; $scope.useradd.error.email = null; $scope.useradd.error.username = null; $scope.useradd.error.displayName = null; var user = { username: $scope.useradd.username || null, email: $scope.useradd.email, displayName: $scope.useradd.displayName, admin: $scope.useradd.admin }; Client.createUser(user, function (error, newUserInfo) { if (error) { $scope.useradd.busy = false; if (error.statusCode === 409) { if (error.message.toLowerCase().indexOf('email') !== -1) { $scope.useradd.error.email = 'Email already taken'; $scope.useradd_form.email.$setPristine(); $('#inputUserAddEmail').focus(); } else if (error.message.toLowerCase().indexOf('username') !== -1 || error.message.toLowerCase().indexOf('mailbox') !== -1) { $scope.useradd.error.username = 'Username already taken'; $scope.useradd_form.username.$setPristine(); $('#inputUserAddUsername').focus(); } else { // should not happen!! console.error(error.message); } return; } else if (error.statusCode === 400) { if (error.message.toLowerCase().indexOf('email') !== -1) { $scope.useradd.error.email = 'Invalid Email'; $scope.useradd.error.emailAttempted = $scope.useradd.email; $scope.useradd_form.email.$setPristine(); $('#inputUserAddEmail').focus(); } else if (error.message.toLowerCase().indexOf('username') !== -1) { $scope.useradd.error.username = error.message; $scope.useradd_form.username.$setPristine(); $('#inputUserAddUsername').focus(); } else { console.error('Unable to create user.', error.statusCode, error.message); } return; } else { return console.error('Unable to create user.', error.statusCode, error.message); } } var groupIds = $scope.useradd.selectedGroups.map(function (g) { return g.id; }); var NOOP = function (next) { next(); }; asyncSeries([ Client.setGroups.bind(Client, newUserInfo.id, groupIds), $scope.useradd.sendInvite ? Client.createInvite.bind(Client, newUserInfo.id) : NOOP, $scope.useradd.sendInvite ? Client.sendInvite.bind(Client, newUserInfo.id) : NOOP ], function (error) { $scope.useradd.busy = false; if (error) return console.error(error); refresh(); $('#userAddModal').modal('hide'); }); }); } }; $scope.useredit = { busy: false, error: {}, userInfo: {}, // form fields email: '', fallbackEmail: '', aliases: {}, displayName: '', admin: false, active: false, source: '', selectedGroups: [], show: function (userInfo) { $scope.useredit.error = {}; $scope.useredit.email = userInfo.email; $scope.useredit.displayName = userInfo.displayName; $scope.useredit.fallbackEmail = userInfo.fallbackEmail; $scope.useredit.userInfo = userInfo; $scope.useredit.selectedGroups = userInfo.groupIds.map(function (gid) { return $scope.groupsById[gid]; }); $scope.useredit.admin = userInfo.admin; $scope.useredit.active = userInfo.active; $scope.useredit.source = userInfo.source; $scope.useredit_form.$setPristine(); $scope.useredit_form.$setUntouched(); $('#userEditModal').modal('show'); }, submit: function () { $scope.useredit.error = {}; $scope.useredit.busy = true; var userId = $scope.useredit.userInfo.id; var data = { id: userId, admin: $scope.useredit.admin, active: $scope.useredit.active }; // only change those if it is a local user if (!$scope.useredit.source) { data.email = $scope.useredit.email; data.displayName = $scope.useredit.displayName; data.fallbackEmail = $scope.useredit.fallbackEmail; } Client.updateUser(data, function (error) { if (error) { $scope.useredit.busy = false; if (error.statusCode === 409) { $scope.useredit.error.email = 'Email already taken'; $scope.useredit_form.email.$setPristine(); $('#inputUserEditEmail').focus(); } else { console.error('Unable to update user:', error); } return; } var groupIds = $scope.useredit.selectedGroups.map(function (g) { return g.id; }); Client.setGroups(data.id, groupIds, function (error) { $scope.useredit.busy = false; if (error) return console.error('Unable to update groups for user:', error); refresh(); $('#userEditModal').modal('hide'); }); }); } }; $scope.groupAdd = { busy: false, error: {}, name: '', selectedUsers: [], show: function () { $scope.groupAdd.busy = false; $scope.groupAdd.error = {}; $scope.groupAdd.name = ''; $scope.groupAddForm.$setUntouched(); $scope.groupAddForm.$setPristine(); $('#groupAddModal').modal('show'); }, submit: function () { $scope.groupAdd.busy = true; $scope.groupAdd.error = {}; Client.createGroup($scope.groupAdd.name, function (error, result) { if (error) { $scope.groupAdd.busy = false; if (error.statusCode === 409) { $scope.groupAdd.error.name = 'Name already taken'; $scope.groupAddForm.name.$setPristine(); $('#groupAddName').focus(); return; } else if (error.statusCode === 400) { $scope.groupAdd.error.name = error.message; $scope.groupAddForm.name.$setPristine(); $('#groupAddName').focus(); return; } else { return console.error('Unable to create group.', error.statusCode, error.message); } } var userIds = $scope.groupAdd.selectedUsers.map(function (u) { return u.id; }); Client.setGroupMembers(result.id, userIds, function (error) { $scope.groupAdd.busy = false; if (error) return console.error('Unable to add memebers.', error.statusCode, error.message); refresh(); $('#groupAddModal').modal('hide'); }); }); } }; $scope.groupEdit = { busy: false, error: {}, groupInfo: {}, name: '', selectedUsers: [], show: function (groupInfo) { $scope.groupEdit.error = {}; $scope.groupEdit.groupInfo = groupInfo; $scope.groupEdit.name = groupInfo.name; $scope.groupEdit.selectedUsers = groupInfo.userIds.map(function (uid) { return $scope.usersById[uid]; }); $scope.groupEdit_form.$setPristine(); $scope.groupEdit_form.$setUntouched(); $('#groupEditModal').modal('show'); }, submit: function () { $scope.groupEdit.busy = true; $scope.groupEdit.error = {}; Client.updateGroup($scope.groupEdit.groupInfo.id, $scope.groupEdit.name, function (error) { if (error) { $scope.groupEdit.busy = false; if (error.statusCode === 409) { $scope.groupEdit.error.name = 'Name already taken'; $scope.groupEditForm.name.$setPristine(); $('#groupEditName').focus(); return; } else if (error.statusCode === 400) { $scope.groupEdit.error.name = error.message; $scope.groupEditForm.name.$setPristine(); $('#groupEditName').focus(); return; } else { return console.error('Unable to edit group.', error.statusCode, error.message); } } var userIds = $scope.groupEdit.selectedUsers.map(function (u) { return u.id; }); Client.setGroupMembers($scope.groupEdit.groupInfo.id, userIds, function (error) { $scope.groupEdit.busy = false; if (error) return console.error('Unable to edit group.', error.statusCode, error.message); refresh(); $('#groupEditModal').modal('hide'); }); }); } }; $scope.groupRemove = { busy: false, error: {}, group: null, memberCount: 0, show: function (group) { $scope.groupRemove.busy = false; $scope.groupRemove.error = {}; $scope.groupRemove.group = angular.copy(group); Client.getGroup(group.id, function (error, result) { if (error) return console.error('Unable to fetch group information.', error.statusCode, error.message); $scope.groupRemove.memberCount = result.userIds.length; $('#groupRemoveModal').modal('show'); }); }, submit: function () { $scope.groupRemove.busy = true; $scope.groupRemove.error = {}; Client.removeGroup($scope.groupRemove.group.id, function (error) { $scope.groupRemove.busy = false; if (error) return console.error('Unable to remove group.', error.statusCode, error.message); refresh(); $('#groupRemoveModal').modal('hide'); }); } }; $scope.isMe = function (user) { return user.username === Client.getUserInfo().username; }; $scope.invitation = { busy: false, setupLink: '', user: null, show: function (user) { $scope.invitation.user = user; $scope.invitation.setupLink = ''; $scope.invitation.busy = false; Client.createInvite(user.id, function (error, resetToken) { if (error) return console.error(error); $scope.invitation.setupLink = location.origin + '/api/v1/session/account/setup.html?reset_token=' + resetToken + '&email=' + encodeURIComponent($scope.invitation.user.email); $('#invitationModal').modal('show'); }); }, email: function () { $scope.invitation.busy = true; Client.sendInvite($scope.invitation.user.id, function (error) { $scope.invitation.busy = false; if (error) return console.error(error); $('#invitationModal').modal('hide'); }); } }; $scope.externalLdap = { busy: false, error: {}, syncBusy: false, taskId: 0, // fields provider: 'noop', url: '', baseDn: '', filter: '', bindDn: '', bindPassword: '', sync: function () { $scope.externalLdap.syncBusy = true; Client.startExternalLdapSync(function (error, taskId) { if (error) { $scope.externalLdap.syncBusy = false; console.error('Unable to start ldap syncer task.', error); return; } $scope.externalLdap.taskId = taskId; function refreshTaskStatus() { Client.getTask(taskId, function (error, result) { if (error) console.error(error); if (result && result.active) return $timeout(refreshTaskStatus, 2000); $scope.externalLdap.syncBusy = false; refreshUsers(); }); } refreshTaskStatus(); }); }, show: function () { $scope.externalLdap.busy = false; $scope.externalLdap.error = {}; $('#externalLdapModal').modal('show'); }, submit: function () { $scope.externalLdap.busy = true; $scope.externalLdap.error = {}; var config = { provider: $scope.externalLdap.provider }; if ($scope.externalLdap.provider !== 'noop') { config.url = $scope.externalLdap.url; config.baseDn = $scope.externalLdap.baseDn; config.filter = $scope.externalLdap.filter; if ($scope.externalLdap.bindDn) { config.bindDn = $scope.externalLdap.bindDn; config.bindPassword = $scope.externalLdap.bindPassword; } } Client.setExternalLdapConfig(config, function (error) { $scope.externalLdap.busy = false; if (error) { console.error(error); if (error.statusCode === 424) { $scope.externalLdap.error.url = true; } else if (error.statusCode === 400 && error.message === 'invalid baseDn') { $scope.externalLdap.error.baseDn = true; } else if (error.statusCode === 400 && error.message === 'invalid filter') { $scope.externalLdap.error.filter = true; } else if (error.statusCode === 400 && error.message === 'invalid bind credentials') { $scope.externalLdap.error.credentials = true; } else { $scope.externalLdap.error.generic = error.message; } } else { $('#externalLdapModal').modal('hide'); } }); } }; $scope.copyToClipboard = function (/*value*/) { document.execCommand('copy'); }; function getUsers(callback) { var users = []; Client.getUsers($scope.userSearchString, $scope.currentPage, $scope.pageItems.value, function (error, results) { if (error) return console.error(error); asyncForEachParallel(results, function (result, iteratorDone) { Client.getUser(result.id, function (error, user) { if (error) return iteratorDone(error); users.push(user); iteratorDone(); }); }, function (error) { callback(error, users); }); }); } function getGroups(callback) { var groups = []; Client.getGroups(function (error, results) { if (error) return console.error(error); asyncForEachParallel(results, function (result, iteratorDone) { Client.getGroup(result.id, function (error, group) { if (error) return iteratorDone(error); groups.push(group); iteratorDone(); }); }, function (error) { callback(error, groups); }); }); } function refreshUsers() { $scope.userRefreshBusy = true; getUsers(function (error, result) { if (error) return console.error('Unable to get user listing.', error); angular.copy(result, $scope.users); $scope.usersById = { }; for (var i = 0; i < result.length; i++) { $scope.usersById[result[i].id] = result[i]; } $scope.ready = true; $scope.userRefreshBusy = false; }); } function refresh() { getGroups(function (error, result) { if (error) return console.error('Unable to get group listing.', error); angular.copy(result, $scope.groups); $scope.groupsById = { }; for (var i = 0; i < result.length; i++) { $scope.groupsById[result[i].id] = result[i]; } refreshUsers(); }); } function loadExternalLdapConfig() { Client.getExternalLdapConfig(function (error, result) { if (error) return console.error('Unable to get external ldap config.', error); $scope.externalLdap.provider = result.provider; if (result.provider !== 'noop') { $scope.externalLdap.url = result.url; $scope.externalLdap.baseDn = result.baseDn; $scope.externalLdap.filter = result.filter; $scope.externalLdap.bindDn = result.bindDn; $scope.externalLdap.bindPassword = result.bindPassword; } Client.getLatestTaskByType('syncExternalLdap', function (error, task) { if (error) return console.error(error); if (!task) return; $scope.externalLdap.taskId = task.id; }); }); } $scope.showNextPage = function () { $scope.currentPage++; refreshUsers(); }; $scope.showPrevPage = function () { if ($scope.currentPage > 1) $scope.currentPage--; else $scope.currentPage = 1; refreshUsers(); }; $scope.updateFilter = function (fresh) { if (fresh) $scope.currentPage = 1; refreshUsers(); }; Client.onReady(refresh); Client.onReady(loadExternalLdapConfig); // setup all the dialog focus handling ['userAddModal', 'userRemoveModal', 'userEditModal', 'groupAddModal', 'groupEditModal', 'groupRemoveModal'].forEach(function (id) { $('#' + id).on('shown.bs.modal', function () { $(this).find("[autofocus]:first").focus(); }); }); var clipboard = new Clipboard('#setupLinkButton'); clipboard.on('success', function(e) { $('#setupLinkButton').tooltip({ title: 'Copied!', trigger: 'manual' }).tooltip('show'); $timeout(function () { $('#setupLinkButton').tooltip('hide'); }, 2000); e.clearSelection(); }); clipboard.on('error', function(/*e*/) { $('#setupLinkButton').tooltip({ title: 'Press Ctrl+C to copy', trigger: 'manual' }).tooltip('show'); $timeout(function () { $('#setupLinkButton').tooltip('hide'); }, 2000); }); $('.modal-backdrop').remove(); }]);