From 90ae5b0e17a21842d1e818d6da78f6003bd07cab Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 20 Aug 2015 17:31:24 -0700 Subject: [PATCH] GUAC-586: Separate authentication from authorization. --- .../net/auth/AbstractAuthenticatedUser.java | 73 +++++++ .../guacamole/net/auth/AuthenticatedUser.java | 51 +++++ .../net/auth/AuthenticationProvider.java | 122 +++++++++--- .../simple/SimpleAuthenticationProvider.java | 185 ++++++++++++++++-- .../guacamole/net/basic/GuacamoleSession.java | 43 ++-- .../AuthenticationProviderFacade.java | 44 ++++- .../net/basic/rest/auth/TokenRESTService.java | 43 ++-- .../net/basic/rest/user/UserRESTService.java | 13 +- 8 files changed, 487 insertions(+), 87 deletions(-) create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractAuthenticatedUser.java create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AuthenticatedUser.java diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractAuthenticatedUser.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractAuthenticatedUser.java new file mode 100644 index 000000000..93256d3cf --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractAuthenticatedUser.java @@ -0,0 +1,73 @@ +/* + * 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.auth; + + +/** + * Basic implementation of an AuthenticatedUser which uses the username to + * determine equality. Username comparison is case-sensitive. + * + * @author Michael Jumper + */ +public abstract class AbstractAuthenticatedUser implements AuthenticatedUser { + + /** + * The name of this user. + */ + private String username; + + @Override + public String getIdentifier() { + return username; + } + + @Override + public void setIdentifier(String username) { + this.username = username; + } + + @Override + public int hashCode() { + if (username == null) return 0; + return username.hashCode(); + } + + @Override + public boolean equals(Object obj) { + + // Not equal if null or not a User + if (obj == null) return false; + if (!(obj instanceof AbstractAuthenticatedUser)) return false; + + // Get username + String objUsername = ((AbstractAuthenticatedUser) obj).username; + + // If null, equal only if this username is null + if (objUsername == null) return username == null; + + // Otherwise, equal only if strings are identical + return objUsername.equals(username); + + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AuthenticatedUser.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AuthenticatedUser.java new file mode 100644 index 000000000..1073d47d3 --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AuthenticatedUser.java @@ -0,0 +1,51 @@ +/* + * 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.auth; + + +/** + * A user of the Guacamole web application who has been authenticated by an + * AuthenticationProvider. + * + * @author Michael Jumper + */ +public interface AuthenticatedUser extends Identifiable { + + /** + * Returns the AuthenticationProvider that authenticated this user. + * + * @return + * The AuthenticationProvider that authenticated this user. + */ + AuthenticationProvider getAuthenticationProvider(); + + /** + * Returns the credentials that the user provided when they successfully + * authenticated. + * + * @return + * The credentials provided by the user when they authenticated. + */ + Credentials getCredentials(); + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AuthenticationProvider.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AuthenticationProvider.java index d843fd232..e269179ec 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AuthenticationProvider.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AuthenticationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Glyptodon LLC + * 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 @@ -25,46 +25,112 @@ package org.glyptodon.guacamole.net.auth; import org.glyptodon.guacamole.GuacamoleException; /** - * Provides means of accessing and managing the available - * GuacamoleConfiguration objects and User objects. Access to each configuration - * and each user is limited by a given Credentials object. + * Provides means of authorizing users and for accessing and managing data + * associated with those users. Access to such data is limited according to the + * AuthenticationProvider implementation. * * @author Michael Jumper */ public interface AuthenticationProvider { /** - * Returns the UserContext of the user authorized by the given credentials. + * Returns an AuthenticatedUser representing the user authenticated by the + * given credentials, if any. * - * @param credentials The credentials to use to retrieve the environment. - * @return The UserContext of the user authorized by the given credentials, - * or null if the credentials are not authorized. + * @param credentials + * The credentials to use for authentication. * - * @throws GuacamoleException If an error occurs while creating the - * UserContext. + * @return + * An AuthenticatedUser representing the user authenticated by the + * given credentials, if any, or null if the credentials are invalid. + * + * @throws GuacamoleException + * If an error occurs while authenticating the user, or if access is + * temporarily, permanently, or conditionally denied, such as if the + * supplied credentials are insufficient or invalid. */ - UserContext getUserContext(Credentials credentials) + AuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException; /** - * Returns a new or updated UserContext for the user authorized by the - * give credentials and having the given existing UserContext. Note that - * because this function will be called for all future requests after - * initial authentication, including tunnel requests, care must be taken - * to avoid using functions of HttpServletRequest which invalidate the - * entire request body, such as getParameter(). - * - * @param context The existing UserContext belonging to the user in - * question. - * @param credentials The credentials to use to retrieve or update the - * environment. - * @return The updated UserContext, which need not be the same as the - * UserContext given, or null if the user is no longer authorized. - * - * @throws GuacamoleException If an error occurs while updating the - * UserContext. + * Returns a new or updated AuthenticatedUser for the given credentials + * already having produced the given AuthenticatedUser. Note that because + * this function will be called for all future requests after initial + * authentication, including tunnel requests, care must be taken to avoid + * using functions of HttpServletRequest which invalidate the entire request + * body, such as getParameter(). Doing otherwise may cause the + * GuacamoleHTTPTunnelServlet to fail. + * + * @param credentials + * The credentials to use for authentication. + * + * @param authenticatedUser + * An AuthenticatedUser object representing the user authenticated by + * an arbitrary set of credentials. The AuthenticatedUser may come from + * this AuthenticationProvider or any other installed + * AuthenticationProvider. + * + * @return + * An updated AuthenticatedUser representing the user authenticated by + * the given credentials, if any, or null if the credentials are + * invalid. + * + * @throws GuacamoleException + * If an error occurs while updating the AuthenticatedUser. */ - UserContext updateUserContext(UserContext context, Credentials credentials) + AuthenticatedUser updateAuthenticatedUser(AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException; + + /** + * Returns the UserContext of the user authenticated by the given + * credentials. + * + * @param authenticatedUser + * An AuthenticatedUser object representing the user authenticated by + * an arbitrary set of credentials. The AuthenticatedUser may come from + * this AuthenticationProvider or any other installed + * AuthenticationProvider. + * + * @return + * A UserContext describing the permissions, connection, connection + * groups, etc. accessible or associated with the given authenticated + * user, or null if this AuthenticationProvider refuses to provide any + * such data. + * + * @throws GuacamoleException + * If an error occurs while creating the UserContext. + */ + UserContext getUserContext(AuthenticatedUser authenticatedUser) throws GuacamoleException; + + /** + * Returns a new or updated UserContext for the given AuthenticatedUser + * already having the given UserContext. Note that because this function + * will be called for all future requests after initial authentication, + * including tunnel requests, care must be taken to avoid using functions + * of HttpServletRequest which invalidate the entire request body, such as + * getParameter(). Doing otherwise may cause the GuacamoleHTTPTunnelServlet + * to fail. + * + * @param context + * The existing UserContext belonging to the user in question. + * + * @param authenticatedUser + * An AuthenticatedUser object representing the user authenticated by + * an arbitrary set of credentials. The AuthenticatedUser may come from + * this AuthenticationProvider or any other installed + * AuthenticationProvider. + * + * @return + * An updated UserContext describing the permissions, connection, + * connection groups, etc. accessible or associated with the given + * authenticated user, or null if this AuthenticationProvider refuses + * to provide any such data. + * + * @throws GuacamoleException + * If an error occurs while updating the UserContext. + */ + UserContext updateUserContext(UserContext context, + AuthenticatedUser authenticatedUser) throws GuacamoleException; } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleAuthenticationProvider.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleAuthenticationProvider.java index f48ffaaba..83571db75 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleAuthenticationProvider.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleAuthenticationProvider.java @@ -23,8 +23,11 @@ package org.glyptodon.guacamole.net.auth.simple; import java.util.Map; +import java.util.UUID; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.AbstractAuthenticatedUser; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; +import org.glyptodon.guacamole.net.auth.AuthenticatedUser; import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; @@ -62,12 +65,100 @@ public abstract class SimpleAuthenticationProvider getAuthorizedConfigurations(Credentials credentials) throws GuacamoleException; - @Override - public UserContext getUserContext(Credentials credentials) - throws GuacamoleException { + /** + * AuthenticatedUser which contains its own predefined set of authorized + * configurations. + * + * @author Michael Jumper + */ + private class SimpleAuthenticatedUser extends AbstractAuthenticatedUser { - // Get username, if any - String username = credentials.getUsername(); + /** + * The credentials provided when this AuthenticatedUser was + * authenticated. + */ + private final Credentials credentials; + + /** + * The GuacamoleConfigurations that this AuthenticatedUser is + * authorized to use. + */ + private final Map configs; + + /** + * Creates a new SimpleAuthenticatedUser associated with the given + * credentials and having access to the given Map of + * GuacamoleConfigurations. + * + * @param credentials + * The credentials provided by the user when they authenticated. + * + * @param configs + * A Map of all GuacamoleConfigurations for which this user has + * access. The keys of this Map are Strings which uniquely identify + * each configuration. + */ + public SimpleAuthenticatedUser(Credentials credentials, Map configs) { + + // Store credentials and configurations + this.credentials = credentials; + this.configs = configs; + + // Pull username from credentials if it exists + String username = credentials.getUsername(); + if (username != null && !username.isEmpty()) + setIdentifier(username); + + // Otherwise generate a random username + else + setIdentifier(UUID.randomUUID().toString()); + + } + + /** + * Returns a Map containing all GuacamoleConfigurations that this user + * is authorized to use. The keys of this Map are Strings which + * uniquely identify each configuration. + * + * @return + * A Map of all configurations for which this user is authorized. + */ + public Map getAuthorizedConfigurations() { + return configs; + } + + @Override + public AuthenticationProvider getAuthenticationProvider() { + return SimpleAuthenticationProvider.this; + } + + @Override + public Credentials getCredentials() { + return credentials; + } + + } + + /** + * Given an arbitrary credentials object, returns a Map containing all + * configurations authorized by those credentials, filtering those + * configurations using a TokenFilter and the standard credential tokens + * (like ${GUAC_USERNAME} and ${GUAC_PASSWORD}). The keys of this Map + * are Strings which uniquely identify each configuration. + * + * @param credentials + * The credentials to use to retrieve authorized configurations. + * + * @return + * A Map of all configurations authorized by the given credentials, or + * null if the credentials given are not authorized. + * + * @throws GuacamoleException + * If an error occurs while retrieving configurations. + */ + private Map + getFilteredAuthorizedConfigurations(Credentials credentials) + throws GuacamoleException { // Get configurations Map configs = @@ -80,24 +171,90 @@ public abstract class SimpleAuthenticationProvider // Build credential TokenFilter TokenFilter tokenFilter = new TokenFilter(); StandardTokens.addStandardTokens(tokenFilter, credentials); - + // Filter each configuration for (GuacamoleConfiguration config : configs.values()) tokenFilter.filterValues(config.getParameters()); - - // Return user context restricted to authorized configs - if (username != null && !username.isEmpty()) - return new SimpleUserContext(username, configs); - // If there is no associated username, let SimpleUserContext generate one - else - return new SimpleUserContext(configs); + return configs; + + } + + /** + * Given a user who has already been authenticated, returns a Map + * containing all configurations for which that user is authorized, + * filtering those configurations using a TokenFilter and the standard + * credential tokens (like ${GUAC_USERNAME} and ${GUAC_PASSWORD}). The keys + * of this Map are Strings which uniquely identify each configuration. + * + * @param authenticatedUser + * The user whose authorized configurations are to be retrieved. + * + * @return + * A Map of all configurations authorized for use by the given user, or + * null if the user is not authorized to use any configurations. + * + * @throws GuacamoleException + * If an error occurs while retrieving configurations. + */ + private Map + getFilteredAuthorizedConfigurations(AuthenticatedUser authenticatedUser) + throws GuacamoleException { + + // Pull cached configurations, if any + if (authenticatedUser instanceof SimpleAuthenticatedUser) + return ((SimpleAuthenticatedUser) authenticatedUser).getAuthorizedConfigurations(); + + // Otherwise, pull using credentials + return getFilteredAuthorizedConfigurations(authenticatedUser.getCredentials()); + + } + + @Override + public AuthenticatedUser authenticateUser(final Credentials credentials) + throws GuacamoleException { + + // Get configurations + Map configs = + getFilteredAuthorizedConfigurations(credentials); + + // Return as unauthorized if not authorized to retrieve configs + if (configs == null) + return null; + + return new SimpleAuthenticatedUser(credentials, configs); + + } + + @Override + public UserContext getUserContext(AuthenticatedUser authenticatedUser) + throws GuacamoleException { + + // Get configurations + Map configs = + getFilteredAuthorizedConfigurations(authenticatedUser); + + // Return as unauthorized if not authorized to retrieve configs + if (configs == null) + return null; + + // Return user context restricted to authorized configs + return new SimpleUserContext(authenticatedUser.getIdentifier(), configs); + + } + + @Override + public AuthenticatedUser updateAuthenticatedUser(AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { + + // Simply return the given user, updating nothing + return authenticatedUser; } @Override public UserContext updateUserContext(UserContext context, - Credentials credentials) throws GuacamoleException { + AuthenticatedUser authorizedUser) throws GuacamoleException { // Simply return the given context, updating nothing return context; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleSession.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleSession.java index 89a83c94c..e57afe67c 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleSession.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleSession.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Glyptodon LLC + * 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 @@ -27,7 +27,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.net.GuacamoleTunnel; -import org.glyptodon.guacamole.net.auth.Credentials; +import org.glyptodon.guacamole.net.auth.AuthenticatedUser; import org.glyptodon.guacamole.net.auth.UserContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,9 +46,9 @@ public class GuacamoleSession { private static final Logger logger = LoggerFactory.getLogger(GuacamoleSession.class); /** - * The credentials provided when the user authenticated. + * The user associated with this session. */ - private Credentials credentials; + private AuthenticatedUser authenticatedUser; /** * The user context associated with this session. @@ -72,8 +72,8 @@ public class GuacamoleSession { * The environment of the Guacamole server associated with this new * session. * - * @param credentials - * The credentials provided by the user during login. + * @param authenticatedUser + * The authenticated user to associate this session with. * * @param userContext * The user context to associate this session with. @@ -81,36 +81,35 @@ public class GuacamoleSession { * @throws GuacamoleException * If an error prevents the session from being created. */ - public GuacamoleSession(Environment environment, Credentials credentials, - UserContext userContext) throws GuacamoleException { + public GuacamoleSession(Environment environment, + AuthenticatedUser authenticatedUser, UserContext userContext) + throws GuacamoleException { this.lastAccessedTime = System.currentTimeMillis(); - this.credentials = credentials; + this.authenticatedUser = authenticatedUser; this.userContext = userContext; } /** - * Returns the credentials used when the user associated with this session - * authenticated. + * Returns the authenticated user associated with this session. * * @return - * The credentials used when the user associated with this session - * authenticated. + * The authenticated user associated with this session. */ - public Credentials getCredentials() { - return credentials; + public AuthenticatedUser getAuthenticatedUser() { + return authenticatedUser; } /** - * Replaces the credentials associated with this session with the given - * credentials. + * Replaces the authenticated user associated with this session with the + * given authenticated user. * - * @param credentials - * The credentials to associate with this session. + * @param authenticatedUser + * The authenticated user to associated with this session. */ - public void setCredentials(Credentials credentials) { - this.credentials = credentials; + public void setAuthenticatedUser(AuthenticatedUser authenticatedUser) { + this.authenticatedUser = authenticatedUser; } - + /** * Returns the UserContext associated with this session. * diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/AuthenticationProviderFacade.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/AuthenticationProviderFacade.java index 8a5b0d42d..4db9abfd3 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/AuthenticationProviderFacade.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/AuthenticationProviderFacade.java @@ -24,6 +24,7 @@ package org.glyptodon.guacamole.net.basic.extension; import java.lang.reflect.InvocationTargetException; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.AuthenticatedUser; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.UserContext; @@ -118,7 +119,7 @@ public class AuthenticationProviderFacade implements AuthenticationProvider { } @Override - public UserContext getUserContext(Credentials credentials) + public AuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException { // Ignore auth attempts if no auth provider could be loaded @@ -128,13 +129,13 @@ public class AuthenticationProviderFacade implements AuthenticationProvider { } // Delegate to underlying auth provider - return authProvider.getUserContext(credentials); - + return authProvider.authenticateUser(credentials); + } @Override - public UserContext updateUserContext(UserContext context, Credentials credentials) - throws GuacamoleException { + public AuthenticatedUser updateAuthenticatedUser(AuthenticatedUser authenticatedUser, + Credentials credentials) throws GuacamoleException { // Ignore auth attempts if no auth provider could be loaded if (authProvider == null) { @@ -143,7 +144,38 @@ public class AuthenticationProviderFacade implements AuthenticationProvider { } // Delegate to underlying auth provider - return authProvider.updateUserContext(context, credentials); + return authProvider.updateAuthenticatedUser(authenticatedUser, credentials); + + } + + @Override + public UserContext getUserContext(AuthenticatedUser authenticatedUser) + throws GuacamoleException { + + // Ignore auth attempts if no auth provider could be loaded + if (authProvider == null) { + logger.warn("User data retrieval attempt denied because the authentication system could not be loaded. Please check for errors earlier in the logs."); + return null; + } + + // Delegate to underlying auth provider + return authProvider.getUserContext(authenticatedUser); + + } + + @Override + public UserContext updateUserContext(UserContext context, + AuthenticatedUser authenticatedUser) + throws GuacamoleException { + + // Ignore auth attempts if no auth provider could be loaded + if (authProvider == null) { + logger.warn("User data refresh attempt denied because the authentication system could not be loaded. Please check for errors earlier in the logs."); + return null; + } + + // Delegate to underlying auth provider + return authProvider.updateUserContext(context, authenticatedUser); } 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 5b87456ce..6d9a90923 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 @@ -38,6 +38,7 @@ import javax.ws.rs.core.MultivaluedMap; import javax.xml.bind.DatatypeConverter; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.environment.Environment; +import org.glyptodon.guacamole.net.auth.AuthenticatedUser; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.UserContext; @@ -222,28 +223,28 @@ public class TokenRESTService { credentials.setPassword(password); credentials.setRequest(request); credentials.setSession(request.getSession(true)); - - UserContext userContext; + + AuthenticatedUser authenticatedUser; try { - // Update existing user context if session already exists + // Re-authenticate user if session exists if (existingSession != null) - userContext = authProvider.updateUserContext(existingSession.getUserContext(), credentials); + authenticatedUser = authProvider.updateAuthenticatedUser(existingSession.getAuthenticatedUser(), credentials); - /// Otherwise, generate a new user context + /// Otherwise, authenticate as a new user else { - userContext = authProvider.getUserContext(credentials); + authenticatedUser = authProvider.authenticateUser(credentials); // Log successful authentication - if (userContext != null && logger.isInfoEnabled()) + if (authenticatedUser != null && logger.isInfoEnabled()) logger.info("User \"{}\" successfully authenticated from {}.", - userContext.self().getIdentifier(), getLoggableAddress(request)); + authenticatedUser.getIdentifier(), getLoggableAddress(request)); } - // Request standard username/password if no user context was produced - if (userContext == null) + // Request standard username/password if no user was produced + if (authenticatedUser == null) throw new GuacamoleInvalidCredentialsException("Permission Denied.", CredentialsInfo.USERNAME_PASSWORD); @@ -264,23 +265,35 @@ public class TokenRESTService { throw e; } - + + // Generate or update user context + UserContext userContext; + if (existingSession != null) + userContext = authProvider.updateUserContext(existingSession.getUserContext(), authenticatedUser); + else + userContext = authProvider.getUserContext(authenticatedUser); + + // STUB: Request standard username/password if no user context was produced + if (userContext == null) + throw new GuacamoleInvalidCredentialsException("Permission Denied.", + CredentialsInfo.USERNAME_PASSWORD); + // Update existing session, if it exists String authToken; if (existingSession != null) { authToken = token; - existingSession.setCredentials(credentials); + existingSession.setAuthenticatedUser(authenticatedUser); existingSession.setUserContext(userContext); } // If no existing session, generate a new token/session pair else { authToken = authTokenGenerator.getToken(); - tokenSessionMap.put(authToken, new GuacamoleSession(environment, credentials, userContext)); + tokenSessionMap.put(authToken, new GuacamoleSession(environment, authenticatedUser, userContext)); } - logger.debug("Login was successful for user \"{}\".", userContext.self().getIdentifier()); - return new APIAuthToken(authToken, userContext.self().getIdentifier()); + logger.debug("Login was successful for user \"{}\".", authenticatedUser.getIdentifier()); + return new APIAuthToken(authToken, authenticatedUser.getIdentifier()); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java index a99134d91..03696b7e6 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java @@ -46,6 +46,7 @@ import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.UserContext; +import org.glyptodon.guacamole.net.auth.credentials.GuacamoleCredentialsException; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; import org.glyptodon.guacamole.net.auth.permission.Permission; @@ -337,8 +338,16 @@ public class UserRESTService { credentials.setRequest(request); credentials.setSession(request.getSession(true)); - // Verify that the old password was correct - if (authProvider.getUserContext(credentials) == null) { + // Verify that the old password was correct + try { + if (authProvider.authenticateUser(credentials) == null) { + throw new APIException(APIError.Type.PERMISSION_DENIED, + "Permission denied."); + } + } + + // Pass through any credentials exceptions as simple permission denied + catch (GuacamoleCredentialsException e) { throw new APIException(APIError.Type.PERMISSION_DENIED, "Permission denied."); }