Update user context appropriately. Use UserContext for events.

This commit is contained in:
Michael Jumper
2013-08-07 14:51:14 -07:00
parent e8ec136c29
commit c325f443a5
6 changed files with 136 additions and 117 deletions

View File

@@ -1,15 +1,25 @@
package net.sourceforge.guacamole.net.event; package net.sourceforge.guacamole.net.event;
import net.sourceforge.guacamole.net.auth.Credentials; import net.sourceforge.guacamole.net.auth.Credentials;
import net.sourceforge.guacamole.net.auth.UserContext;
/** /**
* An event which is triggered whenever a user's credentials pass * An event which is triggered whenever a user's credentials pass
* authentication. The credentials that passed authentication are included * authentication. The credentials that passed authentication are included
* within this event, and can be retrieved using getCredentials(). * within this event, and can be retrieved using getCredentials().
* *
* Note that this event is only triggered when the UserContext is initially
* created. Any further updates to the UserContext to not trigger this event.
*
* @author Michael Jumper * @author Michael Jumper
*/ */
public class AuthenticationSuccessEvent implements CredentialEvent { public class AuthenticationSuccessEvent implements UserEvent, CredentialEvent {
/**
* The UserContext associated with the request that is connecting the
* tunnel, if any.
*/
private UserContext context;
/** /**
* The credentials which passed authentication. * The credentials which passed authentication.
@@ -20,12 +30,20 @@ public class AuthenticationSuccessEvent implements CredentialEvent {
* Creates a new AuthenticationSuccessEvent which represents a successful * Creates a new AuthenticationSuccessEvent which represents a successful
* authentication attempt with the given credentials. * authentication attempt with the given credentials.
* *
* @param context The UserContext created as a result of successful
* authentication.
* @param credentials The credentials which passed authentication. * @param credentials The credentials which passed authentication.
*/ */
public AuthenticationSuccessEvent(Credentials credentials) { public AuthenticationSuccessEvent(UserContext context, Credentials credentials) {
this.context = context;
this.credentials = credentials; this.credentials = credentials;
} }
@Override
public UserContext getUserContext() {
return context;
}
@Override @Override
public Credentials getCredentials() { public Credentials getCredentials() {
return credentials; return credentials;

View File

@@ -1,7 +1,7 @@
package net.sourceforge.guacamole.net.event; package net.sourceforge.guacamole.net.event;
import net.sourceforge.guacamole.net.GuacamoleTunnel; import net.sourceforge.guacamole.net.GuacamoleTunnel;
import net.sourceforge.guacamole.net.auth.Credentials; import net.sourceforge.guacamole.net.auth.UserContext;
/** /**
* An event which is triggered whenever a tunnel is being closed. The tunnel * An event which is triggered whenever a tunnel is being closed. The tunnel
@@ -11,13 +11,13 @@ import net.sourceforge.guacamole.net.auth.Credentials;
* *
* @author Michael Jumper * @author Michael Jumper
*/ */
public class TunnelCloseEvent implements CredentialEvent, TunnelEvent { public class TunnelCloseEvent implements UserEvent, TunnelEvent {
/** /**
* The credentials associated with the request that is closing the * The UserContext associated with the request that is connecting the
* tunnel, if any. * tunnel, if any.
*/ */
private Credentials credentials; private UserContext context;
/** /**
* The tunnel being closed. * The tunnel being closed.
@@ -28,18 +28,18 @@ public class TunnelCloseEvent implements CredentialEvent, TunnelEvent {
* Creates a new TunnelCloseEvent which represents the closing of the * Creates a new TunnelCloseEvent which represents the closing of the
* given tunnel via a request associated with the given credentials. * given tunnel via a request associated with the given credentials.
* *
* @param credentials The credentials associated with the request * @param context The UserContext associated with the request connecting
* closing the tunnel. * the tunnel.
* @param tunnel The tunnel being closed. * @param tunnel The tunnel being closed.
*/ */
public TunnelCloseEvent(Credentials credentials, GuacamoleTunnel tunnel) { public TunnelCloseEvent(UserContext context, GuacamoleTunnel tunnel) {
this.credentials = credentials; this.context = context;
this.tunnel = tunnel; this.tunnel = tunnel;
} }
@Override @Override
public Credentials getCredentials() { public UserContext getUserContext() {
return credentials; return context;
} }
@Override @Override

View File

@@ -1,7 +1,7 @@
package net.sourceforge.guacamole.net.event; package net.sourceforge.guacamole.net.event;
import net.sourceforge.guacamole.net.GuacamoleTunnel; import net.sourceforge.guacamole.net.GuacamoleTunnel;
import net.sourceforge.guacamole.net.auth.Credentials; import net.sourceforge.guacamole.net.auth.UserContext;
/** /**
* An event which is triggered whenever a tunnel is being connected. The tunnel * An event which is triggered whenever a tunnel is being connected. The tunnel
@@ -11,13 +11,13 @@ import net.sourceforge.guacamole.net.auth.Credentials;
* *
* @author Michael Jumper * @author Michael Jumper
*/ */
public class TunnelConnectEvent implements CredentialEvent, TunnelEvent { public class TunnelConnectEvent implements UserEvent, TunnelEvent {
/** /**
* The credentials associated with the request that is connecting the * The UserContext associated with the request that is connecting the
* tunnel, if any. * tunnel, if any.
*/ */
private Credentials credentials; private UserContext context;
/** /**
* The tunnel being connected. * The tunnel being connected.
@@ -28,18 +28,18 @@ public class TunnelConnectEvent implements CredentialEvent, TunnelEvent {
* Creates a new TunnelConnectEvent which represents the connecting of the * Creates a new TunnelConnectEvent which represents the connecting of the
* given tunnel via a request associated with the given credentials. * given tunnel via a request associated with the given credentials.
* *
* @param credentials The credentials associated with the request * @param context The UserContext associated with the request connecting
* connecting the tunnel. * the tunnel.
* @param tunnel The tunnel being connected. * @param tunnel The tunnel being connected.
*/ */
public TunnelConnectEvent(Credentials credentials, GuacamoleTunnel tunnel) { public TunnelConnectEvent(UserContext context, GuacamoleTunnel tunnel) {
this.credentials = credentials; this.context = context;
this.tunnel = tunnel; this.tunnel = tunnel;
} }
@Override @Override
public Credentials getCredentials() { public UserContext getUserContext() {
return credentials; return context;
} }
@Override @Override

View File

@@ -0,0 +1,21 @@
package net.sourceforge.guacamole.net.event;
import net.sourceforge.guacamole.net.auth.UserContext;
/**
* Abstract basis for events which may have an associated UserContext when
* triggered.
*
* @author Michael Jumper
*/
public interface UserEvent {
/**
* Returns the current UserContext of the user triggering the event, if any.
*
* @return The current UserContext of the user triggering the event, if
* any, or null if no UserContext is associated with the event.
*/
UserContext getUserContext();
}

View File

@@ -72,11 +72,6 @@ public abstract class AuthenticatingHttpServlet extends HttpServlet {
*/ */
private static final String CONTEXT_ATTRIBUTE = "GUAC_CONTEXT"; private static final String CONTEXT_ATTRIBUTE = "GUAC_CONTEXT";
/**
* The session attribute holding the credentials authorizing this session.
*/
private static final String CREDENTIALS_ATTRIBUTE = "GUAC_CREDS";
/** /**
* The AuthenticationProvider to use to authenticate all requests. * The AuthenticationProvider to use to authenticate all requests.
*/ */
@@ -127,6 +122,8 @@ public abstract class AuthenticatingHttpServlet extends HttpServlet {
* successful. * successful.
* *
* @param listeners A collection of all listeners that should be notified. * @param listeners A collection of all listeners that should be notified.
* @param context The UserContext created as a result of authentication
* success.
* @param credentials The credentials associated with the authentication * @param credentials The credentials associated with the authentication
* request that succeeded. * request that succeeded.
* @return true if all listeners are allowing the authentication success, * @return true if all listeners are allowing the authentication success,
@@ -138,11 +135,12 @@ public abstract class AuthenticatingHttpServlet extends HttpServlet {
* error, the success is canceled, and no other * error, the success is canceled, and no other
* listeners will run. * listeners will run.
*/ */
private boolean notifySuccess(Collection listeners, Credentials credentials) private boolean notifySuccess(Collection listeners, UserContext context,
throws GuacamoleException { Credentials credentials) throws GuacamoleException {
// Build event for auth success // Build event for auth success
AuthenticationSuccessEvent event = new AuthenticationSuccessEvent(credentials); AuthenticationSuccessEvent event =
new AuthenticationSuccessEvent(context, credentials);
// Notify all listeners // Notify all listeners
for (Object listener : listeners) { for (Object listener : listeners) {
@@ -203,17 +201,6 @@ public abstract class AuthenticatingHttpServlet extends HttpServlet {
} }
/**
* Returns the credentials associated with the given session.
*
* @param session The session to retrieve credentials from.
* @return The credentials associated with the given session.
*/
protected Credentials getCredentials(HttpSession session) {
return (Credentials) session.getAttribute(CREDENTIALS_ATTRIBUTE);
}
/** /**
* Returns the UserContext associated with the given session. * Returns the UserContext associated with the given session.
* *
@@ -239,89 +226,90 @@ public abstract class AuthenticatingHttpServlet extends HttpServlet {
HttpSession httpSession = request.getSession(true); HttpSession httpSession = request.getSession(true);
SessionListenerCollection listeners;
try {
listeners = new SessionListenerCollection(httpSession);
}
catch (GuacamoleException e) {
logger.error("Failed to retrieve listeners. Authentication canceled.", e);
failAuthentication(response);
return;
}
// Build credentials object
Credentials credentials = new Credentials();
credentials.setSession(httpSession);
credentials.setRequest(request);
// Try to get user context from session // Try to get user context from session
UserContext context = getUserContext(httpSession); UserContext context = getUserContext(httpSession);
// If no context, try to authenticate the user to get the context using // If no cached context, attempt to get new context
// this request.
if (context == null) { if (context == null) {
SessionListenerCollection listeners;
try {
listeners = new SessionListenerCollection(httpSession);
}
catch (GuacamoleException e) {
logger.error("Failed to retrieve listeners. Authentication canceled.", e);
failAuthentication(response);
return;
}
// Build credentials object
Credentials credentials = new Credentials();
credentials.setSession(httpSession);
credentials.setRequest(request);
// Get authorized context
try { try {
context = authProvider.getUserContext(credentials); context = authProvider.getUserContext(credentials);
} }
// Log any authentication errors
/******** HANDLE FAILED AUTHENTICATION ********/
// If error retrieving context, fail authentication, notify listeners
catch (GuacamoleException e) { catch (GuacamoleException e) {
logger.error("Error retrieving context for user \"{}\".", logger.error("Error retrieving context for user \"{}\".",
credentials.getUsername(), e); credentials.getUsername(), e);
notifyFailed(listeners, credentials);
failAuthentication(response);
return;
} }
// If no context, fail authentication, notify listeners // If successful, log success and notify listeners
if (context == null) { if (context != null) {
logger.warn("Authentication attempt from {} for user \"{}\" failed.",
request.getRemoteAddr(), credentials.getUsername());
notifyFailed(listeners, credentials); // Log successful authentication
failAuthentication(response);
return;
}
/******** HANDLE SUCCESSFUL AUTHENTICATION ********/
try {
// Otherwise, authentication has been succesful
logger.info("User \"{}\" successfully authenticated from {}.", logger.info("User \"{}\" successfully authenticated from {}.",
credentials.getUsername(), request.getRemoteAddr()); context.self().getUsername(), request.getRemoteAddr());
// Notify of success, cancel if requested // Notify any listeners of success, cancel if requested
if (!notifySuccess(listeners, credentials)) { try {
logger.info("Successful authentication canceled by hook."); if (!notifySuccess(listeners, context, credentials)) {
failAuthentication(response); logger.info("Successful authentication canceled by hook.");
return; context = null;
}
} }
}
catch (GuacamoleException e) {
// Cancel authentication success if hook throws exception // Cancel authentication success if hook throws exception
logger.error("Successful authentication canceled by error in hook.", e); catch (GuacamoleException e) {
failAuthentication(response); logger.error("Successful authentication canceled by error in hook.", e);
return; context = null;
}
} // end if auth success
} // end if no cached context
// Otherwise, update existing context
else {
try {
context = authProvider.updateUserContext(context, credentials);
} }
// Associate context and credentials with session // If error updating context, fail authentication, notify listeners
httpSession.setAttribute(CONTEXT_ATTRIBUTE, context); catch (GuacamoleException e) {
httpSession.setAttribute(CREDENTIALS_ATTRIBUTE, credentials); logger.error("Error updating context for user \"{}\".",
context.self().getUsername(), e);
}
} // end if cached context
// If no context, fail authentication, notify listeners
if (context == null) {
logger.warn("Authentication attempt from {} for user \"{}\" failed.",
request.getRemoteAddr(), credentials.getUsername());
notifyFailed(listeners, credentials);
failAuthentication(response);
return;
} }
// Associate context and credentials with session
httpSession.setAttribute(CONTEXT_ATTRIBUTE, context);
try { try {
// Allow servlet to run now that authentication has been validated // Allow servlet to run now that authentication has been validated

View File

@@ -30,7 +30,6 @@ import net.sourceforge.guacamole.GuacamoleSecurityException;
import net.sourceforge.guacamole.net.GuacamoleSocket; import net.sourceforge.guacamole.net.GuacamoleSocket;
import net.sourceforge.guacamole.net.GuacamoleTunnel; import net.sourceforge.guacamole.net.GuacamoleTunnel;
import net.sourceforge.guacamole.net.auth.Connection; import net.sourceforge.guacamole.net.auth.Connection;
import net.sourceforge.guacamole.net.auth.Credentials;
import net.sourceforge.guacamole.net.auth.Directory; import net.sourceforge.guacamole.net.auth.Directory;
import net.sourceforge.guacamole.net.auth.UserContext; import net.sourceforge.guacamole.net.auth.UserContext;
import net.sourceforge.guacamole.net.basic.event.SessionListenerCollection; import net.sourceforge.guacamole.net.basic.event.SessionListenerCollection;
@@ -85,8 +84,7 @@ public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
* connected. * connected.
* *
* @param listeners A collection of all listeners that should be notified. * @param listeners A collection of all listeners that should be notified.
* @param credentials The credentials associated with the authentication * @param context The UserContext associated with the current session.
* request that connected the tunnel.
* @param tunnel The tunnel being connected. * @param tunnel The tunnel being connected.
* @return true if all listeners are allowing the tunnel to connect, * @return true if all listeners are allowing the tunnel to connect,
* or if there are no listeners, and false if any listener is * or if there are no listeners, and false if any listener is
@@ -98,11 +96,11 @@ public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
* listeners will run. * listeners will run.
*/ */
private boolean notifyConnect(Collection listeners, private boolean notifyConnect(Collection listeners,
Credentials credentials, GuacamoleTunnel tunnel) UserContext context, GuacamoleTunnel tunnel)
throws GuacamoleException { throws GuacamoleException {
// Build event for auth success // Build event for auth success
TunnelConnectEvent event = new TunnelConnectEvent(credentials, tunnel); TunnelConnectEvent event = new TunnelConnectEvent(context, tunnel);
// Notify all listeners // Notify all listeners
for (Object listener : listeners) { for (Object listener : listeners) {
@@ -124,8 +122,7 @@ public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
* closed. * closed.
* *
* @param listeners A collection of all listeners that should be notified. * @param listeners A collection of all listeners that should be notified.
* @param credentials The credentials associated with the authentication * @param context The UserContext associated with the current session.
* request that closed the tunnel.
* @param tunnel The tunnel being closed. * @param tunnel The tunnel being closed.
* @return true if all listeners are allowing the tunnel to close, * @return true if all listeners are allowing the tunnel to close,
* or if there are no listeners, and false if any listener is * or if there are no listeners, and false if any listener is
@@ -137,11 +134,11 @@ public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
* listeners will run. * listeners will run.
*/ */
private boolean notifyClose(Collection listeners, private boolean notifyClose(Collection listeners,
Credentials credentials, GuacamoleTunnel tunnel) UserContext context, GuacamoleTunnel tunnel)
throws GuacamoleException { throws GuacamoleException {
// Build event for auth success // Build event for auth success
TunnelCloseEvent event = new TunnelCloseEvent(credentials, tunnel); TunnelCloseEvent event = new TunnelCloseEvent(context, tunnel);
// Notify all listeners // Notify all listeners
for (Object listener : listeners) { for (Object listener : listeners) {
@@ -182,19 +179,14 @@ public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
// Get ID of connection // Get ID of connection
String id = request.getParameter("id"); String id = request.getParameter("id");
// Get credentials
final Credentials credentials = getCredentials(httpSession);
// Get context // Get context
UserContext context = getUserContext(httpSession); final UserContext context = getUserContext(httpSession);
if (context == null)
throw new GuacamoleSecurityException("Cannot connect - user not logged in.");
// Get connection directory // Get connection directory
Directory<String, Connection> directory = context.getConnectionDirectory(); Directory<String, Connection> directory = context.getConnectionDirectory();
// If no credentials in session, not authorized
if (credentials == null)
throw new GuacamoleSecurityException("Cannot connect - user not logged in.");
// Get authorized connection // Get authorized connection
Connection connection = directory.get(id); Connection connection = directory.get(id);
if (connection == null) { if (connection == null) {
@@ -237,7 +229,7 @@ public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
public void close() throws GuacamoleException { public void close() throws GuacamoleException {
// Only close if not canceled // Only close if not canceled
if (!notifyClose(listeners, credentials, this)) if (!notifyClose(listeners, context, this))
throw new GuacamoleException("Tunnel close canceled by listener."); throw new GuacamoleException("Tunnel close canceled by listener.");
// Close if no exception due to listener // Close if no exception due to listener
@@ -248,7 +240,7 @@ public class BasicGuacamoleTunnelServlet extends AuthenticatingHttpServlet {
}; };
// Notify listeners about connection // Notify listeners about connection
if (!notifyConnect(listeners, credentials, tunnel)) { if (!notifyConnect(listeners, context, tunnel)) {
logger.info("Connection canceled by listener."); logger.info("Connection canceled by listener.");
return null; return null;
} }