1   package com.atlassian.seraph.filter;
2   
3   import com.atlassian.seraph.auth.AuthenticationErrorType;
4   import com.atlassian.seraph.auth.Authenticator;
5   import com.atlassian.seraph.auth.AuthenticatorException;
6   import com.atlassian.seraph.config.SecurityConfig;
7   import com.atlassian.seraph.elevatedsecurity.CountingElevatedSecurityGuard;
8   import com.atlassian.seraph.interceptor.LoginInterceptor;
9   import com.atlassian.seraph.util.LocalMockHttpServletRequest;
10  import com.mockobjects.dynamic.C;
11  import com.mockobjects.dynamic.Mock;
12  import junit.framework.TestCase;
13  
14  import javax.servlet.http.HttpServletRequest;
15  import javax.servlet.http.HttpServletResponse;
16  import javax.servlet.http.HttpSession;
17  import java.security.Principal;
18  import java.util.ArrayList;
19  import java.util.Collections;
20  import java.util.List;
21  import java.util.Map;
22  
23  /**
24   * Tests for {@link com.atlassian.seraph.filter.PasswordBasedLoginFilter}
25   */
26  public class TestPasswordBasedLoginFilter extends TestCase
27  {
28      public void testNullUserNameFails()
29      {
30          final MockSecurityConfig securityConfig = new MockSecurityConfig(Collections.EMPTY_LIST);
31          final PasswordBasedLoginFilter passwordBasedLoginFilter = new TestedPasswordBasedLoginFilter(null, "password", securityConfig);
32  
33          final String actualStatus = passwordBasedLoginFilter.login(makeHttpRequest(), makeHttpResponse());
34          assertEquals(PasswordBasedLoginFilter.LOGIN_NOATTEMPT, actualStatus);
35      }
36  
37  
38      public void testNullPasswordFails()
39      {
40          final MockSecurityConfig securityConfig = new MockSecurityConfig(Collections.EMPTY_LIST);
41          final PasswordBasedLoginFilter passwordBasedLoginFilter = new TestedPasswordBasedLoginFilter("userName", null, securityConfig);
42  
43          final String actualStatus = passwordBasedLoginFilter.login(makeHttpRequest(), makeHttpResponse());
44          assertEquals(PasswordBasedLoginFilter.LOGIN_NOATTEMPT, actualStatus);
45      }
46  
47      public void testNullUserPairFails()
48      {
49          final PasswordBasedLoginFilter passwordBasedLoginFilter = new PasswordBasedLoginFilter()
50          {
51              protected PasswordBasedLoginFilter.UserPasswordPair extractUserPasswordPair(final HttpServletRequest request)
52              {
53                  return null;
54              }
55          };
56  
57          final String actualStatus = passwordBasedLoginFilter.login(makeHttpRequest(), makeHttpResponse());
58          assertEquals(PasswordBasedLoginFilter.LOGIN_NOATTEMPT, actualStatus);
59      }
60  
61      public void testElevatedSecurityGuard_FailedCheck()
62      {
63          final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(false, "userName");
64  
65          final MockSecurityConfig securityConfig = new MockSecurityConfig(securityGuard, null, null, Collections.EMPTY_LIST);
66          final PasswordBasedLoginFilter passwordBasedLoginFilter = new TestedPasswordBasedLoginFilter("userName", "password", securityConfig);
67  
68  
69          final String actualStatus = passwordBasedLoginFilter.login(makeHttpRequest(), makeHttpResponse());
70          assertEquals(PasswordBasedLoginFilter.LOGIN_FAILED, actualStatus);
71          assertEquals(1, securityGuard.getFailedCount());
72          assertEquals(0, securityGuard.getSuccessCount());
73      }
74  
75      public void testElevatedSecurityGuard_PassedCheck_ButFailedAuthenticator()
76      {
77          final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true, "userName");
78  
79          final AssertingAuthenticator authenticator = new AssertingAuthenticator(false, "userName", "password");
80  
81          final MockSecurityConfig securityConfig = new MockSecurityConfig(securityGuard, authenticator, null, Collections.EMPTY_LIST);
82          final PasswordBasedLoginFilter passwordBasedLoginFilter = new TestedPasswordBasedLoginFilter("userName", "password", securityConfig);
83  
84  
85          final String actualStatus = passwordBasedLoginFilter.login(makeHttpRequest(), makeHttpResponse());
86          assertEquals(PasswordBasedLoginFilter.LOGIN_FAILED, actualStatus);
87          assertEquals(1, securityGuard.getFailedCount());
88          assertEquals(0, securityGuard.getSuccessCount());
89      }
90  
91      public void testElevatedSecurityGuard_PassedCheck_ButAuthenticatorThrewException()
92      {
93          final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true, "userName");
94  
95          @SuppressWarnings({"ThrowableInstanceNeverThrown"})
96          final AssertingAuthenticator authenticator = new AssertingAuthenticator(false, "userName", "password", new AuthenticatorException());
97  
98          final MockSecurityConfig securityConfig = new MockSecurityConfig(securityGuard, authenticator, null, Collections.EMPTY_LIST);
99          final PasswordBasedLoginFilter passwordBasedLoginFilter = new TestedPasswordBasedLoginFilter("userName", "password", securityConfig);
100 
101 
102         final String actualStatus = passwordBasedLoginFilter.login(makeHttpRequest(), makeHttpResponse());
103         assertEquals(PasswordBasedLoginFilter.LOGIN_ERROR, actualStatus);
104 
105         // we dont run the security guard on exception!  Its not really a fail against the user
106         assertEquals(0, securityGuard.getFailedCount());
107         assertEquals(0, securityGuard.getSuccessCount());
108     }
109 
110     public void testElevatedSecurityGuard_PassedCheck_AndPassedAuthenticator()
111     {
112         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true, "userName");
113 
114         final AssertingAuthenticator authenticator = new AssertingAuthenticator(true, "userName", "password");
115         final MockSecurityConfig securityConfig = new MockSecurityConfig(securityGuard, authenticator, null, Collections.EMPTY_LIST);
116         final PasswordBasedLoginFilter passwordBasedLoginFilter = new TestedPasswordBasedLoginFilter("userName", "password", securityConfig);
117 
118 
119         final String actualStatus = passwordBasedLoginFilter.login(makeHttpRequest(), makeHttpResponse());
120         assertEquals(PasswordBasedLoginFilter.LOGIN_SUCCESS, actualStatus);
121         assertEquals(0, securityGuard.getFailedCount());
122         assertEquals(1, securityGuard.getSuccessCount());
123     }
124 
125 
126     public void testInterceptorsAreCalled()
127     {
128         CountingLoginInterceptor loginInterceptor = new CountingLoginInterceptor();
129         List<CountingLoginInterceptor> interceptors = new ArrayList<CountingLoginInterceptor>();
130         interceptors.add(loginInterceptor);
131 
132         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true, "userName");
133 
134         final AssertingAuthenticator authenticator = new AssertingAuthenticator(true, "userName", "password", null);
135 
136         final MockSecurityConfig securityConfig = new MockSecurityConfig(securityGuard, authenticator, null, interceptors);
137         final PasswordBasedLoginFilter passwordBasedLoginFilter = new TestedPasswordBasedLoginFilter("userName", "password", securityConfig);
138 
139 
140         passwordBasedLoginFilter.login(makeHttpRequest(), makeHttpResponse());
141         assertEquals(1, loginInterceptor.getBeforeLogin());
142         assertEquals(1, loginInterceptor.getAfterLogin());
143 
144     }
145 
146     public void testLoginNullErrorType()
147     {
148         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true, "userName");
149 
150         @SuppressWarnings({"ThrowableInstanceNeverThrown"})
151         final AssertingAuthenticator authenticator = new AssertingAuthenticator(false, "userName", "password", new AuthenticatorException());
152 
153         final MockSecurityConfig securityConfig = new MockSecurityConfig(securityGuard, authenticator, null, Collections.EMPTY_LIST);
154         final PasswordBasedLoginFilter passwordBasedLoginFilter = new TestedPasswordBasedLoginFilter("userName", "password", securityConfig);
155 
156         final LocalMockHttpServletRequest mockHttpServletRequest = makeHttpRequest();
157         final String actualStatus = passwordBasedLoginFilter.login(mockHttpServletRequest, makeHttpResponse());
158         assertEquals(PasswordBasedLoginFilter.LOGIN_ERROR, actualStatus);
159         assertEquals(null, mockHttpServletRequest.getAttribute(BaseLoginFilter.AUTHENTICATION_ERROR_TYPE));
160 
161         // we dont run the security guard on exception!  Its not really a fail against the user
162         assertEquals(0, securityGuard.getFailedCount());
163         assertEquals(0, securityGuard.getSuccessCount());
164     }
165 
166     public void testLoginCommunicationError()
167     {
168         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true, "userName");
169 
170         @SuppressWarnings({"ThrowableInstanceNeverThrown"})
171         final AssertingAuthenticator authenticator = new AssertingAuthenticator(false, "userName", "password", new AuthenticatorException(AuthenticationErrorType.CommunicationError));
172 
173         final MockSecurityConfig securityConfig = new MockSecurityConfig(securityGuard, authenticator, null, Collections.EMPTY_LIST);
174         final PasswordBasedLoginFilter passwordBasedLoginFilter = new TestedPasswordBasedLoginFilter("userName", "password", securityConfig);
175 
176         final LocalMockHttpServletRequest mockHttpServletRequest = makeHttpRequest();
177         final String actualStatus = passwordBasedLoginFilter.login(mockHttpServletRequest, makeHttpResponse());
178         assertEquals(PasswordBasedLoginFilter.LOGIN_ERROR, actualStatus);
179         assertEquals(AuthenticationErrorType.CommunicationError, mockHttpServletRequest.getAttribute(BaseLoginFilter.AUTHENTICATION_ERROR_TYPE));
180 
181         // we dont run the security guard on exception!  Its not really a fail against the user
182         assertEquals(0, securityGuard.getFailedCount());
183         assertEquals(0, securityGuard.getSuccessCount());
184     }
185 
186     public void testLoginUnknownError()
187     {
188         final CountingElevatedSecurityGuard securityGuard = new CountingElevatedSecurityGuard(true, "userName");
189 
190         @SuppressWarnings({"ThrowableInstanceNeverThrown"})
191         final AssertingAuthenticator authenticator = new AssertingAuthenticator(false, "userName", "password", new AuthenticatorException(AuthenticationErrorType.UnknownError));
192 
193         final MockSecurityConfig securityConfig = new MockSecurityConfig(securityGuard, authenticator, null, Collections.EMPTY_LIST);
194         final PasswordBasedLoginFilter passwordBasedLoginFilter = new TestedPasswordBasedLoginFilter("userName", "password", securityConfig);
195 
196         final LocalMockHttpServletRequest mockHttpServletRequest = makeHttpRequest();
197         final String actualStatus = passwordBasedLoginFilter.login(mockHttpServletRequest, makeHttpResponse());
198         assertEquals(PasswordBasedLoginFilter.LOGIN_ERROR, actualStatus);
199         assertEquals(AuthenticationErrorType.UnknownError, mockHttpServletRequest.getAttribute(BaseLoginFilter.AUTHENTICATION_ERROR_TYPE));
200 
201         // we dont run the security guard on exception!  Its not really a fail against the user
202         assertEquals(0, securityGuard.getFailedCount());
203         assertEquals(0, securityGuard.getSuccessCount());
204     }
205 
206     class TestedPasswordBasedLoginFilter extends PasswordBasedLoginFilter
207     {
208         private final String userName;
209         private final String password;
210         private final MockSecurityConfig mockSecurityConfig;
211 
212 
213         TestedPasswordBasedLoginFilter(final String userName, final String password, final MockSecurityConfig mockSecurityConfig)
214         {
215             this.userName = userName;
216             this.password = password;
217             this.mockSecurityConfig = mockSecurityConfig;
218         }
219 
220         @Override
221         protected PasswordBasedLoginFilter.UserPasswordPair extractUserPasswordPair(final HttpServletRequest request)
222         {
223             return new PasswordBasedLoginFilter.UserPasswordPair(userName, password, true);
224         }
225 
226         @Override
227         protected SecurityConfig getSecurityConfig()
228         {
229             return mockSecurityConfig;
230         }
231     }
232 
233     class AssertingAuthenticator implements Authenticator
234     {
235         private final boolean loginOK;
236         private final String expectedUserName;
237         private final String expectedPassword;
238         private final AuthenticatorException authenticatorException;
239 
240         public AssertingAuthenticator(final boolean loginOK, final String expectedUserName, final String expectedPassword)
241         {
242             this(loginOK, expectedUserName, expectedPassword, null);
243         }
244 
245         public AssertingAuthenticator(final boolean loginOK, final String expectedUserName, final String expectedPassword, final AuthenticatorException authenticatorException)
246         {
247             this.loginOK = loginOK;
248             this.expectedUserName = expectedUserName;
249             this.expectedPassword = expectedPassword;
250             this.authenticatorException = authenticatorException;
251         }
252 
253         public boolean login(final HttpServletRequest request, final HttpServletResponse response, final String username, final String password)
254                 throws AuthenticatorException
255         {
256             return loginImpl(username, password);
257         }
258 
259         public boolean login(final HttpServletRequest request, final HttpServletResponse response, final String username, final String password, final boolean storeCookie)
260                 throws AuthenticatorException
261         {
262             return loginImpl(username, password);
263         }
264 
265         private boolean loginImpl(final String username, final String password) throws AuthenticatorException
266         {
267             assertEquals(expectedUserName, username);
268             assertEquals(expectedPassword, password);
269             if (authenticatorException != null)
270             {
271                 throw authenticatorException;
272             }
273             return loginOK;
274         }
275 
276         public String getRemoteUser(final HttpServletRequest request)
277         {
278             return null;
279         }
280 
281         public void destroy()
282         {
283         }
284 
285         public Principal getUser(final HttpServletRequest request)
286         {
287             return null;
288         }
289 
290         public Principal getUser(final HttpServletRequest request, final HttpServletResponse response)
291         {
292             return null;
293         }
294 
295         public boolean isUserInRole(final HttpServletRequest request, final String role)
296         {
297             return false;
298         }
299 
300         public boolean logout(final HttpServletRequest request, final HttpServletResponse response)
301                 throws AuthenticatorException
302         {
303             return false;
304         }
305 
306         public void init(final Map<String, String> params, final SecurityConfig config)
307         {
308         }
309     }
310 
311     class CountingLoginInterceptor implements LoginInterceptor
312     {
313         private int beforeLogin;
314         private int afterLogin;
315 
316         public void beforeLogin(final HttpServletRequest request, final HttpServletResponse response, final String username, final String password, final boolean cookieLogin)
317         {
318             beforeLogin++;
319         }
320 
321         public void afterLogin(final HttpServletRequest request, final HttpServletResponse response, final String username, final String password, final boolean cookieLogin, final String loginStatus)
322         {
323             afterLogin++;
324         }
325 
326         public void destroy()
327         {
328         }
329 
330         public void init(final Map<String, String> params, final SecurityConfig config)
331         {
332         }
333 
334         public int getBeforeLogin()
335         {
336             return beforeLogin;
337         }
338 
339         public int getAfterLogin()
340         {
341             return afterLogin;
342         }
343     }
344 
345     private HttpServletResponse makeHttpResponse()
346     {
347         final Mock mockHttpServletResponse = new Mock(HttpServletResponse.class);
348         mockHttpServletResponse.expect("addHeader", C.ANY_ARGS);
349         return (HttpServletResponse) mockHttpServletResponse.proxy();
350     }
351 
352     private LocalMockHttpServletRequest makeHttpRequest()
353     {
354         final LocalMockHttpServletRequest httpServletRequest = new LocalMockHttpServletRequest();
355         httpServletRequest.setupGetAttribute(null);
356 
357         final HttpSession session = new MockSession();
358         httpServletRequest.setSession(session);
359         return httpServletRequest;
360     }
361 
362 
363 }