Skip to content
This repository has been archived by the owner on Jul 17, 2023. It is now read-only.

Commit

Permalink
GUAC-586: Load multiple AuthenticationProviders.
Browse files Browse the repository at this point in the history
  • Loading branch information
mike-jumper committed Aug 28, 2015
1 parent 15e9481 commit 343c8e6
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 77 deletions.
Expand Up @@ -22,6 +22,8 @@

package org.glyptodon.guacamole.net.basic;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.glyptodon.guacamole.GuacamoleException;
Expand Down Expand Up @@ -51,9 +53,10 @@ public class GuacamoleSession {
private AuthenticatedUser authenticatedUser;

/**
* The user context associated with this session.
* All UserContexts associated with this session. Each
* AuthenticationProvider may provide its own UserContext.
*/
private UserContext userContext;
private List<UserContext> userContexts;

/**
* All currently-active tunnels, indexed by tunnel UUID.
Expand All @@ -66,7 +69,8 @@ public class GuacamoleSession {
private long lastAccessedTime;

/**
* Creates a new Guacamole session associated with the given user context.
* Creates a new Guacamole session associated with the given
* AuthenticatedUser and UserContexts.
*
* @param environment
* The environment of the Guacamole server associated with this new
Expand All @@ -75,18 +79,19 @@ public class GuacamoleSession {
* @param authenticatedUser
* The authenticated user to associate this session with.
*
* @param userContext
* The user context to associate this session with.
* @param userContexts
* The List of UserContexts to associate with this session.
*
* @throws GuacamoleException
* If an error prevents the session from being created.
*/
public GuacamoleSession(Environment environment,
AuthenticatedUser authenticatedUser, UserContext userContext)
AuthenticatedUser authenticatedUser,
List<UserContext> userContexts)
throws GuacamoleException {
this.lastAccessedTime = System.currentTimeMillis();
this.authenticatedUser = authenticatedUser;
this.userContext = userContext;
this.userContexts = userContexts;
}

/**
Expand Down Expand Up @@ -116,18 +121,56 @@ public void setAuthenticatedUser(AuthenticatedUser authenticatedUser) {
* @return The UserContext associated with this session.
*/
public UserContext getUserContext() {
return userContext;

// Warn of deprecation
logger.debug(
"\n****************************************************************"
+ "\n"
+ "\n !!!! PLEASE DO NOT USE getUserContext() !!!!"
+ "\n"
+ "\n getUserContext() has been replaced by getUserContexts(), which"
+ "\n properly handles multiple authentication providers. All use of"
+ "\n the old getUserContext() must be removed before GUAC-586 can"
+ "\n be considered complete."
+ "\n"
+ "\n****************************************************************"
);

// Return the UserContext associated with the AuthenticationProvider
// that authenticated the current user.
String authProviderIdentifier = authenticatedUser.getAuthenticationProvider().getIdentifier();
for (UserContext userContext : userContexts) {
if (userContext.getAuthenticationProvider().getIdentifier().equals(authProviderIdentifier))
return userContext;
}

// If not found, return null
return null;

}

/**
* Returns a list of all UserContexts associated with this session. Each
* AuthenticationProvider currently loaded by Guacamole may provide its own
* UserContext for any successfully-authenticated user.
*
* @return
* An unmodifiable list of all UserContexts associated with this
* session.
*/
public List<UserContext> getUserContexts() {
return Collections.unmodifiableList(userContexts);
}

/**
* Replaces the user context associated with this session with the given
* user context.
* Replaces all UserContexts associated with this session with the given
* List of UserContexts.
*
* @param userContext
* The user context to associate with this session.
* @param userContexts
* The List of UserContexts to associate with this session.
*/
public void setUserContext(UserContext userContext) {
this.userContext = userContext;
public void setUserContexts(List<UserContext> userContexts) {
this.userContexts = userContexts;
}

/**
Expand Down
Expand Up @@ -244,13 +244,13 @@ public void close() throws GuacamoleException {
// Connection identifiers
case CONNECTION:
logger.info("User \"{}\" disconnected from connection \"{}\". Duration: {} milliseconds",
session.getUserContext().self().getIdentifier(), id, duration);
session.getAuthenticatedUser().getIdentifier(), id, duration);
break;

// Connection group identifiers
case CONNECTION_GROUP:
logger.info("User \"{}\" disconnected from connection group \"{}\". Duration: {} milliseconds",
session.getUserContext().self().getIdentifier(), id, duration);
session.getAuthenticatedUser().getIdentifier(), id, duration);
break;

// Type is guaranteed to be one of the above
Expand Down
Expand Up @@ -22,6 +22,7 @@

package org.glyptodon.guacamole.net.basic.extension;

import com.google.inject.Provides;
import com.google.inject.servlet.ServletModule;
import java.io.File;
import java.io.FileFilter;
Expand Down Expand Up @@ -91,10 +92,10 @@ public class ExtensionModule extends ServletModule {
private final Environment environment;

/**
* The currently-bound authentication provider, if any. At the moment, we
* only support one authentication provider loaded at any one time.
* All currently-bound authentication providers, if any.
*/
private Class<? extends AuthenticationProvider> boundAuthenticationProvider = null;
private final List<AuthenticationProvider> boundAuthenticationProviders =
new ArrayList<AuthenticationProvider>();

/**
* Service for adding and retrieving language resources.
Expand Down Expand Up @@ -179,40 +180,24 @@ private Class<AuthenticationProvider> getAuthProviderProperty() {
/**
* Binds the given AuthenticationProvider class such that any service
* requiring access to the AuthenticationProvider can obtain it via
* injection.
* injection, along with any other bound AuthenticationProviders.
*
* @param authenticationProvider
* The AuthenticationProvider class to bind.
*/
private void bindAuthenticationProvider(Class<? extends AuthenticationProvider> authenticationProvider) {

// Choose auth provider for binding if not already chosen
if (boundAuthenticationProvider == null)
boundAuthenticationProvider = authenticationProvider;

// If an auth provider is already chosen, skip and warn
else {
logger.debug("Ignoring AuthenticationProvider \"{}\".", authenticationProvider);
logger.warn("Only one authentication extension may be used at a time. Please "
+ "make sure that only one authentication extension is present "
+ "within the GUACAMOLE_HOME/" + EXTENSIONS_DIRECTORY + " "
+ "directory, and that you are not also specifying the deprecated "
+ "\"auth-provider\" property within guacamole.properties.");
return;
}

// Bind authentication provider
logger.debug("Binding AuthenticationProvider \"{}\".", authenticationProvider);
bind(AuthenticationProvider.class).toInstance(new AuthenticationProviderFacade(authenticationProvider));
logger.debug("[{}] Binding AuthenticationProvider \"{}\".",
boundAuthenticationProviders.size(), authenticationProvider.getName());
boundAuthenticationProviders.add(new AuthenticationProviderFacade(authenticationProvider));

}

/**
* Binds each of the the given AuthenticationProvider classes such that any
* service requiring access to the AuthenticationProvider can obtain it via
* injection. Note that, as multiple simultaneous authentication providers
* are not currently supported, attempting to bind more than one
* authentication provider will result in warnings being logged.
* injection.
*
* @param authProviders
* The AuthenticationProvider classes to bind.
Expand All @@ -225,6 +210,18 @@ private void bindAuthenticationProviders(Collection<Class<AuthenticationProvider

}

/**
* Returns a list of all currently-bound AuthenticationProvider instances.
*
* @return
* A List of all currently-bound AuthenticationProvider. The List is
* not modifiable.
*/
@Provides
public List<AuthenticationProvider> getAuthenticationProviders() {
return Collections.unmodifiableList(boundAuthenticationProviders);
}

/**
* Serves each of the given resources as a language resource. Language
* resources are served from within the "/translations" directory as JSON
Expand Down Expand Up @@ -415,11 +412,8 @@ protected void configureServlets() {
// Load all extensions
loadExtensions(javaScriptResources, cssResources);

// Bind basic auth if nothing else chosen/provided
if (boundAuthenticationProvider == null) {
logger.info("Using default, \"basic\", XML-driven authentication.");
bindAuthenticationProvider(BasicFileAuthenticationProvider.class);
}
// Always bind basic auth last
bindAuthenticationProvider(BasicFileAuthenticationProvider.class);

// Dynamically generate app.js and app.css from extensions
serve("/app.js").with(new ResourceServlet(new SequenceResource(javaScriptResources)));
Expand Down
Expand Up @@ -22,13 +22,15 @@

package org.glyptodon.guacamole.net.basic.rest;

import java.util.List;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleResourceNotFoundException;
import org.glyptodon.guacamole.net.auth.Connection;
import org.glyptodon.guacamole.net.auth.ConnectionGroup;
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.basic.GuacamoleSession;
import org.glyptodon.guacamole.net.basic.rest.connectiongroup.APIConnectionGroup;

/**
Expand All @@ -39,6 +41,39 @@
*/
public class ObjectRetrievalService {

/**
* Retrieves a single UserContext from the given GuacamoleSession, which
* may contain multiple UserContexts.
*
* @param session
* The GuacamoleSession to retrieve the UserContext from.
*
* @param id
* The numeric ID of the UserContext to retrieve. This ID is the index
* of the UserContext within the overall list of UserContexts
* associated with the user's session.
*
* @return
* The user having the given identifier.
*
* @throws GuacamoleException
* If an error occurs while retrieving the user, or if the
* user does not exist.
*/
public UserContext retrieveUserContext(GuacamoleSession session,
int id) throws GuacamoleException {

// Get list of UserContexts
List<UserContext> userContexts = session.getUserContexts();

// Verify context exists
if (id < 0 || id >= userContexts.size())
throw new GuacamoleResourceNotFoundException("No such user context: \"" + id + "\"");

return userContexts.get(id);

}

/**
* Retrieves a single user from the given user context.
*
Expand Down
Expand Up @@ -23,6 +23,7 @@
package org.glyptodon.guacamole.net.basic.rest.auth;

import com.google.inject.Inject;
import java.util.List;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleUnauthorizedException;
import org.glyptodon.guacamole.net.auth.UserContext;
Expand Down Expand Up @@ -77,5 +78,24 @@ public GuacamoleSession getGuacamoleSession(String authToken)
public UserContext getUserContext(String authToken) throws GuacamoleException {
return getGuacamoleSession(authToken).getUserContext();
}


/**
* Returns all UserContexts associated with a given auth token, if the auth
* token represents a currently logged in user. Throws an unauthorized
* error otherwise.
*
* @param authToken
* The auth token to check against the map of logged in users.
*
* @return
* A List of all UserContexts associated with the provided auth token.
*
* @throws GuacamoleException
* If the auth token does not correspond to any logged in user.
*/
public List<UserContext> getUserContexts(String authToken)
throws GuacamoleException {
return getGuacamoleSession(authToken).getUserContexts();
}

}

0 comments on commit 343c8e6

Please sign in to comment.