mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 13:41:21 +00:00
Merge pull request #178 from glyptodon/extension-format
GUAC-587: Adopt extension format including manifest.
This commit is contained in:
@@ -98,6 +98,18 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
@Inject
|
@Inject
|
||||||
private ConnectionRecordMapper connectionRecordMapper;
|
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.
|
* All active connections through the tunnel having a given UUID.
|
||||||
*/
|
*/
|
||||||
@@ -266,17 +278,17 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
throws GuacamoleException {
|
throws GuacamoleException {
|
||||||
|
|
||||||
// Use SSL if requested
|
// Use SSL if requested
|
||||||
if (environment.getProperty(Environment.GUACD_SSL, true))
|
if (environment.getProperty(Environment.GUACD_SSL, false))
|
||||||
return new ManagedInetGuacamoleSocket(
|
return new ManagedSSLGuacamoleSocket(
|
||||||
environment.getRequiredProperty(Environment.GUACD_HOSTNAME),
|
environment.getProperty(Environment.GUACD_HOSTNAME, DEFAULT_GUACD_HOSTNAME),
|
||||||
environment.getRequiredProperty(Environment.GUACD_PORT),
|
environment.getProperty(Environment.GUACD_PORT, DEFAULT_GUACD_PORT),
|
||||||
socketClosedCallback
|
socketClosedCallback
|
||||||
);
|
);
|
||||||
|
|
||||||
// Otherwise, just use straight TCP
|
// Otherwise, just use straight TCP
|
||||||
return new ManagedInetGuacamoleSocket(
|
return new ManagedInetGuacamoleSocket(
|
||||||
environment.getRequiredProperty(Environment.GUACD_HOSTNAME),
|
environment.getProperty(Environment.GUACD_HOSTNAME, DEFAULT_GUACD_HOSTNAME),
|
||||||
environment.getRequiredProperty(Environment.GUACD_PORT),
|
environment.getProperty(Environment.GUACD_PORT, DEFAULT_GUACD_PORT),
|
||||||
socketClosedCallback
|
socketClosedCallback
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -51,6 +51,7 @@
|
|||||||
<goal>unpack-dependencies</goal>
|
<goal>unpack-dependencies</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
|
<includeScope>runtime</includeScope>
|
||||||
<outputDirectory>${project.build.directory}/classes</outputDirectory>
|
<outputDirectory>${project.build.directory}/classes</outputDirectory>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"guacamoleVersion" : "0.9.6",
|
||||||
|
|
||||||
|
"name" : "MySQL Authentication",
|
||||||
|
"namespace" : "guac-mysql",
|
||||||
|
|
||||||
|
"authProviders" : [
|
||||||
|
"net.sourceforge.guacamole.net.auth.mysql.MySQLAuthenticationProvider"
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@@ -51,6 +51,7 @@
|
|||||||
<goal>unpack-dependencies</goal>
|
<goal>unpack-dependencies</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
|
<includeScope>runtime</includeScope>
|
||||||
<outputDirectory>${project.build.directory}/classes</outputDirectory>
|
<outputDirectory>${project.build.directory}/classes</outputDirectory>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"guacamoleVersion" : "0.9.6",
|
||||||
|
|
||||||
|
"name" : "PostgreSQL Authentication",
|
||||||
|
"namespace" : "guac-postgresql",
|
||||||
|
|
||||||
|
"authProviders" : [
|
||||||
|
"org.glyptodon.guacamole.auth.postgresql.PostgreSQLAuthenticationProvider"
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@@ -32,6 +32,26 @@
|
|||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
<!-- Copy dependencies prior to packaging -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<version>2.10</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>unpack-dependencies</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>unpack-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<includeScope>runtime</includeScope>
|
||||||
|
<outputDirectory>${project.build.directory}/classes</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
<!-- Assembly plugin - for easy distribution -->
|
<!-- Assembly plugin - for easy distribution -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
@@ -64,6 +84,7 @@
|
|||||||
<groupId>org.glyptodon.guacamole</groupId>
|
<groupId>org.glyptodon.guacamole</groupId>
|
||||||
<artifactId>guacamole-common</artifactId>
|
<artifactId>guacamole-common</artifactId>
|
||||||
<version>0.9.6</version>
|
<version>0.9.6</version>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Guacamole Extension API -->
|
<!-- Guacamole Extension API -->
|
||||||
@@ -71,6 +92,7 @@
|
|||||||
<groupId>org.glyptodon.guacamole</groupId>
|
<groupId>org.glyptodon.guacamole</groupId>
|
||||||
<artifactId>guacamole-ext</artifactId>
|
<artifactId>guacamole-ext</artifactId>
|
||||||
<version>0.9.6</version>
|
<version>0.9.6</version>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- JLDAP -->
|
<!-- JLDAP -->
|
||||||
|
@@ -11,43 +11,29 @@
|
|||||||
<format>tar.gz</format>
|
<format>tar.gz</format>
|
||||||
</formats>
|
</formats>
|
||||||
|
|
||||||
<!-- Include docs and schema -->
|
<!-- Include docs, schema, and extension .jar -->
|
||||||
<fileSets>
|
<fileSets>
|
||||||
|
|
||||||
<!-- Include docs -->
|
<!-- Include docs -->
|
||||||
<fileSet>
|
<fileSet>
|
||||||
<directory>doc</directory>
|
<directory>doc</directory>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
|
|
||||||
<!-- Include schema -->
|
<!-- Include schema -->
|
||||||
<fileSet>
|
<fileSet>
|
||||||
<outputDirectory>schema</outputDirectory>
|
<outputDirectory>schema</outputDirectory>
|
||||||
<directory>schema</directory>
|
<directory>schema</directory>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
|
|
||||||
|
<!-- Include extension .jar -->
|
||||||
|
<fileSet>
|
||||||
|
<directory>target</directory>
|
||||||
|
<outputDirectory></outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
|
||||||
</fileSets>
|
</fileSets>
|
||||||
|
|
||||||
<!-- Include self and all dependencies except guacamole-common
|
|
||||||
and guacamole-ext -->
|
|
||||||
<dependencySets>
|
|
||||||
<dependencySet>
|
|
||||||
|
|
||||||
<outputDirectory>lib</outputDirectory>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
<unpack>false</unpack>
|
|
||||||
<useProjectArtifact>true</useProjectArtifact>
|
|
||||||
<useTransitiveFiltering>true</useTransitiveFiltering>
|
|
||||||
|
|
||||||
<excludes>
|
|
||||||
|
|
||||||
<!-- Do not include guacamole-common -->
|
|
||||||
<exclude>org.glyptodon.guacamole:guacamole-common</exclude>
|
|
||||||
|
|
||||||
<!-- Do not include guacamole-ext -->
|
|
||||||
<exclude>org.glyptodon.guacamole:guacamole-ext</exclude>
|
|
||||||
|
|
||||||
</excludes>
|
|
||||||
</dependencySet>
|
|
||||||
</dependencySets>
|
|
||||||
|
|
||||||
</assembly>
|
</assembly>
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"guacamoleVersion" : "0.9.6",
|
||||||
|
|
||||||
|
"name" : "LDAP Authentication",
|
||||||
|
"namespace" : "guac-ldap",
|
||||||
|
|
||||||
|
"authProviders" : [
|
||||||
|
"net.sourceforge.guacamole.net.auth.ldap.LDAPAuthenticationProvider"
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
@@ -32,6 +32,26 @@
|
|||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
<!-- Copy dependencies prior to packaging -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<version>2.10</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>unpack-dependencies</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>unpack-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<includeScope>runtime</includeScope>
|
||||||
|
<outputDirectory>${project.build.directory}/classes</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
<!-- Assembly plugin - for easy distribution -->
|
<!-- Assembly plugin - for easy distribution -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
@@ -64,6 +84,7 @@
|
|||||||
<groupId>org.glyptodon.guacamole</groupId>
|
<groupId>org.glyptodon.guacamole</groupId>
|
||||||
<artifactId>guacamole-common</artifactId>
|
<artifactId>guacamole-common</artifactId>
|
||||||
<version>0.9.6</version>
|
<version>0.9.6</version>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Guacamole Extension API -->
|
<!-- Guacamole Extension API -->
|
||||||
@@ -71,6 +92,7 @@
|
|||||||
<groupId>org.glyptodon.guacamole</groupId>
|
<groupId>org.glyptodon.guacamole</groupId>
|
||||||
<artifactId>guacamole-ext</artifactId>
|
<artifactId>guacamole-ext</artifactId>
|
||||||
<version>0.9.6</version>
|
<version>0.9.6</version>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
<format>tar.gz</format>
|
<format>tar.gz</format>
|
||||||
</formats>
|
</formats>
|
||||||
|
|
||||||
<!-- Include docs and schema -->
|
<!-- Include docs and extension .jar -->
|
||||||
<fileSets>
|
<fileSets>
|
||||||
|
|
||||||
<!-- Include docs -->
|
<!-- Include docs -->
|
||||||
@@ -19,29 +19,15 @@
|
|||||||
<directory>doc</directory>
|
<directory>doc</directory>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
|
|
||||||
|
<!-- Include extension .jar -->
|
||||||
|
<fileSet>
|
||||||
|
<directory>target</directory>
|
||||||
|
<outputDirectory></outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
|
||||||
</fileSets>
|
</fileSets>
|
||||||
|
|
||||||
<!-- Include self and all dependencies except guacamole-common
|
|
||||||
and guacamole-ext -->
|
|
||||||
<dependencySets>
|
|
||||||
<dependencySet>
|
|
||||||
|
|
||||||
<outputDirectory>lib</outputDirectory>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
<unpack>false</unpack>
|
|
||||||
<useProjectArtifact>true</useProjectArtifact>
|
|
||||||
<useTransitiveFiltering>true</useTransitiveFiltering>
|
|
||||||
|
|
||||||
<excludes>
|
|
||||||
|
|
||||||
<!-- Do not include guacamole-common -->
|
|
||||||
<exclude>org.glyptodon.guacamole:guacamole-common</exclude>
|
|
||||||
|
|
||||||
<!-- Do not include guacamole-ext -->
|
|
||||||
<exclude>org.glyptodon.guacamole:guacamole-ext</exclude>
|
|
||||||
|
|
||||||
</excludes>
|
|
||||||
</dependencySet>
|
|
||||||
</dependencySets>
|
|
||||||
|
|
||||||
</assembly>
|
</assembly>
|
||||||
|
@@ -54,7 +54,6 @@ import org.xml.sax.helpers.XMLReaderFactory;
|
|||||||
*
|
*
|
||||||
* Example `guacamole.properties`:
|
* Example `guacamole.properties`:
|
||||||
*
|
*
|
||||||
* auth-provider: net.sourceforge.guacamole.net.auth.noauth.NoAuthenticationProvider
|
|
||||||
* noauth-config: /etc/guacamole/noauth-config.xml
|
* noauth-config: /etc/guacamole/noauth-config.xml
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@@ -93,7 +92,7 @@ public class NoAuthenticationProvider extends SimpleAuthenticationProvider {
|
|||||||
private final Environment environment;
|
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() {
|
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
|
* Creates a new NoAuthenticationProvider that does not perform any
|
||||||
* authentication at all. All attempts to access the Guacamole system are
|
* authentication at all. All attempts to access the Guacamole system are
|
||||||
@@ -125,7 +130,14 @@ public class NoAuthenticationProvider extends SimpleAuthenticationProvider {
|
|||||||
* property.
|
* property.
|
||||||
*/
|
*/
|
||||||
private File getConfigurationFile() throws GuacamoleException {
|
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 {
|
public synchronized void init() throws GuacamoleException {
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"guacamoleVersion" : "0.9.6",
|
||||||
|
|
||||||
|
"name" : "Disabled Authentication",
|
||||||
|
"namespace" : "guac-noauth",
|
||||||
|
|
||||||
|
"authProviders" : [
|
||||||
|
"net.sourceforge.guacamole.net.auth.noauth.NoAuthenticationProvider"
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
@@ -95,28 +95,28 @@ public class LocalEnvironment implements Environment {
|
|||||||
properties = new Properties();
|
properties = new Properties();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
InputStream stream;
|
InputStream stream = null;
|
||||||
|
|
||||||
// If not a directory, load from classpath
|
// If not a directory, load from classpath
|
||||||
if (!guacHome.isDirectory()) {
|
if (!guacHome.isDirectory())
|
||||||
|
|
||||||
// Read from classpath
|
|
||||||
stream = LocalEnvironment.class.getResourceAsStream("/guacamole.properties");
|
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
|
// Otherwise, try to load from file
|
||||||
else
|
else {
|
||||||
stream = new FileInputStream(new File(guacHome, "guacamole.properties"));
|
File propertiesFile = new File(guacHome, "guacamole.properties");
|
||||||
|
if (propertiesFile.exists())
|
||||||
|
stream = new FileInputStream(propertiesFile);
|
||||||
|
}
|
||||||
|
|
||||||
// Load properties, always close stream
|
// Load properties from stream, if any, always closing stream when done
|
||||||
try { properties.load(stream); }
|
if (stream != null) {
|
||||||
finally { stream.close(); }
|
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) {
|
catch (IOException e) {
|
||||||
|
@@ -45,6 +45,18 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
|
|||||||
*/
|
*/
|
||||||
public class SimpleConnection extends AbstractConnection {
|
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.
|
* Backing configuration, containing all sensitive information.
|
||||||
*/
|
*/
|
||||||
@@ -92,8 +104,8 @@ public class SimpleConnection extends AbstractConnection {
|
|||||||
Environment env = new LocalEnvironment();
|
Environment env = new LocalEnvironment();
|
||||||
|
|
||||||
// Get guacd connection parameters
|
// Get guacd connection parameters
|
||||||
String hostname = env.getProperty(Environment.GUACD_HOSTNAME);
|
String hostname = env.getProperty(Environment.GUACD_HOSTNAME, DEFAULT_GUACD_HOSTNAME);
|
||||||
int port = env.getProperty(Environment.GUACD_PORT);
|
int port = env.getProperty(Environment.GUACD_PORT, DEFAULT_GUACD_PORT);
|
||||||
|
|
||||||
GuacamoleSocket socket;
|
GuacamoleSocket socket;
|
||||||
|
|
||||||
|
@@ -76,7 +76,7 @@ public class BasicFileAuthenticationProvider extends SimpleAuthenticationProvide
|
|||||||
private final Environment environment;
|
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() {
|
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
|
* Creates a new BasicFileAuthenticationProvider that authenticates users
|
||||||
* against simple, monolithic XML file.
|
* against simple, monolithic XML file.
|
||||||
@@ -110,9 +116,10 @@ public class BasicFileAuthenticationProvider extends SimpleAuthenticationProvide
|
|||||||
*/
|
*/
|
||||||
private UserMapping getUserMapping() throws GuacamoleException {
|
private UserMapping getUserMapping() throws GuacamoleException {
|
||||||
|
|
||||||
// Get user user_mapping file
|
// Get user mapping file, defaulting to GUACAMOLE_HOME/user-mapping.xml
|
||||||
File user_mapping_file =
|
File user_mapping_file = environment.getProperty(BASIC_USER_MAPPING);
|
||||||
environment.getRequiredProperty(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 not yet read, or user_mapping has been modified, reread
|
||||||
if (user_mapping == null ||
|
if (user_mapping == null ||
|
||||||
|
@@ -30,6 +30,7 @@ import javax.servlet.ServletContextEvent;
|
|||||||
import org.glyptodon.guacamole.GuacamoleException;
|
import org.glyptodon.guacamole.GuacamoleException;
|
||||||
import org.glyptodon.guacamole.environment.Environment;
|
import org.glyptodon.guacamole.environment.Environment;
|
||||||
import org.glyptodon.guacamole.environment.LocalEnvironment;
|
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.log.LogModule;
|
||||||
import org.glyptodon.guacamole.net.basic.rest.RESTAuthModule;
|
import org.glyptodon.guacamole.net.basic.rest.RESTAuthModule;
|
||||||
import org.glyptodon.guacamole.net.basic.rest.RESTServletModule;
|
import org.glyptodon.guacamole.net.basic.rest.RESTServletModule;
|
||||||
@@ -83,7 +84,8 @@ public class BasicServletContextListener extends GuiceServletContextListener {
|
|||||||
return Guice.createInjector(Stage.PRODUCTION,
|
return Guice.createInjector(Stage.PRODUCTION,
|
||||||
new EnvironmentModule(environment),
|
new EnvironmentModule(environment),
|
||||||
new LogModule(environment),
|
new LogModule(environment),
|
||||||
new RESTAuthModule(environment, sessionMap),
|
new ExtensionModule(environment),
|
||||||
|
new RESTAuthModule(sessionMap),
|
||||||
new RESTServletModule(),
|
new RESTServletModule(),
|
||||||
new TunnelModule()
|
new TunnelModule()
|
||||||
);
|
);
|
||||||
|
@@ -39,10 +39,13 @@ import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A ClassLoader implementation which finds classes within a configurable
|
* A ClassLoader implementation which finds classes within a configurable
|
||||||
* directory. This directory is set within guacamole.properties.
|
* directory. This directory is set within guacamole.properties. This class
|
||||||
|
* is deprecated in favor of DirectoryClassLoader, which is automatically
|
||||||
|
* configured based on the presence/absence of GUACAMOLE_HOME/lib.
|
||||||
*
|
*
|
||||||
* @author Michael Jumper
|
* @author Michael Jumper
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class GuacamoleClassLoader extends ClassLoader {
|
public class GuacamoleClassLoader extends ClassLoader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -60,11 +60,6 @@ public class GuacamoleSession {
|
|||||||
*/
|
*/
|
||||||
private UserContext userContext;
|
private UserContext userContext;
|
||||||
|
|
||||||
/**
|
|
||||||
* Collection of all event listeners configured in guacamole.properties.
|
|
||||||
*/
|
|
||||||
private final Collection<Object> listeners = new ArrayList<Object>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current clipboard state.
|
* The current clipboard state.
|
||||||
*/
|
*/
|
||||||
@@ -98,52 +93,9 @@ public class GuacamoleSession {
|
|||||||
*/
|
*/
|
||||||
public GuacamoleSession(Environment environment, Credentials credentials,
|
public GuacamoleSession(Environment environment, Credentials credentials,
|
||||||
UserContext userContext) throws GuacamoleException {
|
UserContext userContext) throws GuacamoleException {
|
||||||
|
|
||||||
this.lastAccessedTime = System.currentTimeMillis();
|
this.lastAccessedTime = System.currentTimeMillis();
|
||||||
this.credentials = credentials;
|
this.credentials = credentials;
|
||||||
this.userContext = userContext;
|
this.userContext = userContext;
|
||||||
|
|
||||||
// Load listeners from guacamole.properties
|
|
||||||
try {
|
|
||||||
|
|
||||||
// Get all listener classes from properties
|
|
||||||
Collection<Class> 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;
|
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<Object> getListeners() {
|
|
||||||
return Collections.unmodifiableCollection(listeners);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this session has any associated active tunnels.
|
* Returns whether this session has any associated active tunnels.
|
||||||
*
|
*
|
||||||
|
@@ -24,7 +24,6 @@ package org.glyptodon.guacamole.net.basic;
|
|||||||
|
|
||||||
import com.google.inject.servlet.ServletModule;
|
import com.google.inject.servlet.ServletModule;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import org.glyptodon.guacamole.GuacamoleException;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ public class TunnelModule extends ServletModule {
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
// Attempt to find WebSocket module
|
// Attempt to find WebSocket module
|
||||||
Class<?> module = (Class<?>) GuacamoleClassLoader.getInstance().findClass(classname);
|
Class<?> module = Class.forName(classname);
|
||||||
|
|
||||||
// Create loader
|
// Create loader
|
||||||
TunnelLoader loader = (TunnelLoader) module.getConstructor().newInstance();
|
TunnelLoader loader = (TunnelLoader) module.getConstructor().newInstance();
|
||||||
@@ -85,12 +84,6 @@ public class TunnelModule extends ServletModule {
|
|||||||
logger.debug("Error instantiating WebSocket module.", e);
|
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
|
// Load attempt failed
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@@ -38,10 +38,6 @@ import org.glyptodon.guacamole.net.auth.ConnectionGroup;
|
|||||||
import org.glyptodon.guacamole.net.auth.Directory;
|
import org.glyptodon.guacamole.net.auth.Directory;
|
||||||
import org.glyptodon.guacamole.net.auth.UserContext;
|
import org.glyptodon.guacamole.net.auth.UserContext;
|
||||||
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
|
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.glyptodon.guacamole.protocol.GuacamoleClientInformation;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -76,84 +72,6 @@ public class TunnelRequestService {
|
|||||||
@Inject
|
@Inject
|
||||||
private AuthenticationService authenticationService;
|
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
|
* Reads and returns the client information provided within the given
|
||||||
* request.
|
* request.
|
||||||
@@ -335,10 +253,6 @@ public class TunnelRequestService {
|
|||||||
@Override
|
@Override
|
||||||
public void close() throws GuacamoleException {
|
public void close() throws GuacamoleException {
|
||||||
|
|
||||||
// Signal listeners
|
|
||||||
if (!notifyClose(session, this))
|
|
||||||
throw new GuacamoleException("Tunnel close canceled by listener.");
|
|
||||||
|
|
||||||
session.removeTunnel(getUUID().toString());
|
session.removeTunnel(getUUID().toString());
|
||||||
|
|
||||||
// Close if no exception due to listener
|
// 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
|
// Associate tunnel with session
|
||||||
session.addTunnel(monitoredTunnel);
|
session.addTunnel(monitoredTunnel);
|
||||||
return monitoredTunnel;
|
return monitoredTunnel;
|
||||||
|
@@ -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<DirectoryClassLoader>() {
|
||||||
|
|
||||||
|
@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<URL> jarURLs = new ArrayList<URL>();
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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<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.
|
||||||
|
*
|
||||||
|
* @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<ClassLoader>() {
|
||||||
|
|
||||||
|
@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<Resource> 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<Resource> 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<Class<AuthenticationProvider>> getAuthenticationProviderClasses() {
|
||||||
|
return authenticationProviderClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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<String> javaScriptPaths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The paths of all CSS resources within the .jar of the extension
|
||||||
|
* associated with this manifest.
|
||||||
|
*/
|
||||||
|
private Collection<String> cssPaths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The names of all authentication provider classes within this extension,
|
||||||
|
* if any.
|
||||||
|
*/
|
||||||
|
private Collection<String> 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<String> 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<String> 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<String> 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<String> 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<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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<String> 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<? extends AuthenticationProvider> boundAuthenticationProvider = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the classloader that should be used as the parent classloader
|
||||||
|
* for all extensions. If the GUACAMOLE_HOME/lib directory exists, this
|
||||||
|
* 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<AuthenticationProvider> getAuthProviderProperty() {
|
||||||
|
|
||||||
|
// Get and bind auth provider instance, if defined via property
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Use "auth-provider" property if present, but warn about deprecation
|
||||||
|
Class<AuthenticationProvider> authenticationProvider = environment.getProperty(BasicGuacamoleProperties.AUTH_PROVIDER);
|
||||||
|
if (authenticationProvider != null)
|
||||||
|
logger.warn("The \"auth-provider\" and \"lib-directory\" properties are now deprecated. Please use the \"extensions\" and \"lib\" directories within GUACAMOLE_HOME instead.");
|
||||||
|
|
||||||
|
return authenticationProvider;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (GuacamoleException e) {
|
||||||
|
logger.warn("Value of deprecated \"auth-provider\" property within guacamole.properties is not valid: {}", e.getMessage());
|
||||||
|
logger.debug("Error reading authentication provider from guacamole.properties.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds the given AuthenticationProvider class such that any service
|
||||||
|
* requiring access to the AuthenticationProvider can obtain it via
|
||||||
|
* injection.
|
||||||
|
*
|
||||||
|
* @param authenticationProvider
|
||||||
|
* The AuthenticationProvider class to bind.
|
||||||
|
*/
|
||||||
|
private void bindAuthenticationProvider(Class<? extends AuthenticationProvider> authenticationProvider) {
|
||||||
|
|
||||||
|
// Choose auth provider for binding if not already chosen
|
||||||
|
if (boundAuthenticationProvider == null)
|
||||||
|
boundAuthenticationProvider = authenticationProvider;
|
||||||
|
|
||||||
|
// If an auth provider is already chosen, skip and warn
|
||||||
|
else {
|
||||||
|
logger.debug("Ignoring AuthenticationProvider \"{}\".", authenticationProvider);
|
||||||
|
logger.warn("Only one authentication extension may be used at a time. Please "
|
||||||
|
+ "make sure that only one authentication extension is present "
|
||||||
|
+ "within the GUACAMOLE_HOME/" + EXTENSIONS_DIRECTORY + " "
|
||||||
|
+ "directory, and that you are not also specifying the deprecated "
|
||||||
|
+ "\"auth-provider\" property within guacamole.properties.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind authentication provider
|
||||||
|
logger.debug("Binding AuthenticationProvider \"{}\".", authenticationProvider);
|
||||||
|
bind(AuthenticationProvider.class).to(authenticationProvider).in(Singleton.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<AuthenticationProvider> 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<Resource> javaScriptResources = new ArrayList<Resource>();
|
||||||
|
javaScriptResources.add(new WebApplicationResource(getServletContext(), "/guacamole.min.js"));
|
||||||
|
|
||||||
|
// Init CSS resources with base guacamole.min.css
|
||||||
|
Collection<Resource> cssResources = new ArrayList<Resource>();
|
||||||
|
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<Class<AuthenticationProvider>> authenticationProviders = extension.getAuthenticationProviderClasses();
|
||||||
|
for (Class<AuthenticationProvider> 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)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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;
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
* Copyright (C) 2015 Glyptodon LLC
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -22,22 +22,24 @@
|
|||||||
|
|
||||||
package org.glyptodon.guacamole.net.basic.properties;
|
package org.glyptodon.guacamole.net.basic.properties;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import org.glyptodon.guacamole.GuacamoleException;
|
import org.glyptodon.guacamole.GuacamoleException;
|
||||||
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
|
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
|
||||||
import org.glyptodon.guacamole.net.basic.GuacamoleClassLoader;
|
|
||||||
import org.glyptodon.guacamole.properties.GuacamoleProperty;
|
import org.glyptodon.guacamole.properties.GuacamoleProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A GuacamoleProperty whose value is the name of a class to use to
|
* A GuacamoleProperty whose value is the name of a class to use to
|
||||||
* authenticate users. This class must implement AuthenticationProvider.
|
* authenticate users. This class must implement AuthenticationProvider. Use
|
||||||
|
* of this property type is deprecated in favor of the
|
||||||
|
* GUACAMOLE_HOME/extensions directory.
|
||||||
*
|
*
|
||||||
* @author Michael Jumper
|
* @author Michael Jumper
|
||||||
*/
|
*/
|
||||||
public abstract class AuthenticationProviderProperty implements GuacamoleProperty<AuthenticationProvider> {
|
@Deprecated
|
||||||
|
public abstract class AuthenticationProviderProperty implements GuacamoleProperty<Class<AuthenticationProvider>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationProvider parseValue(String authProviderClassName) throws GuacamoleException {
|
@SuppressWarnings("unchecked") // Explicitly checked within by isAssignableFrom()
|
||||||
|
public Class<AuthenticationProvider> parseValue(String authProviderClassName) throws GuacamoleException {
|
||||||
|
|
||||||
// If no property provided, return null.
|
// If no property provided, return null.
|
||||||
if (authProviderClassName == null)
|
if (authProviderClassName == null)
|
||||||
@@ -46,35 +48,21 @@ public abstract class AuthenticationProviderProperty implements GuacamolePropert
|
|||||||
// Get auth provider instance
|
// Get auth provider instance
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Object obj = GuacamoleClassLoader.getInstance().loadClass(authProviderClassName)
|
// Get authentication provider class
|
||||||
.getConstructor().newInstance();
|
Class<?> authProviderClass = 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.");
|
throw new GuacamoleException("Specified authentication provider class is not a AuthenticationProvider.");
|
||||||
|
|
||||||
return (AuthenticationProvider) obj;
|
// Return located class
|
||||||
|
return (Class<AuthenticationProvider>) authProviderClass;
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException e) {
|
catch (ClassNotFoundException e) {
|
||||||
throw new GuacamoleException("Authentication provider class not found", e);
|
throw new GuacamoleException("Authentication provider class not found", e);
|
||||||
}
|
}
|
||||||
catch (NoSuchMethodException e) {
|
|
||||||
throw new GuacamoleException("Default constructor for authentication provider not present", e);
|
|
||||||
}
|
|
||||||
catch (SecurityException e) {
|
|
||||||
throw new GuacamoleException("Creation of authentication provider disallowed; check your security settings", e);
|
|
||||||
}
|
|
||||||
catch (InstantiationException e) {
|
|
||||||
throw new GuacamoleException("Unable to instantiate authentication provider", e);
|
|
||||||
}
|
|
||||||
catch (IllegalAccessException e) {
|
|
||||||
throw new GuacamoleException("Unable to access default constructor of authentication provider", e);
|
|
||||||
}
|
|
||||||
catch (InvocationTargetException e) {
|
|
||||||
throw new GuacamoleException("Internal error in constructor of authentication provider", e.getTargetException());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
package org.glyptodon.guacamole.net.basic.properties;
|
package org.glyptodon.guacamole.net.basic.properties;
|
||||||
|
|
||||||
import org.glyptodon.guacamole.properties.BooleanGuacamoleProperty;
|
|
||||||
import org.glyptodon.guacamole.properties.FileGuacamoleProperty;
|
import org.glyptodon.guacamole.properties.FileGuacamoleProperty;
|
||||||
import org.glyptodon.guacamole.properties.IntegerGuacamoleProperty;
|
import org.glyptodon.guacamole.properties.IntegerGuacamoleProperty;
|
||||||
|
|
||||||
@@ -40,8 +39,10 @@ public class BasicGuacamoleProperties {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The authentication provider to user when retrieving the authorized
|
* The authentication provider to user when retrieving the authorized
|
||||||
* configurations of a user.
|
* configurations of a user. This property is currently supported, but
|
||||||
|
* deprecated in favor of the GUACAMOLE_HOME/extensions directory.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static final AuthenticationProviderProperty AUTH_PROVIDER = new AuthenticationProviderProperty() {
|
public static final AuthenticationProviderProperty AUTH_PROVIDER = new AuthenticationProviderProperty() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -50,8 +51,11 @@ public class BasicGuacamoleProperties {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The directory to search for authentication provider classes.
|
* The directory to search for authentication provider classes. This
|
||||||
|
* property is currently supported, but deprecated in favor of the
|
||||||
|
* GUACAMOLE_HOME/lib directory.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static final FileGuacamoleProperty LIB_DIRECTORY = new FileGuacamoleProperty() {
|
public static final FileGuacamoleProperty LIB_DIRECTORY = new FileGuacamoleProperty() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -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.
|
* The session timeout for the API, in minutes.
|
||||||
*/
|
*/
|
||||||
|
@@ -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<Collection<Class>> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Class> 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<Class> listeners = new ArrayList<Class>();
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@@ -23,10 +23,6 @@
|
|||||||
package org.glyptodon.guacamole.net.basic.rest;
|
package org.glyptodon.guacamole.net.basic.rest;
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import org.glyptodon.guacamole.GuacamoleException;
|
|
||||||
import org.glyptodon.guacamole.environment.Environment;
|
|
||||||
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
|
|
||||||
import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties;
|
|
||||||
import org.glyptodon.guacamole.net.basic.rest.auth.AuthTokenGenerator;
|
import org.glyptodon.guacamole.net.basic.rest.auth.AuthTokenGenerator;
|
||||||
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
|
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
|
||||||
import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerator;
|
import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerator;
|
||||||
@@ -47,11 +43,6 @@ public class RESTAuthModule extends AbstractModule {
|
|||||||
*/
|
*/
|
||||||
private final Logger logger = LoggerFactory.getLogger(RESTAuthModule.class);
|
private final Logger logger = LoggerFactory.getLogger(RESTAuthModule.class);
|
||||||
|
|
||||||
/**
|
|
||||||
* The Guacamole server environment.
|
|
||||||
*/
|
|
||||||
private final Environment environment;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton instance of TokenSessionMap.
|
* Singleton instance of TokenSessionMap.
|
||||||
*/
|
*/
|
||||||
@@ -61,16 +52,11 @@ public class RESTAuthModule extends AbstractModule {
|
|||||||
* Creates a module which handles binding of authentication-related
|
* Creates a module which handles binding of authentication-related
|
||||||
* objects, including the singleton TokenSessionMap.
|
* objects, including the singleton TokenSessionMap.
|
||||||
*
|
*
|
||||||
* @param environment
|
|
||||||
* The environment to use when configuring authentication.
|
|
||||||
*
|
|
||||||
* @param tokenSessionMap
|
* @param tokenSessionMap
|
||||||
* An instance of TokenSessionMap to inject as a singleton wherever
|
* An instance of TokenSessionMap to inject as a singleton wherever
|
||||||
* needed.
|
* needed.
|
||||||
*/
|
*/
|
||||||
public RESTAuthModule(Environment environment,
|
public RESTAuthModule(TokenSessionMap tokenSessionMap) {
|
||||||
TokenSessionMap tokenSessionMap) {
|
|
||||||
this.environment = environment;
|
|
||||||
this.tokenSessionMap = tokenSessionMap;
|
this.tokenSessionMap = tokenSessionMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,17 +70,6 @@ public class RESTAuthModule extends AbstractModule {
|
|||||||
bind(AuthenticationService.class);
|
bind(AuthenticationService.class);
|
||||||
bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class);
|
bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class);
|
||||||
|
|
||||||
// Get and bind auth provider instance
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -28,8 +28,6 @@ import java.util.Arrays;
|
|||||||
import javax.websocket.DeploymentException;
|
import javax.websocket.DeploymentException;
|
||||||
import javax.websocket.server.ServerContainer;
|
import javax.websocket.server.ServerContainer;
|
||||||
import javax.websocket.server.ServerEndpointConfig;
|
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.TunnelLoader;
|
||||||
import org.glyptodon.guacamole.net.basic.TunnelRequestService;
|
import org.glyptodon.guacamole.net.basic.TunnelRequestService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -53,7 +51,7 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
// Attempt to find WebSocket servlet
|
// Attempt to find WebSocket servlet
|
||||||
GuacamoleClassLoader.getInstance().findClass("javax.websocket.Endpoint");
|
Class.forName("javax.websocket.Endpoint");
|
||||||
|
|
||||||
// Support found
|
// Support found
|
||||||
return true;
|
return true;
|
||||||
@@ -65,12 +63,6 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader
|
|||||||
catch (ClassNotFoundException e) {}
|
catch (ClassNotFoundException e) {}
|
||||||
catch (NoClassDefFoundError 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
|
// Support not found
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@@ -23,8 +23,6 @@
|
|||||||
package org.glyptodon.guacamole.net.basic.websocket.jetty8;
|
package org.glyptodon.guacamole.net.basic.websocket.jetty8;
|
||||||
|
|
||||||
import com.google.inject.servlet.ServletModule;
|
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.glyptodon.guacamole.net.basic.TunnelLoader;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -47,7 +45,7 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
// Attempt to find WebSocket servlet
|
// 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
|
// Support found
|
||||||
return true;
|
return true;
|
||||||
@@ -59,12 +57,6 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader
|
|||||||
catch (ClassNotFoundException e) {}
|
catch (ClassNotFoundException e) {}
|
||||||
catch (NoClassDefFoundError 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
|
// Support not found
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@@ -23,8 +23,6 @@
|
|||||||
package org.glyptodon.guacamole.net.basic.websocket.jetty9;
|
package org.glyptodon.guacamole.net.basic.websocket.jetty9;
|
||||||
|
|
||||||
import com.google.inject.servlet.ServletModule;
|
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.glyptodon.guacamole.net.basic.TunnelLoader;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -47,7 +45,7 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
// Attempt to find WebSocket servlet
|
// 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
|
// Support found
|
||||||
return true;
|
return true;
|
||||||
@@ -59,12 +57,6 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader
|
|||||||
catch (ClassNotFoundException e) {}
|
catch (ClassNotFoundException e) {}
|
||||||
catch (NoClassDefFoundError 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
|
// Support not found
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@@ -23,8 +23,6 @@
|
|||||||
package org.glyptodon.guacamole.net.basic.websocket.tomcat;
|
package org.glyptodon.guacamole.net.basic.websocket.tomcat;
|
||||||
|
|
||||||
import com.google.inject.servlet.ServletModule;
|
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.glyptodon.guacamole.net.basic.TunnelLoader;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -47,7 +45,7 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
// Attempt to find WebSocket servlet
|
// 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
|
// Support found
|
||||||
return true;
|
return true;
|
||||||
@@ -59,12 +57,6 @@ public class WebSocketTunnelModule extends ServletModule implements TunnelLoader
|
|||||||
catch (ClassNotFoundException e) {}
|
catch (ClassNotFoundException e) {}
|
||||||
catch (NoClassDefFoundError 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
|
// Support not found
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<link rel="icon" type="image/png" href="images/guacamole-logo-64.png"/>
|
<link rel="icon" type="image/png" href="images/guacamole-logo-64.png"/>
|
||||||
<link rel="icon" type="image/png" sizes="144x144" href="images/guacamole-logo-144.png"/>
|
<link rel="icon" type="image/png" sizes="144x144" href="images/guacamole-logo-144.png"/>
|
||||||
<link rel="apple-touch-icon" type="image/png" href="images/guacamole-logo-144.png"/>
|
<link rel="apple-touch-icon" type="image/png" href="images/guacamole-logo-144.png"/>
|
||||||
<link rel="stylesheet" type="text/css" href="guacamole.min.css">
|
<link rel="stylesheet" type="text/css" href="app.css">
|
||||||
<title ng-bind="page.title | translate"></title>
|
<title ng-bind="page.title | translate"></title>
|
||||||
</head>
|
</head>
|
||||||
<!--
|
<!--
|
||||||
@@ -52,6 +52,6 @@
|
|||||||
<!-- Login screen for logged-out users -->
|
<!-- Login screen for logged-out users -->
|
||||||
<guac-login ng-show="expectedCredentials" form="expectedCredentials"></guac-login>
|
<guac-login ng-show="expectedCredentials" form="expectedCredentials"></guac-login>
|
||||||
|
|
||||||
<script type="text/javascript" src="guacamole.min.js"></script>
|
<script type="text/javascript" src="app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Reference in New Issue
Block a user