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.cookie.InsecureCookieEncoder;
7   import com.atlassian.seraph.elevatedsecurity.CountingElevatedSecurityGuard;
8   import com.atlassian.seraph.interceptor.Interceptor;
9   import com.atlassian.seraph.interceptor.LogoutInterceptor;
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  
15  import java.security.Principal;
16  import javax.servlet.http.Cookie;
17  import javax.servlet.http.HttpServletRequest;
18  import javax.servlet.http.HttpServletResponse;
19  import javax.servlet.http.HttpSession;
20  
21  public class TestDefaultAuthenticator extends TestCase
22  {
23      private Mock request;
24      private Mock response;
25      private SecurityConfigImpl config;
26      private Authenticator auth;
27      private Mock session;
28      private Principal user;
29  
30      protected void setUp() throws Exception
31      {
32          super.setUp();
33          request = new Mock(HttpServletRequest.class);
34          response = new Mock(HttpServletResponse.class);
35          session = new Mock(HttpSession.class);
36          SecurityConfigFactory.setSecurityConfig(null);
37          config = (SecurityConfigImpl) SecurityConfigFactory.getInstance("test-seraph-config.xml");
38          auth = config.getAuthenticator();
39          user = new Principal()
40          {
41              public String getName()
42              {
43                  return "Test User";
44              }
45          };
46      }
47  
48      public void testLogout() throws ConfigurationException, AuthenticatorException
49      {
50          // mocks
51          final Mock logoutInterceptor = new Mock(LogoutInterceptor.class);
52  
53          // expectations
54          request.expectAndReturn("getSession", session.proxy());
55          request.expectAndReturn("getSession", session.proxy());
56          request.expectAndReturn("getContextPath", "");
57          request.expectAndReturn("isSecure", false);
58          request.expectAndReturn("getCookies", new Cookie[] { new Cookie(config.getLoginCookieKey(), null) });
59          response.expect("addCookie", C.ANY_ARGS);
60  
61          mockOutLoginReason(LoginReason.OUT);
62  
63          session.expect("setAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_IN_KEY), C.IS_NULL));
64          session.expect("setAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_OUT_KEY), C.eq(Boolean.TRUE)));
65  
66          logoutInterceptor.expect("beforeLogout", C.ANY_ARGS);
67          logoutInterceptor.expect("afterLogout", C.ANY_ARGS);
68          config.addInterceptor((Interceptor) logoutInterceptor.proxy());
69  
70          // do test!
71          auth.logout((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
72  
73          // verify everything
74          request.verify();
75          response.verify();
76          session.verify();
77          logoutInterceptor.verify();
78      }
79  
80      public void testLoginWithNoCustomCookiePath()
81      {
82          request.expectAndReturn("getContextPath", "/bar");
83          final DefaultAuthenticator dauth = (DefaultAuthenticator) auth;
84          assertEquals("/bar", dauth.getCookiePath((HttpServletRequest) request.proxy()));
85      }
86  
87      public void testLoginWithCustomCookiePath()
88      {
89          SecurityConfigFactory.setSecurityConfig(null);
90          config = (SecurityConfigImpl) SecurityConfigFactory.getInstance("test-seraph-config-with-custom-cookie-path.xml");
91          final DefaultAuthenticator dauth = (DefaultAuthenticator) config.getAuthenticator();
92          assertEquals("/foo", dauth.getCookiePath((HttpServletRequest) request.proxy()));
93      }
94  
95      public void testGetUserChecksSessionFirst()
96      {
97  
98          request.matchAndReturn("getSession", C.ANY_ARGS, session.proxy());
99          session.matchAndReturn("getAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_OUT_KEY)), null);
100         session.matchAndReturn("getAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_IN_KEY)), user);
101 
102         mockOutLoginReason(LoginReason.OK);
103 
104         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
105         assertEquals(user, result);
106 
107         request.verify();
108         response.verify();
109         session.verify();
110     }
111 
112     private StubAuthenticator setupRememberMeCookieState(LoginReason loginReason)
113     {
114         request.matchAndReturn("getSession", C.IS_FALSE, null); // no session during initial check
115         request.matchAndReturn("getSession", C.NO_ARGS, session.proxy()); // session "created" when cookie login() gets it
116 
117         mockOutLoginReason(loginReason);
118         
119         final String cookieValue = new InsecureCookieEncoder().encodePasswordCookie("test", "test", config.getCookieEncoding());
120         request.expectAndReturn("getCookies", new Cookie[] { new Cookie(config.getLoginCookieKey(), cookieValue) });
121 
122         session.matchAndReturn("getAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_OUT_KEY)), null);
123         session.matchAndReturn("getAttribute", C.args(C.eq(DefaultAuthenticator.LOGGED_IN_KEY)), user);
124         return (StubAuthenticator) auth;
125     }
126 
127     public void testGetUserChecksCookieIfNoSession()
128     {
129         setupRememberMeCookieState(LoginReason.OK);
130 
131         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
132         assertEquals(user, result);
133 
134         request.verify();
135         response.verify();
136         session.verify();
137     }
138 
139     public void testGetUserChecksCookieIfNoSession_ButFailsElevatedSecurity()
140     {
141         final StubAuthenticator stubAuthenticator = setupRememberMeCookieState(LoginReason.AUTHENTICATION_DENIED);
142 
143         // basic auth checks happen now if we fail
144         request.expectAndReturn("getQueryString", "p=v");
145         request.expectAndReturn("getHeader", "Authorization", null);
146 
147         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(false,"test");
148         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
149 
150         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
151         assertNull(result);
152 
153         assertEquals(0, securityGuard.getSuccessCount());
154         assertEquals(1, securityGuard.getFailedCount());
155 
156         request.verify();
157         response.verify();
158         session.verify();
159     }
160 
161     public void testGetUserChecksCookieIfNoSession_PassesElevatedSecurity_ButFailsLogin()
162     {
163         final StubAuthenticator stubAuthenticator = setupRememberMeCookieState(LoginReason.AUTHENTICATED_FAILED);
164 
165         // basic auth checks happen now if we fail
166         request.expectAndReturn("getQueryString", "p=v");
167         request.expectAndReturn("getHeader", "Authorization", null);
168 
169         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true,"test");
170         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
171         stubAuthenticator.setDesiredLoginAnswer(false);
172 
173         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
174         assertNull(result);
175 
176         assertEquals(0, securityGuard.getSuccessCount());
177         assertEquals(1, securityGuard.getFailedCount());
178 
179         request.verify();
180         response.verify();
181         session.verify();
182     }
183 
184     public void testGetUserChecksCookieIfNoSession_PassesElevatedSecurity_AndPassesLogin()
185     {
186         final StubAuthenticator stubAuthenticator = setupRememberMeCookieState(LoginReason.OK);
187 
188         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true,"test");
189         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
190         stubAuthenticator.setDesiredLoginAnswer(true);
191 
192         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
193         assertEquals(user, result);
194         
195         assertEquals(1, securityGuard.getSuccessCount());
196         assertEquals(0, securityGuard.getFailedCount());
197 
198         request.verify();
199         response.verify();
200         session.verify();
201     }
202 
203     public void testGetUserReturnsNullWithNoValidAuthentication()
204     {
205         request.matchAndReturn("getSession", C.IS_FALSE, null);
206         request.expectAndReturn("getCookies", null);
207         request.expectAndReturn("getQueryString", null); // no basic auth
208         request.expectAndReturn("getHeader", C.args(C.eq("Authorization")), null);
209 
210         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
211         assertEquals(null, result);
212 
213         request.verify();
214         response.verify();
215         session.verify();
216     }
217 
218     public void testGetUserBasicAuthRequiredButMissing()
219     {
220         request.matchAndReturn("getSession", C.IS_FALSE, null);
221         request.matchAndReturn("getCookies", null);
222         request.matchAndReturn("getQueryString", "os_authType=basic"); // basic auth
223 
224         // no Authorization header
225         request.expectAndReturn("getHeader", C.eq("Authorization"), null);
226         // so we expect a 401 Authentication required
227         response.expect("setStatus", C.eq(401));
228         response.expect("setHeader", C.eq("WWW-Authenticate", "BASIC realm=\"protected-area\""));
229 
230         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
231         assertEquals(null, result);
232 
233         request.verify();
234         response.verify();
235         session.verify();
236     }
237 
238     private StubAuthenticator setupBasicAuthHttpState(LoginReason loginReason)
239     {
240         request.matchAndReturn("getSession", C.IS_FALSE, null);
241         request.matchAndReturn("getCookies", null);
242         request.matchAndReturn("getQueryString", "os_authType=basic"); // basic auth
243 
244         mockOutLoginReason(loginReason);
245         
246         // valid Authorization header
247         final String token = new String(Base64.encode("username:password".getBytes()));
248         request.expectAndReturn("getHeader", C.eq("Authorization"), "Basic " + token);
249         // add the correct user to the stub
250         final StubAuthenticator stubAuthenticator = (StubAuthenticator) auth;
251         stubAuthenticator.addUser("username", user);
252         return stubAuthenticator;
253     }
254 
255     public void testGetUserBasicAuthProvidedUsingRequestParameter()
256     {
257         setupBasicAuthHttpState(LoginReason.OK);
258 
259         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
260         assertEquals(user, result);
261 
262         request.verify();
263         response.verify();
264         session.verify();
265     }
266 
267     public void testGetUserBasicAuthProvidedUsingRequestParameter_ButFailsElevatedSecurity() throws ConfigurationException
268     {
269         final StubAuthenticator stubAuthenticator = setupBasicAuthHttpState(LoginReason.AUTHENTICATION_DENIED);
270 
271         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(false,"username");
272         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
273 
274         // when we fail we send this back
275         response.expect("sendError", C.args(C.eq(401), C.eq("Basic Authentication Failure - Reason : "  + LoginReason.AUTHENTICATION_DENIED)));
276 
277         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
278         assertNull(result);
279 
280         assertEquals(0, securityGuard.getSuccessCount());
281         assertEquals(1, securityGuard.getFailedCount());
282 
283         request.verify();
284         response.verify();
285         session.verify();
286     }
287 
288     public void testGetUserBasicAuthProvidedUsingRequestParameter_PassesElevatedSecurity_ButFailsLogin() throws ConfigurationException
289     {
290         final StubAuthenticator stubAuthenticator = setupBasicAuthHttpState(LoginReason.AUTHENTICATED_FAILED);
291         stubAuthenticator.setDesiredLoginAnswer(false);
292 
293         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true,"username");
294         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
295 
296         // when we fail we send this back
297         response.expect("sendError", C.args(C.eq(401), C.eq("Basic Authentication Failure - Reason : "  + LoginReason.AUTHENTICATED_FAILED)));
298         
299         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
300         assertNull(result);
301 
302         assertEquals(0, securityGuard.getSuccessCount());
303         assertEquals(1, securityGuard.getFailedCount());
304 
305         request.verify();
306         response.verify();
307         session.verify();
308     }
309 
310     public void testGetUserBasicAuthProvidedUsingRequestParameter_PassesElevatedSecurity_AndPassesLogin() throws ConfigurationException
311     {
312         final StubAuthenticator stubAuthenticator = setupBasicAuthHttpState(LoginReason.OK);
313         stubAuthenticator.setDesiredLoginAnswer(true);
314 
315         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true,"username");
316         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
317 
318         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
319         assertEquals(user, result);
320 
321         assertEquals(1, securityGuard.getSuccessCount());
322         assertEquals(0, securityGuard.getFailedCount());
323 
324         request.verify();
325         response.verify();
326         session.verify();
327     }
328 
329     public void testGetUserBasicAuthProvidedUsingHeader()
330     {
331         final String authorizationHeader = "Basic " + new String(Base64.encode("username:password".getBytes()));
332         
333         request.matchAndReturn("getSession", C.IS_FALSE, null);
334         request.matchAndReturn("getCookies", null);
335         request.matchAndReturn("getQueryString", null); // no basic auth
336 
337         request.expectAndReturn("getHeader", C.eq("Authorization"), authorizationHeader);
338         request.expectAndReturn("getHeader", C.eq("Authorization"), authorizationHeader);
339 
340         mockOutLoginReason(LoginReason.OK);
341 
342         // add the correct user to the stub
343         final StubAuthenticator stubAuthenticator = (StubAuthenticator) auth;
344         stubAuthenticator.addUser("username", user);
345 
346         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true,"username");
347         stubAuthenticator.setElevatedSecurityGuard(securityGuard);
348         
349         final Principal result = auth.getUser((HttpServletRequest) request.proxy(), (HttpServletResponse) response.proxy());
350         assertEquals(user, result);
351 
352         assertEquals(1, securityGuard.getSuccessCount());
353         assertEquals(0, securityGuard.getFailedCount());
354         
355         request.verify();
356         response.verify();
357         session.verify();
358     }
359 
360     private void mockOutLoginReason(LoginReason loginReason)
361     {
362         request.expectAndReturn("getAttribute", C.args(C.eq("com.atlassian.seraph.auth.LoginReason")), null);
363         request.expect("setAttribute", C.args(C.eq("com.atlassian.seraph.auth.LoginReason"), C.eq(loginReason)));
364 
365         response.expect("addHeader", C.ANY_ARGS);
366     }
367 }