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