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 EnvironmentModule(environment),
new LogModule(environment), new LogModule(environment),
new ExtensionModule(environment), new ExtensionModule(environment),
new RESTAuthModule(environment, sessionMap), new RESTAuthModule(sessionMap),
new RESTServletModule(), new RESTServletModule(),
new TunnelModule() 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 * 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 * @author Michael Jumper
*/ */
@Deprecated
public class GuacamoleClassLoader extends ClassLoader { public class GuacamoleClassLoader extends ClassLoader {
/** /**

View File

@@ -28,9 +28,11 @@ import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import net.sourceforge.guacamole.net.basic.BasicFileAuthenticationProvider;
import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider; 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.Resource;
import org.glyptodon.guacamole.net.basic.resource.ResourceServlet; import org.glyptodon.guacamole.net.basic.resource.ResourceServlet;
import org.glyptodon.guacamole.net.basic.resource.SequenceResource; import org.glyptodon.guacamole.net.basic.resource.SequenceResource;
@@ -74,6 +76,12 @@ public class ExtensionModule extends ServletModule {
*/ */
private final Environment environment; 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 * Returns the classloader that should be used as the parent classloader
* for all extensions. If the GUACAMOLE_HOME/lib directory exists, this * for all extensions. If the GUACAMOLE_HOME/lib directory exists, this
@@ -113,9 +121,81 @@ public class ExtensionModule extends ServletModule {
this.environment = environment; 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 @Override
protected void configureServlets() { 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 // Retrieve and validate extensions directory
File extensionsDir = new File(environment.getGuacamoleHome(), EXTENSIONS_DIRECTORY); File extensionsDir = new File(environment.getGuacamoleHome(), EXTENSIONS_DIRECTORY);
if (!extensionsDir.isDirectory()) if (!extensionsDir.isDirectory())
@@ -153,12 +233,10 @@ public class ExtensionModule extends ServletModule {
javaScriptResources.addAll(extension.getJavaScriptResources()); javaScriptResources.addAll(extension.getJavaScriptResources());
cssResources.addAll(extension.getCSSResources()); cssResources.addAll(extension.getCSSResources());
// Load all authentication providers as singletons // Attempt to load all authentication providers
Collection<Class<AuthenticationProvider>> authenticationProviders = extension.getAuthenticationProviderClasses(); Collection<Class<AuthenticationProvider>> authenticationProviders = extension.getAuthenticationProviderClasses();
for (Class<AuthenticationProvider> authenticationProvider : authenticationProviders) { for (Class<AuthenticationProvider> authenticationProvider : authenticationProviders)
logger.debug("Binding AuthenticationProvider \"{}\".", authenticationProvider); bindAuthenticationProvider(authenticationProvider);
bind(AuthenticationProvider.class).to(authenticationProvider).in(Singleton.class);
}
// Log successful loading of extension by name // Log successful loading of extension by name
logger.info("Extension \"{}\" loaded.", extension.getName()); 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 // Dynamically generate app.js and app.css from extensions
serve("/app.js").with(new ResourceServlet(new SequenceResource(javaScriptResources))); serve("/app.js").with(new ResourceServlet(new SequenceResource(javaScriptResources)));
serve("/app.css").with(new ResourceServlet(new SequenceResource(cssResources))); 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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,6 @@
package org.glyptodon.guacamole.net.basic.properties; package org.glyptodon.guacamole.net.basic.properties;
import java.lang.reflect.InvocationTargetException;
import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
import org.glyptodon.guacamole.net.basic.GuacamoleClassLoader; 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 * 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 * @author Michael Jumper
*/ */
public abstract class AuthenticationProviderProperty implements GuacamoleProperty<AuthenticationProvider> { @Deprecated
public abstract class AuthenticationProviderProperty implements GuacamoleProperty<Class<AuthenticationProvider>> {
@Override @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 no property provided, return null.
if (authProviderClassName == null) if (authProviderClassName == null)
@@ -46,35 +49,21 @@ public abstract class AuthenticationProviderProperty implements GuacamolePropert
// Get auth provider instance // Get auth provider instance
try { try {
Object obj = GuacamoleClassLoader.getInstance().loadClass(authProviderClassName) // Get authentication provider class
.getConstructor().newInstance(); 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."); throw new GuacamoleException("Specified authentication provider class is not a AuthenticationProvider.");
return (AuthenticationProvider) obj; // Return located class
return (Class<AuthenticationProvider>) authProviderClass;
} }
catch (ClassNotFoundException e) { catch (ClassNotFoundException e) {
throw new GuacamoleException("Authentication provider class not found", 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; package org.glyptodon.guacamole.net.basic.properties;
import org.glyptodon.guacamole.properties.BooleanGuacamoleProperty;
import org.glyptodon.guacamole.properties.FileGuacamoleProperty; import org.glyptodon.guacamole.properties.FileGuacamoleProperty;
import org.glyptodon.guacamole.properties.IntegerGuacamoleProperty; import org.glyptodon.guacamole.properties.IntegerGuacamoleProperty;
@@ -40,8 +39,10 @@ public class BasicGuacamoleProperties {
/** /**
* The authentication provider to user when retrieving the authorized * 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() { public static final AuthenticationProviderProperty AUTH_PROVIDER = new AuthenticationProviderProperty() {
@Override @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() { public static final FileGuacamoleProperty LIB_DIRECTORY = new FileGuacamoleProperty() {
@Override @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() { 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, * 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 * @author Michael Jumper
*/ */
@SuppressWarnings("deprecation")
public abstract class EventListenersProperty implements GuacamoleProperty<Collection<Class>> { public abstract class EventListenersProperty implements GuacamoleProperty<Collection<Class>> {
@Override @Override

View File

@@ -23,10 +23,6 @@
package org.glyptodon.guacamole.net.basic.rest; package org.glyptodon.guacamole.net.basic.rest;
import com.google.inject.AbstractModule; 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.AuthTokenGenerator;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerator; 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); private final Logger logger = LoggerFactory.getLogger(RESTAuthModule.class);
/**
* The Guacamole server environment.
*/
private final Environment environment;
/** /**
* Singleton instance of TokenSessionMap. * Singleton instance of TokenSessionMap.
*/ */
@@ -61,16 +52,11 @@ public class RESTAuthModule extends AbstractModule {
* Creates a module which handles binding of authentication-related * Creates a module which handles binding of authentication-related
* objects, including the singleton TokenSessionMap. * objects, including the singleton TokenSessionMap.
* *
* @param environment
* The environment to use when configuring authentication.
*
* @param tokenSessionMap * @param tokenSessionMap
* An instance of TokenSessionMap to inject as a singleton wherever * An instance of TokenSessionMap to inject as a singleton wherever
* needed. * needed.
*/ */
public RESTAuthModule(Environment environment, public RESTAuthModule(TokenSessionMap tokenSessionMap) {
TokenSessionMap tokenSessionMap) {
this.environment = environment;
this.tokenSessionMap = tokenSessionMap; this.tokenSessionMap = tokenSessionMap;
} }
@@ -84,22 +70,6 @@ public class RESTAuthModule extends AbstractModule {
bind(AuthenticationService.class); bind(AuthenticationService.class);
bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.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);
}
} }
} }