1   package com.atlassian.seraph.auth;
2   
3   import com.atlassian.seraph.config.ConfigurationException;
4   import com.atlassian.seraph.config.SecurityConfigFactory;
5   import com.atlassian.seraph.config.SecurityConfigImpl;
6   import com.atlassian.seraph.elevatedsecurity.CountingElevatedSecurityGuard;
7   import com.atlassian.seraph.interceptor.Interceptor;
8   import com.atlassian.seraph.interceptor.LogoutInterceptor;
9   import com.atlassian.seraph.service.rememberme.RememberMeService;
10  import com.mockobjects.dynamic.C;
11  import com.mockobjects.dynamic.Mock;
12  import com.opensymphony.user.provider.ejb.util.Base64;
13  import junit.framework.TestCase;
14  import static org.mockito.Mockito.mock;
15  import static org.mockito.Mockito.when;
16  
17  import java.security.Principal;
18  import javax.servlet.http.HttpServletRequest;
19  import javax.servlet.http.HttpServletResponse;
20  import javax.servlet.http.HttpSession;
21  
22  /**
23   * This test uses a mix of shitty old Mock objects and the new Mockito framework.
24   *
25   * Like most of the olden day Atlassian code, this test is an abomination of hacks and forced calls.
26   *
27   * I tried but failed to get it into shape and hence its still an abomination of hacks and forced calls.
28   */
29  public class TestDefaultAuthenticator extends TestCase
30  {
31      private Mock request;
32      private Mock response;
33      private SecurityConfigImpl config;
34      private StubAuthenticator authenticator;
35      private Mock session;
36      private Principal user;
37      private RememberMeService rememberMeService;
38  
39      protected void setUp() throws Exception
40      {
41          super.setUp();
42          request = new Mock(HttpServletRequest.class);
43          response = new Mock(HttpServletResponse.class);
44          session = new Mock(HttpSession.class);
45          SecurityConfigFactory.setSecurityConfig(null);
46          config = (SecurityConfigImpl) SecurityConfigFactory.getInstance("test-seraph-config.xml");
47          authenticator = (StubAuthenticator) config.getAuthenticator();
48          user = new Principal()
49          {
50              public String getName()
51              {
52                  return "Test User";
53              }
54          };
55  
56          rememberMeService = mock(RememberMeService.class);
57          authenticator.setRememberMeService(rememberMeService);
58  
59          authenticator.addUser(user.getName(),user);
60      }
61  
62      public void testLogout() throws ConfigurationException, AuthenticatorException
63      {
64          // mocks
65          final Mock logoutInterceptor = new Mock(LogoutInterceptor.class);
66  
67          // expectations
68          request.expectAndReturn("getSession", session.proxy());
69          request.expectAndReturn("getSession", session.proxy());
70          request.expectAndReturn("getSession", session.proxy());
71  
72          mockOutLoginReason(LoginReason.OUT);
73  
74          session.expect("setAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_IN_KEY), C.IS_NULL));
75          session.expect("setAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_OUT_KEY), C.eq(Boolean.TRUE)));
76  
77          logoutInterceptor.expect("beforeLogout", C.ANY_ARGS);
78          logoutInterceptor.expect("afterLogout", C.ANY_ARGS);
79          config.addInterceptor((Interceptor) logoutInterceptor.proxy());
80  
81          // do test!
82          authenticator.logout((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
83  
84          // verify everything
85          request.verify();
86          response.verify();
87          session.verify();
88          logoutInterceptor.verify();
89      }
90  
91      public void testGetUserChecksSessionFirst()
92      {
93  
94          request.matchAndReturn("getSession", C.ANY_ARGS, session.proxy());
95          session.matchAndReturn("getAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_OUT_KEY)), null);
96          session.matchAndReturn("getAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_IN_KEY)), user);
97  
98          mockOutLoginReason(LoginReason.OK);
99  
100         final Principal result = authenticator.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
101         assertEquals(user, result);
102 
103         request.verify();
104         response.verify();
105         session.verify();
106     }
107 
108     private StubAuthenticator setupRememberMeCookieState(LoginReason loginReason)
109     {
110         request.matchAndReturn("getSession", C.IS_FALSE, null); // no session during initial check
111         request.matchAndReturn("getSession", C.NO_ARGS, session.proxy()); // session "created" when cookie login() gets it
112 
113         mockOutLoginReason(loginReason);
114         
115         session.matchAndReturn("getAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_OUT_KEY)), null);
116         session.matchAndReturn("getAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_IN_KEY)), user);
117         return (StubAuthenticator) authenticator;
118     }
119 
120     public void testGetUserChecksCookieIfNoSession()
121     {
122         setupRememberMeCookieState(LoginReason.OK);
123         
124         final HttpServletRequest httpServletRequest = (HttpServletRequest) request.proxy();
125         final HttpServletResponse httpServletResponse = (HttpServletResponse) response.proxy();
126         
127         when(rememberMeService.getRememberMeCookieAuthenticatedUsername(httpServletRequest,httpServletResponse)).thenReturn(user.getName());
128 
129         final Principal result = authenticator.getUser(httpServletRequest, httpServletResponse);
130         assertEquals(user, result);
131 
132         request.verify();
133         response.verify();
134         session.verify();
135     }
136 
137     public void testGetUserChecksCookieIfNoSession_ButFailedAuthorisation()
138     {
139         authenticator.setDesiredLoginAnswer(false);
140         setupRememberMeCookieState(LoginReason.AUTHORISATION_FAILED);
141 
142         // basic auth checks happen now if we fail
143         request.expectAndReturn("getQueryString", "p=v");
144         request.expectAndReturn("getHeader", "Authorization", null);
145 
146         final HttpServletRequest httpServletRequest = (HttpServletRequest) request.proxy();
147         final HttpServletResponse httpServletResponse = (HttpServletResponse) response.proxy();
148 
149         when(rememberMeService.getRememberMeCookieAuthenticatedUsername(httpServletRequest,httpServletResponse)).thenReturn(user.getName());
150 
151         final Principal result = authenticator.getUser(httpServletRequest, httpServletResponse);
152         assertNull(result);
153 
154         request.verify();
155         response.verify();
156         session.verify();
157     }
158 
159 
160 
161     public void testGetUserReturnsNullWithNoValidAuthentication()
162     {
163         request.matchAndReturn("getSession", C.IS_FALSE, null);
164         request.expectAndReturn("getQueryString", null); // no basic auth
165         request.expectAndReturn("getHeader", C.args(C.eq("Authorization")), null);
166 
167         final Principal result = authenticator.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
168         assertEquals(null, result);
169 
170         request.verify();
171         response.verify();
172         session.verify();
173     }
174 
175     public void testGetUserBasicAuthRequiredButMissing()
176     {
177         request.matchAndReturn("getSession", C.IS_FALSE, null);
178         request.matchAndReturn("getCookies", null);
179         request.matchAndReturn("getQueryString", "os_authType=basic"); // basic auth
180 
181         // no Authorization header
182         request.expectAndReturn("getHeader", C.eq("Authorization"), null);
183         // so we expect a 401 Authentication required
184         response.expect("setStatus", C.eq(401));
185         response.expect("setHeader", C.eq("WWW-Authenticate", "BASIC realm=\"protected-area\""));
186 
187         final Principal result = authenticator.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
188         assertEquals(null, result);
189 
190         request.verify();
191         response.verify();
192         session.verify();
193     }
194 
195     private StubAuthenticator setupBasicAuthHttpState(LoginReason loginReason)
196     {
197         request.matchAndReturn("getSession", C.IS_FALSE, null);
198         request.matchAndReturn("getCookies", null);
199         request.matchAndReturn("getQueryString", "os_authType=basic"); // basic auth
200 
201         mockOutLoginReason(loginReason);
202         
203         // valid Authorization header
204         final String token = new String(Base64.encode("username:password".getBytes()));
205         request.expectAndReturn("getHeader", C.eq("Authorization"), "Basic " + token);
206         // add the correct user to the stub
207         final StubAuthenticator stubAuthenticator = (StubAuthenticator) authenticator;
208         stubAuthenticator.addUser("username", user);
209         return stubAuthenticator;
210     }
211 
212     public void testGetUserBasicAuthProvidedUsingRequestParameter()
213     {
214         setupBasicAuthHttpState(LoginReason.OK);
215 
216         final Principal result = authenticator.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
217         assertEquals(user, result);
218 
219         request.verify();
220         response.verify();
221         session.verify();
222     }
223 
224     public void testGetUserBasicAuthProvidedUsingRequestParameter_ButFailsElevatedSecurity() throws ConfigurationException
225     {
226         final StubAuthenticator stubAuthenticator = setupBasicAuthHttpState(LoginReason.AUTHENTICATION_DENIED);
227 
228         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(false,"username");
229         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
230 
231         // when we fail we send this back
232         response.expect("sendError", C.args(C.eq(401), C.eq("Basic Authentication Failure - Reason : "  + LoginReason.AUTHENTICATION_DENIED)));
233 
234         final Principal result = authenticator.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
235         assertNull(result);
236 
237         assertEquals(0, securityGuard.getSuccessCount());
238         assertEquals(1, securityGuard.getFailedCount());
239 
240         request.verify();
241         response.verify();
242         session.verify();
243     }
244 
245     public void testGetUserBasicAuthProvidedUsingRequestParameter_PassesElevatedSecurity_ButFailsLogin() throws ConfigurationException
246     {
247         final StubAuthenticator stubAuthenticator = setupBasicAuthHttpState(LoginReason.AUTHENTICATED_FAILED);
248         stubAuthenticator.setDesiredLoginAnswer(false);
249 
250         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true,"username");
251         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
252 
253         // when we fail we send this back
254         response.expect("sendError", C.args(C.eq(401), C.eq("Basic Authentication Failure - Reason : "  + LoginReason.AUTHENTICATED_FAILED)));
255         
256         final Principal result = authenticator.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
257         assertNull(result);
258 
259         assertEquals(0, securityGuard.getSuccessCount());
260         assertEquals(1, securityGuard.getFailedCount());
261 
262         request.verify();
263         response.verify();
264         session.verify();
265     }
266 
267     public void testGetUserBasicAuthProvidedUsingRequestParameter_PassesElevatedSecurity_AndPassesLogin() throws ConfigurationException
268     {
269         final StubAuthenticator stubAuthenticator = setupBasicAuthHttpState(LoginReason.OK);
270         stubAuthenticator.setDesiredLoginAnswer(true);
271 
272         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true,"username");
273         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
274 
275         final Principal result = authenticator.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
276         assertEquals(user, result);
277 
278         assertEquals(1, securityGuard.getSuccessCount());
279         assertEquals(0, securityGuard.getFailedCount());
280 
281         request.verify();
282         response.verify();
283         session.verify();
284     }
285 
286     public void testGetUserBasicAuthProvidedUsingHeader()
287     {
288         final String authorizationHeader = "Basic " + new String(Base64.encode("username:password".getBytes()));
289         
290         request.matchAndReturn("getSession", C.IS_FALSE, null);
291         request.matchAndReturn("getCookies", null);
292         request.matchAndReturn("getQueryString", null); // no basic auth
293 
294         request.expectAndReturn("getHeader", C.eq("Authorization"), authorizationHeader);
295         request.expectAndReturn("getHeader", C.eq("Authorization"), authorizationHeader);
296 
297         mockOutLoginReason(LoginReason.OK);
298 
299         // add the correct user to the stub
300         final StubAuthenticator stubAuthenticator = (StubAuthenticator) authenticator;
301         stubAuthenticator.addUser("username", user);
302 
303         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true,"username");
304         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
305         
306         final Principal result = authenticator.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
307         assertEquals(user, result);
308 
309         assertEquals(1, securityGuard.getSuccessCount());
310         assertEquals(0, securityGuard.getFailedCount());
311         
312         request.verify();
313         response.verify();
314         session.verify();
315     }
316 
317     private void mockOutLoginReason(LoginReason loginReason)
318     {
319         request.expectAndReturn("getAttribute", C.args(C.eq("com.atlassian.seraph.auth.LoginReason")), null);
320         request.expect("setAttribute", C.args(C.eq("com.atlassian.seraph.auth.LoginReason"), C.eq(loginReason)));
321 
322         response.expect("addHeader", C.ANY_ARGS);
323     }
324 }