mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	GUACAMOLE-566: Replace the entire RESTExceptionWrapper with the ExceptionMapper implementation.
This commit is contained in:
		| @@ -39,7 +39,6 @@ import org.apache.guacamole.resource.Resource; | ||||
| import org.apache.guacamole.resource.ResourceServlet; | ||||
| import org.apache.guacamole.resource.SequenceResource; | ||||
| import org.apache.guacamole.resource.WebApplicationResource; | ||||
| import org.apache.guacamole.rest.GuacamoleExceptionMapper; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| @@ -418,10 +417,7 @@ public class ExtensionModule extends ServletModule { | ||||
|         // Bind resource services | ||||
|         bind(LanguageResourceService.class).toInstance(languageResourceService); | ||||
|         bind(PatchResourceService.class).toInstance(patchResourceService); | ||||
|  | ||||
|         // Get ExceptionMapper to rewrite exceptions in JSON. | ||||
|         bind(GuacamoleExceptionMapper.class); | ||||
|          | ||||
|   | ||||
|         // Load initial language resources from servlet context | ||||
|         languageResourceService.addLanguageResources(getServletContext()); | ||||
|  | ||||
|   | ||||
| @@ -19,12 +19,23 @@ | ||||
|  | ||||
| package org.apache.guacamole.rest; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Singleton; | ||||
| import java.lang.annotation.Annotation; | ||||
| import java.lang.reflect.Method; | ||||
| import java.util.Map; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.ws.rs.FormParam; | ||||
| import javax.ws.rs.QueryParam; | ||||
| import javax.ws.rs.core.Context; | ||||
| import javax.ws.rs.core.MediaType; | ||||
| import javax.ws.rs.core.Response; | ||||
| import javax.ws.rs.ext.ExceptionMapper; | ||||
| import javax.ws.rs.ext.Provider; | ||||
| import org.aopalliance.intercept.MethodInvocation; | ||||
| import org.apache.guacamole.GuacamoleException; | ||||
| import org.apache.guacamole.GuacamoleUnauthorizedException; | ||||
| import org.apache.guacamole.rest.auth.AuthenticationService; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| @@ -37,12 +48,59 @@ import org.slf4j.LoggerFactory; | ||||
| @Singleton | ||||
| public class GuacamoleExceptionMapper | ||||
|         implements ExceptionMapper<GuacamoleException> { | ||||
|          | ||||
|      | ||||
|     /** | ||||
|      * The logger for this class. | ||||
|      */ | ||||
|     private final Logger logger = LoggerFactory.getLogger(GuacamoleExceptionMapper.class); | ||||
|      | ||||
|     /** | ||||
|      * The request associated with this instance of this mapper. | ||||
|      */ | ||||
|     @Context private HttpServletRequest request; | ||||
|      | ||||
|     /** | ||||
|      * The authentication service associated with the currently active session. | ||||
|      */ | ||||
|     @Inject | ||||
|     private AuthenticationService authenticationService; | ||||
|      | ||||
|     /** | ||||
|      * Returns the authentication token that is in use in the current session, | ||||
|      * if present, or null if otherwise. | ||||
|      * | ||||
|      * @return | ||||
|      *     The authentication token for the current session, or null if no | ||||
|      *     token is present. | ||||
|      */ | ||||
|     private String getAuthenticationToken() { | ||||
|  | ||||
|         @SuppressWarnings("unchecked") | ||||
|         Map<String, String[]> parameters = request.getParameterMap(); | ||||
|  | ||||
|         for (String paramName : parameters.keySet()) { | ||||
|             if (paramName.equals("token")) { | ||||
|                 String tokenParams[] = parameters.get(paramName); | ||||
|                 if (tokenParams[0] != null && !tokenParams[0].isEmpty()) | ||||
|                     return tokenParams[0]; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         return null; | ||||
|  | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public Response toResponse(GuacamoleException e) { | ||||
|         logger.debug(">>>EXMAPPER<<< Mapping exception {}", e.getMessage()); | ||||
|          | ||||
|         if (e instanceof GuacamoleUnauthorizedException) { | ||||
|             String token = getAuthenticationToken(); | ||||
|              | ||||
|             if (authenticationService.destroyGuacamoleSession(token)) | ||||
|                 logger.debug("Implicitly invalidated session for token \"{}\"", token); | ||||
|         } | ||||
|          | ||||
|         return Response | ||||
|                 .status(e.getHttpStatusCode()) | ||||
|                 .entity(new APIError(e)) | ||||
|   | ||||
| @@ -1,200 +0,0 @@ | ||||
| /* | ||||
|  * Licensed to the Apache Software Foundation (ASF) under one | ||||
|  * or more contributor license agreements.  See the NOTICE file | ||||
|  * distributed with this work for additional information | ||||
|  * regarding copyright ownership.  The ASF licenses this file | ||||
|  * to you under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance | ||||
|  * with the License.  You may obtain a copy of the License at | ||||
|  * | ||||
|  *   http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, | ||||
|  * software distributed under the License is distributed on an | ||||
|  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||||
|  * KIND, either express or implied.  See the License for the | ||||
|  * specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.rest; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import java.lang.annotation.Annotation; | ||||
| import java.lang.reflect.Method; | ||||
| import javax.ws.rs.FormParam; | ||||
| import javax.ws.rs.QueryParam; | ||||
| import javax.ws.rs.WebApplicationException; | ||||
| import javax.ws.rs.core.Response; | ||||
| import org.aopalliance.intercept.MethodInterceptor; | ||||
| import org.aopalliance.intercept.MethodInvocation; | ||||
| import org.apache.guacamole.GuacamoleException; | ||||
| import org.apache.guacamole.GuacamoleUnauthorizedException; | ||||
| import org.apache.guacamole.rest.auth.AuthenticationService; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| /** | ||||
|  * A method interceptor which wraps custom exception handling around methods | ||||
|  * which can throw GuacamoleExceptions and which are exposed through the REST | ||||
|  * interface. The various types of GuacamoleExceptions are automatically | ||||
|  * translated into appropriate HTTP responses, including JSON describing the | ||||
|  * error that occurred. | ||||
|  */ | ||||
| public class RESTExceptionWrapper implements MethodInterceptor { | ||||
|  | ||||
|     /** | ||||
|      * Logger for this class. | ||||
|      */ | ||||
|     private final Logger logger = LoggerFactory.getLogger(RESTExceptionWrapper.class); | ||||
|  | ||||
|     /** | ||||
|      * Service for authenticating users and managing their Guacamole sessions. | ||||
|      */ | ||||
|     @Inject | ||||
|     private AuthenticationService authenticationService; | ||||
|  | ||||
|     /** | ||||
|      * Determines whether the given set of annotations describes an 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 | ||||
|      * <code>@QueryParam</code> or <code>@FormParam</code> annotations. | ||||
|      * | ||||
|      * @param annotations | ||||
|      *     The annotations associated with the Java parameter being checked. | ||||
|      * | ||||
|      * @param name | ||||
|      *     The name of the HTTP request parameter. | ||||
|      * | ||||
|      * @return | ||||
|      *     true if the given set of annotations describes an HTTP request | ||||
|      *     parameter having the given name, false otherwise. | ||||
|      */ | ||||
|     private boolean isRequestParameter(Annotation[] annotations, String name) { | ||||
|  | ||||
|         // Search annotations for associated HTTP parameters | ||||
|         for (Annotation annotation : annotations) { | ||||
|  | ||||
|             // Check if parameter is associated with the HTTP query string | ||||
|             if (annotation instanceof QueryParam && name.equals(((QueryParam) annotation).value())) | ||||
|                 return true; | ||||
|  | ||||
|             // Failing that, check whether the parameter is associated with the | ||||
|             // HTTP request body | ||||
|             if (annotation instanceof FormParam && name.equals(((FormParam) annotation).value())) | ||||
|                 return true; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // No parameter annotations are present | ||||
|         return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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(); | ||||
|  | ||||
|         // Get the types and annotations associated with each parameter | ||||
|         Annotation[][] parameterAnnotations = method.getParameterAnnotations(); | ||||
|         Class<?>[] parameterTypes = method.getParameterTypes(); | ||||
|  | ||||
|         // The Java standards require these to be parallel arrays | ||||
|         assert(parameterAnnotations.length == parameterTypes.length); | ||||
|  | ||||
|         // Iterate through all parameters, looking for the authentication token | ||||
|         for (int i = 0; i < parameterTypes.length; i++) { | ||||
|  | ||||
|             // Only inspect String parameters | ||||
|             Class<?> parameterType = parameterTypes[i]; | ||||
|             if (parameterType != String.class) | ||||
|                 continue; | ||||
|  | ||||
|             // Parameter must be declared as a REST service parameter | ||||
|             Annotation[] annotations = parameterAnnotations[i]; | ||||
|             if (!isRequestParameter(annotations, "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 WebApplicationException { | ||||
|  | ||||
|         try { | ||||
|  | ||||
|             // 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 (authenticationService.destroyGuacamoleSession(token)) | ||||
|                     logger.debug("Implicitly invalidated session for token \"{}\".", token); | ||||
|  | ||||
|                 // Continue with exception processing | ||||
|                 throw e; | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // Translate GuacamoleException subclasses to HTTP error codes | ||||
|         catch (GuacamoleException e) { | ||||
|             throw new APIException( | ||||
|                 Response.Status.fromStatusCode(e.getHttpStatusCode()), | ||||
|                 e | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         // Rethrow unchecked exceptions such that they are properly wrapped | ||||
|         catch (Throwable t) { | ||||
|  | ||||
|             // Log all reasonable details of error | ||||
|             String message = t.getMessage(); | ||||
|             if (message != null) | ||||
|                 logger.error("Unexpected internal error: {}", message); | ||||
|             else | ||||
|                 logger.error("An internal error occurred, but did not contain " | ||||
|                            + "an error message. Enable debug-level logging for " | ||||
|                            + "details."); | ||||
|  | ||||
|             // Ensure internal errors are fully logged at the debug level | ||||
|             logger.debug("Unexpected error in REST endpoint.", t); | ||||
|  | ||||
|             throw new APIException(Response.Status.INTERNAL_SERVER_ERROR, | ||||
|                     new GuacamoleException("Unexpected internal error.", t)); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -83,11 +83,6 @@ public class RESTServiceModule extends ServletModule { | ||||
|         bind(AuthenticationService.class); | ||||
|         bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); | ||||
|         bind(DecorationService.class); | ||||
|  | ||||
|         // Automatically translate GuacamoleExceptions for REST methods | ||||
|         MethodInterceptor interceptor = new RESTExceptionWrapper(); | ||||
|         requestInjection(interceptor); | ||||
|         bindInterceptor(Matchers.any(), new RESTMethodMatcher(), interceptor); | ||||
|          | ||||
|         // Get the ExceptionMapper that will rewrite exceptions into JSON. | ||||
|         bind(GuacamoleExceptionMapper.class); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user