1   package com.atlassian.seraph.filter;
2   
3   import com.atlassian.seraph.config.SecurityConfig;
4   import com.atlassian.seraph.util.LocalMockHttpServletRequest;
5   import com.mockobjects.dynamic.Mock;
6   import com.mockobjects.servlet.MockHttpServletResponse;
7   import junit.framework.TestCase;
8   
9   import java.io.IOException;
10  import java.util.Collections;
11  import javax.servlet.FilterChain;
12  import javax.servlet.ServletException;
13  import javax.servlet.ServletRequest;
14  import javax.servlet.ServletResponse;
15  import javax.servlet.http.HttpServletRequest;
16  import javax.servlet.http.HttpServletResponse;
17  import javax.servlet.http.HttpSession;
18  
19  public class TestBaseLoginFilter extends TestCase
20  {
21      private SecurityConfig securityConfig;
22      private CountingBaseLoginFilter baseLoginFilter;
23  
24      @Override
25      protected void setUp() throws Exception
26      {
27          baseLoginFilter = new CountingBaseLoginFilter();
28      }
29  
30      public void testRedirectToOriginalDestinationNoURL() throws Exception
31      {
32          // Build up the mock HttpServletRequest.
33          final LocalMockHttpServletRequest request = new LocalMockHttpServletRequest();
34          final HttpSession session = new MockSession();
35          request.setSession(session);
36          request.setupGetContextPath("/jira");
37          request.setupAddParameter("os_destination", (String) null);
38  
39          // Build up the mock response.
40          final Mock mockHttpServletResponse = new Mock(HttpServletResponse.class);
41          // expect not to be called
42  
43          assertFalse(baseLoginFilter.redirectToOriginalDestination(request, (HttpServletResponse) mockHttpServletResponse.proxy()));
44  
45          mockHttpServletResponse.verify();
46      }
47  
48      public void testRedirectToOriginalDestinationViaSession() throws Exception
49      {
50          final LocalMockHttpServletRequest request = new LocalMockHttpServletRequest();
51          // Set a redirect URL in the session
52          final HttpSession session = new MockSession();
53          session.setAttribute("seraph_originalurl", "/Stuff");
54          request.setSession(session);
55          request.setupGetContextPath("/jira");
56  
57          // Build up the mock response.
58          final Mock mockHttpServletResponse = new Mock(HttpServletResponse.class);
59          // expect to be able to redirect to given URL
60          mockHttpServletResponse.expect("sendRedirect", "/jira/Stuff");
61  
62          assertTrue(baseLoginFilter.redirectToOriginalDestination(request, (HttpServletResponse) mockHttpServletResponse.proxy()));
63  
64          mockHttpServletResponse.verify();
65      }
66  
67      public void testRedirectToOriginalDestinationViaRequestParameter() throws Exception
68      {
69          final LocalMockHttpServletRequest request = new LocalMockHttpServletRequest();
70          // No redirect URL in the session
71          request.setSession(new MockSession());
72          request.setupScheme("http");
73          request.setupServerName("example.com");
74          request.setupPort(80);
75          request.setupGetContextPath("/jira");
76          request.setupAddParameter("os_destination", "http://example.com/jira/Stuff");
77  
78          // Build up the mock response.
79          final Mock mockHttpServletResponse = new Mock(HttpServletResponse.class);
80          // expect to be able to redirect to given URL
81          mockHttpServletResponse.expect("sendRedirect", "http://example.com/jira/Stuff");
82  
83          assertTrue(baseLoginFilter.redirectToOriginalDestination(request, (HttpServletResponse) mockHttpServletResponse.proxy()));
84  
85          mockHttpServletResponse.verify();
86      }
87  
88      public void testRedirectToOriginalDestinationViaBoth() throws Exception
89      {
90          final LocalMockHttpServletRequest request = new LocalMockHttpServletRequest();
91          // No redirect URL in the session
92          request.setSession(new MockSession());
93          request.setupScheme("http");
94          request.setupServerName("example.com");
95          request.setupGetContextPath("/jira");
96          request.setupAddParameter("os_destination", "http://evil.com/jira/Stuff");
97  
98          // Build up the mock response.
99          final Mock mockHttpServletResponse = new Mock(HttpServletResponse.class);
100         // expect to be NOT able to redirect to given URL (security violation) - we send you to the home of the context instead.
101         mockHttpServletResponse.expect("sendRedirect", "/jira/");
102 
103         assertTrue(baseLoginFilter.redirectToOriginalDestination(request, (HttpServletResponse) mockHttpServletResponse.proxy()));
104 
105         mockHttpServletResponse.verify();
106     }
107 
108     public void testSecurityWrappingAndLoginCalled() throws IOException, ServletException
109     {
110         securityConfig = new MockSecurityConfig(null, null, null, Collections.EMPTY_LIST);
111 
112         final LocalMockHttpServletRequest request = new LocalMockHttpServletRequest();
113         request.setupGetAttribute(null);
114         request.setupGetParameterMap(Collections.EMPTY_MAP);
115 
116         final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
117 
118         FilterChain filterChain = new FilterChain()
119         {
120             int filterCount = 0;
121 
122             public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse)
123                     throws IOException, ServletException
124             {
125                 // at this stage the servlet request should be a security wrapped HttpServletRequest
126                 // see SER-142
127                 assertTrue(servletRequest instanceof BaseLoginFilter.SecurityHttpRequestWrapper);
128                 filterCount++;
129                 assertEquals("We must not call the FilterChain more than once", 1, filterCount);
130             }
131         };
132 
133         baseLoginFilter.doFilter(request, mockHttpServletResponse, filterChain);
134         assertEquals(1, baseLoginFilter.getLoginCount());
135     }
136 
137     public void testAlreadyFilterNeverCallsLogin() throws IOException, ServletException
138     {
139         final LocalMockHttpServletRequest request = new LocalMockHttpServletRequest();
140         request.setAttribute(BaseLoginFilter.ALREADY_FILTERED, true);   // cause ALREADY_FILTERED to be true
141 
142 
143         final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
144         FilterChain filterChain = new FilterChain()
145         {
146             int filterCount = 0;
147 
148             public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse)
149                     throws IOException, ServletException
150             {
151                 // at this stage the servlet request should be a security wrapped HttpServletRequest
152                 // see SER-142
153                 assertTrue(servletRequest instanceof BaseLoginFilter.SecurityHttpRequestWrapper);
154                 filterCount++;
155                 assertEquals("We must not call the FilterChain more than once", 1, filterCount);
156             }
157         };
158 
159         baseLoginFilter.doFilter(request, mockHttpServletResponse, filterChain);
160         assertEquals(0, baseLoginFilter.getLoginCount());
161     }
162 
163 
164     class CountingBaseLoginFilter extends BaseLoginFilter
165     {
166         private int loginCount = 0;
167         private final String loginStatus;
168 
169         public CountingBaseLoginFilter()
170         {
171             this(LOGIN_NOATTEMPT);
172         }
173 
174         public CountingBaseLoginFilter(final String loginStatus)
175         {
176             this.loginStatus = loginStatus;
177         }
178 
179 
180         /**
181          * We have to override this because BaseLoginFilter is abstract on this method
182          *
183          * @param request  the HTTP request in play
184          * @param response the HTTP response in play
185          *
186          * @return the state of the login
187          */
188         @Override
189         public String login(final HttpServletRequest request, final HttpServletResponse response)
190         {
191             loginCount++;
192             return loginStatus;
193 
194         }
195 
196         @Override
197         protected SecurityConfig getSecurityConfig()
198         {
199             if (securityConfig == null)
200             {
201                 securityConfig = new MockSecurityConfig();
202             }
203             return securityConfig;
204         }
205 
206         public int getLoginCount()
207         {
208             return loginCount;
209         }
210     }
211 }