GUACAMOLE-1656: Merge support for per-user KSM vaults.

This commit is contained in:
Mike Jumper
2022-09-28 15:06:40 -07:00
committed by GitHub
26 changed files with 1279 additions and 266 deletions

View File

@@ -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

View File

@@ -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,27 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe
}
};
/**
* An action which closes the current dialog, and refreshes
* the user data on dialog close.
*/
const ACKNOWLEDGE_ACTION_RELOAD = {
name : 'SETTINGS_PREFERENCES.ACTION_ACKNOWLEDGE',
// Handle action
callback : function acknowledgeCallback() {
userService.getUser(dataSource, username)
.then(user => $scope.user = user)
.then(() => guacNotification.showStatus(false));
}
};
/**
* The user being modified.
*
* @type User
*/
$scope.user = null;
/**
* The username of the current user.
*
@@ -78,6 +101,15 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe
*/
$scope.preferences = preferenceService.preferences;
/**
* 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 +229,33 @@ 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'
},
// Reload the user on successful save in case any attributes changed
actions : [ ACKNOWLEDGE_ACTION_RELOAD ]
}),
guacNotification.SHOW_REQUEST_ERROR);
};
// Fetch the user record
userService.getUser(dataSource, username).then(function saveUserData(user) {
$scope.user = user;
})
// Fetch all user preference attribute forms defined
schemaService.getUserPreferenceAttributes(dataSource).then(function saveAttributes(attributes) {
$scope.attributes = attributes;
});
}]
};
}]);

View File

@@ -89,4 +89,13 @@
</div>
</div>
<!-- User attributes section -->
<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>

View File

@@ -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 settings saved.",
"NAME_INPUT_METHOD_NONE" : "@:CLIENT.NAME_INPUT_METHOD_NONE",
"NAME_INPUT_METHOD_OSK" : "@:CLIENT.NAME_INPUT_METHOD_OSK",

View File

@@ -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> getUserPreferenceAttributes()
throws GuacamoleException {
// Retrieve all possible user preference attributes
return userContext.getUserPreferenceAttributes();
}
/**
* Retrieves the possible attributes of a user group object.
*

View File

@@ -59,9 +59,22 @@ public class UserObjectTranslator
public void filterExternalObject(UserContext userContext, APIUser object)
throws GuacamoleException {
// Filter object attributes by defined schema
object.setAttributes(filterAttributes(userContext.getUserAttributes(),
object.getAttributes()));
// If a user is editing themselves ...
if (object.getUsername().equals(userContext.self().getIdentifier())) {
// ... they may only edit preference attributes
object.setAttributes(filterAttributes(userContext.getUserPreferenceAttributes(),
object.getAttributes()));
}
else {
// In all other cases, filter object attributes by defined schema
object.setAttributes(filterAttributes(userContext.getUserAttributes(),
object.getAttributes()));
}
}

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.rest.user;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -145,9 +146,15 @@ 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.");
// A user may not use this endpoint to update their password
User currentUser = userContext.self();
if (
currentUser.getIdentifier().equals(modifiedObject.getUsername())
&& modifiedObject.getPassword() != null) {
throw new GuacamoleSecurityException(
"Permission denied. The password update endpoint must"
+ " be used to change the current user's password.");
}
super.updateObject(modifiedObject);