diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTExceptionWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTExceptionWrapper.java
index 9cb0bc807..7f52d95b9 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTExceptionWrapper.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTExceptionWrapper.java
@@ -22,12 +22,17 @@
package org.glyptodon.guacamole.net.basic.rest;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.QueryParam;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.glyptodon.guacamole.GuacamoleClientException;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleResourceNotFoundException;
import org.glyptodon.guacamole.GuacamoleSecurityException;
+import org.glyptodon.guacamole.GuacamoleUnauthorizedException;
import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
import org.slf4j.Logger;
@@ -45,14 +50,112 @@ import org.slf4j.LoggerFactory;
*/
public class RESTExceptionWrapper implements MethodInterceptor {
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger = LoggerFactory.getLogger(RESTExceptionWrapper.class);
+
+ /**
+ * Determines whether the given parameter is associated with the HTTP
+ * request parameter of the given name. For a parameter to be associated
+ * with an HTTP request parameter, it must be annotated with either the
+ * @QueryParam
or @FormParam
annotations.
+ *
+ * @param parameter
+ * The Java parameter to check.
+ *
+ * @param name
+ * The name of the HTTP request parameter.
+ *
+ * @return
+ * true if the given parameter is associated with the HTTP request
+ * parameter having the given name, false otherwise.
+ */
+ private boolean isRequestParameter(Parameter parameter, String name) {
+
+ // Check if parameter is associated with the HTTP query string
+ QueryParam queryParam = parameter.getAnnotation(QueryParam.class);
+ if (queryParam != null && name.equals(queryParam.value()))
+ return true;
+
+ // Failing that, check whether the parameter is associated with the
+ // HTTP request body
+ FormParam formParam = parameter.getAnnotation(FormParam.class);
+ return formParam != null && name.equals(formParam.value());
+
+ }
+
+ /**
+ * Returns the authentication token that was passed in the given method
+ * invocation. If the given method invocation is not associated with an
+ * HTTP request (it lacks the appropriate JAX-RS annotations) or there is
+ * no authentication token, null is returned.
+ *
+ * @param invocation
+ * The method invocation whose corresponding authentication token
+ * should be determined.
+ *
+ * @return
+ * The authentication token passed in the given method invocation, or
+ * null if there is no such token.
+ */
+ private String getAuthenticationToken(MethodInvocation invocation) {
+
+ Method method = invocation.getMethod();
+
+ // Iterate through all parameters, looking for the authentication token
+ Parameter[] parameters = method.getParameters();
+ for (int i = 0; i < parameters.length; i++) {
+
+ // Get current parameter
+ Parameter parameter = parameters[i];
+
+ // Only inspect String parameters
+ if (parameter.getType() != String.class)
+ continue;
+
+ // Parameter must be declared as a REST service parameter
+ if (!isRequestParameter(parameter, "token"))
+ continue;
+
+ // The token parameter has been found - return its value
+ Object[] args = invocation.getArguments();
+ return (String) args[i];
+
+ }
+
+ // No token parameter is defined
+ return null;
+
+ }
+
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
- // Get the logger for the intercepted class
- Logger logger = LoggerFactory.getLogger(invocation.getMethod().getDeclaringClass());
-
try {
- return invocation.proceed();
+
+ // Invoke wrapped method
+ try {
+ return invocation.proceed();
+ }
+
+ // Ensure any associated session is invalidated if unauthorized
+ catch (GuacamoleUnauthorizedException e) {
+
+ // Pull authentication token from request
+ String token = getAuthenticationToken(invocation);
+
+ // If there is an associated auth token, invalidate it
+ if (token != null) {
+ logger.debug("Implicitly invalidating token \"{}\" due to GuacamoleUnauthorizedException.", token);
+ // STUB - Does not actually invalidate anything at the moment
+ }
+
+ // Continue with exception processing
+ throw e;
+
+ }
+
}
// Additional credentials are needed
@@ -138,7 +241,9 @@ public class RESTExceptionWrapper implements MethodInterceptor {
if (message == null)
message = "Unexpected server error.";
+ // Ensure internal errors are logged at the debug level
logger.debug("Unexpected exception in REST endpoint.", e);
+
throw new APIException(
APIError.Type.INTERNAL_ERROR,
message