/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.monitor; import static org.junit.Assert.*; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.Collections; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import org.junit.After; import org.junit.Before; import org.junit.Test; import junit.framework.TestCase; import static org.easymock.EasyMock.*; import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; public class MonitorFilterTest { DummyMonitorDAO dao; MonitorFilter filter; MockFilterChain chain; static final int MAX_BODY_SIZE = 10; static final int LONG_BODY_SIZE = 3*MAX_BODY_SIZE; @Before public void setUp() throws Exception { dao = new DummyMonitorDAO(); filter = new MonitorFilter(new Monitor(dao), new MonitorRequestFilter()); chain = new MockFilterChain(new HttpServlet() { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { req.getInputStream().read(new byte[LONG_BODY_SIZE]); res.getOutputStream().write(new byte[0]); } }); filter.monitor.config.props.put("maxBodySize", Integer.toString(MAX_BODY_SIZE)); // Ensure the configured property is correct for the tests } @After public void tearDown() throws Exception { } @Test public void testSimple() throws Exception { HttpServletRequest req = request("GET", "/foo/bar", "12.34.56.78", null, null); filter.doFilter(req, response(), chain); RequestData data = dao.getLast(); assertEquals("GET", data.getHttpMethod()); assertEquals("/foo/bar", data.getPath()); assertEquals("12.34.56.78", data.getRemoteAddr()); assertNull(data.getHttpReferer()); } @Test public void testWithBody() throws Exception { chain = new MockFilterChain(new HttpServlet() { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { req.getInputStream().read(new byte[LONG_BODY_SIZE]); res.getOutputStream().write("hello".getBytes()); } }); HttpServletRequest req = request("POST", "/bar/foo", "78.56.34.12", "baz", null); filter.doFilter(req, response(), chain); RequestData data = dao.getLast(); assertEquals("POST", data.getHttpMethod()); assertEquals("/bar/foo", data.getPath()); assertEquals("78.56.34.12", data.getRemoteAddr()); assertNull(data.getHttpReferer()); assertEquals(new String(data.getBody()), "baz"); assertEquals(3, data.getBodyContentLength()); assertEquals(5, data.getResponseLength()); } @Test public void testWithLongBody() throws Exception { chain = new MockFilterChain(new HttpServlet() { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { req.getInputStream().read(new byte[LONG_BODY_SIZE]); res.getOutputStream().write("hello".getBytes()); } }); StringBuilder b = new StringBuilder(); for (int i=0; i<MAX_BODY_SIZE; i++) { b.append('b'); } String wanted_body = b.toString(); for (int i=MAX_BODY_SIZE; i<LONG_BODY_SIZE; i++) { b.append('b'); } String given_body = b.toString(); HttpServletRequest req = request("POST", "/bar/foo", "78.56.34.12", given_body, null); filter.doFilter(req, response(), chain); RequestData data = dao.getLast(); assertEquals(wanted_body, new String(data.getBody())); // Should be trimmed to the maximum length assertEquals(LONG_BODY_SIZE, data.getBodyContentLength()); // Should be the full length, not the trimmed one } @Test public void testWithUnboundedBody() throws Exception { final int UNBOUNDED_BODY_SIZE= 10000; // Something really big filter.monitor.config.props.put("maxBodySize", Integer.toString(UNBOUNDED_BODY_SIZE)); // Ensure the configured property is correct for the tests chain = new MockFilterChain(new HttpServlet() { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { while(req.getInputStream().read()!=-1); // "read" the stream until the end. req.getInputStream().read(); res.getOutputStream().write("hello".getBytes()); } }); StringBuilder b = new StringBuilder(); for (int i=0; i<UNBOUNDED_BODY_SIZE; i++) { b.append(i%10); } String wanted_body = b.toString(); String given_body = b.toString(); HttpServletRequest req = request("POST", "/bar/foo", "78.56.34.12", given_body, null); filter.doFilter(req, response(), chain); RequestData data = dao.getLast(); assertEquals(wanted_body, new String(data.getBody())); // Should be trimmed to the maximum length assertEquals(UNBOUNDED_BODY_SIZE, data.getBodyContentLength()); // Should be the full length, not the trimmed one } @Test public void testReferer() throws Exception { HttpServletRequest req = request("GET", "/foo/bar", "12.34.56.78", null, "http://testhost/testpath"); filter.doFilter(req, response(), chain); RequestData data = dao.getLast(); assertEquals("GET", data.getHttpMethod()); assertEquals("/foo/bar", data.getPath()); assertEquals("12.34.56.78", data.getRemoteAddr()); assertEquals("http://testhost/testpath", data.getHttpReferer()); } @Test public void testReferrer() throws Exception { // "Referrer" was misspelled in the HTTP spec, check if it works with the "correct" // spelling. MockHttpServletRequest req = request("POST", "/bar/foo", "78.56.34.12", null, null); ((MockHttpServletRequest)req).addHeader("Referrer", "http://testhost/testpath"); filter.doFilter(req, response(), chain); RequestData data = dao.getLast(); assertEquals("POST", data.getHttpMethod()); assertEquals("/bar/foo", data.getPath()); assertEquals("78.56.34.12", data.getRemoteAddr()); assertEquals("http://testhost/testpath", data.getHttpReferer()); } @Test public void testUserRemoteUser() throws Exception { Object principal = new User("username", "", Collections.<GrantedAuthority>emptyList()); testRemoteUser(principal); } @Test public void testUserDetailsRemoteUser() throws Exception { UserDetails principal = createMock(UserDetails.class); expect(principal.getUsername()).andReturn("username"); replay(principal); testRemoteUser(principal); } private void testRemoteUser(Object principal) throws Exception { Authentication authentication = new TestingAuthenticationToken(principal, null); SecurityContextHolder.getContext().setAuthentication(authentication); HttpServletRequest req = request("POST", "/bar/foo", "78.56.34.12", null, null); filter.doFilter(req, response(), chain); RequestData data = dao.getLast(); assertEquals("username", data.getRemoteUser()); SecurityContextHolder.getContext().setAuthentication(null); } MockHttpServletRequest request(String method, String path, String remoteAddr, String body, String referer) throws UnsupportedEncodingException { MockHttpServletRequest req = new MockHttpServletRequest(); req.setMethod(method); req.setServerName("localhost"); req.setServletPath(path.substring(0, path.indexOf('/', 1))); req.setPathInfo(path.substring(path.indexOf('/', 1))); req.setRemoteAddr(remoteAddr); if(body==null) body=""; // MockHttpServletRequest#getInputStream doesn't like null bodies // and throws NullPointerException. It should probably do something useful like return an empty stream or throw // IOException. req.setContent(body.getBytes("UTF-8")); if(referer!=null) req.addHeader("Referer", referer); return req; } MockHttpServletResponse response() { MockHttpServletResponse response = new MockHttpServletResponse(); return response; } }