mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUAC-587: Load authentication providers from extensions.
This commit is contained in:
@@ -40,6 +40,7 @@ import org.codehaus.jackson.JsonParseException;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.glyptodon.guacamole.GuacamoleException;
|
||||
import org.glyptodon.guacamole.GuacamoleServerException;
|
||||
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
|
||||
import org.glyptodon.guacamole.net.basic.resource.ClassPathResource;
|
||||
import org.glyptodon.guacamole.net.basic.resource.Resource;
|
||||
|
||||
@@ -74,6 +75,125 @@ public class Extension {
|
||||
*/
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* The collection of all JavaScript resources defined within the extension.
|
||||
*/
|
||||
private final Collection<Resource> javaScriptResources;
|
||||
|
||||
/**
|
||||
* The collection of all CSS resources defined within the extension.
|
||||
*/
|
||||
private final Collection<Resource> cssResources;
|
||||
|
||||
/**
|
||||
* The collection of all AuthenticationProvider classes defined within the
|
||||
* extension.
|
||||
*/
|
||||
private final Collection<Class<AuthenticationProvider>> authenticationProviderClasses;
|
||||
|
||||
/**
|
||||
* Returns a new collection of resources corresponding to the collection of
|
||||
* paths provided. Each resource will be associated with the given
|
||||
* mimetype.
|
||||
*
|
||||
* @param mimetype
|
||||
* The mimetype to associate with each resource.
|
||||
*
|
||||
* @param paths
|
||||
* The paths corresponding to the resources desired.
|
||||
*
|
||||
* @return
|
||||
* A new, unmodifiable collection of resources corresponding to the
|
||||
* collection of paths provided.
|
||||
*/
|
||||
private Collection<Resource> getClassPathResources(String mimetype, Collection<String> paths) {
|
||||
|
||||
// If no paths are provided, just return an empty list
|
||||
if (paths == null)
|
||||
return Collections.<Resource>emptyList();
|
||||
|
||||
// Add classpath resource for each path provided
|
||||
Collection<Resource> resources = new ArrayList<Resource>(paths.size());
|
||||
for (String path : paths)
|
||||
resources.add(new ClassPathResource(classLoader, mimetype, path));
|
||||
|
||||
// Callers should not rely on modifying the result
|
||||
return Collections.unmodifiableCollection(resources);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the AuthenticationProvider subclass having the given name. If
|
||||
* the class having the given name does not exist or isn't actually a
|
||||
* subclass of AuthenticationProvider, an exception will be thrown.
|
||||
*
|
||||
* @param name
|
||||
* The name of the AuthenticationProvider class to retrieve.
|
||||
*
|
||||
* @return
|
||||
* The subclass of AuthenticationProvider having the given name.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If no such class exists, or if the class with the given name is not
|
||||
* a subclass of AuthenticationProvider.
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // We check this ourselves with isAssignableFrom()
|
||||
private Class<AuthenticationProvider> getAuthenticationProviderClass(String name)
|
||||
throws GuacamoleException {
|
||||
|
||||
try {
|
||||
|
||||
// Get authentication provider class
|
||||
Class<?> authenticationProviderClass = classLoader.loadClass(name);
|
||||
|
||||
// Verify the located class is actually a subclass of AuthenticationProvider
|
||||
if (!AuthenticationProvider.class.isAssignableFrom(authenticationProviderClass))
|
||||
throw new GuacamoleServerException("Authentication providers MUST extend the AuthenticationProvider class.");
|
||||
|
||||
// Return located class
|
||||
return (Class<AuthenticationProvider>) authenticationProviderClass;
|
||||
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new GuacamoleException("Authentication provider class not found.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new collection of all AuthenticationProvider subclasses having
|
||||
* the given names. If any class does not exist or isn't actually a
|
||||
* subclass of AuthenticationProvider, an exception will be thrown, and
|
||||
* no further AuthenticationProvider classes will be loaded.
|
||||
*
|
||||
* @param names
|
||||
* The names of the AuthenticationProvider classes to retrieve.
|
||||
*
|
||||
* @return
|
||||
* A new collection of all AuthenticationProvider subclasses having the
|
||||
* given names.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If any given class does not exist, or if any given class is not a
|
||||
* subclass of AuthenticationProvider.
|
||||
*/
|
||||
private Collection<Class<AuthenticationProvider>> getAuthenticationProviderClasses(Collection<String> names)
|
||||
throws GuacamoleException {
|
||||
|
||||
// If no classnames are provided, just return an empty list
|
||||
if (names == null)
|
||||
return Collections.<Class<AuthenticationProvider>>emptyList();
|
||||
|
||||
// Define all auth provider classes
|
||||
Collection<Class<AuthenticationProvider>> classes = new ArrayList<Class<AuthenticationProvider>>(names.size());
|
||||
for (String name : names)
|
||||
classes.add(getAuthenticationProviderClass(name));
|
||||
|
||||
// Callers should not rely on modifying the result
|
||||
return Collections.unmodifiableCollection(classes);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given file as an extension, which must be a .jar containing
|
||||
* a guac-manifest.json file describing its contents.
|
||||
@@ -160,6 +280,13 @@ public class Extension {
|
||||
throw new GuacamoleServerException("Unable to read extension: " + file.getName(), e);
|
||||
}
|
||||
|
||||
// Define static resources
|
||||
cssResources = getClassPathResources("text/css", manifest.getCSSPaths());
|
||||
javaScriptResources = getClassPathResources("text/javascript", manifest.getJavaScriptPaths());
|
||||
|
||||
// Define authentication providers
|
||||
authenticationProviderClasses = getAuthenticationProviderClasses(manifest.getAuthProviders());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,37 +311,6 @@ public class Extension {
|
||||
return manifest.getNamespace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new collection of resources corresponding to the collection of
|
||||
* paths provided. Each resource will be associated with the given
|
||||
* mimetype.
|
||||
*
|
||||
* @param mimetype
|
||||
* The mimetype to associate with each resource.
|
||||
*
|
||||
* @param paths
|
||||
* The paths corresponding to the resources desired.
|
||||
*
|
||||
* @return
|
||||
* A new, unmodifiable collection of resources corresponding to the
|
||||
* collection of paths provided.
|
||||
*/
|
||||
private Collection<Resource> getClassPathResources(String mimetype, Collection<String> paths) {
|
||||
|
||||
// If no paths are provided, just return an empty list
|
||||
if (paths == null)
|
||||
return Collections.<Resource>emptyList();
|
||||
|
||||
// Add classpath resource for each path provided
|
||||
Collection<Resource> resources = new ArrayList<Resource>(paths.size());
|
||||
for (String path : paths)
|
||||
resources.add(new ClassPathResource(classLoader, mimetype, path));
|
||||
|
||||
// Callers should not rely on modifying the result
|
||||
return Collections.unmodifiableCollection(resources);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all declared JavaScript resources associated with this
|
||||
* extension. JavaScript resources are declared within the extension
|
||||
@@ -224,7 +320,7 @@ public class Extension {
|
||||
* All declared JavaScript resources associated with this extension.
|
||||
*/
|
||||
public Collection<Resource> getJavaScriptResources() {
|
||||
return getClassPathResources("text/javascript", manifest.getJavaScriptPaths());
|
||||
return javaScriptResources;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,7 +331,19 @@ public class Extension {
|
||||
* All declared CSS resources associated with this extension.
|
||||
*/
|
||||
public Collection<Resource> getCSSResources() {
|
||||
return getClassPathResources("text/css", manifest.getCSSPaths());
|
||||
return cssResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all declared authentication providers classes associated with
|
||||
* this extension. Authentication providers are declared within the
|
||||
* extension manifest.
|
||||
*
|
||||
* @return
|
||||
* All declared authentication provider classes with this extension.
|
||||
*/
|
||||
public Collection<Class<AuthenticationProvider>> getAuthenticationProviderClasses() {
|
||||
return authenticationProviderClasses;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -59,6 +59,12 @@ public class ExtensionManifest {
|
||||
*/
|
||||
private Collection<String> cssPaths;
|
||||
|
||||
/**
|
||||
* The names of all authentication provider classes within this extension,
|
||||
* if any.
|
||||
*/
|
||||
private Collection<String> authProviders;
|
||||
|
||||
/**
|
||||
* Returns the name of the extension associated with this manifest. The
|
||||
* name is human-readable, for display purposes only, and is defined within
|
||||
@@ -169,4 +175,32 @@ public class ExtensionManifest {
|
||||
this.cssPaths = cssPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the classnames of all authentication provider classes within the
|
||||
* extension. These classnames are defined within the manifest by the
|
||||
* "authProviders" property as an array of strings, where each string is an
|
||||
* authentication provider classname.
|
||||
*
|
||||
* @return
|
||||
* A collection of classnames of all authentication providers within
|
||||
* the extension.
|
||||
*/
|
||||
public Collection<String> getAuthProviders() {
|
||||
return authProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the classnames of all authentication provider classes within the
|
||||
* extension. These classnames are defined within the manifest by the
|
||||
* "authProviders" property as an array of strings, where each string is an
|
||||
* authentication provider classname.
|
||||
*
|
||||
* @param authProviders
|
||||
* A collection of classnames of all authentication providers within
|
||||
* the extension.
|
||||
*/
|
||||
public void setAuthProviders(Collection<String> authProviders) {
|
||||
this.authProviders = authProviders;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@
|
||||
|
||||
package org.glyptodon.guacamole.net.basic.extension;
|
||||
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
@@ -29,6 +30,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import org.glyptodon.guacamole.GuacamoleException;
|
||||
import org.glyptodon.guacamole.environment.Environment;
|
||||
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
|
||||
import org.glyptodon.guacamole.net.basic.resource.Resource;
|
||||
import org.glyptodon.guacamole.net.basic.resource.ResourceServlet;
|
||||
import org.glyptodon.guacamole.net.basic.resource.SequenceResource;
|
||||
@@ -118,6 +120,13 @@ public class ExtensionModule extends ServletModule {
|
||||
javaScriptResources.addAll(extension.getJavaScriptResources());
|
||||
cssResources.addAll(extension.getCSSResources());
|
||||
|
||||
// Load all authentication providers as singletons
|
||||
Collection<Class<AuthenticationProvider>> authenticationProviders = extension.getAuthenticationProviderClasses();
|
||||
for (Class<AuthenticationProvider> authenticationProvider : authenticationProviders) {
|
||||
logger.debug("Binding AuthenticationProvider \"{}\".", authenticationProvider);
|
||||
bind(AuthenticationProvider.class).to(authenticationProvider).in(Singleton.class);
|
||||
}
|
||||
|
||||
// Log successful loading of extension by name
|
||||
logger.info("Extension \"{}\" loaded.", extension.getName());
|
||||
|
||||
|
@@ -84,15 +84,20 @@ public class RESTAuthModule extends AbstractModule {
|
||||
bind(AuthenticationService.class);
|
||||
bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class);
|
||||
|
||||
// Get and bind auth provider instance
|
||||
// Get and bind auth provider instance, if defined via property
|
||||
try {
|
||||
AuthenticationProvider authProvider = environment.getRequiredProperty(BasicGuacamoleProperties.AUTH_PROVIDER);
|
||||
bind(AuthenticationProvider.class).toInstance(authProvider);
|
||||
|
||||
// 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\" is now deprecated. Please use the \"extensions\" directory within GUACAMOLE_HOME instead.");
|
||||
bind(AuthenticationProvider.class).toInstance(authProvider);
|
||||
}
|
||||
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("Unable to read authentication provider from guacamole.properties: {}", e.getMessage());
|
||||
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);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user