diff --git a/guacamole/pom.xml b/guacamole/pom.xml index 0693e6aae..3e8941da7 100644 --- a/guacamole/pom.xml +++ b/guacamole/pom.xml @@ -254,6 +254,11 @@ slf4j-api 1.7.7 + + org.slf4j + jul-to-slf4j + 1.7.7 + ch.qos.logback logback-classic @@ -435,34 +440,39 @@ com.google.inject guice - 3.0 + 4.2.3 com.google.inject.extensions guice-assistedinject - 3.0 + 4.2.3 com.google.inject.extensions guice-servlet - 3.0 + 4.2.3 - com.sun.jersey - jersey-server - 1.17.1 + org.glassfish.jersey.containers + jersey-container-servlet-core + 2.31 + + + org.glassfish.jersey.inject + jersey-hk2 + 2.31 - + - com.sun.jersey.contribs - jersey-guice - 1.17.1 - + org.glassfish.hk2 + guice-bridge + 2.6.1 + @@ -473,9 +483,9 @@ - com.sun.jersey - jersey-json - 1.17.1 + org.glassfish.jersey.media + jersey-media-json-jackson + 2.31 diff --git a/guacamole/src/main/java/org/apache/guacamole/GuacamoleApplication.java b/guacamole/src/main/java/org/apache/guacamole/GuacamoleApplication.java new file mode 100644 index 000000000..7f3e4603c --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/GuacamoleApplication.java @@ -0,0 +1,76 @@ +/* + * 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; + +import com.google.inject.Injector; +import javax.inject.Inject; +import javax.servlet.ServletContext; +import javax.ws.rs.ApplicationPath; +import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.jersey.jackson.JacksonFeature; +import org.glassfish.jersey.server.ResourceConfig; +import org.jvnet.hk2.guice.bridge.api.GuiceBridge; +import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge; +import org.slf4j.bridge.SLF4JBridgeHandler; + +/** + * JAX-RS Application which serves as the root definition of the Guacamole + * REST API. The HK2 dependency injection used by Jersey is automatically + * bridged to Guice, allowing injections managed by Guice to be injected within + * classes served by Jersey. + */ +@ApplicationPath("/*") +public class GuacamoleApplication extends ResourceConfig { + + /** + * Creates a new GuacamoleApplication which defines the Guacamole REST API, + * automatically configuring Jersey's HK2 dependency injection to + * additionally pull services from a Guice injector. + * + * @param servletContext + * The ServletContext which has already associated with a Guice + * injector via a GuacamoleServletContextListener. + * + * @param serviceLocator + * The HK2 service locator (injector). + */ + @Inject + public GuacamoleApplication(ServletContext servletContext, + ServiceLocator serviceLocator) { + + // Bridge Jersey logging (java.util.logging) to SLF4J + SLF4JBridgeHandler.removeHandlersForRootLogger(); + SLF4JBridgeHandler.install(); + + // Bridge HK2 service locator with Guice injector + Injector guiceInjector = (Injector) servletContext.getAttribute(GuacamoleServletContextListener.GUICE_INJECTOR); + GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator); + GuiceIntoHK2Bridge bridge = serviceLocator.getService(GuiceIntoHK2Bridge.class); + bridge.bridgeGuiceInjector(guiceInjector); + + // Automatically scan for REST resources + packages("org.apache.guacamole.rest"); + + // Use Jackson for JSON + register(JacksonFeature.class); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/GuacamoleServletContextListener.java b/guacamole/src/main/java/org/apache/guacamole/GuacamoleServletContextListener.java index e8ad6a81f..f793575e6 100644 --- a/guacamole/src/main/java/org/apache/guacamole/GuacamoleServletContextListener.java +++ b/guacamole/src/main/java/org/apache/guacamole/GuacamoleServletContextListener.java @@ -21,11 +21,12 @@ package org.apache.guacamole; import org.apache.guacamole.tunnel.TunnelModule; import com.google.inject.Guice; -import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Stage; import com.google.inject.servlet.GuiceServletContextListener; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import javax.inject.Inject; import javax.servlet.ServletContextEvent; import org.apache.guacamole.environment.Environment; import org.apache.guacamole.environment.LocalEnvironment; @@ -41,9 +42,45 @@ import org.slf4j.LoggerFactory; /** * A ServletContextListener to listen for initialization of the servlet context * in order to set up dependency injection. + * + * NOTE: Guacamole's REST API uses Jersey 2.x which does not natively support + * dependency injection using Guice. It DOES support dependency injection using + * HK2, which supports bi-directional bridging with Guice. + * + * The overall process is thus: + * + * 1. Application initialization proceeds using GuacamoleServletContextListener, + * a subclass of GuiceServletContextListener, with all HTTP requests being + * routed through GuiceFilter which serves as the absolute root. + * + * 2. GuacamoleServletContextListener prepares the Guice injector, storing the + * injector within the ServletContext such that it can later be bridged with + * HK2. + * + * 3. Several of the modules used to prepare the Guice injector are + * ServletModule subclasses, which define HTTP request paths that GuiceFilter + * should route to specific servlets. One of these paths is "/api/*" (the + * root of the REST API) which is routed to Jersey's ServletContainer servlet + * (the root of Jersey's JAX-RS implementation). + * + * 4. Configuration information passed to Jersey's ServletContainer tells Jersey + * to use the GuacamoleApplication class (a subclass of ResourceConfig) to + * define the rest of the resources and any other configuration. + * + * 5. When Jersey creates its instance of GuacamoleApplication, the + * initialization process of GuacamoleApplication pulls the Guice injector + * from the ServletContext, completes the HK2 bridging, and configures Jersey + * to automatically locate and inject all REST services. */ public class GuacamoleServletContextListener extends GuiceServletContextListener { + /** + * The name of the ServletContext attribute which will contain a reference + * to the Guice injector once the contextInitialized() event has been + * handled. + */ + public static final String GUICE_INJECTOR = "GUAC_GUICE_INJECTOR"; + /** * Logger for this class. */ @@ -65,6 +102,12 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener @Inject private List authProviders; + /** + * Internal reference to the Guice injector that was lazily created when + * getInjector() was first invoked. + */ + private final AtomicReference guiceInjector = new AtomicReference<>(); + @Override public void contextInitialized(ServletContextEvent servletContextEvent) { @@ -78,33 +121,47 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener throw new RuntimeException(e); } + // NOTE: The superclass implementation of contextInitialized() is + // expected to invoke getInjector(), hence the need to call AFTER + // setting up the environment and session map super.contextInitialized(servletContextEvent); + // Inject any annotated members of this class + Injector injector = getInjector(); + injector.injectMembers(this); + + // Store reference to injector for use by Jersey and HK2 bridge + servletContextEvent.getServletContext().setAttribute(GUICE_INJECTOR, injector); + } @Override protected Injector getInjector() { + return guiceInjector.updateAndGet((current) -> { - // Create injector - Injector injector = Guice.createInjector(Stage.PRODUCTION, - new EnvironmentModule(environment), - new LogModule(environment), - new ExtensionModule(environment), - new RESTServiceModule(sessionMap), - new TunnelModule() - ); + // Use existing injector if already created + if (current != null) + return current; - // Inject any annotated members of this class - injector.injectMembers(this); + // Create new injector if necessary + Injector injector = Guice.createInjector(Stage.PRODUCTION, + new EnvironmentModule(environment), + new LogModule(environment), + new ExtensionModule(environment), + new RESTServiceModule(sessionMap), + new TunnelModule() + ); - return injector; + return injector; + }); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { - super.contextDestroyed(servletContextEvent); + // Clean up reference to Guice injector + servletContextEvent.getServletContext().removeAttribute(GUICE_INJECTOR); // Shutdown TokenSessionMap if (sessionMap != null) @@ -116,6 +173,9 @@ public class GuacamoleServletContextListener extends GuiceServletContextListener authProvider.shutdown(); } + // Continue any Guice-specific cleanup + super.contextDestroyed(servletContextEvent); + } } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java b/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java index 01103b0ed..e37b106a9 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java @@ -33,18 +33,15 @@ import org.apache.guacamole.GuacamoleException; public class APIException extends WebApplicationException { /** - * Construct a new APIException based on the given GuacamoleException and - * HTTP status. The details of the GuacamoleException relevant to the REST - * API will be exposed via an APIError. - * - * @param status - * The HTTP status which corresponds to the GuacamoleException. + * Construct a new APIException based on the given GuacamoleException. The + * details of the GuacamoleException relevant to the REST API will be + * exposed via an APIError. * * @param exception * The GuacamoleException that occurred. */ - public APIException(Response.Status status, GuacamoleException exception) { - super(Response.status(status) + public APIException(GuacamoleException exception) { + super(Response.status(exception.getHttpStatusCode()) .type(MediaType.APPLICATION_JSON) .entity(new APIError(exception)) .build()); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/LimitedRequestInputStream.java b/guacamole/src/main/java/org/apache/guacamole/rest/LimitedRequestInputStream.java new file mode 100644 index 000000000..840ef1917 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/LimitedRequestInputStream.java @@ -0,0 +1,152 @@ +/* + * 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 java.io.IOException; +import java.io.InputStream; +import org.apache.guacamole.GuacamoleClientOverrunException; + +/** + * InputStream implementation which limits the body of REST API requests to + * a particular maximum size. If an attempt is made to read from a REST API + * request which exceeds this limit, the read attempt will be aborted by + * throwing an APIException. + */ +public class LimitedRequestInputStream extends InputStream { + + /** + * The InputStream being limited. + */ + private final InputStream stream; + + /** + * The maximum number of bytes to allow to be read or skipped. + */ + private final long maxLength; + + /** + * The total number of bytes that have been read or skipped from the stream + * thus far. + */ + private long bytesRead = 0; + + /** + * Wraps the given InputStream, ensuring that the overall number of bytes + * read or skipped does not exceed the given maximum length. + * + * @param stream + * The InputStream to limit. + * + * @param maxLength + * The maximum number of bytes to allow to be read or skipped. + */ + public LimitedRequestInputStream(InputStream stream, long maxLength) { + this.stream = stream; + this.maxLength = maxLength; + } + + /** + * Immediately verifies that the stream length limit has not been exceeded. + * If the length limit has been exceeded, an APIException is thrown + * indicating that the request body is too large. + * + * @throws APIException + * If the length limit has been exceeded. + */ + private synchronized void recheckLength() throws APIException { + if (bytesRead > maxLength) + throw new APIException(new GuacamoleClientOverrunException("Request body/entity too large.")); + } + + /** + * Updates the current number of bytes read based on the return value of a + * read-like operation such as read() or skip(). If the maximum stream + * length is exceeded as a result of the read, an APIException indicating + * this is thrown. + * + * NOTE: To avoid unnecessary read operations, recheckLength() should be + * manually called before performing any read operation. This function will + * perform the same checks, but can inherently only do so AFTER the read + * operation has occurred. + * + * @param change + * The number of bytes that have been read or skipped, or -1 if the + * read-like operation has failed (and no bytes have been read). + * + * @return + * The provided number of bytes read/skipped. + * + * @throws APIException + * If the read-like operation that occurred has caused the stream + * length to exceed its maximum. + */ + private synchronized long limitedRead(long change) throws APIException { + + if (change != -1) { + bytesRead += change; + recheckLength(); + } + + return change; + + } + + @Override + public void close() throws IOException { + stream.close(); + } + + @Override + public int available() throws IOException { + return stream.available(); + } + + @Override + public long skip(long l) throws IOException { + recheckLength(); + return limitedRead(stream.skip(l)); + } + + @Override + public int read(byte[] bytes, int i, int i1) throws IOException { + recheckLength(); + return (int) limitedRead(stream.read(bytes, i, i1)); + } + + @Override + public int read(byte[] bytes) throws IOException { + recheckLength(); + return (int) limitedRead(stream.read(bytes)); + } + + @Override + public int read() throws IOException { + + recheckLength(); + + int value = stream.read(); + if (value != -1) + limitedRead(1); + + return value; + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/PATCH.java b/guacamole/src/main/java/org/apache/guacamole/rest/PATCH.java deleted file mode 100644 index 171719eed..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/rest/PATCH.java +++ /dev/null @@ -1,35 +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 java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import javax.ws.rs.HttpMethod; - -/** - * An annotation for using the HTTP PATCH method in the REST endpoints. - */ -@Target({ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -@HttpMethod("PATCH") -public @interface PATCH {} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionMapper.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionMapper.java index e6c9b4c5b..85430ae19 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionMapper.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionMapper.java @@ -19,9 +19,10 @@ package org.apache.guacamole.rest; -import com.google.inject.Inject; -import com.google.inject.Singleton; +import javax.inject.Inject; +import javax.inject.Singleton; import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -82,6 +83,10 @@ public class RESTExceptionMapper implements ExceptionMapper { @Override public Response toResponse(Throwable t) { + + // Pass WebApplicationException responses through untouched + if (t instanceof WebApplicationException) + return ((WebApplicationException) t).getResponse(); // Ensure any associated session is invalidated if unauthorized if (t instanceof GuacamoleUnauthorizedException) { diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java deleted file mode 100644 index 875f4161c..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java +++ /dev/null @@ -1,128 +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.matcher.AbstractMatcher; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.Path; -import org.apache.guacamole.GuacamoleException; - -/** - * A Guice Matcher which matches only methods which throw GuacamoleException - * (or a subclass thereof) and are explicitly annotated as with an HTTP method - * annotation like @GET or @POST. Any method which - * throws GuacamoleException and is annotated with an annotation that is - * annotated with @HttpMethod will match. - */ -public class RESTMethodMatcher extends AbstractMatcher { - - /** - * Returns whether the given method throws the specified exception type, - * including any subclasses of that type. - * - * @param method - * The method to test. - * - * @param exceptionType - * The exception type to test for. - * - * @return - * true if the given method throws an exception of the specified type, - * false otherwise. - */ - private boolean methodThrowsException(Method method, - Class exceptionType) { - - // Check whether the method throws an exception of the specified type - for (Class thrownType : method.getExceptionTypes()) { - if (exceptionType.isAssignableFrom(thrownType)) - return true; - } - - // No such exception is declared to be thrown - return false; - - } - - /** - * Returns whether the given method is annotated as a REST method. A REST - * method is annotated with an annotation which is annotated with - * @HttpMethod or @Path. - * - * @param method - * The method to test. - * - * @return - * true if the given method is annotated as a REST method, false - * otherwise. - */ - private boolean isRESTMethod(Method method) { - - // Check whether the required REST annotations are present - for (Annotation annotation : method.getAnnotations()) { - - // A method is a REST method if it is annotated with @HttpMethod - Class annotationType = annotation.annotationType(); - if (annotationType.isAnnotationPresent(HttpMethod.class)) - return true; - - // A method is a REST method if it is annotated with @Path - if (Path.class.isAssignableFrom(annotationType)) - return true; - - } - - // A method is also REST method if it overrides a REST method within - // the superclass - Class superclass = method.getDeclaringClass().getSuperclass(); - if (superclass != null) { - - // Recheck against identical method within superclass - try { - return isRESTMethod(superclass.getMethod(method.getName(), - method.getParameterTypes())); - } - - // If there is no such method, then this method cannot possibly be - // a REST method - catch (NoSuchMethodException e) { - return false; - } - - } - - // Lacking a superclass, the search stops here - it's not a REST method - return false; - - } - - @Override - public boolean matches(Method method) { - - // Guacamole REST methods are REST methods which throw - // GuacamoleExceptions - return isRESTMethod(method) - && methodThrowsException(method, GuacamoleException.class); - - } - -} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java index 4efab0ff5..32878b1ee 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java @@ -19,18 +19,14 @@ package org.apache.guacamole.rest; -import org.apache.guacamole.rest.event.ListenerService; -import org.apache.guacamole.rest.session.UserContextResourceFactory; -import org.apache.guacamole.rest.session.SessionRESTService; import com.google.inject.Scopes; import com.google.inject.assistedinject.FactoryModuleBuilder; -import com.google.inject.matcher.Matchers; import com.google.inject.servlet.ServletModule; -import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; -import org.aopalliance.intercept.MethodInterceptor; +import java.util.Collections; +import org.apache.guacamole.rest.event.ListenerService; +import org.apache.guacamole.rest.session.UserContextResourceFactory; +import org.apache.guacamole.GuacamoleApplication; import org.apache.guacamole.rest.activeconnection.ActiveConnectionModule; -import org.codehaus.jackson.jaxrs.JacksonJsonProvider; -import org.apache.guacamole.rest.auth.TokenRESTService; import org.apache.guacamole.rest.auth.AuthTokenGenerator; import org.apache.guacamole.rest.auth.AuthenticationService; import org.apache.guacamole.rest.auth.DecorationService; @@ -38,15 +34,14 @@ import org.apache.guacamole.rest.auth.SecureRandomAuthTokenGenerator; import org.apache.guacamole.rest.auth.TokenSessionMap; import org.apache.guacamole.rest.connection.ConnectionModule; import org.apache.guacamole.rest.connectiongroup.ConnectionGroupModule; -import org.apache.guacamole.rest.extension.ExtensionRESTService; -import org.apache.guacamole.rest.language.LanguageRESTService; -import org.apache.guacamole.rest.patch.PatchRESTService; import org.apache.guacamole.rest.session.SessionResourceFactory; import org.apache.guacamole.rest.sharingprofile.SharingProfileModule; import org.apache.guacamole.rest.tunnel.TunnelCollectionResourceFactory; import org.apache.guacamole.rest.tunnel.TunnelResourceFactory; import org.apache.guacamole.rest.user.UserModule; import org.apache.guacamole.rest.usergroup.UserGroupModule; +import org.glassfish.jersey.servlet.ServletContainer; +import org.glassfish.jersey.servlet.ServletProperties; import org.webjars.servlet.WebjarsServlet; /** @@ -84,17 +79,7 @@ public class RESTServiceModule extends ServletModule { bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); bind(DecorationService.class); - // Automatically translate GuacamoleExceptions for REST methods - bind(RESTExceptionMapper.class); - - // Set up the API endpoints - bind(ExtensionRESTService.class); - bind(LanguageRESTService.class); - bind(PatchRESTService.class); - bind(TokenRESTService.class); - // Root-level resources - bind(SessionRESTService.class); install(new FactoryModuleBuilder().build(SessionResourceFactory.class)); install(new FactoryModuleBuilder().build(TunnelCollectionResourceFactory.class)); install(new FactoryModuleBuilder().build(TunnelResourceFactory.class)); @@ -108,10 +93,12 @@ public class RESTServiceModule extends ServletModule { install(new UserModule()); install(new UserGroupModule()); - // Set up the servlet and JSON mappings - bind(GuiceContainer.class); - bind(JacksonJsonProvider.class).in(Scopes.SINGLETON); - serve("/api/*").with(GuiceContainer.class); + // Serve REST services using Jersey 2.x + bind(ServletContainer.class).in(Scopes.SINGLETON); + serve("/api/*").with(ServletContainer.class, Collections.singletonMap( + ServletProperties.JAXRS_APPLICATION_CLASS, + GuacamoleApplication.class.getName() + )); // Serve Webjar JavaScript dependencies bind(WebjarsServlet.class).in(Scopes.SINGLETON); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RequestSizeFilter.java b/guacamole/src/main/java/org/apache/guacamole/rest/RequestSizeFilter.java new file mode 100644 index 000000000..f638224ad --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RequestSizeFilter.java @@ -0,0 +1,108 @@ +/* + * 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 java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ResourceInfo; +import javax.ws.rs.core.Context; +import javax.ws.rs.ext.Provider; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.properties.LongGuacamoleProperty; + +/** + * Filter which restricts REST API requests to a particular maximum size. + */ +@Singleton +@Provider +public class RequestSizeFilter implements ContainerRequestFilter { + + /** + * Informs the RequestSizeFilter to NOT enforce its request size limits on + * requests serviced by the annotated method. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public static @interface DoNotLimit {} + + /** + * The default maximum number of bytes to accept within the entity body of + * any particular REST request. + */ + private final long DEFAULT_MAX_REQUEST_SIZE = 2097152; + + /** + * The maximum number of bytes to accept within the entity body of any + * particular REST request. If not specified, requests will be limited to + * 2 MB by default. Specifying 0 disables request size limitations. + */ + private final LongGuacamoleProperty API_MAX_REQUEST_SIZE = new LongGuacamoleProperty() { + + @Override + public String getName() { return "api-max-request-size"; } + + }; + + /** + * The Guacamole server environment. + */ + @Inject + private Environment environment; + + /** + * Information describing the resource that was requested. + */ + @Context + private ResourceInfo resourceInfo; + + @Override + public void filter(ContainerRequestContext context) throws IOException { + + // Retrieve configured request size limits + final long maxRequestSize; + try { + maxRequestSize = environment.getProperty(API_MAX_REQUEST_SIZE, DEFAULT_MAX_REQUEST_SIZE); + } + catch (GuacamoleException e) { + throw new APIException(e); + } + + // Ignore request size if limit is disabled + if (maxRequestSize == 0 || resourceInfo.getResourceMethod().isAnnotationPresent(DoNotLimit.class)) + return; + + // Restrict maximum size of requests which have an input stream + // available to be limited + InputStream stream = context.getEntityStream(); + if (stream != null) + context.setEntityStream(new LimitedRequestInputStream(stream, maxRequestSize)); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java index 47ba3d42b..514daa124 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java @@ -19,9 +19,9 @@ package org.apache.guacamole.rest.activeconnection; -import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; +import javax.inject.Inject; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java index 5770a079a..a663ab783 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java @@ -19,10 +19,10 @@ package org.apache.guacamole.rest.auth; -import com.google.inject.Inject; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; +import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.GuacamoleException; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecorationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecorationService.java index b63b037b3..7537e99ef 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecorationService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/DecorationService.java @@ -19,9 +19,9 @@ package org.apache.guacamole.rest.auth; -import com.google.inject.Inject; import java.util.Iterator; import java.util.List; +import javax.inject.Inject; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.AuthenticatedUser; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java index 887b4f029..427af7f39 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java @@ -20,10 +20,10 @@ package org.apache.guacamole.rest.auth; import com.google.common.io.BaseEncoding; -import com.google.inject.Inject; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java index 7d4f0d552..cbd7c5e8d 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java @@ -19,10 +19,10 @@ package org.apache.guacamole.rest.connection; -import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; import java.util.Map; +import javax.inject.Inject; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java index ce9cb8371..e9b4b225b 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.PATCH; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @@ -44,7 +45,6 @@ import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.permission.SystemPermission; import org.apache.guacamole.net.auth.permission.SystemPermissionSet; import org.apache.guacamole.rest.APIPatch; -import org.apache.guacamole.rest.PATCH; /** * A REST resource which abstracts the operations available on all Guacamole diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java b/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java index e92cc8a66..a2b645b44 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/event/ListenerService.java @@ -20,7 +20,7 @@ package org.apache.guacamole.rest.event; import java.util.List; -import com.google.inject.Inject; +import javax.inject.Inject; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.event.listener.Listener; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/extension/ExtensionRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/extension/ExtensionRESTService.java index 487bb7c11..49a3e872c 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/extension/ExtensionRESTService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/extension/ExtensionRESTService.java @@ -19,8 +19,8 @@ package org.apache.guacamole.rest.extension; -import com.google.inject.Inject; import java.util.List; +import javax.inject.Inject; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import org.apache.guacamole.GuacamoleException; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/APISortPredicate.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/APISortPredicate.java index c45ae9a81..0fea582e0 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/history/APISortPredicate.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/APISortPredicate.java @@ -109,10 +109,7 @@ public class APISortPredicate { // Bail out if sort property is not valid catch (IllegalArgumentException e) { - throw new APIException( - Response.Status.BAD_REQUEST, - new GuacamoleClientException(String.format("Invalid sort property: \"%s\"", value)) - ); + throw new APIException(new GuacamoleClientException(String.format("Invalid sort property: \"%s\"", value))); } } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetResource.java index 446b0453e..77b0b4091 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetResource.java @@ -23,13 +23,13 @@ import java.util.List; import java.util.Set; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.PATCH; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.apache.guacamole.GuacamoleClientException; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.RelatedObjectSet; import org.apache.guacamole.rest.APIPatch; -import org.apache.guacamole.rest.PATCH; /** * A REST resource which abstracts the operations available on arbitrary sets diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/language/LanguageRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/language/LanguageRESTService.java index 3f5f32cff..4fccbaef0 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/language/LanguageRESTService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/language/LanguageRESTService.java @@ -19,8 +19,8 @@ package org.apache.guacamole.rest.language; -import com.google.inject.Inject; import java.util.Map; +import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/patch/PatchRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/patch/PatchRESTService.java index e8e5f003d..115b37c93 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/patch/PatchRESTService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/patch/PatchRESTService.java @@ -19,12 +19,12 @@ package org.apache.guacamole.rest.patch; -import com.google.inject.Inject; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java index 739a39c5e..38b337e0e 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java @@ -22,6 +22,7 @@ package org.apache.guacamole.rest.permission; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.PATCH; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.apache.guacamole.GuacamoleClientException; @@ -31,7 +32,6 @@ import org.apache.guacamole.net.auth.permission.ObjectPermission; import org.apache.guacamole.net.auth.permission.Permission; import org.apache.guacamole.net.auth.permission.SystemPermission; import org.apache.guacamole.rest.APIPatch; -import org.apache.guacamole.rest.PATCH; /** * A REST resource which abstracts the operations available on the permissions diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionRESTService.java index 6a980f1cd..546026b53 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionRESTService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionRESTService.java @@ -19,7 +19,7 @@ package org.apache.guacamole.rest.session; -import com.google.inject.Inject; +import javax.inject.Inject; import javax.ws.rs.Consumes; import javax.ws.rs.Path; import javax.ws.rs.Produces; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java index 606e78b4d..702ec867d 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java @@ -19,9 +19,9 @@ package org.apache.guacamole.rest.session; -import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; +import javax.inject.Inject; import javax.ws.rs.Consumes; import javax.ws.rs.Path; import javax.ws.rs.PathParam; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java index edaf9f466..cbb04d172 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java @@ -21,9 +21,9 @@ package org.apache.guacamole.rest.session; import org.apache.guacamole.rest.directory.DirectoryResource; import org.apache.guacamole.rest.directory.DirectoryResourceFactory; -import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; +import javax.inject.Inject; import javax.ws.rs.Consumes; import javax.ws.rs.Path; import javax.ws.rs.Produces; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/StreamResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/StreamResource.java index 70b2ff026..c33255e3e 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/StreamResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/StreamResource.java @@ -31,6 +31,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.StreamingOutput; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.rest.RequestSizeFilter; import org.apache.guacamole.tunnel.StreamInterceptingTunnel; /** @@ -127,6 +128,7 @@ public class StreamResource { */ @POST @Consumes(MediaType.WILDCARD) + @RequestSizeFilter.DoNotLimit public void setStreamContents(InputStream data) throws GuacamoleException { // Send input over stream diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java index d4fac136b..abd7b422d 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java @@ -19,11 +19,11 @@ package org.apache.guacamole.rest.tunnel; -import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; import java.util.Map; import java.util.Set; +import javax.inject.Inject; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java index 74347b721..7c0ec7eef 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java @@ -19,9 +19,9 @@ package org.apache.guacamole.rest.tunnel; -import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; +import javax.inject.Inject; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java index fa56b1932..6851701f1 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java @@ -19,10 +19,10 @@ package org.apache.guacamole.tunnel; -import com.google.inject.Inject; -import com.google.inject.Singleton; import java.util.List; import java.util.Map; +import javax.inject.Inject; +import javax.inject.Singleton; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleResourceNotFoundException; import org.apache.guacamole.GuacamoleSession; diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java index 4a7ef78cc..39f4a2508 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java @@ -19,11 +19,10 @@ package org.apache.guacamole.tunnel.http; -import com.google.inject.Inject; -import com.google.inject.Singleton; +import javax.inject.Inject; +import javax.inject.Singleton; import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.tunnel.TunnelRequestService; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.servlet.GuacamoleHTTPTunnelServlet; diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java index 237bfbaf8..b9e2d3df6 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java @@ -19,8 +19,8 @@ package org.apache.guacamole.tunnel.websocket.jetty8; -import com.google.inject.Inject; -import com.google.inject.Singleton; +import javax.inject.Inject; +import javax.inject.Singleton; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.tunnel.TunnelRequestService; diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java index 9497e5460..2b602160a 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java @@ -19,8 +19,8 @@ package org.apache.guacamole.tunnel.websocket.jetty9; -import com.google.inject.Inject; -import com.google.inject.Singleton; +import javax.inject.Inject; +import javax.inject.Singleton; import org.eclipse.jetty.websocket.servlet.WebSocketServlet; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; import org.apache.guacamole.tunnel.TunnelRequestService; diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/RestrictedGuacamoleWebSocketTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/RestrictedGuacamoleWebSocketTunnelServlet.java index abc28733d..510149527 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/RestrictedGuacamoleWebSocketTunnelServlet.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/RestrictedGuacamoleWebSocketTunnelServlet.java @@ -19,8 +19,8 @@ package org.apache.guacamole.tunnel.websocket.tomcat; -import com.google.inject.Inject; -import com.google.inject.Singleton; +import javax.inject.Inject; +import javax.inject.Singleton; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.tunnel.TunnelRequestService; diff --git a/guacamole/src/main/webapp/WEB-INF/web.xml b/guacamole/src/main/webapp/WEB-INF/web.xml index bc067c704..52a52a624 100644 --- a/guacamole/src/main/webapp/WEB-INF/web.xml +++ b/guacamole/src/main/webapp/WEB-INF/web.xml @@ -28,7 +28,7 @@ index.html - + guiceFilter com.google.inject.servlet.GuiceFilter @@ -37,7 +37,6 @@ guiceFilter /* - org.apache.guacamole.GuacamoleServletContextListener