mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-1656: Add per-user KSM vault functionality.
This commit is contained in:
@@ -501,4 +501,6 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
||||
return userService.deleteUser($scope.dataSource, $scope.user);
|
||||
};
|
||||
|
||||
console.log($scope);
|
||||
|
||||
}]);
|
||||
|
@@ -58,6 +58,34 @@ angular.module('rest').factory('schemaService', ['$injector',
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to get the list of available user preference
|
||||
* attributes, returning a promise that provides an array of @link{Form} objects
|
||||
* if successful. Each element of the array describes a logical grouping of
|
||||
* possible user preference attributes.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the users whose
|
||||
* available user preference attributes are to be retrieved. This
|
||||
* identifier corresponds to an AuthenticationProvider within the
|
||||
* Guacamole web application.
|
||||
*
|
||||
* @returns {Promise.<Form[]>}
|
||||
* A promise which will resolve with an array of @link{Form}
|
||||
* objects, where each @link{Form} describes a logical grouping of
|
||||
* possible attributes.
|
||||
*/
|
||||
service.getUserPreferenceAttributes = function getUserPreferenceAttributes(dataSource) {
|
||||
|
||||
// Retrieve available user attributes
|
||||
return authenticationService.request({
|
||||
cache : cacheService.schema,
|
||||
method : 'GET',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/schema/userPreferenceAttributes'
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to get the list of available attributes
|
||||
* for user group objects, returning a promise that provides an array of
|
||||
|
@@ -21,7 +21,7 @@
|
||||
* A directive for managing preferences local to the current user.
|
||||
*/
|
||||
angular.module('settings').directive('guacSettingsPreferences', [function guacSettingsPreferences() {
|
||||
|
||||
|
||||
return {
|
||||
// Element only
|
||||
restrict: 'E',
|
||||
@@ -33,16 +33,18 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe
|
||||
controller: ['$scope', '$injector', function settingsPreferencesController($scope, $injector) {
|
||||
|
||||
// Get required types
|
||||
var PermissionSet = $injector.get('PermissionSet');
|
||||
const Form = $injector.get('Form');
|
||||
const PermissionSet = $injector.get('PermissionSet');
|
||||
|
||||
// Required services
|
||||
var $translate = $injector.get('$translate');
|
||||
var authenticationService = $injector.get('authenticationService');
|
||||
var guacNotification = $injector.get('guacNotification');
|
||||
var permissionService = $injector.get('permissionService');
|
||||
var preferenceService = $injector.get('preferenceService');
|
||||
var requestService = $injector.get('requestService');
|
||||
var userService = $injector.get('userService');
|
||||
const $translate = $injector.get('$translate');
|
||||
const authenticationService = $injector.get('authenticationService');
|
||||
const guacNotification = $injector.get('guacNotification');
|
||||
const permissionService = $injector.get('permissionService');
|
||||
const preferenceService = $injector.get('preferenceService');
|
||||
const requestService = $injector.get('requestService');
|
||||
const schemaService = $injector.get('schemaService');
|
||||
const userService = $injector.get('userService');
|
||||
|
||||
/**
|
||||
* An action to be provided along with the object sent to
|
||||
@@ -56,6 +58,13 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The user being modified.
|
||||
*
|
||||
* @type User
|
||||
*/
|
||||
$scope.user = null;
|
||||
|
||||
/**
|
||||
* The username of the current user.
|
||||
*
|
||||
@@ -78,6 +87,26 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe
|
||||
*/
|
||||
$scope.preferences = preferenceService.preferences;
|
||||
|
||||
/**
|
||||
* All available user attributes, as a mapping of form name to form
|
||||
* object. The form object contains a name, as well as a Map of fields.
|
||||
*
|
||||
* The Map type is used here to maintain form/name uniqueness, as well as
|
||||
* insertion order, to ensure a consistent UI experience.
|
||||
*
|
||||
* @type Map<String, Object>
|
||||
*/
|
||||
$scope.attributeMap = new Map();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The fields which should be displayed for choosing locale
|
||||
* preferences. Each field name must be a property on
|
||||
@@ -197,7 +226,82 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Saves the current user, displaying an acknowledgement message if
|
||||
* saving was successful, or an error if the save failed.
|
||||
*/
|
||||
$scope.saveUser = function saveUser() {
|
||||
return userService.saveUser(dataSource, $scope.user)
|
||||
.then(() => guacNotification.showStatus({
|
||||
text : {
|
||||
key : 'SETTINGS_PREFERENCES.INFO_PREFERENCE_ATTRIBUTES_CHANGED'
|
||||
},
|
||||
actions : [ ACKNOWLEDGE_ACTION ]
|
||||
}),
|
||||
guacNotification.SHOW_REQUEST_ERROR);
|
||||
};
|
||||
|
||||
// Fetch the user record
|
||||
userService.getUser(dataSource, username).then(function saveUserData(user) {
|
||||
$scope.user = user;
|
||||
})
|
||||
|
||||
// Get all datasources that are available for this user
|
||||
authenticationService.getAvailableDataSources().forEach(function loadAttributesForDataSource(dataSource) {
|
||||
|
||||
// Fetch all user attribute forms defined for the datasource
|
||||
schemaService.getUserPreferenceAttributes(dataSource).then(function saveAttributes(attributes) {
|
||||
|
||||
// Iterate through all attribute forms
|
||||
attributes.forEach(function addAttribute(attributeForm) {
|
||||
|
||||
// If the form with the retrieved name already exists
|
||||
if ($scope.attributeMap.has(attributeForm.name)) {
|
||||
const existingFields = $scope.attributeMap.get(attributeForm.name).fields;
|
||||
|
||||
// Add each field to the existing list for this form
|
||||
attributeForm.fields.forEach(function addAllFieldsToExistingMap(field) {
|
||||
existingFields.set(field.name, field);
|
||||
})
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
// Create a new entry for the form
|
||||
$scope.attributeMap.set(attributeForm.name, {
|
||||
name: attributeForm.name,
|
||||
|
||||
// With the field array from the API converted into a Map
|
||||
fields: attributeForm.fields.reduce(
|
||||
function addFieldToMap(currentFieldMap, field) {
|
||||
currentFieldMap.set(field.name, field);
|
||||
return currentFieldMap;
|
||||
}, new Map()
|
||||
)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Re-generate the attributes array every time
|
||||
$scope.attributes = Array.of(...$scope.attributeMap.values()).map(function convertFieldsToArray(formObject) {
|
||||
|
||||
// Convert each temporary form object to a Form type
|
||||
return new Form({
|
||||
name: formObject.name,
|
||||
|
||||
// Convert the field map to a simple array of fields
|
||||
fields: Array.of(...formObject.fields.values())
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}]
|
||||
};
|
||||
|
||||
|
||||
}]);
|
||||
|
@@ -89,4 +89,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- User attributes section -->
|
||||
<h2 class="header" ng-show="attributes.length">{{'SETTINGS_PREFERENCES.SECTION_HEADER_UPDATE_ATTRIBUTES' | translate}}</h2>
|
||||
<div class="attributes" ng-show="attributes.length">
|
||||
<guac-form namespace="'USER_ATTRIBUTES'" content="attributes"
|
||||
model="user.attributes"></guac-form>
|
||||
|
||||
<!-- User attributes save button -->
|
||||
<button ng-show="attributes.length" ng-click="saveUser()" class="save">{{'SETTINGS_PREFERENCES.ACTION_SAVE' | translate}}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@@ -916,6 +916,7 @@
|
||||
|
||||
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
||||
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
|
||||
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
|
||||
"ACTION_UPDATE_PASSWORD" : "@:APP.ACTION_UPDATE_PASSWORD",
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
@@ -942,6 +943,7 @@
|
||||
"HELP_UPDATE_PASSWORD" : "If you wish to change your password, enter your current password and the desired new password below, and click \"Update Password\". The change will take effect immediately.",
|
||||
|
||||
"INFO_PASSWORD_CHANGED" : "Password changed.",
|
||||
"INFO_PREFERENCE_ATTRIBUTES_CHANGED" : "User attributes saved.",
|
||||
|
||||
"NAME_INPUT_METHOD_NONE" : "@:CLIENT.NAME_INPUT_METHOD_NONE",
|
||||
"NAME_INPUT_METHOD_OSK" : "@:CLIENT.NAME_INPUT_METHOD_OSK",
|
||||
@@ -949,6 +951,7 @@
|
||||
|
||||
"SECTION_HEADER_DEFAULT_INPUT_METHOD" : "Default Input Method",
|
||||
"SECTION_HEADER_DEFAULT_MOUSE_MODE" : "Default Mouse Emulation Mode",
|
||||
"SECTION_HEADER_UPDATE_ATTRIBUTES" : "User Attributes",
|
||||
"SECTION_HEADER_UPDATE_PASSWORD" : "Change Password"
|
||||
|
||||
},
|
||||
|
@@ -77,18 +77,6 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
optimization: {
|
||||
minimizer: [
|
||||
|
||||
// Minify using Google Closure Compiler
|
||||
new ClosureWebpackPlugin({ mode: 'STANDARD' }, {
|
||||
languageIn: 'ECMASCRIPT_2020',
|
||||
languageOut: 'ECMASCRIPT5',
|
||||
compilationLevel: 'SIMPLE'
|
||||
}),
|
||||
|
||||
new CssMinimizerPlugin()
|
||||
|
||||
],
|
||||
splitChunks: {
|
||||
cacheGroups: {
|
||||
|
||||
|
@@ -77,6 +77,26 @@ public class SchemaResource {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the possible user preference attributes of a user object.
|
||||
*
|
||||
* @return
|
||||
* A collection of forms which describe the possible preference attributes of a
|
||||
* user object.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while retrieving the possible attributes.
|
||||
*/
|
||||
@GET
|
||||
@Path("userPreferenceAttributes")
|
||||
public Collection<Form> getUserAttrigetUserPreferenceAttributesbutes()
|
||||
throws GuacamoleException {
|
||||
|
||||
// Retrieve all possible user preference attributes
|
||||
return userContext.getUserPreferenceAttributes();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the possible attributes of a user group object.
|
||||
*
|
||||
|
@@ -21,6 +21,11 @@ package org.apache.guacamole.rest.user;
|
||||
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import com.google.inject.assistedinject.AssistedInject;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
@@ -145,9 +150,51 @@ public class UserResource
|
||||
@Override
|
||||
public void updateObject(APIUser modifiedObject) throws GuacamoleException {
|
||||
|
||||
// A user may not use this endpoint to modify himself
|
||||
if (userContext.self().getIdentifier().equals(modifiedObject.getUsername()))
|
||||
throw new GuacamoleSecurityException("Permission denied.");
|
||||
User currentUser = userContext.self();
|
||||
|
||||
// A user may not use this endpoint to modify themself, except in the case
|
||||
// that they are modifying one of the user attributes explicitly exposed
|
||||
// in the user preferences form
|
||||
if (currentUser.getIdentifier().equals(modifiedObject.getUsername())) {
|
||||
|
||||
// A user may not use this endpoint to update their password
|
||||
if (currentUser.getPassword() != null)
|
||||
throw new GuacamoleSecurityException(
|
||||
"Permission denied. The password update endpoint must"
|
||||
+ " be used to change the current user's password.");
|
||||
|
||||
// All attributes exposed in the preferences forms
|
||||
Set<String> preferenceAttributes = (
|
||||
userContext.getUserPreferenceAttributes().stream()
|
||||
.flatMap(form -> form.getFields().stream().map(
|
||||
field -> field.getName())))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// Go through every attribute value and check if it's changed
|
||||
Iterator<String> keyIterator = modifiedObject.getAttributes().keySet().iterator();
|
||||
while(keyIterator.hasNext()) {
|
||||
|
||||
String key = keyIterator.next();
|
||||
String newValue = modifiedObject.getAttributes().get(key);
|
||||
|
||||
// If it's not a preference attribute, editing is not allowed
|
||||
if (!preferenceAttributes.contains(key)) {
|
||||
|
||||
String currentValue = currentUser.getAttributes().get(key);
|
||||
|
||||
// If the value of the attribute has been modified
|
||||
if (
|
||||
!(currentValue == null && newValue == null) && (
|
||||
(currentValue == null && newValue != null) ||
|
||||
!currentValue.equals(newValue)
|
||||
)
|
||||
)
|
||||
throw new GuacamoleSecurityException(
|
||||
"Permission denied. Only user preference attributes"
|
||||
+ " can be modified for the current user.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.updateObject(modifiedObject);
|
||||
|
||||
|
Reference in New Issue
Block a user