Author: markt Date: Sun Apr 4 10:37:17 2010 New Revision: 930659 URL: http://svn.apache.org/viewvc?rev=930659&view=rev Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=49014 Add test cases for the RemoteIpFilter Patch provided by Cyrille Le Clerc
Added: tomcat/trunk/test/org/apache/catalina/filters/ tomcat/trunk/test/org/apache/catalina/filters/TestRemoteIpFilter.java (with props) Added: tomcat/trunk/test/org/apache/catalina/filters/TestRemoteIpFilter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/filters/TestRemoteIpFilter.java?rev=930659&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/catalina/filters/TestRemoteIpFilter.java (added) +++ tomcat/trunk/test/org/apache/catalina/filters/TestRemoteIpFilter.java Sun Apr 4 10:37:17 2010 @@ -0,0 +1,529 @@ +/* + * 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.catalina.filters; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.deploy.FilterDef; +import org.apache.catalina.deploy.FilterMap; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; + +import junit.framework.Assert; + +public class TestRemoteIpFilter extends TomcatBaseTest { + + /** + * Mock {...@link FilterChain} to keep a handle on the passed + * {...@link ServletRequest}. + */ + public static class MockFilterChain implements FilterChain { + private HttpServletRequest request; + + @Override + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + this.request = (HttpServletRequest) request; + } + + public HttpServletRequest getRequest() { + return request; + } + } + + public static class MockHttpServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + HttpServletRequest request; + + public HttpServletRequest getRequest() { + return request; + } + + @Override + public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + this.request = request; + PrintWriter writer = response.getWriter(); + + writer.println("request.remoteAddr=" + request.getRemoteAddr()); + writer.println("request.remoteHost=" + request.getRemoteHost()); + writer.println("request.secure=" + request.isSecure()); + writer.println("request.scheme=" + request.getScheme()); + writer.println("request.serverPort=" + request.getServerPort()); + + writer.println(); + for (Enumeration<String> headers = request.getHeaderNames(); headers.hasMoreElements();) { + String name = headers.nextElement().toString(); + writer.println("request.header['" + name + "']=" + Collections.list(request.getHeaders(name))); + } + } + } + + /** + * Enhanced {...@link Request} to ease testing. + */ + public static class MockHttpServletRequest extends Request { + public MockHttpServletRequest() { + super(); + setCoyoteRequest(new org.apache.coyote.Request()); + setConnector(new Connector()); + } + + public void setHeader(String name, String value) { + getCoyoteRequest().getMimeHeaders().setValue(name).setString(value); + } + + public void setScheme(String scheme) { + getCoyoteRequest().scheme().setString(scheme); + } + } + + public static final String TEMP_DIR = System.getProperty("java.io.tmpdir"); + + public void testCommaDelimitedListToStringArray() { + List<String> elements = Arrays.asList("element1", "element2", "element3"); + String actual = RemoteIpFilter.listToCommaDelimitedString(elements); + assertEquals("element1, element2, element3", actual); + } + + public void testCommaDelimitedListToStringArrayEmptyList() { + List<String> elements = new ArrayList<String>(); + String actual = RemoteIpFilter.listToCommaDelimitedString(elements); + assertEquals("", actual); + } + + public void testCommaDelimitedListToStringArrayNullList() { + String actual = RemoteIpFilter.listToCommaDelimitedString(null); + assertEquals("", actual); + } + + public void testHeaderNamesCaseInsensitivity() { + RemoteIpFilter.XForwardedRequest request = new RemoteIpFilter.XForwardedRequest(new MockHttpServletRequest()); + request.setHeader("myheader", "lower Case"); + request.setHeader("MYHEADER", "UPPER CASE"); + request.setHeader("MyHeader", "Camel Case"); + assertEquals(1, request.headers.size()); + assertEquals("Camel Case", request.getHeader("myheader")); + } + + public void testIncomingRequestIsSecuredButProtocolHeaderSaysItIsNotWithCustomValues() throws Exception { + // PREPARE + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("protocolHeader", "x-forwarded-proto"); + filterDef.addInitParameter("remoteIPHeader", "x-my-forwarded-for"); + filterDef.addInitParameter("httpServerPort", "8080"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRemoteAddr("192.168.0.10"); + request.setSecure(true); + request.setScheme("https"); + request.setHeader("x-my-forwarded-for", "140.211.11.130"); + request.setHeader("x-forwarded-proto", "http"); + + // TEST + HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request); + + // VERIFY + boolean actualSecure = actualRequest.isSecure(); + assertEquals("request must be unsecured as header x-forwarded-proto said it is http", false, actualSecure); + + String actualScheme = actualRequest.getScheme(); + assertEquals("scheme must be http as header x-forwarded-proto said it is http", "http", actualScheme); + + int actualServerPort = actualRequest.getServerPort(); + assertEquals("wrong http server port", 8080, actualServerPort); + + String actualRemoteAddr = actualRequest.getRemoteAddr(); + assertEquals("remoteAddr", "140.211.11.130", actualRemoteAddr); + + String actualRemoteHost = actualRequest.getRemoteHost(); + assertEquals("remoteHost", "140.211.11.130", actualRemoteHost); + } + + public void testIncomingRequestIsSecuredButProtocolHeaderSaysItIsNotWithDefaultValues() throws Exception { + // PREPARE + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("protocolHeader", "x-forwarded-proto"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRemoteAddr("192.168.0.10"); + request.setSecure(true); + request.setScheme("https"); + request.setHeader("x-forwarded-for", "140.211.11.130"); + request.setHeader("x-forwarded-proto", "http"); + + // TEST + HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request); + + // VERIFY + boolean actualSecure = actualRequest.isSecure(); + assertEquals("request must be unsecured as header x-forwarded-proto said it is http", false, actualSecure); + + String actualScheme = actualRequest.getScheme(); + assertEquals("scheme must be http as header x-forwarded-proto said it is http", "http", actualScheme); + + String actualRemoteAddr = actualRequest.getRemoteAddr(); + assertEquals("remoteAddr", "140.211.11.130", actualRemoteAddr); + + String actualRemoteHost = actualRequest.getRemoteHost(); + assertEquals("remoteHost", "140.211.11.130", actualRemoteHost); + + } + + public void testInvokeAllowedRemoteAddrWithNullRemoteIpHeader() throws Exception { + // PREPARE + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("internalProxies", "192\\.168\\.0\\.10, 192\\.168\\.0\\.11"); + filterDef.addInitParameter("trustedProxies", "proxy1, proxy2, proxy3"); + filterDef.addInitParameter("remoteIPHeader", "x-forwarded-for"); + filterDef.addInitParameter("proxiesHeader", "x-forwarded-by"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRemoteAddr("192.168.0.10"); + request.setRemoteHost("remote-host-original-value"); + + // TEST + HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request); + + // VERIFY + String actualXForwardedFor = request.getHeader("x-forwarded-for"); + assertNull("x-forwarded-for must be null", actualXForwardedFor); + + String actualXForwardedBy = request.getHeader("x-forwarded-by"); + assertNull("x-forwarded-by must be null", actualXForwardedBy); + + String actualRemoteAddr = actualRequest.getRemoteAddr(); + assertEquals("remoteAddr", "192.168.0.10", actualRemoteAddr); + + String actualRemoteHost = actualRequest.getRemoteHost(); + assertEquals("remoteHost", "remote-host-original-value", actualRemoteHost); + + } + + public void testInvokeAllProxiesAreInternal() throws Exception { + + // PREPARE + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("internalProxies", "192\\.168\\.0\\.10, 192\\.168\\.0\\.11"); + filterDef.addInitParameter("trustedProxies", "proxy1, proxy2, proxy3"); + filterDef.addInitParameter("remoteIPHeader", "x-forwarded-for"); + filterDef.addInitParameter("proxiesHeader", "x-forwarded-by"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRemoteAddr("192.168.0.10"); + request.setRemoteHost("remote-host-original-value"); + request.setHeader("x-forwarded-for", "140.211.11.130, 192.168.0.10, 192.168.0.11"); + + // TEST + HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request); + + // VERIFY + String actualXForwardedFor = actualRequest.getHeader("x-forwarded-for"); + assertNull("all proxies are internal, x-forwarded-for must be null", actualXForwardedFor); + + String actualXForwardedBy = actualRequest.getHeader("x-forwarded-by"); + assertNull("all proxies are internal, x-forwarded-by must be null", actualXForwardedBy); + + String actualRemoteAddr = actualRequest.getRemoteAddr(); + assertEquals("remoteAddr", "140.211.11.130", actualRemoteAddr); + + String actualRemoteHost = actualRequest.getRemoteHost(); + assertEquals("remoteHost", "140.211.11.130", actualRemoteHost); + } + + public void testInvokeAllProxiesAreTrusted() throws Exception { + + // PREPARE + RemoteIpFilter remoteIpFilter = new RemoteIpFilter(); + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("internalProxies", "192\\.168\\.0\\.10, 192\\.168\\.0\\.11"); + filterDef.addInitParameter("trustedProxies", "proxy1, proxy2, proxy3"); + filterDef.addInitParameter("remoteIPHeader", "x-forwarded-for"); + filterDef.addInitParameter("proxiesHeader", "x-forwarded-by"); + + filterDef.setFilter(remoteIpFilter); + MockHttpServletRequest request = new MockHttpServletRequest(); + + request.setRemoteAddr("192.168.0.10"); + request.setRemoteHost("remote-host-original-value"); + request.setHeader("x-forwarded-for", "140.211.11.130, proxy1, proxy2"); + + // TEST + HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request); + + // VERIFY + String actualXForwardedFor = actualRequest.getHeader("x-forwarded-for"); + assertNull("all proxies are trusted, x-forwarded-for must be null", actualXForwardedFor); + + String actualXForwardedBy = actualRequest.getHeader("x-forwarded-by"); + assertEquals("all proxies are trusted, they must appear in x-forwarded-by", "proxy1, proxy2", actualXForwardedBy); + + String actualRemoteAddr = actualRequest.getRemoteAddr(); + assertEquals("remoteAddr", "140.211.11.130", actualRemoteAddr); + + String actualRemoteHost = actualRequest.getRemoteHost(); + assertEquals("remoteHost", "140.211.11.130", actualRemoteHost); + } + + public void testInvokeAllProxiesAreTrustedAndRemoteAddrMatchRegexp() throws Exception { + + // PREPARE + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("internalProxies", "127\\.0\\.0\\.1, 192\\.168\\..*, another-internal-proxy"); + filterDef.addInitParameter("trustedProxies", "proxy1, proxy2, proxy3"); + filterDef.addInitParameter("remoteIPHeader", "x-forwarded-for"); + filterDef.addInitParameter("proxiesHeader", "x-forwarded-by"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRemoteAddr("192.168.0.10"); + request.setRemoteHost("remote-host-original-value"); + request.setHeader("x-forwarded-for", "140.211.11.130, proxy1, proxy2"); + + // TEST + HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request); + + // VERIFY + String actualXForwardedFor = actualRequest.getHeader("x-forwarded-for"); + assertNull("all proxies are trusted, x-forwarded-for must be null", actualXForwardedFor); + + String actualXForwardedBy = actualRequest.getHeader("x-forwarded-by"); + assertEquals("all proxies are trusted, they must appear in x-forwarded-by", "proxy1, proxy2", actualXForwardedBy); + + String actualRemoteAddr = actualRequest.getRemoteAddr(); + assertEquals("remoteAddr", "140.211.11.130", actualRemoteAddr); + + String actualRemoteHost = actualRequest.getRemoteHost(); + assertEquals("remoteHost", "140.211.11.130", actualRemoteHost); + } + + public void testInvokeAllProxiesAreTrustedOrInternal() throws Exception { + + // PREPARE + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("internalProxies", "192\\.168\\.0\\.10, 192\\.168\\.0\\.11"); + filterDef.addInitParameter("trustedProxies", "proxy1, proxy2, proxy3"); + filterDef.addInitParameter("remoteIPHeader", "x-forwarded-for"); + filterDef.addInitParameter("proxiesHeader", "x-forwarded-by"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + + request.setRemoteAddr("192.168.0.10"); + request.setRemoteHost("remote-host-original-value"); + request.setHeader("x-forwarded-for", "140.211.11.130, proxy1, proxy2, 192.168.0.10, 192.168.0.11"); + + // TEST + HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request); + + // VERIFY + String actualXForwardedFor = actualRequest.getHeader("x-forwarded-for"); + assertNull("all proxies are trusted, x-forwarded-for must be null", actualXForwardedFor); + + String actualXForwardedBy = actualRequest.getHeader("x-forwarded-by"); + assertEquals("all proxies are trusted, they must appear in x-forwarded-by", "proxy1, proxy2", actualXForwardedBy); + + String actualRemoteAddr = actualRequest.getRemoteAddr(); + assertEquals("remoteAddr", "140.211.11.130", actualRemoteAddr); + + String actualRemoteHost = actualRequest.getRemoteHost(); + assertEquals("remoteHost", "140.211.11.130", actualRemoteHost); + } + + public void testInvokeNotAllowedRemoteAddr() throws Exception { + // PREPARE + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("internalProxies", "192\\.168\\.0\\.10, 192\\.168\\.0\\.11"); + filterDef.addInitParameter("trustedProxies", "proxy1, proxy2, proxy3"); + filterDef.addInitParameter("remoteIPHeader", "x-forwarded-for"); + filterDef.addInitParameter("proxiesHeader", "x-forwarded-by"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + + request.setRemoteAddr("not-allowed-internal-proxy"); + request.setRemoteHost("not-allowed-internal-proxy-host"); + request.setHeader("x-forwarded-for", "140.211.11.130, proxy1, proxy2"); + + // TEST + HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request); + + // VERIFY + String actualXForwardedFor = actualRequest.getHeader("x-forwarded-for"); + assertEquals("x-forwarded-for must be unchanged", "140.211.11.130, proxy1, proxy2", actualXForwardedFor); + + String actualXForwardedBy = actualRequest.getHeader("x-forwarded-by"); + assertNull("x-forwarded-by must be null", actualXForwardedBy); + + String actualRemoteAddr = actualRequest.getRemoteAddr(); + assertEquals("remoteAddr", "not-allowed-internal-proxy", actualRemoteAddr); + + String actualRemoteHost = actualRequest.getRemoteHost(); + assertEquals("remoteHost", "not-allowed-internal-proxy-host", actualRemoteHost); + } + + public void testInvokeUntrustedProxyInTheChain() throws Exception { + // PREPARE + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("internalProxies", "192\\.168\\.0\\.10, 192\\.168\\.0\\.11"); + filterDef.addInitParameter("trustedProxies", "proxy1, proxy2, proxy3"); + filterDef.addInitParameter("remoteIPHeader", "x-forwarded-for"); + filterDef.addInitParameter("proxiesHeader", "x-forwarded-by"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + + request.setRemoteAddr("192.168.0.10"); + request.setRemoteHost("remote-host-original-value"); + request.setHeader("x-forwarded-for", "140.211.11.130, proxy1, untrusted-proxy, proxy2"); + + // TEST + HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request); + + // VERIFY + String actualXForwardedFor = actualRequest.getHeader("x-forwarded-for"); + assertEquals("ip/host before untrusted-proxy must appear in x-forwarded-for", "140.211.11.130, proxy1", actualXForwardedFor); + + String actualXForwardedBy = actualRequest.getHeader("x-forwarded-by"); + assertEquals("ip/host after untrusted-proxy must appear in x-forwarded-by", "proxy2", actualXForwardedBy); + + String actualRemoteAddr = actualRequest.getRemoteAddr(); + assertEquals("remoteAddr", "untrusted-proxy", actualRemoteAddr); + + String actualRemoteHost = actualRequest.getRemoteHost(); + assertEquals("remoteHost", "untrusted-proxy", actualRemoteHost); + } + + public void testListToCommaDelimitedString() { + String[] actual = RemoteIpFilter.commaDelimitedListToStringArray("element1, element2, element3"); + String[] expected = new String[] { "element1", "element2", "element3" }; + assertEquals(expected.length, actual.length); + for (int i = 0; i < actual.length; i++) { + assertEquals(expected[i], actual[i]); + } + } + + public void testListToCommaDelimitedStringMixedSpaceChars() { + String[] actual = RemoteIpFilter.commaDelimitedListToStringArray("element1 , element2,\t element3"); + String[] expected = new String[] { "element1", "element2", "element3" }; + assertEquals(expected.length, actual.length); + for (int i = 0; i < actual.length; i++) { + assertEquals(expected[i], actual[i]); + } + } + + private HttpServletRequest testRemoteIpFilter(FilterDef filterDef, Request request) throws LifecycleException, IOException, + ServletException { + Tomcat tomcat = getTomcatInstance(); + Context root = tomcat.addContext("", TEMP_DIR); + + RemoteIpFilter remoteIpFilter = new RemoteIpFilter(); + filterDef.setFilterClass(RemoteIpFilter.class.getName()); + filterDef.setFilter(remoteIpFilter); + filterDef.setFilterName(RemoteIpFilter.class.getName()); + root.addFilterDef(filterDef); + + FilterMap filterMap = new FilterMap(); + filterMap.setFilterName(RemoteIpFilter.class.getName()); + filterMap.addURLPattern("*"); + root.addFilterMap(filterMap); + + getTomcatInstance().start(); + + MockFilterChain filterChain = new MockFilterChain(); + + // TEST + remoteIpFilter.doFilter(request, new Response(), filterChain); + return filterChain.getRequest(); + } + + /** + * Test {...@link RemoteIpFilter} in Tomcat standalone server + */ + public void testWithTomcatServer() throws Exception { + + // mostly default configuration : enable "x-forwarded-proto" + Map<String, String> remoteIpFilterParameter = new HashMap<String, String>(); + remoteIpFilterParameter.put("protocolHeader", "x-forwarded-proto"); + + // SETUP + Tomcat tomcat = getTomcatInstance(); + Context root = tomcat.addContext("", TEMP_DIR); + + FilterDef filterDef = new FilterDef(); + filterDef.getParameterMap().putAll(remoteIpFilterParameter); + filterDef.setFilterClass(RemoteIpFilter.class.getName()); + filterDef.setFilterName(RemoteIpFilter.class.getName()); + + root.addFilterDef(filterDef); + + FilterMap filterMap = new FilterMap(); + filterMap.setFilterName(RemoteIpFilter.class.getName()); + filterMap.addURLPattern("*"); + root.addFilterMap(filterMap); + + MockHttpServlet mockServlet = new MockHttpServlet(); + + Tomcat.addServlet(root, mockServlet.getClass().getName(), mockServlet); + root.addServletMapping("/test", mockServlet.getClass().getName()); + + getTomcatInstance().start(); + + // TEST + HttpURLConnection httpURLConnection = (HttpURLConnection) new URL("http://localhost:" + tomcat.getConnector().getPort() + "/test") + .openConnection(); + String expectedRemoteAddr = "my-remote-addr"; + httpURLConnection.addRequestProperty("x-forwarded-for", expectedRemoteAddr); + httpURLConnection.addRequestProperty("x-forwarded-proto", "https"); + + // VALIDATE + + Assert.assertEquals(HttpURLConnection.HTTP_OK, httpURLConnection.getResponseCode()); + HttpServletRequest request = mockServlet.getRequest(); + Assert.assertNotNull(request); + + // VALIDATE X-FOWARDED-FOR + Assert.assertEquals(expectedRemoteAddr, request.getRemoteAddr()); + Assert.assertEquals(expectedRemoteAddr, request.getRemoteHost()); + + // VALIDATE X-FORWARDED-PROTO + Assert.assertTrue(request.isSecure()); + Assert.assertEquals("https", request.getScheme()); + Assert.assertEquals(443, request.getServerPort()); + + } +} Propchange: tomcat/trunk/test/org/apache/catalina/filters/TestRemoteIpFilter.java ------------------------------------------------------------------------------ svn:eol-style = native --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org