GUAC-586: Clarify auth result and include data source. Consistently refer to usernames as "username", not "user IDs".

This commit is contained in:
Michael Jumper
2015-08-27 23:00:34 -07:00
parent b0ac5d22ff
commit 405448116f
13 changed files with 218 additions and 99 deletions

View File

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

View File

@@ -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<String> 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<String> 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<String> availableDataSources) {
this.authToken = authToken;
this.username = username;
this.dataSource = dataSource;
this.availableDataSources = Collections.unmodifiableList(availableDataSources);
}
}

View File

@@ -456,12 +456,16 @@ public class TokenRESTService {
* map, even if those parameters are no longer accessible within the * map, even if those parameters are no longer accessible within the
* now-fully-consumed HTTP request. * now-fully-consumed HTTP request.
* *
* @return The auth token for the newly logged-in user. * @return
* @throws GuacamoleException If an error prevents successful login. * 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 @POST
@AuthProviderRESTExposure @AuthProviderRESTExposure
public APIAuthToken createToken(@FormParam("username") String username, public APIAuthenticationResult createToken(@FormParam("username") String username,
@FormParam("password") String password, @FormParam("password") String password,
@FormParam("token") String token, @FormParam("token") String token,
@Context HttpServletRequest consumedRequest, @Context HttpServletRequest consumedRequest,
@@ -500,8 +504,18 @@ public class TokenRESTService {
logger.debug("Login was successful for user \"{}\".", authenticatedUser.getIdentifier()); logger.debug("Login was successful for user \"{}\".", authenticatedUser.getIdentifier());
} }
// Build list of all available auth providers
List<String> authProviderIdentifiers = new ArrayList<String>(userContexts.size());
for (UserContext userContext : userContexts)
authProviderIdentifiers.add(userContext.getAuthenticationProvider().getIdentifier());
// Return possibly-new auth token // Return possibly-new auth token
return new APIAuthToken(authToken, authenticatedUser.getIdentifier()); return new APIAuthenticationResult(
authToken,
authenticatedUser.getIdentifier(),
authenticatedUser.getAuthenticationProvider().getIdentifier(),
authProviderIdentifiers
);
} }

View File

@@ -57,7 +57,7 @@ angular.module('auth').factory('authenticationService', ['$injector',
/** /**
* The unique identifier of the local cookie which stores the user's * 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 * @type String
*/ */
@@ -68,7 +68,7 @@ angular.module('auth').factory('authenticationService', ['$injector',
* and given arbitrary parameters, returning a promise that succeeds only * and given arbitrary parameters, returning a promise that succeeds only
* if the authentication operation was successful. The resulting * if the authentication operation was successful. The resulting
* authentication data can be retrieved later via getCurrentToken() or * authentication data can be retrieved later via getCurrentToken() or
* getCurrentUserID(). * getCurrentUsername().
* *
* The provided parameters can be virtually any object, as each property * The provided parameters can be virtually any object, as each property
* will be sent as an HTTP parameter in the authentication request. * will be sent as an HTTP parameter in the authentication request.
@@ -100,7 +100,8 @@ angular.module('auth').factory('authenticationService', ['$injector',
// Store auth data // Store auth data
$cookieStore.put(AUTH_COOKIE_ID, { $cookieStore.put(AUTH_COOKIE_ID, {
authToken : data.authToken, authToken : data.authToken,
userID : data.userID username : data.username,
dataSource : data.dataSource
}); });
// Process is complete // Process is complete
@@ -174,7 +175,7 @@ angular.module('auth').factory('authenticationService', ['$injector',
* its properties will be included as parameters in the update request. * its properties will be included as parameters in the update request.
* This function returns a promise that succeeds only if the authentication * This function returns a promise that succeeds only if the authentication
* operation was successful. The resulting authentication data can be * 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 * If there is no current auth token, this function behaves identically to
* authenticate(), and makes a general authentication request. * 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, * with a username and password, ignoring any currently-stored token,
* returning a promise that succeeds only if the login operation was * returning a promise that succeeds only if the login operation was
* successful. The resulting authentication data can be retrieved later * successful. The resulting authentication data can be retrieved later
* via getCurrentToken() or getCurrentUserID(). * via getCurrentToken() or getCurrentUsername().
* *
* @param {String} username * @param {String} username
* The username to log in with. * 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 * Returns the username of the current user. If the current user is not
* logged in, this ID may not be valid. * logged in, this value may not be valid.
* *
* @returns {String} * @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. * 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); var authData = $cookieStore.get(AUTH_COOKIE_ID);
if (authData) if (authData)
return authData.userID; return authData.username;
// No auth data present // No auth data present
return null; 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; return service;
}]); }]);

View File

@@ -190,7 +190,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
}); });
// Query the user's permissions for the current connection // Query the user's permissions for the current connection
permissionService.getPermissions(authenticationService.getCurrentUserID()) permissionService.getPermissions(authenticationService.getCurrentUsername())
.success(function permissionsReceived(permissions) { .success(function permissionsReceived(permissions) {
$scope.permissions = permissions; $scope.permissions = permissions;

View File

@@ -128,7 +128,7 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
}); });
// Query the user's permissions for the current connection group // Query the user's permissions for the current connection group
permissionService.getPermissions(authenticationService.getCurrentUserID()) permissionService.getPermissions(authenticationService.getCurrentUsername())
.success(function permissionsReceived(permissions) { .success(function permissionsReceived(permissions) {
$scope.permissions = permissions; $scope.permissions = permissions;

View File

@@ -153,7 +153,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
}); });
// Query the user's permissions for the current connection // Query the user's permissions for the current connection
permissionService.getPermissions(authenticationService.getCurrentUserID()) permissionService.getPermissions(authenticationService.getCurrentUsername())
.success(function permissionsReceived(permissions) { .success(function permissionsReceived(permissions) {
$scope.permissions = permissions; $scope.permissions = permissions;

View File

@@ -78,7 +78,7 @@ angular.module('navigation').directive('guacUserMenu', [function guacUserMenu()
* *
* @type String * @type String
*/ */
$scope.username = authenticationService.getCurrentUserID(); $scope.username = authenticationService.getCurrentUsername();
/** /**
* The available main pages for the current user. * The available main pages for the current user.

View File

@@ -156,7 +156,7 @@ angular.module('navigation').factory('userPageService', ['$injector',
PermissionSet.removeConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, ConnectionGroup.ROOT_IDENTIFIER); PermissionSet.removeConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, ConnectionGroup.ROOT_IDENTIFIER);
// Ignore permission to update self // 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 // Determine whether the current user needs access to the user management UI
var canManageUsers = var canManageUsers =
@@ -247,7 +247,7 @@ angular.module('navigation').factory('userPageService', ['$injector',
// Retrieve current permissions, resolving main pages if possible // Retrieve current permissions, resolving main pages if possible
// Resolve promise using settings pages derived from permissions // Resolve promise using settings pages derived from permissions
permissionService.getPermissions(authenticationService.getCurrentUserID()) permissionService.getPermissions(authenticationService.getCurrentUsername())
.success(function permissionsRetrieved(permissions) { .success(function permissionsRetrieved(permissions) {
deferred.resolve(generateSettingsPages(permissions)); deferred.resolve(generateSettingsPages(permissions));
}); });
@@ -328,7 +328,7 @@ angular.module('navigation').factory('userPageService', ['$injector',
}); });
// Retrieve current permissions, resolving main pages if possible // Retrieve current permissions, resolving main pages if possible
permissionService.getPermissions(authenticationService.getCurrentUserID()) permissionService.getPermissions(authenticationService.getCurrentUsername())
.success(function permissionsRetrieved(retrievedPermissions) { .success(function permissionsRetrieved(retrievedPermissions) {
permissions = retrievedPermissions; permissions = retrievedPermissions;
resolveMainPages(); resolveMainPages();

View File

@@ -48,7 +48,7 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe
var permissionService = $injector.get('permissionService'); var permissionService = $injector.get('permissionService');
// Identifier of the current user // Identifier of the current user
var currentUserID = authenticationService.getCurrentUserID(); var currentUsername = authenticationService.getCurrentUsername();
/** /**
* An action to be provided along with the object sent to * 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 // Retrieve current permissions
permissionService.getPermissions(currentUserID) permissionService.getPermissions(currentUsername)
.success(function permissionsRetrieved(permissions) { .success(function permissionsRetrieved(permissions) {
$scope.permissions = permissions; $scope.permissions = permissions;

View File

@@ -64,7 +64,7 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe
* *
* @type String * @type String
*/ */
var username = authenticationService.getCurrentUserID(); var username = authenticationService.getCurrentUsername();
/** /**
* All currently-set preferences, or their defaults if not yet set. * All currently-set preferences, or their defaults if not yet set.

View File

@@ -187,7 +187,7 @@ angular.module('settings').directive('guacSettingsSessions', [function guacSetti
}; };
// Query the user's permissions // Query the user's permissions
permissionService.getPermissions(authenticationService.getCurrentUserID()) permissionService.getPermissions(authenticationService.getCurrentUsername())
.success(function permissionsReceived(retrievedPermissions) { .success(function permissionsReceived(retrievedPermissions) {
$scope.permissions = retrievedPermissions; $scope.permissions = retrievedPermissions;
}); });

View File

@@ -48,7 +48,7 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings
var userService = $injector.get('userService'); var userService = $injector.get('userService');
// Identifier of the current user // Identifier of the current user
var currentUserID = authenticationService.getCurrentUserID(); var currentUsername = authenticationService.getCurrentUsername();
/** /**
* An action to be provided along with the object sent to * 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 // Retrieve current permissions
permissionService.getPermissions(currentUserID) permissionService.getPermissions(currentUsername)
.success(function permissionsRetrieved(permissions) { .success(function permissionsRetrieved(permissions) {
$scope.permissions = permissions; $scope.permissions = permissions;
@@ -147,7 +147,7 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings
// Display only other users, not self // Display only other users, not self
$scope.users = users.filter(function isNotSelf(user) { $scope.users = users.filter(function isNotSelf(user) {
return user.username !== currentUserID; return user.username !== currentUsername;
}); });
}); });