Use utility class, not tunnel servlet itself. Init underling auth servlet within websocket auth.

This commit is contained in:
Michael Jumper
2013-10-15 23:34:40 -07:00
parent 1079809e2e
commit d7986bba59
5 changed files with 15 additions and 245 deletions

View File

@@ -19,28 +19,12 @@ package org.glyptodon.guacamole.net.basic;
*/
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.glyptodon.guacamole.GuacamoleClientException;
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.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.event.SessionListenerCollection;
import org.glyptodon.guacamole.net.event.TunnelCloseEvent;
import org.glyptodon.guacamole.net.event.TunnelConnectEvent;
import org.glyptodon.guacamole.net.event.listener.TunnelCloseListener;
import org.glyptodon.guacamole.net.event.listener.TunnelConnectListener;
import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
import org.glyptodon.guacamole.servlet.GuacamoleHTTPTunnelServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -139,230 +123,6 @@ public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
}
/**
* Notifies all listeners in the given collection 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 tunnel The tunnel being connected.
* @return true if all listeners are allowing the tunnel to connect,
* or if there are no listeners, and false if any listener is
* canceling the connection. Note that once one listener cancels,
* no other listeners will run.
* @throws GuacamoleException If any listener throws an error while being
* notified. Note that if any listener throws an
* error, the connect is canceled, and no other
* listeners will run.
*/
public static boolean notifyConnect(Collection listeners, UserContext context,
Credentials credentials, GuacamoleTunnel tunnel)
throws GuacamoleException {
// Build event for auth success
TunnelConnectEvent event = new TunnelConnectEvent(context,
credentials, tunnel);
// Notify all listeners
for (Object listener : listeners) {
if (listener instanceof TunnelConnectListener) {
// Cancel immediately if hook returns false
if (!((TunnelConnectListener) listener).tunnelConnected(event))
return false;
}
}
return true;
}
/**
* Notifies all listeners in the given collection 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 tunnel The tunnel being closed.
* @return true if all listeners are allowing the tunnel to close,
* or if there are no listeners, and false if any listener is
* canceling the close. Note that once one listener cancels,
* no other listeners will run.
* @throws GuacamoleException If any listener throws an error while being
* notified. Note that if any listener throws an
* error, the close is canceled, and no other
* listeners will run.
*/
public static boolean notifyClose(Collection listeners, UserContext context,
Credentials credentials, GuacamoleTunnel tunnel)
throws GuacamoleException {
// Build event for auth success
TunnelCloseEvent event = new TunnelCloseEvent(context,
credentials, tunnel);
// Notify all listeners
for (Object listener : listeners) {
if (listener instanceof TunnelCloseListener) {
// Cancel immediately if hook returns false
if (!((TunnelCloseListener) listener).tunnelClosed(event))
return false;
}
}
return true;
}
/**
* Creates a new tunnel using the parameters and credentials present in
* the given request.
*
* @param request The HttpServletRequest describing the tunnel to create.
* @return The created tunnel, or null if the tunnel could not be created.
* @throws GuacamoleException If an error occurs while creating the tunnel.
*/
public static GuacamoleTunnel createTunnel(HttpServletRequest request)
throws GuacamoleException {
HttpSession httpSession = request.getSession(true);
// Get listeners
final SessionListenerCollection listeners;
try {
listeners = new SessionListenerCollection(httpSession);
}
catch (GuacamoleException e) {
logger.error("Failed to retrieve listeners. Authentication canceled.", e);
throw e;
}
// Get ID of connection
String id = request.getParameter("id");
IdentifierType id_type = IdentifierType.getType(id);
// Do not continue if unable to determine type
if (id_type == null)
throw new GuacamoleClientException("Illegal identifier - unknown type.");
// Remove prefix
id = id.substring(id_type.PREFIX.length());
// Get credentials
final Credentials credentials = getCredentials(httpSession);
// Get context
final UserContext context = getUserContext(httpSession);
// If no context or no credentials, not logged in
if (context == null || credentials == null)
throw new GuacamoleSecurityException("Cannot connect - user not logged in.");
// Get client information
GuacamoleClientInformation info = new GuacamoleClientInformation();
// Set width if provided
String width = request.getParameter("width");
if (width != null)
info.setOptimalScreenWidth(Integer.parseInt(width));
// Set height if provided
String height = request.getParameter("height");
if (height != null)
info.setOptimalScreenHeight(Integer.parseInt(height));
// Add audio mimetypes
String[] audio_mimetypes = request.getParameterValues("audio");
if (audio_mimetypes != null)
info.getAudioMimetypes().addAll(Arrays.asList(audio_mimetypes));
// Add video mimetypes
String[] video_mimetypes = request.getParameterValues("video");
if (video_mimetypes != null)
info.getVideoMimetypes().addAll(Arrays.asList(video_mimetypes));
// Create connected socket from identifier
GuacamoleSocket socket;
switch (id_type) {
// Connection identifiers
case CONNECTION: {
// Get connection directory
Directory<String, Connection> directory =
context.getRootConnectionGroup().getConnectionDirectory();
// Get authorized connection
Connection connection = directory.get(id);
if (connection == null) {
logger.warn("Connection id={} not found.", id);
throw new GuacamoleSecurityException("Requested connection is not authorized.");
}
// Connect socket
socket = connection.connect(info);
logger.info("Successful connection from {} to \"{}\".", request.getRemoteAddr(), id);
break;
}
// Connection group identifiers
case CONNECTION_GROUP: {
// Get connection group directory
Directory<String, ConnectionGroup> directory =
context.getRootConnectionGroup().getConnectionGroupDirectory();
// Get authorized connection group
ConnectionGroup group = directory.get(id);
if (group == null) {
logger.warn("Connection group id={} not found.", id);
throw new GuacamoleSecurityException("Requested connection group is not authorized.");
}
// Connect socket
socket = group.connect(info);
logger.info("Successful connection from {} to group \"{}\".", request.getRemoteAddr(), id);
break;
}
// Fail if unsupported type
default:
throw new GuacamoleClientException("Connection not supported for provided identifier type.");
}
// Associate socket with tunnel
GuacamoleTunnel tunnel = new GuacamoleTunnel(socket) {
@Override
public void close() throws GuacamoleException {
// Only close if not canceled
if (!notifyClose(listeners, context, credentials, this))
throw new GuacamoleException("Tunnel close canceled by listener.");
// Close if no exception due to listener
super.close();
}
};
// Notify listeners about connection
if (!notifyConnect(listeners, context, credentials, tunnel)) {
logger.info("Connection canceled by listener.");
return null;
}
return tunnel;
}
/**
* Wrapped GuacamoleHTTPTunnelServlet which will handle all authenticated
* requests.
@@ -371,7 +131,7 @@ public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
@Override
protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException {
return createTunnel(request);
return BasicTunnelRequestUtility.createTunnel(request);
}
};

View File

@@ -73,6 +73,11 @@ public abstract class AuthenticatingWebSocketServlet extends WebSocketServlet {
};
@Override
public void init() throws ServletException {
auth_servlet.init();
}
@Override
protected void service(HttpServletRequest request,
HttpServletResponse response)

View File

@@ -23,7 +23,7 @@ import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.GuacamoleTunnel;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.eclipse.jetty.websocket.WebSocket;
import org.glyptodon.guacamole.net.basic.BasicGuacamoleTunnelServlet;
import org.glyptodon.guacamole.net.basic.BasicTunnelRequestUtility;
/**
* Authenticating tunnel servlet implementation which uses WebSocket as a
@@ -41,7 +41,7 @@ public class BasicGuacamoleWebSocketTunnelServlet extends AuthenticatingWebSocke
@Override
protected GuacamoleTunnel doConnect(HttpServletRequest request)
throws GuacamoleException {
return BasicGuacamoleTunnelServlet.createTunnel(request);
return BasicTunnelRequestUtility.createTunnel(request);
}
};

View File

@@ -73,6 +73,11 @@ public abstract class AuthenticatingWebSocketServlet extends WebSocketServlet {
};
@Override
public void init() throws ServletException {
auth_servlet.init();
}
@Override
protected void service(HttpServletRequest request,
HttpServletResponse response)

View File

@@ -23,7 +23,7 @@ import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.GuacamoleTunnel;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.apache.catalina.websocket.StreamInbound;
import org.glyptodon.guacamole.net.basic.BasicGuacamoleTunnelServlet;
import org.glyptodon.guacamole.net.basic.BasicTunnelRequestUtility;
/**
* Authenticating tunnel servlet implementation which uses WebSocket as a
@@ -41,7 +41,7 @@ public class BasicGuacamoleWebSocketTunnelServlet extends AuthenticatingWebSocke
@Override
protected GuacamoleTunnel doConnect(HttpServletRequest request)
throws GuacamoleException {
return BasicGuacamoleTunnelServlet.createTunnel(request);
return BasicTunnelRequestUtility.createTunnel(request);
}
};