From 405448116f657203f9f2a1836c23c28a703f069e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 27 Aug 2015 23:00:34 -0700 Subject: [PATCH] GUAC-586: Clarify auth result and include data source. Consistently refer to usernames as "username", not "user IDs". --- .../net/basic/rest/auth/APIAuthToken.java | 69 --------- .../rest/auth/APIAuthenticationResult.java | 133 ++++++++++++++++++ .../net/basic/rest/auth/TokenRESTService.java | 22 ++- .../app/auth/service/authenticationService.js | 65 +++++++-- .../controllers/manageConnectionController.js | 2 +- .../manageConnectionGroupController.js | 2 +- .../controllers/manageUserController.js | 2 +- .../app/navigation/directives/guacUserMenu.js | 2 +- .../navigation/services/userPageService.js | 6 +- .../directives/guacSettingsConnections.js | 4 +- .../directives/guacSettingsPreferences.js | 2 +- .../directives/guacSettingsSessions.js | 2 +- .../settings/directives/guacSettingsUsers.js | 6 +- 13 files changed, 218 insertions(+), 99 deletions(-) delete mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthenticationResult.java diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java deleted file mode 100644 index 5d71c9edb..000000000 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2014 Glyptodon LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package org.glyptodon.guacamole.net.basic.rest.auth; - -/** - * A simple object to represent an auth token/userID pair in the API. - * - * @author James Muehlner - */ -public class APIAuthToken { - - /** - * The auth token. - */ - private final String authToken; - - - /** - * The user ID. - */ - private final String userID; - - /** - * Get the auth token. - * @return The auth token. - */ - public String getAuthToken() { - return authToken; - } - - /** - * Get the user ID. - * @return The user ID. - */ - public String getUserID() { - return userID; - } - - /** - * Create a new APIAuthToken Object with the given auth token. - * - * @param authToken The auth token to create the new APIAuthToken with. - * @param userID The ID of the user owning the given token. - */ - public APIAuthToken(String authToken, String userID) { - this.authToken = authToken; - this.userID = userID; - } -} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthenticationResult.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthenticationResult.java new file mode 100644 index 000000000..ab8e50d8d --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthenticationResult.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.basic.rest.auth; + +import java.util.Collections; +import java.util.List; + +/** + * A simple object which describes the result of an authentication operation, + * including the resulting token. + * + * @author James Muehlner + * @author Michael Jumper + */ +public class APIAuthenticationResult { + + /** + * The unique token generated for the user that authenticated. + */ + private final String authToken; + + /** + * The username of the user that authenticated. + */ + private final String username; + + /** + * The unique identifier of the data source from which this user account + * came. Although this user account may exist across several data sources + * (AuthenticationProviders), this will be the unique identifier of the + * AuthenticationProvider that authenticated this user for the current + * session. + */ + private final String dataSource; + + /** + * The identifiers of all data sources available to this user. + */ + private final List availableDataSources; + + /** + * Returns the unique authentication token which identifies the current + * session. + * + * @return + * The user's authentication token. + */ + public String getAuthToken() { + return authToken; + } + + /** + * Returns the user identified by the authentication token associated with + * the current session. + * + * @return + * The user identified by this authentication token. + */ + public String getUsername() { + return username; + } + + /** + * Returns the unique identifier of the data source associated with the user + * account associated with the current session. + * + * @return + * The unique identifier of the data source associated with the user + * account associated with the current session. + */ + public String getDataSource() { + return dataSource; + } + + /** + * Returns the identifiers of all data sources available to the user + * associated with the current session. + * + * @return + * The identifiers of all data sources available to the user associated + * with the current session. + */ + public List getAvailableDataSources() { + return availableDataSources; + } + + /** + * Create a new APIAuthenticationResult object containing the given data. + * + * @param authToken + * The unique token generated for the user that authenticated, to be + * used for the duration of their session. + * + * @param username + * The username of the user owning the given token. + * + * @param dataSource + * The unique identifier of the AuthenticationProvider which + * authenticated the user. + * + * @param availableDataSources + * The unique identifier of all AuthenticationProviders to which the + * user now has access. + */ + public APIAuthenticationResult(String authToken, String username, + String dataSource, List availableDataSources) { + this.authToken = authToken; + this.username = username; + this.dataSource = dataSource; + this.availableDataSources = Collections.unmodifiableList(availableDataSources); + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java index 3e2432ad2..998cd5e44 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java @@ -456,12 +456,16 @@ public class TokenRESTService { * map, even if those parameters are no longer accessible within the * now-fully-consumed HTTP request. * - * @return The auth token for the newly logged-in user. - * @throws GuacamoleException If an error prevents successful login. + * @return + * An authentication response object containing the possible-new auth + * token, as well as other related data. + * + * @throws GuacamoleException + * If an error prevents successful authentication. */ @POST @AuthProviderRESTExposure - public APIAuthToken createToken(@FormParam("username") String username, + public APIAuthenticationResult createToken(@FormParam("username") String username, @FormParam("password") String password, @FormParam("token") String token, @Context HttpServletRequest consumedRequest, @@ -500,8 +504,18 @@ public class TokenRESTService { logger.debug("Login was successful for user \"{}\".", authenticatedUser.getIdentifier()); } + // Build list of all available auth providers + List authProviderIdentifiers = new ArrayList(userContexts.size()); + for (UserContext userContext : userContexts) + authProviderIdentifiers.add(userContext.getAuthenticationProvider().getIdentifier()); + // Return possibly-new auth token - return new APIAuthToken(authToken, authenticatedUser.getIdentifier()); + return new APIAuthenticationResult( + authToken, + authenticatedUser.getIdentifier(), + authenticatedUser.getAuthenticationProvider().getIdentifier(), + authProviderIdentifiers + ); } diff --git a/guacamole/src/main/webapp/app/auth/service/authenticationService.js b/guacamole/src/main/webapp/app/auth/service/authenticationService.js index cece87b17..8c17cbea2 100644 --- a/guacamole/src/main/webapp/app/auth/service/authenticationService.js +++ b/guacamole/src/main/webapp/app/auth/service/authenticationService.js @@ -57,7 +57,7 @@ angular.module('auth').factory('authenticationService', ['$injector', /** * The unique identifier of the local cookie which stores the user's - * current authentication token and user ID. + * current authentication token and username. * * @type String */ @@ -68,7 +68,7 @@ angular.module('auth').factory('authenticationService', ['$injector', * and given arbitrary parameters, returning a promise that succeeds only * if the authentication operation was successful. The resulting * authentication data can be retrieved later via getCurrentToken() or - * getCurrentUserID(). + * getCurrentUsername(). * * The provided parameters can be virtually any object, as each property * will be sent as an HTTP parameter in the authentication request. @@ -99,8 +99,9 @@ angular.module('auth').factory('authenticationService', ['$injector', // Store auth data $cookieStore.put(AUTH_COOKIE_ID, { - authToken : data.authToken, - userID : data.userID + authToken : data.authToken, + username : data.username, + dataSource : data.dataSource }); // Process is complete @@ -174,7 +175,7 @@ angular.module('auth').factory('authenticationService', ['$injector', * its properties will be included as parameters in the update request. * This function returns a promise that succeeds only if the authentication * operation was successful. The resulting authentication data can be - * retrieved later via getCurrentToken() or getCurrentUserID(). + * retrieved later via getCurrentToken() or getCurrentUsername(). * * If there is no current auth token, this function behaves identically to * authenticate(), and makes a general authentication request. @@ -209,7 +210,7 @@ angular.module('auth').factory('authenticationService', ['$injector', * with a username and password, ignoring any currently-stored token, * returning a promise that succeeds only if the login operation was * successful. The resulting authentication data can be retrieved later - * via getCurrentToken() or getCurrentUserID(). + * via getCurrentToken() or getCurrentUsername(). * * @param {String} username * The username to log in with. @@ -254,19 +255,19 @@ angular.module('auth').factory('authenticationService', ['$injector', }; /** - * Returns the user ID of the current user. If the current user is not - * logged in, this ID may not be valid. + * Returns the username of the current user. If the current user is not + * logged in, this value may not be valid. * * @returns {String} - * The user ID of the current user, or null if no authentication data + * The username of the current user, or null if no authentication data * is present. */ - service.getCurrentUserID = function getCurrentUserID() { + service.getCurrentUsername = function getCurrentUsername() { - // Return user ID, if available + // Return username, if available var authData = $cookieStore.get(AUTH_COOKIE_ID); if (authData) - return authData.userID; + return authData.username; // No auth data present return null; @@ -293,5 +294,45 @@ angular.module('auth').factory('authenticationService', ['$injector', }; + /** + * Returns the identifier of the data source that authenticated the current + * user. If the current user is not logged in, this value may not be valid. + * + * @returns {String} + * The identifier of the data source that authenticated the current + * user, or null if no authentication data is present. + */ + service.getDataSource = function getDataSource() { + + // Return data source, if available + var authData = $cookieStore.get(AUTH_COOKIE_ID); + if (authData) + return authData.dataSource; + + // No auth data present + return null; + + }; + + /** + * Returns the identifiers of all data sources available to the current + * user. If the current user is not logged in, this value may not be valid. + * + * @returns {String[]} + * The identifiers of all data sources availble to the current user, + * or null if no authentication data is present. + */ + service.getAvailableDataSources = function getAvailableDataSources() { + + // Return data sources, if available + var authData = $cookieStore.get(AUTH_COOKIE_ID); + if (authData) + return authData.availableDataSources; + + // No auth data present + return null; + + }; + return service; }]); diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js index 04694f2dd..04e1066d4 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js @@ -190,7 +190,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i }); // Query the user's permissions for the current connection - permissionService.getPermissions(authenticationService.getCurrentUserID()) + permissionService.getPermissions(authenticationService.getCurrentUsername()) .success(function permissionsReceived(permissions) { $scope.permissions = permissions; diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js index 02119c4d0..64fb03b08 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js @@ -128,7 +128,7 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' }); // Query the user's permissions for the current connection group - permissionService.getPermissions(authenticationService.getCurrentUserID()) + permissionService.getPermissions(authenticationService.getCurrentUsername()) .success(function permissionsReceived(permissions) { $scope.permissions = permissions; diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js index e1e89e82c..00c09c5e1 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js @@ -153,7 +153,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto }); // Query the user's permissions for the current connection - permissionService.getPermissions(authenticationService.getCurrentUserID()) + permissionService.getPermissions(authenticationService.getCurrentUsername()) .success(function permissionsReceived(permissions) { $scope.permissions = permissions; diff --git a/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js index 4efe62cf0..6cc80c885 100644 --- a/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js +++ b/guacamole/src/main/webapp/app/navigation/directives/guacUserMenu.js @@ -78,7 +78,7 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu() * * @type String */ - $scope.username = authenticationService.getCurrentUserID(); + $scope.username = authenticationService.getCurrentUsername(); /** * The available main pages for the current user. diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js index 86453c576..9489581b0 100644 --- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js +++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js @@ -156,7 +156,7 @@ angular.module('navigation').factory('userPageService', ['$injector', PermissionSet.removeConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, ConnectionGroup.ROOT_IDENTIFIER); // Ignore permission to update self - PermissionSet.removeUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, authenticationService.getCurrentUserID()); + PermissionSet.removeUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, authenticationService.getCurrentUsername()); // Determine whether the current user needs access to the user management UI var canManageUsers = @@ -247,7 +247,7 @@ angular.module('navigation').factory('userPageService', ['$injector', // Retrieve current permissions, resolving main pages if possible // Resolve promise using settings pages derived from permissions - permissionService.getPermissions(authenticationService.getCurrentUserID()) + permissionService.getPermissions(authenticationService.getCurrentUsername()) .success(function permissionsRetrieved(permissions) { deferred.resolve(generateSettingsPages(permissions)); }); @@ -328,7 +328,7 @@ angular.module('navigation').factory('userPageService', ['$injector', }); // Retrieve current permissions, resolving main pages if possible - permissionService.getPermissions(authenticationService.getCurrentUserID()) + permissionService.getPermissions(authenticationService.getCurrentUsername()) .success(function permissionsRetrieved(retrievedPermissions) { permissions = retrievedPermissions; resolveMainPages(); diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js index a900aed11..e1b77893f 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js @@ -48,7 +48,7 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe var permissionService = $injector.get('permissionService'); // Identifier of the current user - var currentUserID = authenticationService.getCurrentUserID(); + var currentUsername = authenticationService.getCurrentUsername(); /** * An action to be provided along with the object sent to @@ -120,7 +120,7 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe }; // Retrieve current permissions - permissionService.getPermissions(currentUserID) + permissionService.getPermissions(currentUsername) .success(function permissionsRetrieved(permissions) { $scope.permissions = permissions; diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js index 482529a37..30a52e265 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js @@ -64,7 +64,7 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe * * @type String */ - var username = authenticationService.getCurrentUserID(); + var username = authenticationService.getCurrentUsername(); /** * All currently-set preferences, or their defaults if not yet set. diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsSessions.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsSessions.js index e478fbd69..3635d38cb 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsSessions.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsSessions.js @@ -187,7 +187,7 @@ angular.module('settings').directive('guacSettingsSessions', [function guacSetti }; // Query the user's permissions - permissionService.getPermissions(authenticationService.getCurrentUserID()) + permissionService.getPermissions(authenticationService.getCurrentUsername()) .success(function permissionsReceived(retrievedPermissions) { $scope.permissions = retrievedPermissions; }); diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js index 20c877b46..1d0f411fb 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js @@ -48,7 +48,7 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings var userService = $injector.get('userService'); // Identifier of the current user - var currentUserID = authenticationService.getCurrentUserID(); + var currentUsername = authenticationService.getCurrentUsername(); /** * An action to be provided along with the object sent to @@ -118,7 +118,7 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings }; // Retrieve current permissions - permissionService.getPermissions(currentUserID) + permissionService.getPermissions(currentUsername) .success(function permissionsRetrieved(permissions) { $scope.permissions = permissions; @@ -147,7 +147,7 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings // Display only other users, not self $scope.users = users.filter(function isNotSelf(user) { - return user.username !== currentUserID; + return user.username !== currentUsername; }); });