From 79c3bfc01953b03cee1e0bf210cf11734ce9ef24 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Tue, 6 Sep 2016 12:23:50 +0200 Subject: [PATCH] SONAR-7714 Remove JSESSIONID generation from Tomcat --- .../org/sonar/server/app/TomcatContexts.java | 1 + .../platform/web/MasterServletFilter.java | 5 +- .../{ProfilingFilter.java => RootFilter.java} | 45 ++++++++++---- .../platform/web/MasterServletFilterTest.java | 15 +++-- ...ingFilterTest.java => RootFilterTest.java} | 62 ++++++++++++++++--- .../sonar-web/src/main/webapp/WEB-INF/web.xml | 14 +---- 6 files changed, 104 insertions(+), 38 deletions(-) rename server/sonar-server/src/main/java/org/sonar/server/platform/web/{ProfilingFilter.java => RootFilter.java} (81%) rename server/sonar-server/src/test/java/org/sonar/server/platform/web/{ProfilingFilterTest.java => RootFilterTest.java} (60%) diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/TomcatContexts.java b/server/sonar-server/src/main/java/org/sonar/server/app/TomcatContexts.java index 56dcf66f5ff..b15d76e0854 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/app/TomcatContexts.java +++ b/server/sonar-server/src/main/java/org/sonar/server/app/TomcatContexts.java @@ -112,6 +112,7 @@ public class TomcatContexts { context.setDelegate(true); context.setJarScanner(new NullJarScanner()); context.setAllowCasualMultipartParsing(true); + context.setCookies(false); return context; } catch (ServletException e) { throw new IllegalStateException("Fail to configure webapp from " + dir, e); diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java index 76f3abd0822..230bddd3cd6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java @@ -82,7 +82,7 @@ public class MasterServletFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest hsr = (HttpServletRequest) request; if (filters.length == 0) { - chain.doFilter(request, response); + chain.doFilter(hsr, response); } else { String path = hsr.getRequestURI().replaceFirst(hsr.getContextPath(), ""); GodFilterChain godChain = new GodFilterChain(chain); @@ -92,7 +92,7 @@ public class MasterServletFilter implements Filter { godChain.addFilter(filter); } } - godChain.doFilter(request, response); + godChain.doFilter(hsr, response); } } @@ -134,4 +134,5 @@ public class MasterServletFilter implements Filter { filters.add(filter); } } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/ProfilingFilter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/RootFilter.java similarity index 81% rename from server/sonar-server/src/main/java/org/sonar/server/platform/web/ProfilingFilter.java rename to server/sonar-server/src/main/java/org/sonar/server/platform/web/RootFilter.java index 8728a7b401c..dec5a8d76c1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/web/ProfilingFilter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/web/RootFilter.java @@ -19,11 +19,10 @@ */ package org.sonar.server.platform.web; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; -import org.apache.commons.lang.StringUtils; -import org.sonar.api.utils.log.Loggers; -import org.sonar.api.utils.log.Profiler; - +import java.io.IOException; +import java.util.Set; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -31,9 +30,11 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; - -import java.io.IOException; -import java.util.Set; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpSession; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.log.Profiler; /** *

Profile HTTP requests using platform profiling utility.

@@ -44,7 +45,7 @@ import java.util.Set; * * @since 4.1 */ -public class ProfilingFilter implements Filter { +public class RootFilter implements Filter { private static final String CONFIG_SEPARATOR = ","; private static final String URL_SEPARATOR = "/"; @@ -71,17 +72,17 @@ public class ProfilingFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (request instanceof HttpServletRequest) { - HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletRequest httpRequest = new ServletRequestWrapper((HttpServletRequest) request); String requestUri = httpRequest.getRequestURI(); String rootDir = getRootDir(requestUri); if (staticResourceDirs.contains(rootDir)) { // Static resource, not profiled - chain.doFilter(request, response); + chain.doFilter(httpRequest, response); } else { Profiler profiler = Profiler.createIfDebug(Logger).start(); try { - chain.doFilter(request, response); + chain.doFilter(httpRequest, response); } finally { if (profiler.isDebugEnabled()) { String queryString = httpRequest.getQueryString(); @@ -112,4 +113,26 @@ public class ProfilingFilter implements Filter { public void destroy() { // Nothing } + + @VisibleForTesting + static class ServletRequestWrapper extends HttpServletRequestWrapper { + + ServletRequestWrapper(HttpServletRequest request) { + super(request); + } + + @Override + public HttpSession getSession(boolean create) { + throw notSupported(); + } + + @Override + public HttpSession getSession() { + throw notSupported(); + } + + private static UnsupportedOperationException notSupported() { + return new UnsupportedOperationException("Sessions are disabled so that web server is stateless"); + } + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java index 255dab8f8e3..3ac703c5819 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java @@ -46,7 +46,7 @@ import static org.mockito.Mockito.when; public class MasterServletFilterTest { @Rule - public ExpectedException thrown = ExpectedException.none(); + public ExpectedException expectedException = ExpectedException.none(); @Before public void resetSingleton() { @@ -71,15 +71,15 @@ public class MasterServletFilterTest { public void servlet_container_should_instantiate_only_a_single_master_instance() { new MasterServletFilter(); - thrown.expect(IllegalStateException.class); - thrown.expectMessage("Servlet filter org.sonar.server.platform.web.MasterServletFilter is already instantiated"); + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Servlet filter org.sonar.server.platform.web.MasterServletFilter is already instantiated"); new MasterServletFilter(); } @Test public void should_propagate_initialization_failure() throws Exception { - thrown.expect(IllegalStateException.class); - thrown.expectMessage("foo"); + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("foo"); ServletFilter filter = mock(ServletFilter.class); doThrow(new IllegalStateException("foo")).when(filter).init(any(FilterConfig.class)); @@ -126,17 +126,20 @@ public class MasterServletFilterTest { private static int globalCount = 0; private int count = 0; + @Override public void init(FilterConfig filterConfig) throws ServletException { } + @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { globalCount++; count = globalCount; filterChain.doFilter(servletRequest, servletResponse); } + @Override public void destroy() { } - } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/web/ProfilingFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/web/RootFilterTest.java similarity index 60% rename from server/sonar-server/src/test/java/org/sonar/server/platform/web/ProfilingFilterTest.java rename to server/sonar-server/src/test/java/org/sonar/server/platform/web/RootFilterTest.java index 93c339c9986..aff2ff1b642 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/web/ProfilingFilterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/web/RootFilterTest.java @@ -19,10 +19,6 @@ */ package org.sonar.server.platform.web; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; @@ -30,15 +26,25 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; -import org.sonar.server.platform.web.ProfilingFilter; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import static org.assertj.core.api.Java6Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class ProfilingFilterTest { +public class RootFilterTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); - private ProfilingFilter filter; + private RootFilter filter; private FilterChain chain; @Before @@ -50,7 +56,7 @@ public class ProfilingFilterTest { when(filterConfig.getServletContext()).thenReturn(context); chain = mock(FilterChain.class); - filter = new ProfilingFilter(); + filter = new RootFilter(); filter.init(filterConfig); } @@ -96,6 +102,46 @@ public class ProfilingFilterTest { filter.doFilter(request("GET", "/context/static/image.png", null), null, chain); } + @Test + public void request_used_in_chain_do_filter_is_a_servlet_wrapper_when_static_resource() throws Exception { + filter.doFilter(request("GET", "/context/static/image.png", null), null, chain); + ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(ServletRequest.class); + + verify(chain).doFilter(requestArgumentCaptor.capture(), any(ServletResponse.class)); + + assertThat(requestArgumentCaptor.getValue()).isInstanceOf(RootFilter.ServletRequestWrapper.class); + } + + @Test + public void request_used_in_chain_do_filter_is_a_servlet_wrapper_when_service_call() throws Exception { + filter.doFilter(request("POST", "/context/service/call", "param=value"), null, chain); + ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(ServletRequest.class); + + verify(chain).doFilter(requestArgumentCaptor.capture(), any(ServletResponse.class)); + + assertThat(requestArgumentCaptor.getValue()).isInstanceOf(RootFilter.ServletRequestWrapper.class); + } + + @Test + public void fail_to_get_session_from_request() throws Exception { + filter.doFilter(request("GET", "/context/static/image.png", null), null, chain); + ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(ServletRequest.class); + verify(chain).doFilter(requestArgumentCaptor.capture(), any(ServletResponse.class)); + + expectedException.expect(UnsupportedOperationException.class); + ((HttpServletRequest) requestArgumentCaptor.getValue()).getSession(); + } + + @Test + public void fail_to_get_session_with_create_from_request() throws Exception { + filter.doFilter(request("GET", "/context/static/image.png", null), null, chain); + ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(ServletRequest.class); + verify(chain).doFilter(requestArgumentCaptor.capture(), any(ServletResponse.class)); + + expectedException.expect(UnsupportedOperationException.class); + ((HttpServletRequest) requestArgumentCaptor.getValue()).getSession(true); + } + private HttpServletRequest request(String method, String path, String query) { HttpServletRequest request = mock(HttpServletRequest.class); when(request.getMethod()).thenReturn(method); diff --git a/server/sonar-web/src/main/webapp/WEB-INF/web.xml b/server/sonar-web/src/main/webapp/WEB-INF/web.xml index b86c1a99b9b..e0bd48e3257 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/web.xml +++ b/server/sonar-web/src/main/webapp/WEB-INF/web.xml @@ -53,8 +53,8 @@ org.sonar.server.platform.web.SecurityServletFilter - ProfilingFilter - org.sonar.server.platform.web.ProfilingFilter + RootFilter + org.sonar.server.platform.web.RootFilter staticDirs /images,/javascripts,/stylesheets @@ -67,7 +67,7 @@ - ProfilingFilter + RootFilter /* @@ -101,14 +101,6 @@ /static/* - - - 20 - - true - - - org.sonar.server.platform.web.PlatformServletContextListener -- 2.39.5