diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java index 9a8c67235..051f6a9cb 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java @@ -98,6 +98,18 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS @Inject private ConnectionRecordMapper connectionRecordMapper; + /** + * The hostname to use when connecting to guacd if no hostname is provided + * within guacamole.properties. + */ + private static final String DEFAULT_GUACD_HOSTNAME = "localhost"; + + /** + * The port to use when connecting to guacd if no port is provided within + * guacamole.properties. + */ + private static final int DEFAULT_GUACD_PORT = 4822; + /** * All active connections through the tunnel having a given UUID. */ @@ -266,17 +278,17 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS throws GuacamoleException { // Use SSL if requested - if (environment.getProperty(Environment.GUACD_SSL, true)) - return new ManagedInetGuacamoleSocket( - environment.getRequiredProperty(Environment.GUACD_HOSTNAME), - environment.getRequiredProperty(Environment.GUACD_PORT), + if (environment.getProperty(Environment.GUACD_SSL, false)) + return new ManagedSSLGuacamoleSocket( + environment.getProperty(Environment.GUACD_HOSTNAME, DEFAULT_GUACD_HOSTNAME), + environment.getProperty(Environment.GUACD_PORT, DEFAULT_GUACD_PORT), socketClosedCallback ); // Otherwise, just use straight TCP return new ManagedInetGuacamoleSocket( - environment.getRequiredProperty(Environment.GUACD_HOSTNAME), - environment.getRequiredProperty(Environment.GUACD_PORT), + environment.getProperty(Environment.GUACD_HOSTNAME, DEFAULT_GUACD_HOSTNAME), + environment.getProperty(Environment.GUACD_PORT, DEFAULT_GUACD_PORT), socketClosedCallback ); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ManagedSSLGuacamoleSocket.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ManagedSSLGuacamoleSocket.java new file mode 100644 index 000000000..cf3f3804d --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ManagedSSLGuacamoleSocket.java @@ -0,0 +1,71 @@ +/* + * 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.auth.jdbc.tunnel; + +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.SSLGuacamoleSocket; + +/** + * Implementation of GuacamoleSocket which connects via SSL to a given hostname + * and port. If the socket is closed for any reason, a given task is run. + * + * @author Michael Jumper + */ +public class ManagedSSLGuacamoleSocket extends SSLGuacamoleSocket { + + /** + * The task to run when the socket is closed. + */ + private final Runnable socketClosedTask; + + /** + * Creates a new socket which connects via SSL to a given hostname and + * port. If the socket is closed for any reason, the given task is run. + * + * @param hostname + * The hostname of the Guacamole proxy server to connect to. + * + * @param port + * The port of the Guacamole proxy server to connect to. + * + * @param socketClosedTask + * The task to run when the socket is closed. This task will NOT be + * run if an exception occurs during connection, and this + * ManagedInetGuacamoleSocket instance is ultimately not created. + * + * @throws GuacamoleException + * If an error occurs while connecting to the Guacamole proxy server. + */ + public ManagedSSLGuacamoleSocket(String hostname, int port, + Runnable socketClosedTask) throws GuacamoleException { + super(hostname, port); + this.socketClosedTask = socketClosedTask; + } + + @Override + public void close() throws GuacamoleException { + super.close(); + socketClosedTask.run(); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml index 436cb6ca3..b0d898f95 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml @@ -51,6 +51,7 @@ unpack-dependencies + runtime ${project.build.directory}/classes diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/guac-manifest.json new file mode 100644 index 000000000..3c7deb865 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/guac-manifest.json @@ -0,0 +1,13 @@ +{ + + "guacamoleVersion" : "0.9.6", + + "name" : "MySQL Authentication", + "namespace" : "guac-mysql", + + "authProviders" : [ + "net.sourceforge.guacamole.net.auth.mysql.MySQLAuthenticationProvider" + ] + +} + diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml index 207348690..85db10341 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml @@ -51,6 +51,7 @@ unpack-dependencies + runtime ${project.build.directory}/classes diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/guac-manifest.json new file mode 100644 index 000000000..2be870f25 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/guac-manifest.json @@ -0,0 +1,13 @@ +{ + + "guacamoleVersion" : "0.9.6", + + "name" : "PostgreSQL Authentication", + "namespace" : "guac-postgresql", + + "authProviders" : [ + "org.glyptodon.guacamole.auth.postgresql.PostgreSQLAuthenticationProvider" + ] + +} + diff --git a/extensions/guacamole-auth-ldap/pom.xml b/extensions/guacamole-auth-ldap/pom.xml index 8e67ab405..340b5f4d9 100644 --- a/extensions/guacamole-auth-ldap/pom.xml +++ b/extensions/guacamole-auth-ldap/pom.xml @@ -32,6 +32,26 @@ + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + unpack-dependencies + prepare-package + + unpack-dependencies + + + runtime + ${project.build.directory}/classes + + + + + maven-assembly-plugin @@ -64,6 +84,7 @@ org.glyptodon.guacamole guacamole-common 0.9.6 + provided @@ -71,6 +92,7 @@ org.glyptodon.guacamole guacamole-ext 0.9.6 + provided diff --git a/extensions/guacamole-auth-ldap/src/main/assembly/dist.xml b/extensions/guacamole-auth-ldap/src/main/assembly/dist.xml index 9b0b27dde..59a416b70 100644 --- a/extensions/guacamole-auth-ldap/src/main/assembly/dist.xml +++ b/extensions/guacamole-auth-ldap/src/main/assembly/dist.xml @@ -11,43 +11,29 @@ tar.gz - + - - - doc - + + + doc + - - - schema - schema - + + + schema + schema + + + + + target + + + *.jar + + - - - - - lib - runtime - false - true - true - - - - - org.glyptodon.guacamole:guacamole-common - - - org.glyptodon.guacamole:guacamole-ext - - - - - diff --git a/extensions/guacamole-auth-ldap/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-ldap/src/main/resources/guac-manifest.json new file mode 100644 index 000000000..e8d9a9e7b --- /dev/null +++ b/extensions/guacamole-auth-ldap/src/main/resources/guac-manifest.json @@ -0,0 +1,12 @@ +{ + + "guacamoleVersion" : "0.9.6", + + "name" : "LDAP Authentication", + "namespace" : "guac-ldap", + + "authProviders" : [ + "net.sourceforge.guacamole.net.auth.ldap.LDAPAuthenticationProvider" + ] + +} diff --git a/extensions/guacamole-auth-noauth/pom.xml b/extensions/guacamole-auth-noauth/pom.xml index 4596d32d6..67899f164 100644 --- a/extensions/guacamole-auth-noauth/pom.xml +++ b/extensions/guacamole-auth-noauth/pom.xml @@ -32,6 +32,26 @@ + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + unpack-dependencies + prepare-package + + unpack-dependencies + + + runtime + ${project.build.directory}/classes + + + + + maven-assembly-plugin @@ -64,6 +84,7 @@ org.glyptodon.guacamole guacamole-common 0.9.6 + provided @@ -71,6 +92,7 @@ org.glyptodon.guacamole guacamole-ext 0.9.6 + provided diff --git a/extensions/guacamole-auth-noauth/src/main/assembly/dist.xml b/extensions/guacamole-auth-noauth/src/main/assembly/dist.xml index 610749432..834d5beff 100644 --- a/extensions/guacamole-auth-noauth/src/main/assembly/dist.xml +++ b/extensions/guacamole-auth-noauth/src/main/assembly/dist.xml @@ -11,7 +11,7 @@ tar.gz - + @@ -19,29 +19,15 @@ doc + + + target + + + *.jar + + + - - - - - lib - runtime - false - true - true - - - - - org.glyptodon.guacamole:guacamole-common - - - org.glyptodon.guacamole:guacamole-ext - - - - - diff --git a/extensions/guacamole-auth-noauth/src/main/java/net/sourceforge/guacamole/net/auth/noauth/NoAuthenticationProvider.java b/extensions/guacamole-auth-noauth/src/main/java/net/sourceforge/guacamole/net/auth/noauth/NoAuthenticationProvider.java index 7b0adfafb..3752f8029 100644 --- a/extensions/guacamole-auth-noauth/src/main/java/net/sourceforge/guacamole/net/auth/noauth/NoAuthenticationProvider.java +++ b/extensions/guacamole-auth-noauth/src/main/java/net/sourceforge/guacamole/net/auth/noauth/NoAuthenticationProvider.java @@ -54,7 +54,6 @@ import org.xml.sax.helpers.XMLReaderFactory; * * Example `guacamole.properties`: * - * auth-provider: net.sourceforge.guacamole.net.auth.noauth.NoAuthenticationProvider * noauth-config: /etc/guacamole/noauth-config.xml * * @@ -93,7 +92,7 @@ public class NoAuthenticationProvider extends SimpleAuthenticationProvider { private final Environment environment; /** - * The filename of the XML file to read the user mapping from. + * The XML file to read the configuration from. */ public static final FileGuacamoleProperty NOAUTH_CONFIG = new FileGuacamoleProperty() { @@ -104,6 +103,12 @@ public class NoAuthenticationProvider extends SimpleAuthenticationProvider { }; + /** + * The default filename to use for the configuration, if not defined within + * guacamole.properties. + */ + public static final String DEFAULT_NOAUTH_CONFIG = "noauth-config.xml"; + /** * Creates a new NoAuthenticationProvider that does not perform any * authentication at all. All attempts to access the Guacamole system are @@ -125,7 +130,14 @@ public class NoAuthenticationProvider extends SimpleAuthenticationProvider { * property. */ private File getConfigurationFile() throws GuacamoleException { - return environment.getRequiredProperty(NOAUTH_CONFIG); + + // Get config file, defaulting to GUACAMOLE_HOME/noauth-config.xml + File configFile = environment.getProperty(NOAUTH_CONFIG); + if (configFile == null) + configFile = new File(environment.getGuacamoleHome(), DEFAULT_NOAUTH_CONFIG); + + return configFile; + } public synchronized void init() throws GuacamoleException { diff --git a/extensions/guacamole-auth-noauth/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-noauth/src/main/resources/guac-manifest.json new file mode 100644 index 000000000..03fd29916 --- /dev/null +++ b/extensions/guacamole-auth-noauth/src/main/resources/guac-manifest.json @@ -0,0 +1,12 @@ +{ + + "guacamoleVersion" : "0.9.6", + + "name" : "Disabled Authentication", + "namespace" : "guac-noauth", + + "authProviders" : [ + "net.sourceforge.guacamole.net.auth.noauth.NoAuthenticationProvider" + ] + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/environment/LocalEnvironment.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/environment/LocalEnvironment.java index 5421bceec..35200c0d1 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/environment/LocalEnvironment.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/environment/LocalEnvironment.java @@ -95,28 +95,28 @@ public class LocalEnvironment implements Environment { properties = new Properties(); try { - InputStream stream; + InputStream stream = null; // If not a directory, load from classpath - if (!guacHome.isDirectory()) { - - // Read from classpath + if (!guacHome.isDirectory()) stream = LocalEnvironment.class.getResourceAsStream("/guacamole.properties"); - if (stream == null) - throw new GuacamoleServerException( - "guacamole.properties not loaded from " + guacHome - + " (not a directory), and guacamole.properties could" - + " not be found as a resource in the classpath."); - - } // Otherwise, try to load from file - else - stream = new FileInputStream(new File(guacHome, "guacamole.properties")); + else { + File propertiesFile = new File(guacHome, "guacamole.properties"); + if (propertiesFile.exists()) + stream = new FileInputStream(propertiesFile); + } - // Load properties, always close stream - try { properties.load(stream); } - finally { stream.close(); } + // Load properties from stream, if any, always closing stream when done + if (stream != null) { + try { properties.load(stream); } + finally { stream.close(); } + } + + // Notify if we're proceeding without guacamole.properties + else + logger.info("No guacamole.properties file found within GUACAMOLE_HOME or the classpath. Using defaults."); } catch (IOException e) { diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java index ee3973359..e5f2f0d1e 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java @@ -45,6 +45,18 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; */ public class SimpleConnection extends AbstractConnection { + /** + * The hostname to use when connecting to guacd if no hostname is provided + * within guacamole.properties. + */ + private static final String DEFAULT_GUACD_HOSTNAME = "localhost"; + + /** + * The port to use when connecting to guacd if no port is provided within + * guacamole.properties. + */ + private static final int DEFAULT_GUACD_PORT = 4822; + /** * Backing configuration, containing all sensitive information. */ @@ -92,8 +104,8 @@ public class SimpleConnection extends AbstractConnection { Environment env = new LocalEnvironment(); // Get guacd connection parameters - String hostname = env.getProperty(Environment.GUACD_HOSTNAME); - int port = env.getProperty(Environment.GUACD_PORT); + String hostname = env.getProperty(Environment.GUACD_HOSTNAME, DEFAULT_GUACD_HOSTNAME); + int port = env.getProperty(Environment.GUACD_PORT, DEFAULT_GUACD_PORT); GuacamoleSocket socket; diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java index 6694f5eea..fd7ffda9f 100644 --- a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java +++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java @@ -76,7 +76,7 @@ public class BasicFileAuthenticationProvider extends SimpleAuthenticationProvide private final Environment environment; /** - * The filename of the XML file to read the user user_mapping from. + * The XML file to read the user mapping from. */ public static final FileGuacamoleProperty BASIC_USER_MAPPING = new FileGuacamoleProperty() { @@ -85,6 +85,12 @@ public class BasicFileAuthenticationProvider extends SimpleAuthenticationProvide }; + /** + * The default filename to use for the user mapping, if not defined within + * guacamole.properties. + */ + public static final String DEFAULT_USER_MAPPING = "user-mapping.xml"; + /** * Creates a new BasicFileAuthenticationProvider that authenticates users * against simple, monolithic XML file. @@ -110,9 +116,10 @@ public class BasicFileAuthenticationProvider extends SimpleAuthenticationProvide */ private UserMapping getUserMapping() throws GuacamoleException { - // Get user user_mapping file - File user_mapping_file = - environment.getRequiredProperty(BASIC_USER_MAPPING); + // Get user mapping file, defaulting to GUACAMOLE_HOME/user-mapping.xml + File user_mapping_file = environment.getProperty(BASIC_USER_MAPPING); + if (user_mapping_file == null) + user_mapping_file = new File(environment.getGuacamoleHome(), DEFAULT_USER_MAPPING); // If user_mapping not yet read, or user_mapping has been modified, reread if (user_mapping == null || diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicServletContextListener.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicServletContextListener.java index 82f3983de..60eeffdbb 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicServletContextListener.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicServletContextListener.java @@ -30,6 +30,7 @@ import javax.servlet.ServletContextEvent; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.environment.LocalEnvironment; +import org.glyptodon.guacamole.net.basic.extension.ExtensionModule; import org.glyptodon.guacamole.net.basic.log.LogModule; import org.glyptodon.guacamole.net.basic.rest.RESTAuthModule; import org.glyptodon.guacamole.net.basic.rest.RESTServletModule; @@ -83,7 +84,8 @@ public class BasicServletContextListener extends GuiceServletContextListener { return Guice.createInjector(Stage.PRODUCTION, new EnvironmentModule(environment), new LogModule(environment), - new RESTAuthModule(environment, sessionMap), + new ExtensionModule(environment), + new RESTAuthModule(sessionMap), new RESTServletModule(), new TunnelModule() ); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleClassLoader.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleClassLoader.java index 1dcb37048..8c3f40c94 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleClassLoader.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleClassLoader.java @@ -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 { /** 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 ed2223436..799907b39 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 @@ -60,11 +60,6 @@ public class GuacamoleSession { */ private UserContext userContext; - /** - * Collection of all event listeners configured in guacamole.properties. - */ - private final Collection listeners = new ArrayList(); - /** * The current clipboard state. */ @@ -98,52 +93,9 @@ public class GuacamoleSession { */ public GuacamoleSession(Environment environment, Credentials credentials, UserContext userContext) throws GuacamoleException { - this.lastAccessedTime = System.currentTimeMillis(); this.credentials = credentials; this.userContext = userContext; - - // Load listeners from guacamole.properties - try { - - // Get all listener classes from properties - Collection listenerClasses = - environment.getProperty(BasicGuacamoleProperties.EVENT_LISTENERS); - - // Add an instance of each class to the list - if (listenerClasses != null) { - for (Class listenerClass : listenerClasses) { - - // Instantiate listener - Object listener = listenerClass.getConstructor().newInstance(); - - // Add listener to collection of listeners - listeners.add(listener); - - } - } - - } - catch (InstantiationException e) { - throw new GuacamoleException("Listener class is abstract.", e); - } - catch (IllegalAccessException e) { - throw new GuacamoleException("No access to listener constructor.", e); - } - catch (IllegalArgumentException e) { - // This should not happen, given there ARE no arguments - throw new GuacamoleException("Illegal arguments to listener constructor.", e); - } - catch (InvocationTargetException e) { - throw new GuacamoleException("Error while instantiating listener.", e); - } - catch (NoSuchMethodException e) { - throw new GuacamoleException("Listener has no default constructor.", e); - } - catch (SecurityException e) { - throw new GuacamoleException("Security restrictions prevent instantiation of listener.", e); - } - } /** @@ -198,19 +150,6 @@ public class GuacamoleSession { return clipboardState; } - /** - * Returns a collection which iterates over instances of all listeners - * defined in guacamole.properties. For each listener defined in - * guacamole.properties, a new instance is created and stored in this - * collection. - * - * @return A collection which iterates over instances of all listeners - * defined in guacamole.properties. - */ - public Collection getListeners() { - return Collections.unmodifiableCollection(listeners); - } - /** * Returns whether this session has any associated active tunnels. * diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelModule.java index 4df334947..9ee464628 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelModule.java @@ -24,7 +24,6 @@ package org.glyptodon.guacamole.net.basic; import com.google.inject.servlet.ServletModule; import java.lang.reflect.InvocationTargetException; -import org.glyptodon.guacamole.GuacamoleException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,7 +54,7 @@ public class TunnelModule extends ServletModule { try { // Attempt to find WebSocket module - Class module = (Class) GuacamoleClassLoader.getInstance().findClass(classname); + Class module = Class.forName(classname); // Create loader TunnelLoader loader = (TunnelLoader) module.getConstructor().newInstance(); @@ -85,12 +84,6 @@ public class TunnelModule extends ServletModule { logger.debug("Error instantiating WebSocket module.", e); } - // Log all GuacamoleExceptions - catch (GuacamoleException e) { - logger.error("Unable to load/detect WebSocket support: {}", e.getMessage()); - logger.debug("Error loading/detecting WebSocket support.", e); - } - // Load attempt failed return false; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java index 3f312160a..cee5cecc1 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java @@ -38,10 +38,6 @@ import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; -import org.glyptodon.guacamole.net.event.TunnelCloseEvent; -import org.glyptodon.guacamole.net.event.TunnelConnectEvent; -import org.glyptodon.guacamole.net.event.listener.TunnelCloseListener; -import org.glyptodon.guacamole.net.event.listener.TunnelConnectListener; import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,84 +72,6 @@ public class TunnelRequestService { @Inject private AuthenticationService authenticationService; - /** - * Notifies all listeners in the given session that a tunnel has been - * connected. - * - * @param session The session associated with the listeners to be notified. - * @param tunnel The tunnel being connected. - * @return true if all listeners are allowing the tunnel to connect, - * or if there are no listeners, and false if any listener is - * canceling the connection. Note that once one listener cancels, - * no other listeners will run. - * @throws GuacamoleException If any listener throws an error while being - * notified. Note that if any listener throws an - * error, the connect is canceled, and no other - * listeners will run. - */ - private boolean notifyConnect(GuacamoleSession session, GuacamoleTunnel tunnel) - throws GuacamoleException { - - // Build event for auth success - TunnelConnectEvent event = new TunnelConnectEvent( - session.getUserContext(), - session.getCredentials(), - tunnel); - - // Notify all listeners - for (Object listener : session.getListeners()) { - if (listener instanceof TunnelConnectListener) { - - // Cancel immediately if hook returns false - if (!((TunnelConnectListener) listener).tunnelConnected(event)) - return false; - - } - } - - return true; - - } - - /** - * Notifies all listeners in the given session that a tunnel has been - * closed. - * - * @param session The session associated with the listeners to be notified. - * @param tunnel The tunnel being closed. - * @return true if all listeners are allowing the tunnel to close, - * or if there are no listeners, and false if any listener is - * canceling the close. Note that once one listener cancels, - * no other listeners will run. - * @throws GuacamoleException If any listener throws an error while being - * notified. Note that if any listener throws an - * error, the close is canceled, and no other - * listeners will run. - */ - private boolean notifyClose(GuacamoleSession session, GuacamoleTunnel tunnel) - throws GuacamoleException { - - // Build event for auth success - TunnelCloseEvent event = new TunnelCloseEvent( - session.getUserContext(), - session.getCredentials(), - tunnel); - - // Notify all listeners - for (Object listener : session.getListeners()) { - if (listener instanceof TunnelCloseListener) { - - // Cancel immediately if hook returns false - if (!((TunnelCloseListener) listener).tunnelClosed(event)) - return false; - - } - } - - return true; - - } - /** * Reads and returns the client information provided within the given * request. @@ -335,10 +253,6 @@ public class TunnelRequestService { @Override public void close() throws GuacamoleException { - // Signal listeners - if (!notifyClose(session, this)) - throw new GuacamoleException("Tunnel close canceled by listener."); - session.removeTunnel(getUUID().toString()); // Close if no exception due to listener @@ -348,12 +262,6 @@ public class TunnelRequestService { }; - // Notify listeners about connection - if (!notifyConnect(session, monitoredTunnel)) { - logger.info("Successful connection canceled by hook."); - return null; - } - // Associate tunnel with session session.addTunnel(monitoredTunnel); return monitoredTunnel; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/DirectoryClassLoader.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/DirectoryClassLoader.java new file mode 100644 index 000000000..4a4a8f271 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/DirectoryClassLoader.java @@ -0,0 +1,154 @@ +/* + * 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.basic.extension; + +import java.io.File; +import java.io.FilenameFilter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Collection; +import org.glyptodon.guacamole.GuacamoleException; + +/** + * A ClassLoader implementation which finds classes within .jar files within a + * given directory. + * + * @author Michael Jumper + */ +public class DirectoryClassLoader extends URLClassLoader { + + /** + * Returns an instance of DirectoryClassLoader configured to load .jar + * files from the given directory. Calling this function multiple times + * will not affect previously-returned instances of DirectoryClassLoader. + * + * @param dir + * The directory from which .jar files should be read. + * + * @return + * A DirectoryClassLoader instance which loads classes from the .jar + * files in the given directory. + * + * @throws GuacamoleException + * If the given file is not a directory, or the contents of the given + * directory cannot be read. + */ + public static DirectoryClassLoader getInstance(final File dir) + throws GuacamoleException { + + try { + // Attempt to create singleton classloader which loads classes from + // all .jar's in the lib directory defined in guacamole.properties + return AccessController.doPrivileged(new PrivilegedExceptionAction() { + + @Override + public DirectoryClassLoader run() throws GuacamoleException { + return new DirectoryClassLoader(dir); + } + + }); + } + + catch (PrivilegedActionException e) { + throw (GuacamoleException) e.getException(); + } + + } + + /** + * Returns all .jar files within the given directory as an array of URLs. + * + * @param dir + * The directory to retrieve all .jar files from. + * + * @return + * An array of the URLs of all .jar files within the given directory. + * + * @throws GuacamoleException + * If the given file is not a directory, or the contents of the given + * directory cannot be read. + */ + private static URL[] getJarURLs(File dir) throws GuacamoleException { + + // Validate directory is indeed a directory + if (!dir.isDirectory()) + throw new GuacamoleException(dir + " is not a directory."); + + // Get list of URLs for all .jar's in the lib directory + Collection jarURLs = new ArrayList(); + File[] files = dir.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + + // If it ends with .jar, accept the file + return name.endsWith(".jar"); + + } + + }); + + // Verify directory was successfully read + if (files == null) + throw new GuacamoleException("Unable to read contents of directory " + dir); + + // Add the URL for each .jar to the jar URL list + for (File file : files) { + + try { + jarURLs.add(file.toURI().toURL()); + } + catch (MalformedURLException e) { + throw new GuacamoleException(e); + } + + } + + // Set delegate classloader to new URLClassLoader which loads from the .jars found above. + URL[] urls = new URL[jarURLs.size()]; + return jarURLs.toArray(urls); + + } + + /** + * Creates a new DirectoryClassLoader configured to load .jar files from + * the given directory. + * + * @param dir + * The directory from which .jar files should be read. + * + * @throws GuacamoleException + * If the given file is not a directory, or the contents of the given + * directory cannot be read. + */ + + private DirectoryClassLoader(File dir) throws GuacamoleException { + super(getJarURLs(dir), DirectoryClassLoader.class.getClassLoader()); + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/Extension.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/Extension.java new file mode 100644 index 000000000..2f3aa17c4 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/Extension.java @@ -0,0 +1,361 @@ +/* + * 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.basic.extension; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +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; + +/** + * A Guacamole extension, which may provide custom authentication, static + * files, theming/branding, etc. + * + * @author Michael Jumper + */ +public class Extension { + + /** + * The Jackson parser for parsing the language JSON files. + */ + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * The name of the manifest file that describes the contents of a + * Guacamole extension. + */ + private static final String MANIFEST_NAME = "guac-manifest.json"; + + /** + * The parsed manifest file of this extension, describing the location of + * resources within the extension. + */ + private final ExtensionManifest manifest; + + /** + * The classloader to use when reading resources from this extension, + * including classes and static files. + */ + private final ClassLoader classLoader; + + /** + * The collection of all JavaScript resources defined within the extension. + */ + private final Collection javaScriptResources; + + /** + * The collection of all CSS resources defined within the extension. + */ + private final Collection cssResources; + + /** + * The collection of all AuthenticationProvider classes defined within the + * extension. + */ + private final Collection> 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 getClassPathResources(String mimetype, Collection paths) { + + // If no paths are provided, just return an empty list + if (paths == null) + return Collections.emptyList(); + + // Add classpath resource for each path provided + Collection resources = new ArrayList(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 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) 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> getAuthenticationProviderClasses(Collection names) + throws GuacamoleException { + + // If no classnames are provided, just return an empty list + if (names == null) + return Collections.>emptyList(); + + // Define all auth provider classes + Collection> classes = new ArrayList>(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. + * + * @param parent + * The classloader to use as the parent for the isolated classloader of + * this extension. + * + * @param file + * The file to load as an extension. + * + * @throws GuacamoleException + * If the provided file is not a .jar file, does not contain the + * guac-manifest.json, or if guac-manifest.json is invalid and cannot + * be parsed. + */ + public Extension(final ClassLoader parent, final File file) throws GuacamoleException { + + try { + + // Open extension + ZipFile extension = new ZipFile(file); + + try { + + // Retrieve extension manifest + ZipEntry manifestEntry = extension.getEntry(MANIFEST_NAME); + if (manifestEntry == null) + throw new GuacamoleServerException("Extension " + file.getName() + " is missing " + MANIFEST_NAME); + + // Parse manifest + manifest = mapper.readValue(extension.getInputStream(manifestEntry), ExtensionManifest.class); + + } + + // Always close zip file, if possible + finally { + extension.close(); + } + + try { + + // Create isolated classloader for this extension + classLoader = AccessController.doPrivileged(new PrivilegedExceptionAction() { + + @Override + public ClassLoader run() throws GuacamoleException { + + try { + + // Classloader must contain only the extension itself + return new URLClassLoader(new URL[]{file.toURI().toURL()}, parent); + + } + catch (MalformedURLException e) { + throw new GuacamoleException(e); + } + + } + + }); + + } + + // Rethrow any GuacamoleException + catch (PrivilegedActionException e) { + throw (GuacamoleException) e.getException(); + } + + } + + // Abort load if not a valid zip file + catch (ZipException e) { + throw new GuacamoleServerException("Extension is not a valid zip file: " + file.getName(), e); + } + + // Abort if manifest cannot be parsed (invalid JSON) + catch (JsonParseException e) { + throw new GuacamoleServerException(MANIFEST_NAME + " is not valid JSON: " + file.getName(), e); + } + + // Abort if zip file cannot be read at all due to I/O errors + catch (IOException e) { + 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()); + + } + + /** + * Returns the version of the Guacamole web application for which this + * extension was built. + * + * @return + * The version of the Guacamole web application for which this + * extension was built. + */ + public String getGuacamoleVersion() { + return manifest.getGuacamoleVersion(); + } + + /** + * Returns the name of this extension, as declared in the extension's + * manifest. + * + * @return + * The name of this extension. + */ + public String getName() { + return manifest.getName(); + } + + /** + * Returns the namespace of this extension, as declared in the extension's + * manifest. + * + * @return + * The namespace of this extension. + */ + public String getNamespace() { + return manifest.getNamespace(); + } + + /** + * Returns all declared JavaScript resources associated with this + * extension. JavaScript resources are declared within the extension + * manifest. + * + * @return + * All declared JavaScript resources associated with this extension. + */ + public Collection getJavaScriptResources() { + return javaScriptResources; + } + + /** + * Returns all declared CSS resources associated with this extension. CSS + * resources are declared within the extension manifest. + * + * @return + * All declared CSS resources associated with this extension. + */ + public Collection getCSSResources() { + 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> getAuthenticationProviderClasses() { + return authenticationProviderClasses; + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/ExtensionManifest.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/ExtensionManifest.java new file mode 100644 index 000000000..093af3b0d --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/ExtensionManifest.java @@ -0,0 +1,237 @@ +/* + * 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.basic.extension; + +import java.util.Collection; +import org.codehaus.jackson.annotate.JsonProperty; + +/** + * Java representation of the JSON manifest contained within every Guacamole + * extension, identifying an extension and describing its contents. + * + * @author Michael Jumper + */ +public class ExtensionManifest { + + /** + * The version of Guacamole for which this extension was built. + * Compatibility rules built into the web application will guard against + * incompatible extensions being loaded. + */ + private String guacamoleVersion; + + /** + * The name of the extension associated with this manifest. The extension + * name is human-readable, and used for display purposes only. + */ + private String name; + + /** + * The namespace of the extension associated with this manifest. The + * extension namespace is required for internal use, and is used wherever + * extension-specific files or resources need to be isolated from those of + * other extensions. + */ + private String namespace; + + /** + * The paths of all JavaScript resources within the .jar of the extension + * associated with this manifest. + */ + private Collection javaScriptPaths; + + /** + * The paths of all CSS resources within the .jar of the extension + * associated with this manifest. + */ + private Collection cssPaths; + + /** + * The names of all authentication provider classes within this extension, + * if any. + */ + private Collection authProviders; + + /** + * Returns the version of the Guacamole web application for which the + * extension was built, such as "0.9.6". + * + * @return + * The version of the Guacamole web application for which the extension + * was built. + */ + public String getGuacamoleVersion() { + return guacamoleVersion; + } + + /** + * Sets the version of the Guacamole web application for which the + * extension was built, such as "0.9.6". + * + * @param guacamoleVersion + * The version of the Guacamole web application for which the extension + * was built. + */ + public void setGuacamoleVersion(String guacamoleVersion) { + this.guacamoleVersion = guacamoleVersion; + } + + /** + * Returns the name of the extension associated with this manifest. The + * name is human-readable, for display purposes only, and is defined within + * the manifest by the "name" property. + * + * @return + * The name of the extension associated with this manifest. + */ + public String getName() { + return name; + } + + /** + * Sets the name of the extension associated with this manifest. The name + * is human-readable, for display purposes only, and is defined within the + * manifest by the "name" property. + * + * @param name + * The name of the extension associated with this manifest. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the namespace of the extension associated with this manifest. + * The namespace is required for internal use, and is used wherever + * extension-specific files or resources need to be isolated from those of + * other extensions. It is defined within the manifest by the "namespace" + * property. + * + * @return + * The namespace of the extension associated with this manifest. + */ + public String getNamespace() { + return namespace; + } + + /** + * Sets the namespace of the extension associated with this manifest. The + * namespace is required for internal use, and is used wherever extension- + * specific files or resources need to be isolated from those of other + * extensions. It is defined within the manifest by the "namespace" + * property. + * + * @param namespace + * The namespace of the extension associated with this manifest. + */ + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + /** + * Returns the paths to all JavaScript resources within the extension. + * These paths are defined within the manifest by the "js" property as an + * array of strings, where each string is a path relative to the root of + * the extension .jar. + * + * @return + * A collection of paths to all JavaScript resources within the + * extension. + */ + @JsonProperty("js") + public Collection getJavaScriptPaths() { + return javaScriptPaths; + } + + /** + * Sets the paths to all JavaScript resources within the extension. These + * paths are defined within the manifest by the "js" property as an array + * of strings, where each string is a path relative to the root of the + * extension .jar. + * + * @param javaScriptPaths + * A collection of paths to all JavaScript resources within the + * extension. + */ + @JsonProperty("js") + public void setJavaScriptPaths(Collection javaScriptPaths) { + this.javaScriptPaths = javaScriptPaths; + } + + /** + * Returns the paths to all CSS resources within the extension. These paths + * are defined within the manifest by the "js" property as an array of + * strings, where each string is a path relative to the root of the + * extension .jar. + * + * @return + * A collection of paths to all CSS resources within the extension. + */ + @JsonProperty("css") + public Collection getCSSPaths() { + return cssPaths; + } + + /** + * Sets the paths to all CSS resources within the extension. These paths + * are defined within the manifest by the "js" property as an array of + * strings, where each string is a path relative to the root of the + * extension .jar. + * + * @param cssPaths + * A collection of paths to all CSS resources within the extension. + */ + @JsonProperty("css") + public void setCSSPaths(Collection cssPaths) { + 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 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 authProviders) { + this.authProviders = authProviders; + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/ExtensionModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/ExtensionModule.java new file mode 100644 index 000000000..e2bae9bdf --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/ExtensionModule.java @@ -0,0 +1,300 @@ +/* + * 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.basic.extension; + +import com.google.inject.Singleton; +import com.google.inject.servlet.ServletModule; +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import net.sourceforge.guacamole.net.basic.BasicFileAuthenticationProvider; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleServerException; +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; +import org.glyptodon.guacamole.net.basic.resource.WebApplicationResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A Guice Module which loads all extensions within the + * GUACAMOLE_HOME/extensions directory, if any. + * + * @author Michael Jumper + */ +public class ExtensionModule extends ServletModule { + + /** + * Logger for this class. + */ + private final Logger logger = LoggerFactory.getLogger(ExtensionModule.class); + + /** + * The version strings of all Guacamole versions whose extensions are + * compatible with this release. + */ + private static final List ALLOWED_GUACAMOLE_VERSIONS = + Collections.unmodifiableList(Arrays.asList( + "0.9.6" + )); + + /** + * The name of the directory within GUACAMOLE_HOME containing any .jars + * which should be included in the classpath of all extensions. + */ + private static final String LIB_DIRECTORY = "lib"; + + /** + * The name of the directory within GUACAMOLE_HOME containing all + * extensions. + */ + private static final String EXTENSIONS_DIRECTORY = "extensions"; + + /** + * The string that the filenames of all extensions must end with to be + * recognized as extensions. + */ + private static final String EXTENSION_SUFFIX = ".jar"; + + /** + * The Guacamole server 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 boundAuthenticationProvider = null; + + /** + * Returns the classloader that should be used as the parent classloader + * for all extensions. If the GUACAMOLE_HOME/lib directory exists, this + * will be a classloader that loads classes from within the .jar files in + * that directory. Lacking the GUACAMOLE_HOME/lib directory, this will + * simply be the classloader associated with the ExtensionModule class. + * + * @return + * The classloader that should be used as the parent classloader for + * all extensions. + * + * @throws GuacamoleException + * If an error occurs while retrieving the classloader. + */ + private ClassLoader getParentClassLoader() throws GuacamoleException { + + // Retrieve lib directory + File libDir = new File(environment.getGuacamoleHome(), LIB_DIRECTORY); + + // If lib directory does not exist, use default class loader + if (!libDir.isDirectory()) + return ExtensionModule.class.getClassLoader(); + + // Return classloader which loads classes from all .jars within the lib directory + return DirectoryClassLoader.getInstance(libDir); + + } + + /** + * Creates a module which loads all extensions within the + * GUACAMOLE_HOME/extensions directory. + * + * @param environment + * The environment to use when configuring authentication. + */ + public ExtensionModule(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 getAuthProviderProperty() { + + // Get and bind auth provider instance, if defined via property + try { + + // Use "auth-provider" property if present, but warn about deprecation + Class 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 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); + + } + + /** + * Returns whether the given version of Guacamole is compatible with this + * version of Guacamole as far as extensions are concerned. + * + * @param guacamoleVersion + * The version of Guacamole the extension was built for. + * + * @return + * true if the given version of Guacamole is compatible with this + * version of Guacamole, false otherwise. + */ + private boolean isCompatible(String guacamoleVersion) { + return ALLOWED_GUACAMOLE_VERSIONS.contains(guacamoleVersion); + } + + @Override + protected void configureServlets() { + + // Load authentication provider from guacamole.properties for sake of backwards compatibility + Class authProviderProperty = getAuthProviderProperty(); + if (authProviderProperty != null) + bindAuthenticationProvider(authProviderProperty); + + // Retrieve and validate extensions directory + File extensionsDir = new File(environment.getGuacamoleHome(), EXTENSIONS_DIRECTORY); + if (!extensionsDir.isDirectory()) + return; + + // Retrieve list of all extension files within extensions directory + File[] extensionFiles = extensionsDir.listFiles(new FileFilter() { + + @Override + public boolean accept(File file) { + return file.isFile() && file.getName().endsWith(EXTENSION_SUFFIX); + } + + }); + + // Init JavaScript resources with base guacamole.min.js + Collection javaScriptResources = new ArrayList(); + javaScriptResources.add(new WebApplicationResource(getServletContext(), "/guacamole.min.js")); + + // Init CSS resources with base guacamole.min.css + Collection cssResources = new ArrayList(); + cssResources.add(new WebApplicationResource(getServletContext(), "/guacamole.min.css")); + + // Load each extension within the extension directory + for (File extensionFile : extensionFiles) { + + logger.debug("Loading extension: \"{}\"", extensionFile.getName()); + + try { + + // Load extension from file + Extension extension = new Extension(getParentClassLoader(), extensionFile); + + // Validate Guacamole version of extension + if (!isCompatible(extension.getGuacamoleVersion())) { + logger.debug("Declared Guacamole version \"{}\" of extension \"{}\" is not compatible with this version of Guacamole.", + extension.getGuacamoleVersion(), extensionFile.getName()); + throw new GuacamoleServerException("Extension \"" + extension.getName() + "\" is not " + + "compatible with this version of Guacamole."); + } + + // Add any JavaScript / CSS resources + javaScriptResources.addAll(extension.getJavaScriptResources()); + cssResources.addAll(extension.getCSSResources()); + + // Attempt to load all authentication providers + Collection> authenticationProviders = extension.getAuthenticationProviderClasses(); + for (Class authenticationProvider : authenticationProviders) + bindAuthenticationProvider(authenticationProvider); + + // Log successful loading of extension by name + logger.info("Extension \"{}\" loaded.", extension.getName()); + + } + catch (GuacamoleException e) { + logger.error("Extension \"{}\" could not be loaded: {}", extensionFile.getName(), e.getMessage()); + logger.debug("Unable to load extension.", e); + } + + } + + // 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))); + + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/package-info.java new file mode 100644 index 000000000..198e4c4b7 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/extension/package-info.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +/** + * Classes which represent and facilitate the loading of extensions to the + * Guacamole web application. + */ +package org.glyptodon.guacamole.net.basic.extension; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/AuthenticationProviderProperty.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/AuthenticationProviderProperty.java index dd633ed29..c2dd97d04 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/AuthenticationProviderProperty.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/AuthenticationProviderProperty.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 @@ -22,22 +22,24 @@ 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; 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 { +@Deprecated +public abstract class AuthenticationProviderProperty implements GuacamoleProperty> { @Override - public AuthenticationProvider parseValue(String authProviderClassName) throws GuacamoleException { + @SuppressWarnings("unchecked") // Explicitly checked within by isAssignableFrom() + public Class parseValue(String authProviderClassName) throws GuacamoleException { // If no property provided, return null. if (authProviderClassName == null) @@ -46,35 +48,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 = org.glyptodon.guacamole.net.basic.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) 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()); - } } } - diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java index f2de45e3b..a1b2e1336 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java @@ -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 @@ -59,16 +63,6 @@ public class BasicGuacamoleProperties { }; - /** - * The comma-separated list of all classes to use as event listeners. - */ - public static final EventListenersProperty EVENT_LISTENERS = new EventListenersProperty() { - - @Override - public String getName() { return "event-listeners"; } - - }; - /** * The session timeout for the API, in minutes. */ diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/EventListenersProperty.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/EventListenersProperty.java deleted file mode 100644 index 5b7e9a4b5..000000000 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/EventListenersProperty.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2013 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.basic.properties; - -import java.util.ArrayList; -import java.util.Collection; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.basic.GuacamoleClassLoader; -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. - * - * @author Michael Jumper - */ -public abstract class EventListenersProperty implements GuacamoleProperty> { - - @Override - public Collection parseValue(String classNameList) throws GuacamoleException { - - // If no property provided, return null. - if (classNameList == null) - return null; - - // Parse list - String[] classNames = classNameList.split(",[\\s]*"); - - // Fill list of classes - Collection listeners = new ArrayList(); - try { - - // Load all classes in list - for (String className : classNames) { - Class clazz = GuacamoleClassLoader.getInstance().loadClass(className); - listeners.add(clazz); - } - - } - catch (ClassNotFoundException e) { - throw new GuacamoleException("Listener class not found.", e); - } - catch (SecurityException e) { - throw new GuacamoleException("Security settings prevent loading of listener class.", e); - } - - return listeners; - - } - -} - diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTAuthModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTAuthModule.java index d79746ad3..ddc5fff78 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTAuthModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTAuthModule.java @@ -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,17 +70,6 @@ public class RESTAuthModule extends AbstractModule { bind(AuthenticationService.class); bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); - // Get and bind auth provider instance - try { - AuthenticationProvider authProvider = environment.getRequiredProperty(BasicGuacamoleProperties.AUTH_PROVIDER); - bind(AuthenticationProvider.class).toInstance(authProvider); - } - catch (GuacamoleException e) { - logger.error("Unable to read authentication provider from guacamole.properties: {}", e.getMessage()); - logger.debug("Error reading authentication provider from guacamole.properties.", e); - throw new RuntimeException(e); - } - } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/WebSocketTunnelModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/WebSocketTunnelModule.java index 1a42bc2fb..69a62fabe 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/WebSocketTunnelModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/WebSocketTunnelModule.java @@ -28,8 +28,6 @@ import java.util.Arrays; import javax.websocket.DeploymentException; import javax.websocket.server.ServerContainer; import javax.websocket.server.ServerEndpointConfig; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.basic.GuacamoleClassLoader; import org.glyptodon.guacamole.net.basic.TunnelLoader; import org.glyptodon.guacamole.net.basic.TunnelRequestService; import org.slf4j.Logger; @@ -53,7 +51,7 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader try { // Attempt to find WebSocket servlet - GuacamoleClassLoader.getInstance().findClass("javax.websocket.Endpoint"); + Class.forName("javax.websocket.Endpoint"); // Support found return true; @@ -65,12 +63,6 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader catch (ClassNotFoundException e) {} catch (NoClassDefFoundError e) {} - // Log all GuacamoleExceptions - catch (GuacamoleException e) { - logger.error("Unable to load/detect WebSocket support: {}", e.getMessage()); - logger.debug("Error loading/detecting WebSocket support.", e); - } - // Support not found return false; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty8/WebSocketTunnelModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty8/WebSocketTunnelModule.java index 8bf0a4a42..9e94da9b2 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty8/WebSocketTunnelModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty8/WebSocketTunnelModule.java @@ -23,8 +23,6 @@ package org.glyptodon.guacamole.net.basic.websocket.jetty8; import com.google.inject.servlet.ServletModule; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.basic.GuacamoleClassLoader; import org.glyptodon.guacamole.net.basic.TunnelLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +45,7 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader try { // Attempt to find WebSocket servlet - GuacamoleClassLoader.getInstance().findClass("org.glyptodon.guacamole.net.basic.websocket.jetty8.BasicGuacamoleWebSocketTunnelServlet"); + Class.forName("org.glyptodon.guacamole.net.basic.websocket.jetty8.BasicGuacamoleWebSocketTunnelServlet"); // Support found return true; @@ -59,12 +57,6 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader catch (ClassNotFoundException e) {} catch (NoClassDefFoundError e) {} - // Log all GuacamoleExceptions - catch (GuacamoleException e) { - logger.error("Unable to load/detect WebSocket support: {}", e.getMessage()); - logger.debug("Error loading/detecting WebSocket support.", e); - } - // Support not found return false; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty9/WebSocketTunnelModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty9/WebSocketTunnelModule.java index 6a67eae49..de27e0213 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty9/WebSocketTunnelModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty9/WebSocketTunnelModule.java @@ -23,8 +23,6 @@ package org.glyptodon.guacamole.net.basic.websocket.jetty9; import com.google.inject.servlet.ServletModule; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.basic.GuacamoleClassLoader; import org.glyptodon.guacamole.net.basic.TunnelLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +45,7 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader try { // Attempt to find WebSocket servlet - GuacamoleClassLoader.getInstance().findClass("org.glyptodon.guacamole.net.basic.websocket.jetty9.BasicGuacamoleWebSocketTunnelServlet"); + Class.forName("org.glyptodon.guacamole.net.basic.websocket.jetty9.BasicGuacamoleWebSocketTunnelServlet"); // Support found return true; @@ -59,12 +57,6 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader catch (ClassNotFoundException e) {} catch (NoClassDefFoundError e) {} - // Log all GuacamoleExceptions - catch (GuacamoleException e) { - logger.error("Unable to load/detect WebSocket support: {}", e.getMessage()); - logger.debug("Error loading/detecting WebSocket support.", e); - } - // Support not found return false; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/tomcat/WebSocketTunnelModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/tomcat/WebSocketTunnelModule.java index 4c59b78c5..432818601 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/tomcat/WebSocketTunnelModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/tomcat/WebSocketTunnelModule.java @@ -23,8 +23,6 @@ package org.glyptodon.guacamole.net.basic.websocket.tomcat; import com.google.inject.servlet.ServletModule; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.basic.GuacamoleClassLoader; import org.glyptodon.guacamole.net.basic.TunnelLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +45,7 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader try { // Attempt to find WebSocket servlet - GuacamoleClassLoader.getInstance().findClass("org.glyptodon.guacamole.net.basic.websocket.tomcat.BasicGuacamoleWebSocketTunnelServlet"); + Class.forName("org.glyptodon.guacamole.net.basic.websocket.tomcat.BasicGuacamoleWebSocketTunnelServlet"); // Support found return true; @@ -59,12 +57,6 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader catch (ClassNotFoundException e) {} catch (NoClassDefFoundError e) {} - // Log all GuacamoleExceptions - catch (GuacamoleException e) { - logger.error("Unable to load/detect WebSocket support: {}", e.getMessage()); - logger.debug("Error loading/detecting WebSocket support.", e); - } - // Support not found return false; diff --git a/guacamole/src/main/webapp/index.html b/guacamole/src/main/webapp/index.html index eb6d28dad..3303ec1a8 100644 --- a/guacamole/src/main/webapp/index.html +++ b/guacamole/src/main/webapp/index.html @@ -8,7 +8,7 @@ - + - +