GUAC-587: Track and load authentication providers only within ExtensionModule. Default to basic auth. Explicitly deprecate.

This commit is contained in:
Michael Jumper
2015-05-11 14:24:56 -07:00
parent b1b62aa2b4
commit 3d924873f1
7 changed files with 123 additions and 68 deletions

View File

@@ -85,7 +85,7 @@ public class BasicServletContextListener extends GuiceServletContextListener {
new EnvironmentModule(environment),
new LogModule(environment),
new ExtensionModule(environment),
new RESTAuthModule(environment, sessionMap),
new RESTAuthModule(sessionMap),
new RESTServletModule(),
new TunnelModule()
);

View File

@@ -39,10 +39,13 @@ import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties;
/**
* A ClassLoader implementation which finds classes within a configurable
* directory. This directory is set within guacamole.properties.
* directory. This directory is set within guacamole.properties. This class
* is deprecated in favor of DirectoryClassLoader, which is automatically
* configured based on the presence/absence of GUACAMOLE_HOME/lib.
*
* @author Michael Jumper
*/
@Deprecated
public class GuacamoleClassLoader extends ClassLoader {
/**

View File

@@ -28,9 +28,11 @@ import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Collection;
import net.sourceforge.guacamole.net.basic.BasicFileAuthenticationProvider;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties;
import org.glyptodon.guacamole.net.basic.resource.Resource;
import org.glyptodon.guacamole.net.basic.resource.ResourceServlet;
import org.glyptodon.guacamole.net.basic.resource.SequenceResource;
@@ -74,6 +76,12 @@ 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.
*/
private Class<? extends AuthenticationProvider> boundAuthenticationProvider = null;
/**
* Returns the classloader that should be used as the parent classloader
* for all extensions. If the GUACAMOLE_HOME/lib directory exists, this
@@ -113,9 +121,81 @@ public class ExtensionModule extends ServletModule {
this.environment = environment;
}
/**
* Reads the value of the now-deprecated "auth-provider" property from
* guacamole.properties, returning the corresponding AuthenticationProvider
* class. If no authentication provider could be read, or the property is
* not present, null is returned.
*
* As this property is deprecated, this function will also log warning
* messages if the property is actually specified.
*
* @return
* The value of the deprecated "auth-provider" property, or null if the
* property is not present.
*/
@SuppressWarnings("deprecation") // We must continue to use this property until it is truly no longer supported
private Class<AuthenticationProvider> getAuthProviderProperty() {
// Get and bind auth provider instance, if defined via property
try {
// Use "auth-provider" property if present, but warn about deprecation
Class<AuthenticationProvider> authenticationProvider = environment.getProperty(BasicGuacamoleProperties.AUTH_PROVIDER);
if (authenticationProvider != null)
logger.warn("The \"auth-provider\" and \"lib-directory\" properties are now deprecated. Please use the \"extensions\" and \"lib\" directories within GUACAMOLE_HOME instead.");
return authenticationProvider;
}
catch (GuacamoleException e) {
logger.warn("Value of deprecated \"auth-provider\" property within guacamole.properties is not valid: {}", e.getMessage());
logger.debug("Error reading authentication provider from guacamole.properties.", e);
}
return null;
}
/**
* Binds the given AuthenticationProvider class such that any service
* requiring access to the AuthenticationProvider can obtain it via
* injection.
*
* @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).to(authenticationProvider).in(Singleton.class);
}
@Override
protected void configureServlets() {
// Load authentication provider from guacamole.properties for sake of backwards compatibility
Class<AuthenticationProvider> authProviderProperty = getAuthProviderProperty();
if (authProviderProperty != null)
bindAuthenticationProvider(authProviderProperty);
// Retrieve and validate extensions directory
File extensionsDir = new File(environment.getGuacamoleHome(), EXTENSIONS_DIRECTORY);
if (!extensionsDir.isDirectory())
@@ -153,12 +233,10 @@ public class ExtensionModule extends ServletModule {
javaScriptResources.addAll(extension.getJavaScriptResources());
cssResources.addAll(extension.getCSSResources());
// Load all authentication providers as singletons
// Attempt to load all authentication providers
Collection<Class<AuthenticationProvider>> authenticationProviders = extension.getAuthenticationProviderClasses();
for (Class<AuthenticationProvider> authenticationProvider : authenticationProviders) {
logger.debug("Binding AuthenticationProvider \"{}\".", authenticationProvider);
bind(AuthenticationProvider.class).to(authenticationProvider).in(Singleton.class);
}
for (Class<AuthenticationProvider> authenticationProvider : authenticationProviders)
bindAuthenticationProvider(authenticationProvider);
// Log successful loading of extension by name
logger.info("Extension \"{}\" loaded.", extension.getName());
@@ -171,6 +249,12 @@ public class ExtensionModule extends ServletModule {
}
// Default to basic auth if nothing else chosen/provided
if (boundAuthenticationProvider == null) {
logger.info("Using default, \"basic\", XML-driven authentication.");
bindAuthenticationProvider(BasicFileAuthenticationProvider.class);
}
// Dynamically generate app.js and app.css from extensions
serve("/app.js").with(new ResourceServlet(new SequenceResource(javaScriptResources)));
serve("/app.css").with(new ResourceServlet(new SequenceResource(cssResources)));

View File

@@ -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
@@ -22,7 +22,6 @@
package org.glyptodon.guacamole.net.basic.properties;
import java.lang.reflect.InvocationTargetException;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
import org.glyptodon.guacamole.net.basic.GuacamoleClassLoader;
@@ -30,14 +29,18 @@ import org.glyptodon.guacamole.properties.GuacamoleProperty;
/**
* A GuacamoleProperty whose value is the name of a class to use to
* authenticate users. This class must implement AuthenticationProvider.
* authenticate users. This class must implement AuthenticationProvider. Use
* of this property type is deprecated in favor of the
* GUACAMOLE_HOME/extensions directory.
*
* @author Michael Jumper
*/
public abstract class AuthenticationProviderProperty implements GuacamoleProperty<AuthenticationProvider> {
@Deprecated
public abstract class AuthenticationProviderProperty implements GuacamoleProperty<Class<AuthenticationProvider>> {
@Override
public AuthenticationProvider parseValue(String authProviderClassName) throws GuacamoleException {
@SuppressWarnings("unchecked") // Explicitly checked within by isAssignableFrom()
public Class<AuthenticationProvider> parseValue(String authProviderClassName) throws GuacamoleException {
// If no property provided, return null.
if (authProviderClassName == null)
@@ -46,35 +49,21 @@ public abstract class AuthenticationProviderProperty implements GuacamolePropert
// Get auth provider instance
try {
Object obj = GuacamoleClassLoader.getInstance().loadClass(authProviderClassName)
.getConstructor().newInstance();
// Get authentication provider class
Class<?> authProviderClass = GuacamoleClassLoader.getInstance().loadClass(authProviderClassName);
if (!(obj instanceof AuthenticationProvider))
// Verify the located class is actually a subclass of AuthenticationProvider
if (!AuthenticationProvider.class.isAssignableFrom(authProviderClass))
throw new GuacamoleException("Specified authentication provider class is not a AuthenticationProvider.");
return (AuthenticationProvider) obj;
// Return located class
return (Class<AuthenticationProvider>) authProviderClass;
}
catch (ClassNotFoundException e) {
throw new GuacamoleException("Authentication provider class not found", e);
}
catch (NoSuchMethodException e) {
throw new GuacamoleException("Default constructor for authentication provider not present", e);
}
catch (SecurityException e) {
throw new GuacamoleException("Creation of authentication provider disallowed; check your security settings", e);
}
catch (InstantiationException e) {
throw new GuacamoleException("Unable to instantiate authentication provider", e);
}
catch (IllegalAccessException e) {
throw new GuacamoleException("Unable to access default constructor of authentication provider", e);
}
catch (InvocationTargetException e) {
throw new GuacamoleException("Internal error in constructor of authentication provider", e.getTargetException());
}
}
}

View File

@@ -22,7 +22,6 @@
package org.glyptodon.guacamole.net.basic.properties;
import org.glyptodon.guacamole.properties.BooleanGuacamoleProperty;
import org.glyptodon.guacamole.properties.FileGuacamoleProperty;
import org.glyptodon.guacamole.properties.IntegerGuacamoleProperty;
@@ -40,8 +39,10 @@ public class BasicGuacamoleProperties {
/**
* The authentication provider to user when retrieving the authorized
* configurations of a user.
* configurations of a user. This property is currently supported, but
* deprecated in favor of the GUACAMOLE_HOME/extensions directory.
*/
@Deprecated
public static final AuthenticationProviderProperty AUTH_PROVIDER = new AuthenticationProviderProperty() {
@Override
@@ -50,8 +51,11 @@ public class BasicGuacamoleProperties {
};
/**
* The directory to search for authentication provider classes.
* The directory to search for authentication provider classes. This
* property is currently supported, but deprecated in favor of the
* GUACAMOLE_HOME/lib directory.
*/
@Deprecated
public static final FileGuacamoleProperty LIB_DIRECTORY = new FileGuacamoleProperty() {
@Override
@@ -60,7 +64,9 @@ public class BasicGuacamoleProperties {
};
/**
* The comma-separated list of all classes to use as event listeners.
* The comma-separated list of all classes to use as event listeners. This
* property is currently supported, but deprecated in favor of declared
* event listeners within extension manifests.
*/
public static final EventListenersProperty EVENT_LISTENERS = new EventListenersProperty() {

View File

@@ -30,10 +30,13 @@ import org.glyptodon.guacamole.properties.GuacamoleProperty;
/**
* A GuacamoleProperty whose value is a comma-separated list of class names,
* where each class will be used as a listener for events.
* where each class will be used as a listener for events. This type of
* property is deprecated in favor of declaring event listeners within
* extension manifests.
*
* @author Michael Jumper
*/
@SuppressWarnings("deprecation")
public abstract class EventListenersProperty implements GuacamoleProperty<Collection<Class>> {
@Override

View File

@@ -23,10 +23,6 @@
package org.glyptodon.guacamole.net.basic.rest;
import com.google.inject.AbstractModule;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthTokenGenerator;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerator;
@@ -47,11 +43,6 @@ public class RESTAuthModule extends AbstractModule {
*/
private final Logger logger = LoggerFactory.getLogger(RESTAuthModule.class);
/**
* The Guacamole server environment.
*/
private final Environment environment;
/**
* Singleton instance of TokenSessionMap.
*/
@@ -61,16 +52,11 @@ public class RESTAuthModule extends AbstractModule {
* Creates a module which handles binding of authentication-related
* objects, including the singleton TokenSessionMap.
*
* @param environment
* The environment to use when configuring authentication.
*
* @param tokenSessionMap
* An instance of TokenSessionMap to inject as a singleton wherever
* needed.
*/
public RESTAuthModule(Environment environment,
TokenSessionMap tokenSessionMap) {
this.environment = environment;
public RESTAuthModule(TokenSessionMap tokenSessionMap) {
this.tokenSessionMap = tokenSessionMap;
}
@@ -84,22 +70,6 @@ public class RESTAuthModule extends AbstractModule {
bind(AuthenticationService.class);
bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class);
// Get and bind auth provider instance, if defined via property
try {
// Use "auth-provider" property if present, but warn about deprecation
AuthenticationProvider authProvider = environment.getProperty(BasicGuacamoleProperties.AUTH_PROVIDER);
if (authProvider != null) {
logger.warn("The \"auth-provider\" and \"lib-directory\" properties are now deprecated. Please use the \"extensions\" and \"lib\" directories within GUACAMOLE_HOME instead.");
bind(AuthenticationProvider.class).toInstance(authProvider);
}
}
catch (GuacamoleException e) {
logger.warn("Value of deprecated \"auth-provider\" property within guacamole.properties is not valid: {}", e.getMessage());
logger.debug("Error reading authentication provider from guacamole.properties.", e);
}
}
}