/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /** * The controller for editing users. */ angular.module('manage').controller('manageUserController', ['$scope', '$injector', function manageUserController($scope, $injector) { // Required types var Error = $injector.get('Error'); var ManagementPermissions = $injector.get('ManagementPermissions'); var PermissionFlagSet = $injector.get('PermissionFlagSet'); var PermissionSet = $injector.get('PermissionSet'); var User = $injector.get('User'); // Required services var $location = $injector.get('$location'); var $routeParams = $injector.get('$routeParams'); var $q = $injector.get('$q'); var authenticationService = $injector.get('authenticationService'); var dataSourceService = $injector.get('dataSourceService'); var permissionService = $injector.get('permissionService'); var requestService = $injector.get('requestService'); var schemaService = $injector.get('schemaService'); var userService = $injector.get('userService'); /** * The identifiers of all data sources currently available to the * authenticated user. * * @type String[] */ var dataSources = authenticationService.getAvailableDataSources(); /** * The username of the current, authenticated user. * * @type String */ var currentUsername = authenticationService.getCurrentUsername(); /** * The username of the original user from which this user is * being cloned. Only valid if this is a new user. * * @type String */ var cloneSourceUsername = $location.search().clone; /** * The username of the user being edited. If a new user is * being created, this will not be defined. * * @type String */ var username = $routeParams.id; /** * The unique identifier of the data source containing the user being * edited. * * @type String */ $scope.dataSource = $routeParams.dataSource; /** * The string value representing the user currently being edited within the * permission flag set. Note that his may not match the user's actual * username - it is a marker that is (1) guaranteed to be associated with * the current user's permissions in the permission set and (2) guaranteed * not to collide with any user that does not represent the current user * within the permission set. * * @type String */ $scope.selfUsername = ''; /** * All user accounts associated with the same username as the account being * created or edited, as a map of data source identifier to the User object * within that data source. * * @type Object. */ $scope.users = null; /** * The user being modified. * * @type User */ $scope.user = null; /** * All permissions associated with the user being modified. * * @type PermissionFlagSet */ $scope.permissionFlags = null; /** * The set of permissions that will be added to the user when the user is * saved. Permissions will only be present in this set if they are * manually added, and not later manually removed before saving. * * @type PermissionSet */ $scope.permissionsAdded = new PermissionSet(); /** * The set of permissions that will be removed from the user when the user * is saved. Permissions will only be present in this set if they are * manually removed, and not later manually added before saving. * * @type PermissionSet */ $scope.permissionsRemoved = new PermissionSet(); /** * For each applicable data source, the management-related actions that the * current user may perform on the user account currently being created * or modified, as a map of data source identifier to the * {@link ManagementPermissions} object describing the actions available * within that data source, or null if the current user's permissions have * not yet been loaded. * * @type Object. */ $scope.managementPermissions = null; /** * All available user attributes. This is only the set of attribute * definitions, organized as logical groupings of attributes, not attribute * values. * * @type Form[] */ $scope.attributes = null; /** * Returns whether critical data has completed being loaded. * * @returns {Boolean} * true if enough data has been loaded for the user interface to be * useful, false otherwise. */ $scope.isLoaded = function isLoaded() { return $scope.users !== null && $scope.permissionFlags !== null && $scope.managementPermissions !== null && $scope.attributes !== null; }; /** * Returns whether the current user can edit the username of the user being * edited within the given data source. * * @param {String} [dataSource] * The identifier of the data source to check. If omitted, this will * default to the currently-selected data source. * * @returns {Boolean} * true if the current user can edit the username of the user being * edited, false otherwise. */ $scope.canEditUsername = function canEditUsername(dataSource) { return !username; }; /** * Loads the data associated with the user having the given username, * preparing the interface for making modifications to that existing user. * * @param {String} dataSource * The unique identifier of the data source containing the user to * load. * * @param {String} username * The username of the user to load. * * @returns {Promise} * A promise which is resolved when the interface has been prepared for * editing the given user. */ var loadExistingUser = function loadExistingUser(dataSource, username) { return $q.all({ users : dataSourceService.apply(userService.getUser, dataSources, username), permissions : permissionService.getPermissions(dataSource, username) }) .then(function userDataRetrieved(values) { $scope.users = values.users; $scope.user = values.users[dataSource]; // Create skeleton user if user does not exist if (!$scope.user) $scope.user = new User({ 'username' : username }); // The current user will be associated with username of the existing // user in the retrieved permission set $scope.selfUsername = username; $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(values.permissions); }); }; /** * Loads the data associated with the user having the given username, * preparing the interface for cloning that existing user. * * @param {String} dataSource * The unique identifier of the data source containing the user to * be cloned. * * @param {String} username * The username of the user being cloned. * * @returns {Promise} * A promise which is resolved when the interface has been prepared for * cloning the given user. */ var loadClonedUser = function loadClonedUser(dataSource, username) { return $q.all({ users : dataSourceService.apply(userService.getUser, [dataSource], username), permissions : permissionService.getPermissions(dataSource, username) }) .then(function userDataRetrieved(values) { $scope.users = {}; $scope.user = values.users[dataSource]; // The current user will be associated with cloneSourceUsername in the // retrieved permission set $scope.selfUsername = username; $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(values.permissions); $scope.permissionsAdded = values.permissions; }); }; /** * Loads skeleton user data, preparing the interface for creating a new * user. * * @returns {Promise} * A promise which is resolved when the interface has been prepared for * creating a new user. */ var loadSkeletonUser = function loadSkeletonUser() { // No users exist regardless of data source if there is no username $scope.users = {}; // Use skeleton user object with no associated permissions $scope.user = new User(); $scope.permissionFlags = new PermissionFlagSet(); // As no permissions are yet associated with the user, it is safe to // use any non-empty username as a placeholder for self-referential // permissions $scope.selfUsername = 'SELF'; return $q.resolve(); }; /** * Loads the data required for performing the management task requested * through the route parameters given at load time, automatically preparing * the interface for editing an existing user, cloning an existing user, or * creating an entirely new user. * * @returns {Promise} * A promise which is resolved when the interface has been prepared * for performing the requested management task. */ var loadRequestedUser = function loadRequestedUser() { // Pull user data and permissions if we are editing an existing user if (username) return loadExistingUser($scope.dataSource, username); // If we are cloning an existing user, pull his/her data instead if (cloneSourceUsername) return loadClonedUser($scope.dataSource, cloneSourceUsername); // If we are creating a new user, populate skeleton user data return loadSkeletonUser(); }; // Populate interface with requested data $q.all({ userData : loadRequestedUser(), permissions : dataSourceService.apply(permissionService.getEffectivePermissions, dataSources, currentUsername), attributes : schemaService.getUserAttributes($scope.dataSource) }) .then(function dataReceived(values) { $scope.attributes = values.attributes; $scope.managementPermissions = {}; angular.forEach(dataSources, function addAccountPage(dataSource) { // Determine whether data source contains this user var exists = (dataSource in $scope.users); // Calculate management actions available for this specific account $scope.managementPermissions[dataSource] = ManagementPermissions.fromPermissionSet( values.permissions[dataSource], PermissionSet.SystemPermissionType.CREATE_USER, PermissionSet.hasUserPermission, exists ? username : null); }); }, requestService.DIE); /** * Returns the URL for the page which manages the user account currently * being edited under the given data source. The given data source need not * be the same as the data source currently selected. * * @param {String} dataSource * The unique identifier of the data source that the URL is being * generated for. * * @returns {String} * The URL for the page which manages the user account currently being * edited under the given data source. */ $scope.getUserURL = function getUserURL(dataSource) { return '/manage/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(username || ''); }; /** * Cancels all pending edits, returning to the main list of users. */ $scope.returnToUserList = function returnToUserList() { $location.url('/settings/users'); }; /** * Cancels all pending edits, opening an edit page for a new user * which is prepopulated with the data from the user currently being edited. */ $scope.cloneUser = function cloneUser() { $location.path('/manage/' + encodeURIComponent($scope.dataSource) + '/users').search('clone', username); }; /** * Saves the current user, creating a new user or updating the existing * user depending on context, returning a promise which is resolved if the * save operation succeeds and rejected if the save operation fails. * * @returns {Promise} * A promise which is resolved if the save operation succeeds and is * rejected with an {@link Error} if the save operation fails. */ $scope.saveUser = function saveUser() { // Verify passwords match if ($scope.passwordMatch !== $scope.user.password) { return $q.reject(new Error({ translatableMessage : { key : 'MANAGE_USER.ERROR_PASSWORD_MISMATCH' } })); } // Save or create the user, depending on whether the user exists var saveUserPromise; if ($scope.dataSource in $scope.users) saveUserPromise = userService.saveUser($scope.dataSource, $scope.user); else saveUserPromise = userService.createUser($scope.dataSource, $scope.user); return saveUserPromise.then(function savedUser() { // Move permission flags if username differs from marker if ($scope.selfUsername !== $scope.user.username) { // Rename added permission if ($scope.permissionsAdded.userPermissions[$scope.selfUsername]) { $scope.permissionsAdded.userPermissions[$scope.user.username] = $scope.permissionsAdded.userPermissions[$scope.selfUsername]; delete $scope.permissionsAdded.userPermissions[$scope.selfUsername]; } // Rename removed permission if ($scope.permissionsRemoved.userPermissions[$scope.selfUsername]) { $scope.permissionsRemoved.userPermissions[$scope.user.username] = $scope.permissionsRemoved.userPermissions[$scope.selfUsername]; delete $scope.permissionsRemoved.userPermissions[$scope.selfUsername]; } } // Upon success, save any changed permissions return permissionService.patchPermissions($scope.dataSource, $scope.user.username, $scope.permissionsAdded, $scope.permissionsRemoved); }); }; /** * Deletes the current user, returning a promise which is resolved if the * delete operation succeeds and rejected if the delete operation fails. * * @returns {Promise} * A promise which is resolved if the delete operation succeeds and is * rejected with an {@link Error} if the delete operation fails. */ $scope.deleteUser = function deleteUser() { return userService.deleteUser($scope.dataSource, $scope.user); }; }]);