From 3603155f36918ff9444a1355c5a5115aed797c93 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 16 Mar 2015 14:26:56 -0700 Subject: [PATCH 01/17] GUAC-1132: Modify API to support listing of active connections and host information. --- .../connection/ModeledConnectionRecord.java | 12 ++++++++++ .../jdbc/socket/ActiveConnectionRecord.java | 13 ++++++++++ .../guacamole/auth/jdbc/user/UserContext.java | 9 +++++++ .../guacamole/net/auth/ConnectionRecord.java | 23 ++++++++++++++++++ .../guacamole/net/auth/UserContext.java | 17 +++++++++++++ .../net/auth/simple/SimpleUserContext.java | 7 ++++++ .../rest/connection/APIConnectionRecord.java | 24 +++++++++++++++---- .../app/rest/types/ConnectionHistoryEntry.js | 7 ++++++ 8 files changed, 108 insertions(+), 4 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java index c86e46588..41615c794 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -24,6 +24,7 @@ package org.glyptodon.guacamole.auth.jdbc.connection; import java.util.Date; +import org.glyptodon.guacamole.net.GuacamoleSocket; import org.glyptodon.guacamole.net.auth.ConnectionRecord; /** @@ -61,6 +62,12 @@ public class ModeledConnectionRecord implements ConnectionRecord { return model.getEndDate(); } + @Override + public String getRemoteHost() { + // STUB + return "STUB"; + } + @Override public String getUsername() { return model.getUsername(); @@ -71,4 +78,9 @@ public class ModeledConnectionRecord implements ConnectionRecord { return false; } + @Override + public GuacamoleSocket getActiveSocket() { + return null; + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java index 5d23a7443..500b274f8 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java @@ -26,6 +26,7 @@ import java.util.Date; import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.net.GuacamoleSocket; import org.glyptodon.guacamole.net.auth.ConnectionRecord; @@ -162,6 +163,12 @@ public class ActiveConnectionRecord implements ConnectionRecord { } + @Override + public String getRemoteHost() { + // STUB + return "STUB"; + } + @Override public String getUsername() { return user.getUser().getIdentifier(); @@ -175,4 +182,10 @@ public class ActiveConnectionRecord implements ConnectionRecord { } + @Override + public GuacamoleSocket getActiveSocket() { + // STUB + return null; + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java index fac5b9af9..b4d3de31f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java @@ -28,10 +28,13 @@ import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupDirector import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionDirectory; import com.google.inject.Inject; import com.google.inject.Provider; +import java.util.Collection; +import java.util.Collections; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; @@ -114,4 +117,10 @@ public class UserContext extends RestrictedObject } + @Override + public Collection getActiveConnections() throws GuacamoleException { + // STUB + return Collections.EMPTY_LIST; + } + } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java index 208b3c397..7a4b4d219 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java @@ -23,6 +23,7 @@ package org.glyptodon.guacamole.net.auth; import java.util.Date; +import org.glyptodon.guacamole.net.GuacamoleSocket; /** * A logging record describing when a user started and ended usage of a @@ -47,6 +48,17 @@ public interface ConnectionRecord { */ public Date getEndDate(); + /** + * Returns the hostname or IP address of the remote host that used the + * connection associated with this record, if known. If the hostname or IP + * address is not known, null is returned. + * + * @return + * The hostname or IP address of the remote host, or null if this + * information is not available. + */ + public String getRemoteHost(); + /** * Returns the name of the user who used or is using the connection at the * times given by this connection record. @@ -65,4 +77,15 @@ public interface ConnectionRecord { */ public boolean isActive(); + /** + * Returns the connected GuacamoleSocket of the connection associated with + * this record, if any. If the connection is not active, or access to + * the socket is denied, null is returned. + * + * @return + * The connected GuacamoleSocket, if any, or null if the connection is + * not active or permission is denied. + */ + public GuacamoleSocket getActiveSocket(); + } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java index c38d9a90f..010e3e3a6 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java @@ -22,6 +22,7 @@ package org.glyptodon.guacamole.net.auth; +import java.util.Collection; import org.glyptodon.guacamole.GuacamoleException; /** @@ -95,4 +96,20 @@ public interface UserContext { */ ConnectionGroup getRootConnectionGroup() throws GuacamoleException; + /** + * Returns a collection of connection records associated with all active + * connections to which the current user has access. For an administrative + * user, this may include connections associated with other users. + * + * @return + * A collection of all connection records associated with active + * connections to which the current user has access. + * + * @throws GuacamoleException + * If an error occurs while reading active connection records, or if + * permission is denied. + */ + Collection getActiveConnections() + throws GuacamoleException; + } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java index 8ee7e84c1..3f19ec7fc 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java @@ -30,6 +30,7 @@ import java.util.UUID; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.UserContext; @@ -167,4 +168,10 @@ public class SimpleUserContext implements UserContext { return rootGroup; } + @Override + public Collection getActiveConnections() + throws GuacamoleException { + return Collections.EMPTY_LIST; + } + } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionRecord.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionRecord.java index e1423ac8f..e3912ecbe 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionRecord.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionRecord.java @@ -43,6 +43,11 @@ public class APIConnectionRecord { */ private final Date endDate; + /** + * The host from which the connection originated, if known. + */ + private final String remoteHost; + /** * The name of the user who used or is using the connection. */ @@ -61,10 +66,11 @@ public class APIConnectionRecord { * The record to copy data from. */ public APIConnectionRecord(ConnectionRecord record) { - this.startDate = record.getStartDate(); - this.endDate = record.getEndDate(); - this.username = record.getUsername(); - this.active = record.isActive(); + this.startDate = record.getStartDate(); + this.endDate = record.getEndDate(); + this.remoteHost = record.getRemoteHost(); + this.username = record.getUsername(); + this.active = record.isActive(); } /** @@ -88,6 +94,16 @@ public class APIConnectionRecord { return endDate; } + /** + * Returns the remote host from which this connection originated. + * + * @return + * The remote host from which this connection originated. + */ + public String getRemoteHost() { + return remoteHost; + } + /** * Returns the name of the user who used or is using the connection at the * times given by this connection record. diff --git a/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js b/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js index 9f0023323..299c65c13 100644 --- a/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js +++ b/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js @@ -60,6 +60,13 @@ angular.module('rest').factory('ConnectionHistoryEntry', [function defineConnect */ this.endDate = template.endDate; + /** + * The remote host that initiated this connection, if known. + * + * @type String + */ + this.remoteHost = template.remoteHost; + /** * The username of the user associated with this particular usage of * the connection. From 3a4f6b85dd97a6c7e725718472d3ad0df7295b29 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 16 Mar 2015 15:57:18 -0700 Subject: [PATCH 02/17] GUAC-1132: Add tunnel rest service stub. --- .../net/basic/rest/RESTServletModule.java | 2 + .../net/basic/rest/tunnel/APITunnel.java | 111 ++++++++++++++++ .../basic/rest/tunnel/TunnelRESTService.java | 125 ++++++++++++++++++ .../webapp/app/rest/types/ActiveTunnel.js | 76 +++++++++++ 4 files changed, 314 insertions(+) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java create mode 100644 guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java index cc4e90e6d..a2f0232f3 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java @@ -31,6 +31,7 @@ import org.glyptodon.guacamole.net.basic.rest.clipboard.ClipboardRESTService; import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService; import org.glyptodon.guacamole.net.basic.rest.connectiongroup.ConnectionGroupRESTService; import org.glyptodon.guacamole.net.basic.rest.protocol.ProtocolRESTService; +import org.glyptodon.guacamole.net.basic.rest.tunnel.TunnelRESTService; import org.glyptodon.guacamole.net.basic.rest.user.UserRESTService; /** @@ -50,6 +51,7 @@ public class RESTServletModule extends ServletModule { bind(ProtocolRESTService.class); bind(UserRESTService.class); bind(TokenRESTService.class); + bind(TunnelRESTService.class); // Set up the servlet and JSON mappings bind(GuiceContainer.class); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java new file mode 100644 index 000000000..795b4e05e --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java @@ -0,0 +1,111 @@ +/* + * 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.rest.tunnel; + +import java.util.Date; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; + +/** + * Tunnel-related information which may be exposed through the REST endpoints. + * + * @author Michael Jumper + */ +public class APITunnel { + + /** + * The date and time the connection began. + */ + private final Date startDate; + + /** + * The host from which the connection originated, if known. + */ + private final String remoteHost; + + /** + * The name of the user who used or is using the connection. + */ + private final String username; + + /** + * The UUID of the tunnel. + */ + private final String uuid; + + /** + * Creates a new APITunnel, copying the information from the given + * connection record. + * + * @param record + * The record to copy data from. + */ + public APITunnel(ConnectionRecord record) { + this.startDate = record.getStartDate(); + this.remoteHost = record.getRemoteHost(); + this.username = record.getUsername(); + this.uuid = "STUB"; // STUB + } + + /** + * Returns the date and time the connection began. + * + * @return + * The date and time the connection began. + */ + public Date getStartDate() { + return startDate; + } + + /** + * Returns the remote host from which this connection originated. + * + * @return + * The remote host from which this connection originated. + */ + public String getRemoteHost() { + return remoteHost; + } + + /** + * Returns the name of the user who used or is using the connection at the + * times given by this tunnel. + * + * @return + * The name of the user who used or is using the associated connection. + */ + public String getUsername() { + return username; + } + + /** + * Returns the UUID of the underlying Guacamole tunnel. Absolutely every + * Guacamole tunnel has an associated UUID. + * + * @return + * The UUID of the underlying Guacamole tunnel. + */ + public String getUUID() { + return uuid; + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java new file mode 100644 index 000000000..885b17a78 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java @@ -0,0 +1,125 @@ +/* + * 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.rest.tunnel; + +import com.google.inject.Inject; +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleUnsupportedException; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; +import org.glyptodon.guacamole.net.auth.UserContext; +import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; +import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A REST Service for retrieving and managing the tunnels of active connections. + * + * @author Michael Jumper + */ +@Path("/tunnels") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class TunnelRESTService { + + /** + * Logger for this class. + */ + private static final Logger logger = LoggerFactory.getLogger(TunnelRESTService.class); + + /** + * A service for authenticating users from auth tokens. + */ + @Inject + private AuthenticationService authenticationService; + + /** + * Retrieves the tunnels of all active connections visible to the current + * user. + * + * @param authToken + * The authentication token that is used to authenticate the user + * performing the operation. + * + * @return + * The tunnels of all active connections visible to the current user. + * + * @throws GuacamoleException + * If an error occurs while retrieving the tunnels. + */ + @GET + @Path("/") + @AuthProviderRESTExposure + public List getTunnels(@QueryParam("token") String authToken) + throws GuacamoleException { + + UserContext userContext = authenticationService.getUserContext(authToken); + + // Retrieve all active tunnels + List apiTunnels = new ArrayList(); + for (ConnectionRecord record : userContext.getActiveConnections()) + apiTunnels.add(new APITunnel(record)); + + return apiTunnels; + + } + + /** + * Deletes the tunnel having the given UUID, effectively closing the + * tunnel and killing the associated connection. + * + * @param authToken + * The authentication token that is used to authenticate the user + * performing the operation. + * + * @param tunnelUUID + * The UUID associated with the tunnel being deleted. + * + * @throws GuacamoleException + * If an error occurs while deleting the tunnel. + */ + @DELETE + @Path("/{tunnelUUID}") + @AuthProviderRESTExposure + public void deleteTunnel(@QueryParam("token") String authToken, + @PathParam("tunnelUUID") String tunnelUUID) + throws GuacamoleException { + + UserContext userContext = authenticationService.getUserContext(authToken); + + // STUB + throw new GuacamoleUnsupportedException("STUB"); + + } + +} diff --git a/guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js b/guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js new file mode 100644 index 000000000..e5fe66bc9 --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js @@ -0,0 +1,76 @@ +/* + * 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. + */ + +/** + * Service which defines the ActiveTunnel class. + */ +angular.module('rest').factory('ActiveTunnel', [function defineActiveTunnel() { + + /** + * The object returned by REST API calls when representing the data + * associated with an active tunnel. Each tunnel denotes an active + * connection, uniquely identified by the tunnel UUID. + * + * @constructor + * @param {ActiveTunnel|Object} [template={}] + * The object whose properties should be copied within the new + * ActiveTunnel. + */ + var ActiveTunnel = function ActiveTunnel(template) { + + // Use empty object by default + template = template || {}; + + /** + * The time that the tunnel began, in seconds since + * 1970-01-01 00:00:00 UTC. + * + * @type Number + */ + this.startDate = template.startDate; + + /** + * The remote host that initiated the tunnel, if known. + * + * @type String + */ + this.remoteHost = template.remoteHost; + + /** + * The username of the user associated with the tunnel. + * + * @type String + */ + this.username = template.username; + + /** + * The UUID which uniquely identifies the tunnel. + * + * @type String + */ + this.uuid = template.uuid; + + }; + + return ActiveTunnel; + +}]); \ No newline at end of file From 5cf48c74988c610af50bcde441997950dd6c6892 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 16 Mar 2015 16:11:20 -0700 Subject: [PATCH 03/17] GUAC-1132: Add identifier to connection records. --- .../jdbc/connection/ModeledConnectionRecord.java | 5 +++++ .../auth/jdbc/socket/ActiveConnectionRecord.java | 5 +++++ .../guacamole/net/auth/ConnectionRecord.java | 10 ++++++++++ .../net/basic/rest/tunnel/APITunnel.java | 16 ++++++++++++++++ .../main/webapp/app/rest/types/ActiveTunnel.js | 7 +++++++ 5 files changed, 43 insertions(+) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java index 41615c794..7e61cdb11 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -52,6 +52,11 @@ public class ModeledConnectionRecord implements ConnectionRecord { this.model = model; } + @Override + public String getIdentifier() { + return model.getConnectionIdentifier(); + } + @Override public Date getStartDate() { return model.getStartDate(); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java index 500b274f8..c7c46470d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java @@ -149,6 +149,11 @@ public class ActiveConnectionRecord implements ConnectionRecord { public boolean hasBalancingGroup() { return balancingGroup != null; } + + @Override + public String getIdentifier() { + return connection.getIdentifier(); + } @Override public Date getStartDate() { diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java index 7a4b4d219..98c1b87c7 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java @@ -33,6 +33,16 @@ import org.glyptodon.guacamole.net.GuacamoleSocket; */ public interface ConnectionRecord { + /** + * Returns the identifier of the connection associated with this connection + * record. + * + * @return + * The identifier of the connection associated with this connection + * record. + */ + public String getIdentifier(); + /** * Returns the date and time the connection began. * diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java index 795b4e05e..279500ba1 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java @@ -32,6 +32,11 @@ import org.glyptodon.guacamole.net.auth.ConnectionRecord; */ public class APITunnel { + /** + * The identifier of the connection associated with this tunnel. + */ + private final String identifier; + /** * The date and time the connection began. */ @@ -60,12 +65,23 @@ public class APITunnel { * The record to copy data from. */ public APITunnel(ConnectionRecord record) { + this.identifier = record.getIdentifier(); this.startDate = record.getStartDate(); this.remoteHost = record.getRemoteHost(); this.username = record.getUsername(); this.uuid = "STUB"; // STUB } + /** + * Returns the identifier of the connection associated with this tunnel. + * + * @return + * The identifier of the connection associated with this tunnel. + */ + public String getIdentifier() { + return identifier; + } + /** * Returns the date and time the connection began. * diff --git a/guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js b/guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js index e5fe66bc9..996105a64 100644 --- a/guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js +++ b/guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js @@ -40,6 +40,13 @@ angular.module('rest').factory('ActiveTunnel', [function defineActiveTunnel() { // Use empty object by default template = template || {}; + /** + * The identifier of the connection associated with this tunnel. + * + * @type String + */ + this.identifier = template.identifier; + /** * The time that the tunnel began, in seconds since * 1970-01-01 00:00:00 UTC. From 1e8d68d611a3d469f571be681c495dae8b214f6e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 16 Mar 2015 16:31:39 -0700 Subject: [PATCH 04/17] GUAC-1132: Set of active connections should be synchronized. --- .../guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java index e20ed733f..845788c71 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionMultimap.java @@ -63,7 +63,7 @@ public class ActiveConnectionMultimap { // Get set of active connection records, creating if necessary Set connections = records.get(identifier); if (connections == null) { - connections = Collections.newSetFromMap(new LinkedHashMap()); + connections = Collections.synchronizedSet(Collections.newSetFromMap(new LinkedHashMap())); records.put(identifier, connections); } From c7827e0e3fb172508dd211b2c9a87081b91ad18c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 16 Mar 2015 16:32:47 -0700 Subject: [PATCH 05/17] GUAC-1132: Implement tracking of all active connections. --- .../AbstractGuacamoleSocketService.java | 22 +++++++++++++++++++ .../jdbc/socket/GuacamoleSocketService.java | 20 ++++++++++++++++- .../guacamole/auth/jdbc/user/UserContext.java | 10 +++++++-- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java index 7647ff54d..279363b7d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java @@ -29,6 +29,8 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; @@ -94,6 +96,12 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS @Inject private ConnectionRecordMapper connectionRecordMapper; + /** + * All records associated with active connections. + */ + private final Set activeConnectionRecords = + Collections.newSetFromMap(new ConcurrentHashMap()); + /** * All active connections to a connection having a given identifier. */ @@ -321,6 +329,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS // Release connection activeConnections.remove(identifier, activeConnection); activeConnectionGroups.remove(parentIdentifier, activeConnection); + activeConnectionRecords.remove(activeConnection); release(user, connection); // Release any associated group @@ -369,6 +378,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS // Record new active connection Runnable cleanupTask = new ConnectionCleanupTask(activeConnection); + activeConnectionRecords.add(activeConnection); activeConnections.put(connection.getIdentifier(), activeConnection); activeConnectionGroups.put(connection.getParentIdentifier(), activeConnection); @@ -432,6 +442,18 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS } + @Override + public Collection getActiveConnections(AuthenticatedUser user) + throws GuacamoleException { + + // Only administrators may see all active connections + if (!user.getUser().isAdministrator()) + return Collections.EMPTY_LIST; + + return Collections.unmodifiableCollection(activeConnectionRecords); + + } + @Override @Transactional public GuacamoleSocket getGuacamoleSocket(final AuthenticatedUser user, diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java index e8daf4653..2502f5360 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java @@ -42,6 +42,24 @@ import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; */ public interface GuacamoleSocketService { + /** + * Returns a connection containing connection records representing all + * currently-active connections visible by the given user. + * + * @param user + * The user retrieving active connections. + * + * @return + * A collection containing connection records representing all + * currently-active connections. + * + * @throws GuacamoleException + * If an error occurs while retrieving all active connections, or if + * permission is denied. + */ + public Collection getActiveConnections(AuthenticatedUser user) + throws GuacamoleException; + /** * Creates a socket for the given user which connects to the given * connection. The given client information will be passed to guacd when @@ -81,7 +99,7 @@ public interface GuacamoleSocketService { * The connection to check. * * @return - * A connection containing connection records representing all + * A collection containing connection records representing all * currently-active connections. */ public Collection getActiveConnections(Connection connection); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java index b4d3de31f..321074cb6 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java @@ -32,6 +32,7 @@ import java.util.Collection; import java.util.Collections; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.net.auth.ConnectionRecord; @@ -48,6 +49,12 @@ import org.glyptodon.guacamole.net.auth.User; public class UserContext extends RestrictedObject implements org.glyptodon.guacamole.net.auth.UserContext { + /** + * Service for creating and tracking sockets. + */ + @Inject + private GuacamoleSocketService socketService; + /** * User directory restricted by the permissions of the user associated * with this context. @@ -119,8 +126,7 @@ public class UserContext extends RestrictedObject @Override public Collection getActiveConnections() throws GuacamoleException { - // STUB - return Collections.EMPTY_LIST; + return socketService.getActiveConnections(getCurrentUser()); } } From f9bb25fdb28cc3a9d890db8ff4c30986bc3ac92e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 12:11:29 -0700 Subject: [PATCH 06/17] GUAC-1132: Associate active socket with connection record. --- .../AbstractGuacamoleSocketService.java | 23 +++++++++++-------- .../jdbc/socket/ActiveConnectionRecord.java | 19 +++++++++++++-- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java index 279363b7d..7276fbe95 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java @@ -352,11 +352,8 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS * The connection will be automatically released when it closes, or if it * fails to establish entirely. * - * @param user - * The user for whom the connection is being established. - * - * @param connection - * The connection the user is connecting to. + * @param activeConnection + * The active connection record of the connection in use. * * @param info * Information describing the Guacamole client connecting to the given @@ -370,7 +367,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS * If an error occurs while the connection is being established, or * while connection configuration information is being retrieved. */ - private GuacamoleSocket getGuacamoleSocket(ActiveConnectionRecord activeConnection, + private GuacamoleSocket assignGuacamoleSocket(ActiveConnectionRecord activeConnection, GuacamoleClientInformation info) throws GuacamoleException { @@ -382,13 +379,19 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS activeConnections.put(connection.getIdentifier(), activeConnection); activeConnectionGroups.put(connection.getParentIdentifier(), activeConnection); - // Return new socket try { - return new ConfiguredGuacamoleSocket( + + // Obtain socket which will automatically run the cleanup task + GuacamoleSocket socket = new ConfiguredGuacamoleSocket( getUnconfiguredGuacamoleSocket(cleanupTask), getGuacamoleConfiguration(activeConnection.getUser(), connection), info ); + + // Assign and return new socket + activeConnection.setActiveSocket(socket); + return socket; + } // Execute cleanup if socket could not be created @@ -462,7 +465,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS // Acquire and connect to single connection acquire(user, Collections.singletonList(connection)); - return getGuacamoleSocket(new ActiveConnectionRecord(user, connection), info); + return assignGuacamoleSocket(new ActiveConnectionRecord(user, connection), info); } @@ -487,7 +490,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS // Acquire and connect to any child ModeledConnection connection = acquire(user, connections); - return getGuacamoleSocket(new ActiveConnectionRecord(user, connectionGroup, connection), info); + return assignGuacamoleSocket(new ActiveConnectionRecord(user, connectionGroup, connection), info); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java index c7c46470d..708148848 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java @@ -62,6 +62,12 @@ public class ActiveConnectionRecord implements ConnectionRecord { */ private final Date startDate = new Date(); + /** + * The GuacamoleSocket used by the connection associated with this + * connection record. + */ + private GuacamoleSocket socket; + /** * Creates a new connection record associated with the given user, * connection, and balancing connection group. The given balancing @@ -189,8 +195,17 @@ public class ActiveConnectionRecord implements ConnectionRecord { @Override public GuacamoleSocket getActiveSocket() { - // STUB - return null; + return socket; + } + + /** + * Associates the given GuacamoleSocket with this connection record. + * + * @param socket + * The GuacamoleSocket to associate with this connection record. + */ + public void setActiveSocket(GuacamoleSocket socket) { + this.socket = socket; } } From 129bb688f4f046abed09c4d1ba1daf2adbb78c75 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 12:13:58 -0700 Subject: [PATCH 07/17] GUAC-1132: For consistency, rename getActiveSocket() to getSocket(). --- .../auth/jdbc/connection/ModeledConnectionRecord.java | 2 +- .../auth/jdbc/socket/AbstractGuacamoleSocketService.java | 2 +- .../guacamole/auth/jdbc/socket/ActiveConnectionRecord.java | 4 ++-- .../org/glyptodon/guacamole/net/auth/ConnectionRecord.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java index 7e61cdb11..76e3fcc8a 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -84,7 +84,7 @@ public class ModeledConnectionRecord implements ConnectionRecord { } @Override - public GuacamoleSocket getActiveSocket() { + public GuacamoleSocket getSocket() { return null; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java index 7276fbe95..f39d03048 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java @@ -389,7 +389,7 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS ); // Assign and return new socket - activeConnection.setActiveSocket(socket); + activeConnection.setSocket(socket); return socket; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java index 708148848..65812d325 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java @@ -194,7 +194,7 @@ public class ActiveConnectionRecord implements ConnectionRecord { } @Override - public GuacamoleSocket getActiveSocket() { + public GuacamoleSocket getSocket() { return socket; } @@ -204,7 +204,7 @@ public class ActiveConnectionRecord implements ConnectionRecord { * @param socket * The GuacamoleSocket to associate with this connection record. */ - public void setActiveSocket(GuacamoleSocket socket) { + public void setSocket(GuacamoleSocket socket) { this.socket = socket; } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java index 98c1b87c7..5789424b2 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java @@ -96,6 +96,6 @@ public interface ConnectionRecord { * The connected GuacamoleSocket, if any, or null if the connection is * not active or permission is denied. */ - public GuacamoleSocket getActiveSocket(); + public GuacamoleSocket getSocket(); } From b33e5158950fe2ddac462497ed5d8963ae380ba5 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 13:21:05 -0700 Subject: [PATCH 08/17] GUAC-1132: Associate tunnels with records, not sockets. Provide tunnel for connect(). --- .../jdbc/connection/ConnectionService.java | 8 +- .../jdbc/connection/ModeledConnection.java | 4 +- .../connection/ModeledConnectionRecord.java | 4 +- .../ConnectionGroupService.java | 8 +- .../ModeledConnectionGroup.java | 4 +- .../connectiongroup/RootConnectionGroup.java | 4 +- .../AbstractGuacamoleSocketService.java | 23 ++- .../jdbc/socket/ActiveConnectionRecord.java | 20 +- .../jdbc/socket/GuacamoleSocketService.java | 10 +- .../guacamole/auth/jdbc/user/UserContext.java | 10 +- .../net/DelegatingGuacamoleTunnel.java | 104 ++++++++++ .../guacamole/net/GuacamoleTunnel.java | 86 ++------- .../net/SynchronizedGuacamoleTunnel.java | 181 ++++++++++++++++++ .../guacamole/net/auth/Connectable.java | 6 +- .../guacamole/net/auth/ConnectionRecord.java | 8 +- .../guacamole/net/auth/UserContext.java | 21 ++ .../net/auth/simple/SimpleConnection.java | 21 +- .../auth/simple/SimpleConnectionGroup.java | 4 +- .../net/auth/simple/SimpleUserContext.java | 6 + .../net/basic/TunnelRequestService.java | 22 +-- .../rest/connection/APIConnectionWrapper.java | 4 +- .../APIConnectionGroupWrapper.java | 4 +- 22 files changed, 415 insertions(+), 147 deletions(-) create mode 100644 guacamole-common/src/main/java/org/glyptodon/guacamole/net/DelegatingGuacamoleTunnel.java create mode 100644 guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java index 8ac5d5da7..e67afad31 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java @@ -40,7 +40,7 @@ import org.glyptodon.guacamole.GuacamoleSecurityException; import org.glyptodon.guacamole.auth.jdbc.base.GroupedDirectoryObjectService; import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionMapper; import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper; -import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; @@ -403,19 +403,19 @@ public class ConnectionService extends GroupedDirectoryObjectService } @Override - public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { + public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { return connectionService.connect(getCurrentUser(), this, info); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java index 76e3fcc8a..f6701da2d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -24,7 +24,7 @@ package org.glyptodon.guacamole.auth.jdbc.connection; import java.util.Date; -import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.ConnectionRecord; /** @@ -84,7 +84,7 @@ public class ModeledConnectionRecord implements ConnectionRecord { } @Override - public GuacamoleSocket getSocket() { + public GuacamoleTunnel getTunnel() { return null; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java index bca97adb6..77a03b012 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java @@ -35,7 +35,7 @@ import org.glyptodon.guacamole.GuacamoleUnsupportedException; import org.glyptodon.guacamole.auth.jdbc.base.GroupedDirectoryObjectService; import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionGroupPermissionMapper; import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper; -import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; @@ -235,19 +235,19 @@ public class ConnectionGroupService extends GroupedDirectoryObjectService getActiveConnections() throws GuacamoleException { + public Collection getActiveConnections() + throws GuacamoleException { return socketService.getActiveConnections(getCurrentUser()); } + @Override + public ConnectionRecord getActiveConnection(String tunnelUUID) + throws GuacamoleException { + // STUB + return null; + } + } diff --git a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/DelegatingGuacamoleTunnel.java b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/DelegatingGuacamoleTunnel.java new file mode 100644 index 000000000..aa562989b --- /dev/null +++ b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/DelegatingGuacamoleTunnel.java @@ -0,0 +1,104 @@ +/* + * 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; + +import java.util.UUID; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.io.GuacamoleReader; +import org.glyptodon.guacamole.io.GuacamoleWriter; + +/** + * GuacamoleTunnel implementation which simply delegates all function calls to + * an underlying GuacamoleTunnel. + * + * @author Michael Jumper + */ +public class DelegatingGuacamoleTunnel implements GuacamoleTunnel { + + /** + * The wrapped GuacamoleTunnel. + */ + private final GuacamoleTunnel tunnel; + + /** + * Wraps the given tunnel such that all function calls against this tunnel + * will be delegated to it. + * + * @param tunnel + * The GuacamoleTunnel to wrap. + */ + public DelegatingGuacamoleTunnel(GuacamoleTunnel tunnel) { + this.tunnel = tunnel; + } + + @Override + public GuacamoleReader acquireReader() { + return tunnel.acquireReader(); + } + + @Override + public void releaseReader() { + tunnel.releaseReader(); + } + + @Override + public boolean hasQueuedReaderThreads() { + return tunnel.hasQueuedReaderThreads(); + } + + @Override + public GuacamoleWriter acquireWriter() { + return tunnel.acquireWriter(); + } + + @Override + public void releaseWriter() { + tunnel.releaseWriter(); + } + + @Override + public boolean hasQueuedWriterThreads() { + return tunnel.hasQueuedWriterThreads(); + } + + @Override + public UUID getUUID() { + return tunnel.getUUID(); + } + + @Override + public GuacamoleSocket getSocket() { + return tunnel.getSocket(); + } + + @Override + public void close() throws GuacamoleException { + tunnel.close(); + } + + @Override + public boolean isOpen() { + return tunnel.isOpen(); + } + +} diff --git a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/GuacamoleTunnel.java b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/GuacamoleTunnel.java index 65a847b18..3c27f73ae 100644 --- a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/GuacamoleTunnel.java +++ b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/GuacamoleTunnel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Glyptodon LLC + * Copyright (C) 2015 Glyptodon LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,7 +24,6 @@ package org.glyptodon.guacamole.net; import java.util.UUID; -import java.util.concurrent.locks.ReentrantLock; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.io.GuacamoleReader; import org.glyptodon.guacamole.io.GuacamoleWriter; @@ -35,46 +34,7 @@ import org.glyptodon.guacamole.io.GuacamoleWriter; * * @author Michael Jumper */ -public class GuacamoleTunnel { - - /** - * The UUID associated with this tunnel. Every tunnel must have a - * corresponding UUID such that tunnel read/write requests can be - * directed to the proper tunnel. - */ - private UUID uuid; - - /** - * The GuacamoleSocket that tunnel should use for communication on - * behalf of the connecting user. - */ - private GuacamoleSocket socket; - - /** - * Lock acquired when a read operation is in progress. - */ - private ReentrantLock readerLock; - - /** - * Lock acquired when a write operation is in progress. - */ - private ReentrantLock writerLock; - - /** - * Creates a new GuacamoleTunnel which synchronizes access to the - * Guacamole instruction stream associated with the given GuacamoleSocket. - * - * @param socket The GuacamoleSocket to provide synchronized access for. - */ - public GuacamoleTunnel(GuacamoleSocket socket) { - - this.socket = socket; - uuid = UUID.randomUUID(); - - readerLock = new ReentrantLock(); - writerLock = new ReentrantLock(); - - } +public interface GuacamoleTunnel { /** * Acquires exclusive read access to the Guacamole instruction stream @@ -83,19 +43,14 @@ public class GuacamoleTunnel { * @return A GuacamoleReader for reading from the Guacamole instruction * stream. */ - public GuacamoleReader acquireReader() { - readerLock.lock(); - return socket.getReader(); - } + GuacamoleReader acquireReader(); /** * Relinquishes exclusive read access to the Guacamole instruction * stream. This function should be called whenever a thread finishes using * a GuacamoleTunnel's GuacamoleReader. */ - public void releaseReader() { - readerLock.unlock(); - } + void releaseReader(); /** * Returns whether there are threads waiting for read access to the @@ -104,9 +59,7 @@ public class GuacamoleTunnel { * @return true if threads are waiting for read access the Guacamole * instruction stream, false otherwise. */ - public boolean hasQueuedReaderThreads() { - return readerLock.hasQueuedThreads(); - } + public boolean hasQueuedReaderThreads(); /** * Acquires exclusive write access to the Guacamole instruction stream @@ -115,19 +68,14 @@ public class GuacamoleTunnel { * @return A GuacamoleWriter for writing to the Guacamole instruction * stream. */ - public GuacamoleWriter acquireWriter() { - writerLock.lock(); - return socket.getWriter(); - } + public GuacamoleWriter acquireWriter(); /** * Relinquishes exclusive write access to the Guacamole instruction * stream. This function should be called whenever a thread finishes using * a GuacamoleTunnel's GuacamoleWriter. */ - public void releaseWriter() { - writerLock.unlock(); - } + public void releaseWriter(); /** * Returns whether there are threads waiting for write access to the @@ -136,18 +84,14 @@ public class GuacamoleTunnel { * @return true if threads are waiting for write access the Guacamole * instruction stream, false otherwise. */ - public boolean hasQueuedWriterThreads() { - return writerLock.hasQueuedThreads(); - } + public boolean hasQueuedWriterThreads(); /** * Returns the unique identifier associated with this GuacamoleTunnel. * * @return The unique identifier associated with this GuacamoleTunnel. */ - public UUID getUUID() { - return uuid; - } + public UUID getUUID(); /** * Returns the GuacamoleSocket used by this GuacamoleTunnel for reading @@ -155,9 +99,7 @@ public class GuacamoleTunnel { * * @return The GuacamoleSocket used by this GuacamoleTunnel. */ - public GuacamoleSocket getSocket() { - return socket; - } + public GuacamoleSocket getSocket(); /** * Release all resources allocated to this GuacamoleTunnel. @@ -165,17 +107,13 @@ public class GuacamoleTunnel { * @throws GuacamoleException if an error occurs while releasing * resources. */ - public void close() throws GuacamoleException { - socket.close(); - } + public void close() throws GuacamoleException; /** * Returns whether this GuacamoleTunnel is open, or has been closed. * * @return true if this GuacamoleTunnel is open, false if it is closed. */ - public boolean isOpen() { - return socket.isOpen(); - } + public boolean isOpen(); } diff --git a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java new file mode 100644 index 000000000..3e8dc77a1 --- /dev/null +++ b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java @@ -0,0 +1,181 @@ +/* + * 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; + + +import java.util.UUID; +import java.util.concurrent.locks.ReentrantLock; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.io.GuacamoleReader; +import org.glyptodon.guacamole.io.GuacamoleWriter; + +/** + * GuacamoleTunnel implementation which synchronizes access to the underlying + * reader and write with reentrant locks. + * + * @author Michael Jumper + */ +public class SynchronizedGuacamoleTunnel implements GuacamoleTunnel { + + /** + * The UUID associated with this tunnel. Every tunnel must have a + * corresponding UUID such that tunnel read/write requests can be + * directed to the proper tunnel. + */ + private final UUID uuid; + + /** + * The GuacamoleSocket that tunnel should use for communication on + * behalf of the connecting user. + */ + private final GuacamoleSocket socket; + + /** + * Lock acquired when a read operation is in progress. + */ + private final ReentrantLock readerLock; + + /** + * Lock acquired when a write operation is in progress. + */ + private final ReentrantLock writerLock; + + /** + * Creates a new GuacamoleTunnel which synchronizes access to the + * Guacamole instruction stream associated with the given GuacamoleSocket. + * + * @param socket The GuacamoleSocket to provide synchronized access for. + */ + public SynchronizedGuacamoleTunnel(GuacamoleSocket socket) { + + this.socket = socket; + uuid = UUID.randomUUID(); + + readerLock = new ReentrantLock(); + writerLock = new ReentrantLock(); + + } + + /** + * Acquires exclusive read access to the Guacamole instruction stream + * and returns a GuacamoleReader for reading from that stream. + * + * @return A GuacamoleReader for reading from the Guacamole instruction + * stream. + */ + public GuacamoleReader acquireReader() { + readerLock.lock(); + return socket.getReader(); + } + + /** + * Relinquishes exclusive read access to the Guacamole instruction + * stream. This function should be called whenever a thread finishes using + * a GuacamoleTunnel's GuacamoleReader. + */ + public void releaseReader() { + readerLock.unlock(); + } + + /** + * Returns whether there are threads waiting for read access to the + * Guacamole instruction stream. + * + * @return true if threads are waiting for read access the Guacamole + * instruction stream, false otherwise. + */ + public boolean hasQueuedReaderThreads() { + return readerLock.hasQueuedThreads(); + } + + /** + * Acquires exclusive write access to the Guacamole instruction stream + * and returns a GuacamoleWriter for writing to that stream. + * + * @return A GuacamoleWriter for writing to the Guacamole instruction + * stream. + */ + public GuacamoleWriter acquireWriter() { + writerLock.lock(); + return socket.getWriter(); + } + + /** + * Relinquishes exclusive write access to the Guacamole instruction + * stream. This function should be called whenever a thread finishes using + * a GuacamoleTunnel's GuacamoleWriter. + */ + public void releaseWriter() { + writerLock.unlock(); + } + + /** + * Returns whether there are threads waiting for write access to the + * Guacamole instruction stream. + * + * @return true if threads are waiting for write access the Guacamole + * instruction stream, false otherwise. + */ + public boolean hasQueuedWriterThreads() { + return writerLock.hasQueuedThreads(); + } + + /** + * Returns the unique identifier associated with this GuacamoleTunnel. + * + * @return The unique identifier associated with this GuacamoleTunnel. + */ + public UUID getUUID() { + return uuid; + } + + /** + * Returns the GuacamoleSocket used by this GuacamoleTunnel for reading + * and writing. + * + * @return The GuacamoleSocket used by this GuacamoleTunnel. + */ + public GuacamoleSocket getSocket() { + return socket; + } + + /** + * Release all resources allocated to this GuacamoleTunnel. + * + * @throws GuacamoleException if an error occurs while releasing + * resources. + */ + public void close() throws GuacamoleException { + socket.close(); + } + + /** + * Returns whether this GuacamoleTunnel is open, or has been closed. + * + * @return true if this GuacamoleTunnel is open, false if it is closed. + */ + public boolean isOpen() { + return socket.isOpen(); + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connectable.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connectable.java index 61d2ebfd9..d81a679af 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connectable.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connectable.java @@ -23,7 +23,7 @@ package org.glyptodon.guacamole.net.auth; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; /** @@ -42,13 +42,13 @@ public interface Connectable { * Information associated with the connecting client. * * @return - * A fully-established GuacamoleSocket. + * A fully-established GuacamoleTunnel. * * @throws GuacamoleException * If an error occurs while connecting to guacd, or if permission to * connect is denied. */ - public GuacamoleSocket connect(GuacamoleClientInformation info) + public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException; /** diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java index 5789424b2..a4e2fcbe5 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java @@ -23,7 +23,7 @@ package org.glyptodon.guacamole.net.auth; import java.util.Date; -import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.GuacamoleTunnel; /** * A logging record describing when a user started and ended usage of a @@ -88,14 +88,14 @@ public interface ConnectionRecord { public boolean isActive(); /** - * Returns the connected GuacamoleSocket of the connection associated with + * Returns the connected GuacamoleTunnel of the connection associated with * this record, if any. If the connection is not active, or access to * the socket is denied, null is returned. * * @return - * The connected GuacamoleSocket, if any, or null if the connection is + * The connected GuacamoleTunnel, if any, or null if the connection is * not active or permission is denied. */ - public GuacamoleSocket getSocket(); + public GuacamoleTunnel getTunnel(); } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java index 010e3e3a6..7c52834de 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java @@ -112,4 +112,25 @@ public interface UserContext { Collection getActiveConnections() throws GuacamoleException; + /** + * Returns the connection record associated with the active connection + * having the tunnel with the given UUID. The active connection will only + * be returned if the current user has access. + * + * @param tunnelUUID + * The UUID of the tunnel whose associated connection record should be + * returned. + * + * @return + * The connection record associated with the active connection having + * the tunnel with the given UUID, if any, or null if no such + * connection exists. + * + * @throws GuacamoleException + * If an error occurs while reading active connection records, or if + * permission is denied. + */ + ConnectionRecord getActiveConnection(String tunnelUUID) + throws GuacamoleException; + } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java index fe5070097..9aeb0c81c 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java @@ -28,8 +28,10 @@ import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.environment.LocalEnvironment; import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.InetGuacamoleSocket; import org.glyptodon.guacamole.net.SSLGuacamoleSocket; +import org.glyptodon.guacamole.net.SynchronizedGuacamoleTunnel; import org.glyptodon.guacamole.net.auth.AbstractConnection; import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.protocol.ConfiguredGuacamoleSocket; @@ -84,7 +86,7 @@ public class SimpleConnection extends AbstractConnection { } @Override - public GuacamoleSocket connect(GuacamoleClientInformation info) + public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { Environment env = new LocalEnvironment(); @@ -93,19 +95,24 @@ public class SimpleConnection extends AbstractConnection { String hostname = env.getProperty(Environment.GUACD_HOSTNAME); int port = env.getProperty(Environment.GUACD_PORT); + GuacamoleSocket socket; + // If guacd requires SSL, use it if (env.getProperty(Environment.GUACD_SSL, false)) - return new ConfiguredGuacamoleSocket( + socket = new ConfiguredGuacamoleSocket( new SSLGuacamoleSocket(hostname, port), config, info ); - // Return connected socket - return new ConfiguredGuacamoleSocket( - new InetGuacamoleSocket(hostname, port), - config, info - ); + // Otherwise, just connect directly via TCP + else + socket = new ConfiguredGuacamoleSocket( + new InetGuacamoleSocket(hostname, port), + config, info + ); + return new SynchronizedGuacamoleTunnel(socket); + } @Override diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroup.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroup.java index 71e73433e..a302f4676 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroup.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroup.java @@ -27,7 +27,7 @@ import java.util.HashSet; import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; -import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.AbstractConnectionGroup; import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; @@ -102,7 +102,7 @@ public class SimpleConnectionGroup extends AbstractConnectionGroup { } @Override - public GuacamoleSocket connect(GuacamoleClientInformation info) + public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { throw new GuacamoleSecurityException("Permission denied."); } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java index 3f19ec7fc..480c79017 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java @@ -174,4 +174,10 @@ public class SimpleUserContext implements UserContext { return Collections.EMPTY_LIST; } + @Override + public ConnectionRecord getActiveConnection(String tunnelUUID) + throws GuacamoleException { + return null; + } + } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java index b0ba6ee39..2e4033893 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java @@ -31,7 +31,7 @@ import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; import org.glyptodon.guacamole.GuacamoleUnauthorizedException; import org.glyptodon.guacamole.io.GuacamoleReader; -import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.DelegatingGuacamoleTunnel; import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; @@ -204,7 +204,7 @@ public class TunnelRequestService { info.getVideoMimetypes().addAll(video_mimetypes); // Create connected socket from identifier - GuacamoleSocket socket; + GuacamoleTunnel tunnel; switch (id_type) { // Connection identifiers @@ -221,8 +221,8 @@ public class TunnelRequestService { throw new GuacamoleSecurityException("Requested connection is not authorized."); } - // Connect socket - socket = connection.connect(info); + // Connect tunnel + tunnel = connection.connect(info); logger.info("User \"{}\" successfully connected to \"{}\".", context.self().getIdentifier(), id); break; } @@ -241,8 +241,8 @@ public class TunnelRequestService { throw new GuacamoleSecurityException("Requested connection group is not authorized."); } - // Connect socket - socket = group.connect(info); + // Connect tunnel + tunnel = group.connect(info); logger.info("User \"{}\" successfully connected to group \"{}\".", context.self().getIdentifier(), id); break; } @@ -253,8 +253,8 @@ public class TunnelRequestService { } - // Associate socket with tunnel - GuacamoleTunnel tunnel = new GuacamoleTunnel(socket) { + // Track tunnel open/close + GuacamoleTunnel monitoredTunnel = new DelegatingGuacamoleTunnel(tunnel) { @Override public GuacamoleReader acquireReader() { @@ -308,13 +308,13 @@ public class TunnelRequestService { }; // Notify listeners about connection - if (!notifyConnect(session, tunnel)) { + if (!notifyConnect(session, monitoredTunnel)) { logger.info("Successful connection canceled by hook."); return null; } - session.addTunnel(tunnel); - return tunnel; + session.addTunnel(monitoredTunnel); + return monitoredTunnel; } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java index 33fe5047f..bbfb6916c 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java @@ -26,7 +26,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; @@ -116,7 +116,7 @@ public class APIConnectionWrapper implements Connection { } @Override - public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { + public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { throw new UnsupportedOperationException("Operation not supported."); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java index 9007e75dd..c37aee94a 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java @@ -24,7 +24,7 @@ package org.glyptodon.guacamole.net.basic.rest.connectiongroup; import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; @@ -106,7 +106,7 @@ public class APIConnectionGroupWrapper implements ConnectionGroup { } @Override - public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { + public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { throw new UnsupportedOperationException("Operation not supported."); } From 5ce0a3a5a410b9bbf93cf652883c7a64330d566f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 13:31:26 -0700 Subject: [PATCH 09/17] GUAC-1132: Socket service is now really tunnel service. --- .../JDBCAuthenticationProviderModule.java | 12 +++++------ .../jdbc/connection/ConnectionService.java | 8 ++++---- .../jdbc/connection/ModeledConnection.java | 6 +++--- .../ConnectionGroupService.java | 6 +++--- .../ModeledConnectionGroup.java | 6 +++--- .../AbstractGuacamoleTunnelService.java} | 6 +++--- .../ActiveConnectionMultimap.java | 2 +- .../ActiveConnectionRecord.java | 2 +- .../BalancedGuacamoleTunnelService.java} | 8 ++++---- .../GuacamoleTunnelService.java} | 4 ++-- .../ManagedInetGuacamoleSocket.java | 2 +- .../MultiseatGuacamoleTunnelService.java} | 8 ++++---- .../auth/jdbc/{socket => tunnel}/Seat.java | 2 +- .../SingleSeatGuacamoleTunnelService.java} | 8 ++++---- .../UnrestrictedGuacamoleTunnelService.java} | 8 ++++---- .../jdbc/{socket => tunnel}/package-info.java | 6 +++--- .../guacamole/auth/jdbc/user/UserContext.java | 6 +++--- .../mysql/MySQLAuthenticationProvider.java | 20 +++++++++---------- .../PostgreSQLAuthenticationProvider.java | 20 +++++++++---------- 19 files changed, 70 insertions(+), 70 deletions(-) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket/AbstractGuacamoleSocketService.java => tunnel/AbstractGuacamoleTunnelService.java} (98%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket => tunnel}/ActiveConnectionMultimap.java (98%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket => tunnel}/ActiveConnectionRecord.java (99%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket/BalancedGuacamoleSocketService.java => tunnel/BalancedGuacamoleTunnelService.java} (93%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket/GuacamoleSocketService.java => tunnel/GuacamoleTunnelService.java} (98%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket => tunnel}/ManagedInetGuacamoleSocket.java (98%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket/MultiseatGuacamoleSocketService.java => tunnel/MultiseatGuacamoleTunnelService.java} (95%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket => tunnel}/Seat.java (98%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket/SingleSeatGuacamoleSocketService.java => tunnel/SingleSeatGuacamoleTunnelService.java} (94%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket/UnrestrictedGuacamoleSocketService.java => tunnel/UnrestrictedGuacamoleTunnelService.java} (92%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/{socket => tunnel}/package-info.java (88%) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java index a50389f48..a02012a90 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java @@ -40,7 +40,7 @@ import org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionMapper; import org.glyptodon.guacamole.auth.jdbc.user.UserMapper; import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupService; import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionService; -import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.auth.jdbc.security.PasswordEncryptionService; import org.glyptodon.guacamole.auth.jdbc.security.SHA256PasswordEncryptionService; import org.glyptodon.guacamole.auth.jdbc.security.SaltService; @@ -80,7 +80,7 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { * The service class to use to provide GuacamoleSockets for each * connection. */ - private final Class socketServiceClass; + private final Class tunnelServiceClass; /** * Creates a new JDBC authentication provider module that configures the @@ -90,13 +90,13 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { * @param environment * The environment to use to configure injected classes. * - * @param socketServiceClass + * @param tunnelServiceClass * The socket service to use to provide sockets for connections. */ public JDBCAuthenticationProviderModule(Environment environment, - Class socketServiceClass) { + Class tunnelServiceClass) { this.environment = environment; - this.socketServiceClass = socketServiceClass; + this.tunnelServiceClass = tunnelServiceClass; } @Override @@ -147,7 +147,7 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { bind(UserService.class); // Bind provided socket service - bind(GuacamoleSocketService.class).to(socketServiceClass); + bind(GuacamoleTunnelService.class).to(tunnelServiceClass); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java index e67afad31..0659e88cb 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java @@ -33,7 +33,7 @@ import java.util.Map; import java.util.Set; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; -import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.GuacamoleClientException; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; @@ -91,7 +91,7 @@ public class ConnectionService extends GroupedDirectoryObjectService getObjectMapper() { @@ -371,7 +371,7 @@ public class ConnectionService extends GroupedDirectoryObjectService models = connectionRecordMapper.select(identifier); // Get currently-active connections - List records = new ArrayList(socketService.getActiveConnections(connection)); + List records = new ArrayList(tunnelService.getActiveConnections(connection)); Collections.reverse(records); // Add past connections from model objects @@ -415,7 +415,7 @@ public class ConnectionService extends GroupedDirectoryObjectService * Service for creating and tracking sockets. */ @Inject - private GuacamoleSocketService socketService; + private GuacamoleTunnelService tunnelService; /** * Provider for lazy-loaded, permission-controlled configurations. @@ -120,7 +120,7 @@ public class ModeledConnection extends GroupedDirectoryObject @Override public int getActiveConnections() { - return socketService.getActiveConnections(this).size(); + return tunnelService.getActiveConnections(this).size(); } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java index 77a03b012..02f019020 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java @@ -27,7 +27,7 @@ import com.google.inject.Provider; import java.util.Set; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; -import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.GuacamoleClientException; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; @@ -74,7 +74,7 @@ public class ConnectionGroupService extends GroupedDirectoryObjectService getObjectMapper() { @@ -247,7 +247,7 @@ public class ConnectionGroupService extends GroupedDirectoryObjectService getActiveConnections() throws GuacamoleException { - return socketService.getActiveConnections(getCurrentUser()); + return tunnelService.getActiveConnections(getCurrentUser()); } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java index 3e7463b69..664ad7e67 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java @@ -29,11 +29,11 @@ import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProviderModule; -import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService; -import org.glyptodon.guacamole.auth.jdbc.socket.MultiseatGuacamoleSocketService; -import org.glyptodon.guacamole.auth.jdbc.socket.BalancedGuacamoleSocketService; -import org.glyptodon.guacamole.auth.jdbc.socket.SingleSeatGuacamoleSocketService; -import org.glyptodon.guacamole.auth.jdbc.socket.UnrestrictedGuacamoleSocketService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.MultiseatGuacamoleTunnelService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.BalancedGuacamoleTunnelService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.SingleSeatGuacamoleTunnelService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.UnrestrictedGuacamoleTunnelService; import org.glyptodon.guacamole.auth.jdbc.user.UserContextService; import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.environment.LocalEnvironment; @@ -69,7 +69,7 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider { * @throws GuacamoleException * If an error occurs while reading the configuration options. */ - private Class + private Class getSocketServiceClass(Environment environment) throws GuacamoleException { @@ -81,11 +81,11 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider { // Connections may not be used concurrently if (disallowDuplicate) - return SingleSeatGuacamoleSocketService.class; + return SingleSeatGuacamoleTunnelService.class; // Connections are reserved for a single user when in use else - return BalancedGuacamoleSocketService.class; + return BalancedGuacamoleTunnelService.class; } @@ -93,11 +93,11 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider { // Connections may be used concurrently, but only once per user if (disallowDuplicate) - return MultiseatGuacamoleSocketService.class; + return MultiseatGuacamoleTunnelService.class; // Connection use is not restricted else - return UnrestrictedGuacamoleSocketService.class; + return UnrestrictedGuacamoleTunnelService.class; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java index 65bd2270a..1ee8afe2f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLAuthenticationProvider.java @@ -29,11 +29,11 @@ import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProviderModule; -import org.glyptodon.guacamole.auth.jdbc.socket.BalancedGuacamoleSocketService; -import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService; -import org.glyptodon.guacamole.auth.jdbc.socket.MultiseatGuacamoleSocketService; -import org.glyptodon.guacamole.auth.jdbc.socket.SingleSeatGuacamoleSocketService; -import org.glyptodon.guacamole.auth.jdbc.socket.UnrestrictedGuacamoleSocketService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.BalancedGuacamoleTunnelService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.MultiseatGuacamoleTunnelService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.SingleSeatGuacamoleTunnelService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.UnrestrictedGuacamoleTunnelService; import org.glyptodon.guacamole.auth.jdbc.user.UserContextService; import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.environment.LocalEnvironment; @@ -68,7 +68,7 @@ public class PostgreSQLAuthenticationProvider implements AuthenticationProvider * @throws GuacamoleException * If an error occurs while reading the configuration options. */ - private Class + private Class getSocketServiceClass(Environment environment) throws GuacamoleException { @@ -80,11 +80,11 @@ public class PostgreSQLAuthenticationProvider implements AuthenticationProvider // Connections may not be used concurrently if (disallowDuplicate) - return SingleSeatGuacamoleSocketService.class; + return SingleSeatGuacamoleTunnelService.class; // Connections are reserved for a single user when in use else - return BalancedGuacamoleSocketService.class; + return BalancedGuacamoleTunnelService.class; } @@ -92,11 +92,11 @@ public class PostgreSQLAuthenticationProvider implements AuthenticationProvider // Connections may be used concurrently, but only once per user if (disallowDuplicate) - return MultiseatGuacamoleSocketService.class; + return MultiseatGuacamoleTunnelService.class; // Connection use is not restricted else - return UnrestrictedGuacamoleSocketService.class; + return UnrestrictedGuacamoleTunnelService.class; } From d0c57a2378a249bc14020f9aad2cb0fb5be55ad1 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 13:45:55 -0700 Subject: [PATCH 10/17] GUAC-1132: Implement tunnel REST service. --- .../net/basic/rest/tunnel/APITunnel.java | 7 +++-- .../basic/rest/tunnel/TunnelRESTService.java | 27 ++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java index 279500ba1..ca1a0604c 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java @@ -63,13 +63,16 @@ public class APITunnel { * * @param record * The record to copy data from. + * + * @param uuid + * The UUID of the associated GuacamoleTunnel. */ - public APITunnel(ConnectionRecord record) { + public APITunnel(ConnectionRecord record, String uuid) { this.identifier = record.getIdentifier(); this.startDate = record.getStartDate(); this.remoteHost = record.getRemoteHost(); this.username = record.getUsername(); - this.uuid = "STUB"; // STUB + this.uuid = uuid; } /** diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java index 885b17a78..231f8586a 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java @@ -34,7 +34,9 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleResourceNotFoundException; import org.glyptodon.guacamole.GuacamoleUnsupportedException; +import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; @@ -62,7 +64,7 @@ public class TunnelRESTService { */ @Inject private AuthenticationService authenticationService; - + /** * Retrieves the tunnels of all active connections visible to the current * user. @@ -87,8 +89,14 @@ public class TunnelRESTService { // Retrieve all active tunnels List apiTunnels = new ArrayList(); - for (ConnectionRecord record : userContext.getActiveConnections()) - apiTunnels.add(new APITunnel(record)); + for (ConnectionRecord record : userContext.getActiveConnections()) { + + // Locate associated tunnel and UUID + GuacamoleTunnel tunnel = record.getTunnel(); + if (tunnel != null) + apiTunnels.add(new APITunnel(record, tunnel.getUUID().toString())); + + } return apiTunnels; @@ -117,9 +125,16 @@ public class TunnelRESTService { UserContext userContext = authenticationService.getUserContext(authToken); - // STUB - throw new GuacamoleUnsupportedException("STUB"); - + // Retrieve specified tunnel + ConnectionRecord record = userContext.getActiveConnection(tunnelUUID); + if (record == null) + throw new GuacamoleResourceNotFoundException("No such tunnel: \"" + tunnelUUID + "\""); + + // Close tunnel, if not already closed + GuacamoleTunnel tunnel = record.getTunnel(); + if (tunnel != null && tunnel.isOpen()) + tunnel.close(); + } } From cd52b25b94eb4d4936cc6468d009e4951b94cb42 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 15:13:32 -0700 Subject: [PATCH 11/17] GUAC-1132: Add support for getRemoteHost() of ConnectionRecord. Store remote host within AuthenticatedUser. --- .../modules/guacamole-auth-jdbc-base/pom.xml | 8 +++++ .../connection/ModeledConnectionRecord.java | 3 +- .../jdbc/tunnel/ActiveConnectionRecord.java | 3 +- .../auth/jdbc/user/AuthenticatedUser.java | 33 +++++++++++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml index 40ca67bee..a5f808c53 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml @@ -37,6 +37,14 @@ + + + javax.servlet + servlet-api + 2.5 + provided + + org.glyptodon.guacamole diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java index f6701da2d..d00d19d13 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -69,8 +69,7 @@ public class ModeledConnectionRecord implements ConnectionRecord { @Override public String getRemoteHost() { - // STUB - return "STUB"; + return null; } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java index a4c50e21a..878e29d25 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java @@ -176,8 +176,7 @@ public class ActiveConnectionRecord implements ConnectionRecord { @Override public String getRemoteHost() { - // STUB - return "STUB"; + return user.getRemoteHost(); } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/AuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/AuthenticatedUser.java index 7b01d4598..f778bbb44 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/AuthenticatedUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/AuthenticatedUser.java @@ -22,6 +22,7 @@ package org.glyptodon.guacamole.auth.jdbc.user; +import javax.servlet.http.HttpServletRequest; import org.glyptodon.guacamole.net.auth.Credentials; /** @@ -41,6 +42,27 @@ public class AuthenticatedUser { */ private final Credentials credentials; + /** + * The host from which this user authenticated. + */ + private final String remoteHost; + + /** + * Derives the remote host of the authenticating user from the given + * credentials object. + * + * @param credentials + * The credentials to derive the remote host from. + * + * @return + * The remote host from which the user with the given credentials is + * authenticating. + */ + private static String getRemoteHost(Credentials credentials) { + HttpServletRequest request = credentials.getRequest(); + return request.getRemoteAddr(); + } + /** * Creates a new AuthenticatedUser associating the given user with their * corresponding credentials. @@ -54,6 +76,7 @@ public class AuthenticatedUser { public AuthenticatedUser(ModeledUser user, Credentials credentials) { this.user = user; this.credentials = credentials; + this.remoteHost = getRemoteHost(credentials); } /** @@ -76,4 +99,14 @@ public class AuthenticatedUser { return credentials; } + /** + * Returns the host from which this user authenticated. + * + * @return + * The host from which this user authenticated. + */ + public String getRemoteHost() { + return remoteHost; + } + } From a345ee7385c68121cc52b86637b47ae914cf562a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 15:29:22 -0700 Subject: [PATCH 12/17] GUAC-1132: Add missing @Override annotations. --- .../net/SynchronizedGuacamoleTunnel.java | 70 +++---------------- 1 file changed, 10 insertions(+), 60 deletions(-) diff --git a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java index 3e8dc77a1..be4271df6 100644 --- a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java +++ b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java @@ -76,104 +76,54 @@ public class SynchronizedGuacamoleTunnel implements GuacamoleTunnel { } - /** - * Acquires exclusive read access to the Guacamole instruction stream - * and returns a GuacamoleReader for reading from that stream. - * - * @return A GuacamoleReader for reading from the Guacamole instruction - * stream. - */ + @Override public GuacamoleReader acquireReader() { readerLock.lock(); return socket.getReader(); } - /** - * Relinquishes exclusive read access to the Guacamole instruction - * stream. This function should be called whenever a thread finishes using - * a GuacamoleTunnel's GuacamoleReader. - */ + @Override public void releaseReader() { readerLock.unlock(); } - /** - * Returns whether there are threads waiting for read access to the - * Guacamole instruction stream. - * - * @return true if threads are waiting for read access the Guacamole - * instruction stream, false otherwise. - */ + @Override public boolean hasQueuedReaderThreads() { return readerLock.hasQueuedThreads(); } - /** - * Acquires exclusive write access to the Guacamole instruction stream - * and returns a GuacamoleWriter for writing to that stream. - * - * @return A GuacamoleWriter for writing to the Guacamole instruction - * stream. - */ + @Override public GuacamoleWriter acquireWriter() { writerLock.lock(); return socket.getWriter(); } - /** - * Relinquishes exclusive write access to the Guacamole instruction - * stream. This function should be called whenever a thread finishes using - * a GuacamoleTunnel's GuacamoleWriter. - */ + @Override public void releaseWriter() { writerLock.unlock(); } - /** - * Returns whether there are threads waiting for write access to the - * Guacamole instruction stream. - * - * @return true if threads are waiting for write access the Guacamole - * instruction stream, false otherwise. - */ + @Override public boolean hasQueuedWriterThreads() { return writerLock.hasQueuedThreads(); } - /** - * Returns the unique identifier associated with this GuacamoleTunnel. - * - * @return The unique identifier associated with this GuacamoleTunnel. - */ + @Override public UUID getUUID() { return uuid; } - /** - * Returns the GuacamoleSocket used by this GuacamoleTunnel for reading - * and writing. - * - * @return The GuacamoleSocket used by this GuacamoleTunnel. - */ + @Override public GuacamoleSocket getSocket() { return socket; } - /** - * Release all resources allocated to this GuacamoleTunnel. - * - * @throws GuacamoleException if an error occurs while releasing - * resources. - */ + @Override public void close() throws GuacamoleException { socket.close(); } - /** - * Returns whether this GuacamoleTunnel is open, or has been closed. - * - * @return true if this GuacamoleTunnel is open, false if it is closed. - */ + @Override public boolean isOpen() { return socket.isOpen(); } From d55de3e86934fb4d520f81fcb2e6f399a5cfdee0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 15:42:07 -0700 Subject: [PATCH 13/17] GUAC-1132: Refactor much of SynchronizedGuacamoleTunnel into AbstractGuacamaoleTunnel. Rename to SimpleGuacamoleTunnel. --- ...nnel.java => AbstractGuacamoleTunnel.java} | 82 ++++++++++--------- .../guacamole/net/SimpleGuacamoleTunnel.java | 69 ++++++++++++++++ .../net/auth/simple/SimpleConnection.java | 4 +- 3 files changed, 113 insertions(+), 42 deletions(-) rename guacamole-common/src/main/java/org/glyptodon/guacamole/net/{SynchronizedGuacamoleTunnel.java => AbstractGuacamoleTunnel.java} (60%) create mode 100644 guacamole-common/src/main/java/org/glyptodon/guacamole/net/SimpleGuacamoleTunnel.java diff --git a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/AbstractGuacamoleTunnel.java similarity index 60% rename from guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java rename to guacamole-common/src/main/java/org/glyptodon/guacamole/net/AbstractGuacamoleTunnel.java index be4271df6..d8da19477 100644 --- a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SynchronizedGuacamoleTunnel.java +++ b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/AbstractGuacamoleTunnel.java @@ -23,32 +23,19 @@ package org.glyptodon.guacamole.net; -import java.util.UUID; import java.util.concurrent.locks.ReentrantLock; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.io.GuacamoleReader; import org.glyptodon.guacamole.io.GuacamoleWriter; /** - * GuacamoleTunnel implementation which synchronizes access to the underlying - * reader and write with reentrant locks. + * Base GuacamoleTunnel implementation which synchronizes access to the + * underlying reader and writer with reentrant locks. Implementations need only + * provide the tunnel's UUID and socket. * * @author Michael Jumper */ -public class SynchronizedGuacamoleTunnel implements GuacamoleTunnel { - - /** - * The UUID associated with this tunnel. Every tunnel must have a - * corresponding UUID such that tunnel read/write requests can be - * directed to the proper tunnel. - */ - private final UUID uuid; - - /** - * The GuacamoleSocket that tunnel should use for communication on - * behalf of the connecting user. - */ - private final GuacamoleSocket socket; +public abstract class AbstractGuacamoleTunnel implements GuacamoleTunnel { /** * Lock acquired when a read operation is in progress. @@ -62,42 +49,67 @@ public class SynchronizedGuacamoleTunnel implements GuacamoleTunnel { /** * Creates a new GuacamoleTunnel which synchronizes access to the - * Guacamole instruction stream associated with the given GuacamoleSocket. - * - * @param socket The GuacamoleSocket to provide synchronized access for. + * Guacamole instruction stream associated with the underlying + * GuacamoleSocket. */ - public SynchronizedGuacamoleTunnel(GuacamoleSocket socket) { - - this.socket = socket; - uuid = UUID.randomUUID(); - + public AbstractGuacamoleTunnel() { readerLock = new ReentrantLock(); writerLock = new ReentrantLock(); - } + /** + * Acquires exclusive read access to the Guacamole instruction stream + * and returns a GuacamoleReader for reading from that stream. + * + * @return A GuacamoleReader for reading from the Guacamole instruction + * stream. + */ @Override public GuacamoleReader acquireReader() { readerLock.lock(); - return socket.getReader(); + return getSocket().getReader(); } + /** + * Relinquishes exclusive read access to the Guacamole instruction + * stream. This function should be called whenever a thread finishes using + * a GuacamoleTunnel's GuacamoleReader. + */ @Override public void releaseReader() { readerLock.unlock(); } + /** + * Returns whether there are threads waiting for read access to the + * Guacamole instruction stream. + * + * @return true if threads are waiting for read access the Guacamole + * instruction stream, false otherwise. + */ @Override public boolean hasQueuedReaderThreads() { return readerLock.hasQueuedThreads(); } + /** + * Acquires exclusive write access to the Guacamole instruction stream + * and returns a GuacamoleWriter for writing to that stream. + * + * @return A GuacamoleWriter for writing to the Guacamole instruction + * stream. + */ @Override public GuacamoleWriter acquireWriter() { writerLock.lock(); - return socket.getWriter(); + return getSocket().getWriter(); } + /** + * Relinquishes exclusive write access to the Guacamole instruction + * stream. This function should be called whenever a thread finishes using + * a GuacamoleTunnel's GuacamoleWriter. + */ @Override public void releaseWriter() { writerLock.unlock(); @@ -108,24 +120,14 @@ public class SynchronizedGuacamoleTunnel implements GuacamoleTunnel { return writerLock.hasQueuedThreads(); } - @Override - public UUID getUUID() { - return uuid; - } - - @Override - public GuacamoleSocket getSocket() { - return socket; - } - @Override public void close() throws GuacamoleException { - socket.close(); + getSocket().close(); } @Override public boolean isOpen() { - return socket.isOpen(); + return getSocket().isOpen(); } } diff --git a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SimpleGuacamoleTunnel.java b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SimpleGuacamoleTunnel.java new file mode 100644 index 000000000..f0c28cb4e --- /dev/null +++ b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/SimpleGuacamoleTunnel.java @@ -0,0 +1,69 @@ +/* + * 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; + + +import java.util.UUID; + +/** + * GuacamoleTunnel implementation which uses a provided socket. The UUID of + * the tunnel will be randomly generated. + * + * @author Michael Jumper + */ +public class SimpleGuacamoleTunnel extends AbstractGuacamoleTunnel { + + /** + * The UUID associated with this tunnel. Every tunnel must have a + * corresponding UUID such that tunnel read/write requests can be + * directed to the proper tunnel. + */ + private final UUID uuid = UUID.randomUUID(); + + /** + * The GuacamoleSocket that tunnel should use for communication on + * behalf of the connecting user. + */ + private final GuacamoleSocket socket; + + /** + * Creates a new GuacamoleTunnel which synchronizes access to the + * Guacamole instruction stream associated with the given GuacamoleSocket. + * + * @param socket The GuacamoleSocket to provide synchronized access for. + */ + public SimpleGuacamoleTunnel(GuacamoleSocket socket) { + this.socket = socket; + } + + @Override + public UUID getUUID() { + return uuid; + } + + @Override + public GuacamoleSocket getSocket() { + return socket; + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java index 9aeb0c81c..a1ae0e3f0 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java @@ -31,7 +31,7 @@ import org.glyptodon.guacamole.net.GuacamoleSocket; import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.InetGuacamoleSocket; import org.glyptodon.guacamole.net.SSLGuacamoleSocket; -import org.glyptodon.guacamole.net.SynchronizedGuacamoleTunnel; +import org.glyptodon.guacamole.net.SimpleGuacamoleTunnel; import org.glyptodon.guacamole.net.auth.AbstractConnection; import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.protocol.ConfiguredGuacamoleSocket; @@ -111,7 +111,7 @@ public class SimpleConnection extends AbstractConnection { config, info ); - return new SynchronizedGuacamoleTunnel(socket); + return new SimpleGuacamoleTunnel(socket); } From c2192c41386d97fb666280bbfb5399e4a27211f0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 16:18:31 -0700 Subject: [PATCH 14/17] GUAC-1132: Implement getActiveConnection() on UserContext. --- .../AbstractGuacamoleTunnelService.java | 31 +++++++---- .../jdbc/tunnel/ActiveConnectionRecord.java | 53 +++++++++++++++++-- .../jdbc/tunnel/GuacamoleTunnelService.java | 26 ++++++++- .../guacamole/auth/jdbc/user/UserContext.java | 4 +- 4 files changed, 94 insertions(+), 20 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java index de8ea905f..94521f51d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java @@ -29,7 +29,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; -import java.util.Set; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; @@ -47,7 +47,6 @@ import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionMapper; import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.net.GuacamoleSocket; import org.glyptodon.guacamole.net.GuacamoleTunnel; -import org.glyptodon.guacamole.net.SynchronizedGuacamoleTunnel; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.net.auth.ConnectionRecord; @@ -99,10 +98,10 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS private ConnectionRecordMapper connectionRecordMapper; /** - * All records associated with active connections. + * All active connections through the tunnel having a given UUID. */ - private final Set activeConnectionRecords = - Collections.newSetFromMap(new ConcurrentHashMap()); + private final Map activeTunnels = + new ConcurrentHashMap(); /** * All active connections to a connection having a given identifier. @@ -329,9 +328,9 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS String parentIdentifier = connection.getParentIdentifier(); // Release connection + activeTunnels.remove(activeConnection.getUUID().toString()); activeConnections.remove(identifier, activeConnection); activeConnectionGroups.remove(parentIdentifier, activeConnection); - activeConnectionRecords.remove(activeConnection); release(user, connection); // Release any associated group @@ -377,7 +376,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS // Record new active connection Runnable cleanupTask = new ConnectionCleanupTask(activeConnection); - activeConnectionRecords.add(activeConnection); + activeTunnels.put(activeConnection.getUUID().toString(), activeConnection); activeConnections.put(connection.getIdentifier(), activeConnection); activeConnectionGroups.put(connection.getParentIdentifier(), activeConnection); @@ -391,9 +390,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS ); // Assign and return new tunnel - GuacamoleTunnel tunnel = new SynchronizedGuacamoleTunnel(socket); - activeConnection.setTunnel(tunnel); - return tunnel; + return activeConnection.assignGuacamoleTunnel(socket); } @@ -456,10 +453,22 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS if (!user.getUser().isAdministrator()) return Collections.EMPTY_LIST; - return Collections.unmodifiableCollection(activeConnectionRecords); + return Collections.unmodifiableCollection(activeTunnels.values()); } + @Override + public ConnectionRecord getActiveConnection(AuthenticatedUser user, + String tunnelUUID) throws GuacamoleException { + + // Only administrators may see all active connections + if (!user.getUser().isAdministrator()) + return null; + + return activeTunnels.get(tunnelUUID); + + } + @Override @Transactional public GuacamoleTunnel getGuacamoleTunnel(final AuthenticatedUser user, diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java index 878e29d25..de2bffaf2 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java @@ -23,9 +23,12 @@ package org.glyptodon.guacamole.auth.jdbc.tunnel; import java.util.Date; +import java.util.UUID; import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.net.AbstractGuacamoleTunnel; +import org.glyptodon.guacamole.net.GuacamoleSocket; import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.ConnectionRecord; @@ -62,6 +65,11 @@ public class ActiveConnectionRecord implements ConnectionRecord { */ private final Date startDate = new Date(); + /** + * The UUID that will be assigned to the underlying tunnel. + */ + private final UUID uuid = UUID.randomUUID(); + /** * The GuacamoleTunnel used by the connection associated with this * connection record. @@ -198,13 +206,48 @@ public class ActiveConnectionRecord implements ConnectionRecord { } /** - * Associates the given GuacamoleTunnel with this connection record. + * Associates a new GuacamoleTunnel with this connection record using the + * given socket. * - * @param tunnel - * The GuacamoleTunnel to associate with this connection record. + * @param socket + * The GuacamoleSocket to use to create the tunnel associated with this + * connection record. + * + * @return + * The newly-created tunnel associated with this connection record. */ - public void setTunnel(GuacamoleTunnel tunnel) { - this.tunnel = tunnel; + public GuacamoleTunnel assignGuacamoleTunnel(final GuacamoleSocket socket) { + + // Create tunnel with given socket + this.tunnel = new AbstractGuacamoleTunnel() { + + @Override + public GuacamoleSocket getSocket() { + return socket; + } + + @Override + public UUID getUUID() { + return uuid; + } + + }; + + // Return newly-created tunnel + return this.tunnel; + } + /** + * Returns the UUID of the underlying tunnel. If there is no underlying + * tunnel, this will be the UUID assigned to the underlying tunnel when the + * tunnel is set. + * + * @return + * The current or future UUID of the underlying tunnel. + */ + public UUID getUUID() { + return uuid; + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java index f56465403..f45d31426 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java @@ -43,7 +43,7 @@ import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; public interface GuacamoleTunnelService { /** - * Returns a connection containing connection records representing all + * Returns a collection containing connection records representing all * currently-active connections visible by the given user. * * @param user @@ -60,6 +60,30 @@ public interface GuacamoleTunnelService { public Collection getActiveConnections(AuthenticatedUser user) throws GuacamoleException; + /** + * Returns the connection records representing the connection associated + * with the tunnel having the given UUID, if that connection is visible to + * the given user. + * + * @param user + * The user retrieving the active connection. + * + * @param tunnelUUID + * The UUID of the tunnel associated with the active connection being + * retrieved. + * + * @return + * The active connection associated with the tunnel having the given + * UUID, or null if no such connection exists. + * + * @throws GuacamoleException + * If an error occurs while retrieving all active connections, or if + * permission is denied. + */ + public ConnectionRecord getActiveConnection(AuthenticatedUser user, + String tunnelUUID) + throws GuacamoleException; + /** * Creates a socket for the given user which connects to the given * connection. The given client information will be passed to guacd when diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java index 6f0212012..199f4fd56 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java @@ -29,7 +29,6 @@ import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionDirectory; import com.google.inject.Inject; import com.google.inject.Provider; import java.util.Collection; -import java.util.Collections; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; @@ -133,8 +132,7 @@ public class UserContext extends RestrictedObject @Override public ConnectionRecord getActiveConnection(String tunnelUUID) throws GuacamoleException { - // STUB - return null; + return tunnelService.getActiveConnection(getCurrentUser(), tunnelUUID); } } From dea4ac3cf12b287868a8c0532206882e8faae5cf Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 16:26:43 -0700 Subject: [PATCH 15/17] GUAC-1132: Implement JavaScript tunnel service. --- .../webapp/app/rest/services/tunnelService.js | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 guacamole/src/main/webapp/app/rest/services/tunnelService.js diff --git a/guacamole/src/main/webapp/app/rest/services/tunnelService.js b/guacamole/src/main/webapp/app/rest/services/tunnelService.js new file mode 100644 index 000000000..ca9c5a838 --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/services/tunnelService.js @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2014 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. + */ + +/** + * Service for operating on tunnels via the REST API. + */ +angular.module('rest').factory('tunnelService', ['$http', 'authenticationService', + function tunnelService($http, authenticationService) { + + var service = {}; + + /** + * Makes a request to the REST API to get the list of active tunnels, + * returning a promise that provides an array of @link{ActiveTunnel} + * objects if successful. + * + * @returns {Promise.} + * A promise which will resolve with an array of @link{ActiveTunnel} + * objects upon success. + */ + service.getActiveTunnels = function getActiveTunnels() { + + // Build HTTP parameters set + var httpParameters = { + token : authenticationService.getCurrentToken() + }; + + // Retrieve tunnels + return $http({ + method : 'GET', + url : 'api/tunnels', + params : httpParameters + }); + + }; + + /** + * Makes a request to the REST API to delete the tunnel having the given + * UUID, effectively disconnecting the tunnel, returning a promise that can + * be used for processing the results of the call. + * + * @param {String} uuid + * The UUID of the tunnel to delete. + * + * @returns {Promise} + * A promise for the HTTP call which will succeed if and only if the + * delete operation is successful. + */ + service.deleteActiveTunnel = function deleteActiveTunnel(uuid) { + + // Build HTTP parameters set + var httpParameters = { + token : authenticationService.getCurrentToken() + }; + + // Delete connection + return $http({ + method : 'DELETE', + url : 'api/tunnels/' + encodeURIComponent(uuid), + params : httpParameters + }); + + }; + + return service; + +}]); From c4eea21deeeba825ab94d0199fd72cfecc264188 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 17 Mar 2015 16:41:13 -0700 Subject: [PATCH 16/17] GUAC-1132: Fix typo - it's a tunnel service now. --- .../guacamole/auth/jdbc/connection/ConnectionService.java | 2 +- .../guacamole/auth/jdbc/connection/ModeledConnection.java | 2 +- .../auth/jdbc/connectiongroup/ConnectionGroupService.java | 2 +- .../auth/jdbc/connectiongroup/ModeledConnectionGroup.java | 2 +- .../org/glyptodon/guacamole/auth/jdbc/user/UserContext.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java index 0659e88cb..8386d1a6d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java @@ -88,7 +88,7 @@ public class ConnectionService extends GroupedDirectoryObjectService connectionProvider; /** - * Service for creating and tracking sockets. + * Service for creating and tracking tunnels. */ @Inject private GuacamoleTunnelService tunnelService; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java index 8ad0b20e4..0502f2272 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java @@ -51,7 +51,7 @@ public class ModeledConnection extends GroupedDirectoryObject private ConnectionService connectionService; /** - * Service for creating and tracking sockets. + * Service for creating and tracking tunnels. */ @Inject private GuacamoleTunnelService tunnelService; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java index 02f019020..4fa9ef9c8 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java @@ -71,7 +71,7 @@ public class ConnectionGroupService extends GroupedDirectoryObjectService connectionGroupProvider; /** - * Service for creating and tracking sockets. + * Service for creating and tracking tunnels. */ @Inject private GuacamoleTunnelService tunnelService; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java index 941ecd357..0b5d6c67c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java @@ -54,7 +54,7 @@ public class ModeledConnectionGroup extends GroupedDirectoryObject Date: Tue, 17 Mar 2015 16:48:19 -0700 Subject: [PATCH 17/17] GUAC-1132: Remove unnecessary public declarations. --- .../glyptodon/guacamole/net/GuacamoleTunnel.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/GuacamoleTunnel.java b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/GuacamoleTunnel.java index 3c27f73ae..c4e30b0f8 100644 --- a/guacamole-common/src/main/java/org/glyptodon/guacamole/net/GuacamoleTunnel.java +++ b/guacamole-common/src/main/java/org/glyptodon/guacamole/net/GuacamoleTunnel.java @@ -59,7 +59,7 @@ public interface GuacamoleTunnel { * @return true if threads are waiting for read access the Guacamole * instruction stream, false otherwise. */ - public boolean hasQueuedReaderThreads(); + boolean hasQueuedReaderThreads(); /** * Acquires exclusive write access to the Guacamole instruction stream @@ -68,14 +68,14 @@ public interface GuacamoleTunnel { * @return A GuacamoleWriter for writing to the Guacamole instruction * stream. */ - public GuacamoleWriter acquireWriter(); + GuacamoleWriter acquireWriter(); /** * Relinquishes exclusive write access to the Guacamole instruction * stream. This function should be called whenever a thread finishes using * a GuacamoleTunnel's GuacamoleWriter. */ - public void releaseWriter(); + void releaseWriter(); /** * Returns whether there are threads waiting for write access to the @@ -84,14 +84,14 @@ public interface GuacamoleTunnel { * @return true if threads are waiting for write access the Guacamole * instruction stream, false otherwise. */ - public boolean hasQueuedWriterThreads(); + boolean hasQueuedWriterThreads(); /** * Returns the unique identifier associated with this GuacamoleTunnel. * * @return The unique identifier associated with this GuacamoleTunnel. */ - public UUID getUUID(); + UUID getUUID(); /** * Returns the GuacamoleSocket used by this GuacamoleTunnel for reading @@ -99,7 +99,7 @@ public interface GuacamoleTunnel { * * @return The GuacamoleSocket used by this GuacamoleTunnel. */ - public GuacamoleSocket getSocket(); + GuacamoleSocket getSocket(); /** * Release all resources allocated to this GuacamoleTunnel. @@ -107,13 +107,13 @@ public interface GuacamoleTunnel { * @throws GuacamoleException if an error occurs while releasing * resources. */ - public void close() throws GuacamoleException; + void close() throws GuacamoleException; /** * Returns whether this GuacamoleTunnel is open, or has been closed. * * @return true if this GuacamoleTunnel is open, false if it is closed. */ - public boolean isOpen(); + boolean isOpen(); }