mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUAC-919: Remove SessionKeepAlive (not needed without HttpSession). Consider sessions to be active so long as they have associated tunnels.
This commit is contained in:
@@ -26,7 +26,10 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.glyptodon.guacamole.GuacamoleException;
|
||||
import org.glyptodon.guacamole.net.GuacamoleTunnel;
|
||||
import org.glyptodon.guacamole.net.auth.Credentials;
|
||||
import org.glyptodon.guacamole.net.auth.UserContext;
|
||||
import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties;
|
||||
@@ -60,6 +63,11 @@ public class GuacamoleSession {
|
||||
*/
|
||||
private final ClipboardState clipboardState = new ClipboardState();
|
||||
|
||||
/**
|
||||
* All currently-active tunnels, indexed by tunnel UUID.
|
||||
*/
|
||||
private final Map<String, GuacamoleTunnel> tunnels = new ConcurrentHashMap<String, GuacamoleTunnel>();
|
||||
|
||||
/**
|
||||
* Creates a new Guacamole session associated with the given user context.
|
||||
*
|
||||
@@ -157,5 +165,49 @@ public class GuacamoleSession {
|
||||
public Collection<Object> getListeners() {
|
||||
return Collections.unmodifiableCollection(listeners);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this session has any associated active tunnels.
|
||||
*
|
||||
* @return true if this session has any associated active tunnels,
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean hasTunnels() {
|
||||
return !tunnels.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of all active tunnels associated with this session, where
|
||||
* each key is the String representation of the tunnel's UUID. Changes to
|
||||
* this map immediately affect the set of tunnels associated with this
|
||||
* session. A tunnel need not be present here to be used by the user
|
||||
* associated with this session, but tunnels not in this set will not
|
||||
* be taken into account when determining whether a session is in use.
|
||||
*
|
||||
* @return A map of all active tunnels associated with this session.
|
||||
*/
|
||||
public Map<String, GuacamoleTunnel> getTunnels() {
|
||||
return tunnels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates the given tunnel with this session, such that it is taken
|
||||
* into account when determining session activity.
|
||||
*
|
||||
* @param tunnel The tunnel to associate with this session.
|
||||
*/
|
||||
public void addTunnel(GuacamoleTunnel tunnel) {
|
||||
tunnels.put(tunnel.getUUID().toString(), tunnel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disassociates the tunnel having the given UUID from this session.
|
||||
*
|
||||
* @param uuid The UUID of the tunnel to disassociate from this session.
|
||||
* @return true if the tunnel existed and was removed, false otherwise.
|
||||
*/
|
||||
public boolean removeTunnel(String uuid) {
|
||||
return tunnels.remove(uuid) != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.glyptodon.guacamole.net.basic;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import org.glyptodon.guacamole.GuacamoleException;
|
||||
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;
|
||||
|
||||
/**
|
||||
* REST service which updates the last access time of the Guacamole session,
|
||||
* preventing the session from becoming invalid.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
@Path("/keep-alive")
|
||||
public class SessionKeepAlive {
|
||||
|
||||
/**
|
||||
* A service for authenticating users from auth tokens.
|
||||
*/
|
||||
@Inject
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(SessionKeepAlive.class);
|
||||
|
||||
@GET
|
||||
@AuthProviderRESTExposure
|
||||
public void updateSession(@QueryParam("token") String authToken) throws GuacamoleException {
|
||||
|
||||
// Tickle the session
|
||||
UserContext context = authenticationService.getUserContext(authToken);
|
||||
|
||||
// Do nothing
|
||||
logger.debug("Keep-alive signal received from user \"{}\".", context.self().getUsername());
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -25,7 +25,6 @@ package org.glyptodon.guacamole.net.basic;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import org.glyptodon.guacamole.net.basic.rest.clipboard.ClipboardRESTService;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.glyptodon.guacamole.GuacamoleClientException;
|
||||
import org.glyptodon.guacamole.GuacamoleException;
|
||||
@@ -35,7 +34,6 @@ 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.ConnectionGroup;
|
||||
import org.glyptodon.guacamole.net.auth.Credentials;
|
||||
import org.glyptodon.guacamole.net.auth.Directory;
|
||||
import org.glyptodon.guacamole.net.auth.UserContext;
|
||||
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
|
||||
@@ -72,12 +70,10 @@ public class TunnelRequestService {
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
/**
|
||||
* Notifies all listeners in the given collection that a tunnel has been
|
||||
* Notifies all listeners in the given session that a tunnel has been
|
||||
* connected.
|
||||
*
|
||||
* @param listeners A collection of all listeners that should be notified.
|
||||
* @param context The UserContext associated with the current session.
|
||||
* @param credentials The credentials associated with the current session.
|
||||
* @param session The session associated with the listeners to be notified.
|
||||
* @param tunnel The tunnel being connected.
|
||||
* @return true if all listeners are allowing the tunnel to connect,
|
||||
* or if there are no listeners, and false if any listener is
|
||||
@@ -88,16 +84,17 @@ public class TunnelRequestService {
|
||||
* error, the connect is canceled, and no other
|
||||
* listeners will run.
|
||||
*/
|
||||
private boolean notifyConnect(Collection listeners, UserContext context,
|
||||
Credentials credentials, GuacamoleTunnel tunnel)
|
||||
private boolean notifyConnect(GuacamoleSession session, GuacamoleTunnel tunnel)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Build event for auth success
|
||||
TunnelConnectEvent event = new TunnelConnectEvent(context,
|
||||
credentials, tunnel);
|
||||
TunnelConnectEvent event = new TunnelConnectEvent(
|
||||
session.getUserContext(),
|
||||
session.getCredentials(),
|
||||
tunnel);
|
||||
|
||||
// Notify all listeners
|
||||
for (Object listener : listeners) {
|
||||
for (Object listener : session.getListeners()) {
|
||||
if (listener instanceof TunnelConnectListener) {
|
||||
|
||||
// Cancel immediately if hook returns false
|
||||
@@ -112,12 +109,10 @@ public class TunnelRequestService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all listeners in the given collection that a tunnel has been
|
||||
* Notifies all listeners in the given session that a tunnel has been
|
||||
* closed.
|
||||
*
|
||||
* @param listeners A collection of all listeners that should be notified.
|
||||
* @param context The UserContext associated with the current session.
|
||||
* @param credentials The credentials associated with the current session.
|
||||
* @param session The session associated with the listeners to be notified.
|
||||
* @param tunnel The tunnel being closed.
|
||||
* @return true if all listeners are allowing the tunnel to close,
|
||||
* or if there are no listeners, and false if any listener is
|
||||
@@ -128,16 +123,17 @@ public class TunnelRequestService {
|
||||
* error, the close is canceled, and no other
|
||||
* listeners will run.
|
||||
*/
|
||||
private boolean notifyClose(Collection listeners, UserContext context,
|
||||
Credentials credentials, GuacamoleTunnel tunnel)
|
||||
private boolean notifyClose(GuacamoleSession session, GuacamoleTunnel tunnel)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Build event for auth success
|
||||
TunnelCloseEvent event = new TunnelCloseEvent(context,
|
||||
credentials, tunnel);
|
||||
TunnelCloseEvent event = new TunnelCloseEvent(
|
||||
session.getUserContext(),
|
||||
session.getCredentials(),
|
||||
tunnel);
|
||||
|
||||
// Notify all listeners
|
||||
for (Object listener : listeners) {
|
||||
for (Object listener : session.getListeners()) {
|
||||
if (listener instanceof TunnelCloseListener) {
|
||||
|
||||
// Cancel immediately if hook returns false
|
||||
@@ -165,8 +161,8 @@ public class TunnelRequestService {
|
||||
|
||||
// Get auth token and session
|
||||
String authToken = request.getParameter("authToken");
|
||||
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
|
||||
|
||||
final GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
|
||||
|
||||
// Get ID of connection
|
||||
String id = request.getParameter("id");
|
||||
TunnelRequest.IdentifierType id_type = TunnelRequest.IdentifierType.getType(id);
|
||||
@@ -178,18 +174,6 @@ public class TunnelRequestService {
|
||||
// Remove prefix
|
||||
id = id.substring(id_type.PREFIX.length());
|
||||
|
||||
// Get session-specific elements
|
||||
final Credentials credentials = session.getCredentials();
|
||||
final UserContext context = session.getUserContext();
|
||||
final Collection listeners = session.getListeners();
|
||||
|
||||
// If no context or no credentials, not logged in
|
||||
if (context == null || credentials == null)
|
||||
throw new GuacamoleSecurityException("Cannot connect - user not logged in.");
|
||||
|
||||
// Get clipboard
|
||||
final ClipboardState clipboard = session.getClipboardState();
|
||||
|
||||
// Get client information
|
||||
GuacamoleClientInformation info = new GuacamoleClientInformation();
|
||||
|
||||
@@ -225,6 +209,8 @@ public class TunnelRequestService {
|
||||
// Connection identifiers
|
||||
case CONNECTION: {
|
||||
|
||||
UserContext context = session.getUserContext();
|
||||
|
||||
// Get connection directory
|
||||
Directory<String, Connection> directory =
|
||||
context.getRootConnectionGroup().getConnectionDirectory();
|
||||
@@ -245,6 +231,8 @@ public class TunnelRequestService {
|
||||
// Connection group identifiers
|
||||
case CONNECTION_GROUP: {
|
||||
|
||||
UserContext context = session.getUserContext();
|
||||
|
||||
// Get connection group directory
|
||||
Directory<String, ConnectionGroup> directory =
|
||||
context.getRootConnectionGroup().getConnectionGroupDirectory();
|
||||
@@ -271,6 +259,11 @@ public class TunnelRequestService {
|
||||
// Associate socket with tunnel
|
||||
GuacamoleTunnel tunnel = new GuacamoleTunnel(socket) {
|
||||
|
||||
/**
|
||||
* The current clipboard state.
|
||||
*/
|
||||
private final ClipboardState clipboard = session.getClipboardState();
|
||||
|
||||
@Override
|
||||
public GuacamoleReader acquireReader() {
|
||||
|
||||
@@ -293,9 +286,11 @@ public class TunnelRequestService {
|
||||
public void close() throws GuacamoleException {
|
||||
|
||||
// Only close if not canceled
|
||||
if (!notifyClose(listeners, context, credentials, this))
|
||||
if (!notifyClose(session, this))
|
||||
throw new GuacamoleException("Tunnel close canceled by listener.");
|
||||
|
||||
session.removeTunnel(getUUID().toString());
|
||||
|
||||
// Close if no exception due to listener
|
||||
super.close();
|
||||
|
||||
@@ -304,7 +299,7 @@ public class TunnelRequestService {
|
||||
};
|
||||
|
||||
// Notify listeners about connection
|
||||
if (!notifyConnect(listeners, context, credentials, tunnel)) {
|
||||
if (!notifyConnect(session, tunnel)) {
|
||||
logger.info("Successful connection canceled by hook.");
|
||||
return null;
|
||||
}
|
||||
|
@@ -26,7 +26,6 @@ import com.google.inject.Scopes;
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
|
||||
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
|
||||
import org.glyptodon.guacamole.net.basic.SessionKeepAlive;
|
||||
import org.glyptodon.guacamole.net.basic.rest.auth.LoginRESTService;
|
||||
import org.glyptodon.guacamole.net.basic.rest.clipboard.ClipboardRESTService;
|
||||
import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService;
|
||||
@@ -51,7 +50,6 @@ public class RESTServletModule extends ServletModule {
|
||||
bind(ConnectionGroupRESTService.class);
|
||||
bind(PermissionRESTService.class);
|
||||
bind(ProtocolRESTService.class);
|
||||
bind(SessionKeepAlive.class);
|
||||
bind(UserRESTService.class);
|
||||
bind(LoginRESTService.class);
|
||||
|
||||
|
@@ -104,8 +104,16 @@ public class BasicTokenSessionMap implements TokenSessionMap {
|
||||
* @param authToken The auth token for the session.
|
||||
* @return True if the session has timed out, false otherwise.
|
||||
*/
|
||||
private boolean sessionHasTimedOut(String authToken) {
|
||||
private boolean isSessionActive(String authToken) {
|
||||
|
||||
GuacamoleSession session = sessionMap.get(authToken);
|
||||
if (session == null)
|
||||
return false;
|
||||
|
||||
// A session is active if it has any active tunnels
|
||||
if (session.hasTunnels())
|
||||
return true;
|
||||
|
||||
if (!lastAccessTimeMap.containsKey(authToken))
|
||||
return true;
|
||||
|
||||
@@ -120,7 +128,7 @@ public class BasicTokenSessionMap implements TokenSessionMap {
|
||||
public GuacamoleSession get(String authToken) {
|
||||
|
||||
// If the session has timed out, evict the token and force the user to log in again
|
||||
if (sessionHasTimedOut(authToken)) {
|
||||
if (isSessionActive(authToken)) {
|
||||
evict(authToken);
|
||||
return null;
|
||||
}
|
||||
|
@@ -1199,11 +1199,6 @@ GuacUI.Client.connect = function() {
|
||||
connect_string += "&video=" + encodeURIComponent(mimetype);
|
||||
});
|
||||
|
||||
// Ping server occasionally to keep HTTP session alive
|
||||
var session_keep_alive = window.setInterval(function _session_keep_alive() {
|
||||
GuacamoleService.KeepAlive.ping();
|
||||
}, GuacUI.Client.KEEP_ALIVE_INTERVAL);
|
||||
|
||||
// Show connection errors from tunnel
|
||||
tunnel.onerror = function(status) {
|
||||
var message = GuacUI.Client.tunnel_errors[status.code] || GuacUI.Client.tunnel_errors.DEFAULT;
|
||||
@@ -1216,13 +1211,11 @@ GuacUI.Client.connect = function() {
|
||||
// Handle disconnect
|
||||
if (state === Guacamole.Tunnel.State.CLOSED) {
|
||||
|
||||
// No need for a keep-alive ping if the tunnel is closed
|
||||
window.clearInterval(session_keep_alive);
|
||||
|
||||
// Notify of disconnections (if not already notified of something else)
|
||||
if (!GuacUI.Client.visibleStatus)
|
||||
GuacUI.Client.showStatus("Disconnected",
|
||||
"You have been disconnected. Reload the page to reconnect.");
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
@@ -1056,29 +1056,6 @@ GuacamoleService.Clipboard = {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Collection of service functions which deal with the session keep-alive. Each
|
||||
* function makes an explicit HTTP query to the server. In the case of the
|
||||
* keep-alive ping, no response is expected, and any received response is
|
||||
* ignored.
|
||||
*/
|
||||
GuacamoleService.KeepAlive = {
|
||||
|
||||
"ping" : function() {
|
||||
|
||||
// Construct request URL
|
||||
var ping_url = "api/keep-alive"
|
||||
+ "?token=" + GuacamoleService.Auth.current().authToken;
|
||||
|
||||
// Send keep-alive "ping"
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", ping_url, true);
|
||||
xhr.send(null);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Collection of service functions which deal with authentication. Note that,
|
||||
* unlike everything else here, not all functions in GuacamoleService.Auth
|
||||
|
Reference in New Issue
Block a user