1   /* Copyright 2004, 2005 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.acegisecurity.ui.switchuser;
17  
18  import junit.framework.TestCase;
19  
20  import org.acegisecurity.AccountExpiredException;
21  import org.acegisecurity.Authentication;
22  import org.acegisecurity.AuthenticationException;
23  import org.acegisecurity.CredentialsExpiredException;
24  import org.acegisecurity.DisabledException;
25  import org.acegisecurity.GrantedAuthority;
26  import org.acegisecurity.GrantedAuthorityImpl;
27  import org.acegisecurity.context.SecurityContextHolder;
28  import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
29  import org.acegisecurity.userdetails.User;
30  import org.acegisecurity.userdetails.UserDetails;
31  import org.acegisecurity.userdetails.UserDetailsService;
32  import org.acegisecurity.userdetails.UsernameNotFoundException;
33  import org.acegisecurity.util.MockFilterChain;
34  import org.springframework.dao.DataAccessException;
35  import org.springframework.mock.web.MockHttpServletRequest;
36  import org.springframework.mock.web.MockHttpServletResponse;
37  
38  
39  /***
40   * Tests {@link org.acegisecurity.ui.switchuser.SwitchUserProcessingFilter}.
41   *
42   * @author Mark St.Godard
43   * @version $Id: SwitchUserProcessingFilterTests.java,v 1.8 2005/11/30 01:23:34 benalex Exp $
44   */
45  public class SwitchUserProcessingFilterTests extends TestCase {
46      //~ Constructors ===========================================================
47  
48      public SwitchUserProcessingFilterTests() {
49          super();
50      }
51  
52      public SwitchUserProcessingFilterTests(String arg0) {
53          super(arg0);
54      }
55  
56      //~ Methods ================================================================
57  
58      public final void setUp() throws Exception {
59          super.setUp();
60      }
61  
62      public static void main(String[] args) {
63          junit.textui.TestRunner.run(SwitchUserProcessingFilterTests.class);
64      }
65  
66      public void testAttemptSwitchToUnknownUser() throws Exception {
67          // set current user 
68          UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("dano",
69                  "hawaii50");
70          SecurityContextHolder.getContext().setAuthentication(auth);
71  
72          MockHttpServletRequest request = new MockHttpServletRequest();
73          request.addParameter(SwitchUserProcessingFilter.ACEGI_SECURITY_SWITCH_USERNAME_KEY,
74              "user-that-doesnt-exist");
75  
76          SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
77          filter.setUserDetailsService(new MockAuthenticationDaoUserJackLord());
78  
79          try {
80              Authentication result = filter.attemptSwitchUser(request);
81  
82              fail("Should not be able to switch to unknown user");
83          } catch (UsernameNotFoundException expected) {}
84      }
85  
86      public void testAttemptSwitchToUserThatIsDisabled()
87          throws Exception {
88          // set current user 
89          UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("dano",
90                  "hawaii50");
91          SecurityContextHolder.getContext().setAuthentication(auth);
92  
93          MockHttpServletRequest request = new MockHttpServletRequest();
94  
95          // this user is disabled
96          request.addParameter(SwitchUserProcessingFilter.ACEGI_SECURITY_SWITCH_USERNAME_KEY,
97              "mcgarrett");
98  
99          SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
100         filter.setUserDetailsService(new MockAuthenticationDaoUserJackLord());
101 
102         try {
103             Authentication result = filter.attemptSwitchUser(request);
104 
105             fail("Should not be able to switch to disabled user");
106         } catch (DisabledException expected) {
107             // user should be disabled
108         }
109     }
110 
111     public void testAttemptSwitchToUserWithAccountExpired()
112         throws Exception {
113         // set current user 
114         UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("dano",
115                 "hawaii50");
116         SecurityContextHolder.getContext().setAuthentication(auth);
117 
118         MockHttpServletRequest request = new MockHttpServletRequest();
119 
120         // this user is disabled
121         request.addParameter(SwitchUserProcessingFilter.ACEGI_SECURITY_SWITCH_USERNAME_KEY,
122             "wofat");
123 
124         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
125         filter.setUserDetailsService(new MockAuthenticationDaoUserJackLord());
126 
127         try {
128             Authentication result = filter.attemptSwitchUser(request);
129 
130             fail("Should not be able to switch to user with expired account");
131         } catch (AccountExpiredException expected) {
132             // expected user account expired
133         }
134     }
135 
136     public void testAttemptSwitchToUserWithExpiredCredentials()
137         throws Exception {
138         // set current user 
139         UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("dano",
140                 "hawaii50");
141         SecurityContextHolder.getContext().setAuthentication(auth);
142 
143         MockHttpServletRequest request = new MockHttpServletRequest();
144 
145         // this user is disabled
146         request.addParameter(SwitchUserProcessingFilter.ACEGI_SECURITY_SWITCH_USERNAME_KEY,
147             "steve");
148 
149         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
150         filter.setUserDetailsService(new MockAuthenticationDaoUserJackLord());
151 
152         try {
153             Authentication result = filter.attemptSwitchUser(request);
154 
155             fail("Should not be able to switch to user with expired account");
156         } catch (CredentialsExpiredException expected) {
157             // user credentials expired
158         }
159     }
160 
161     public void testAttemptSwitchUser() throws Exception {
162         // set current user 
163         UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("dano",
164                 "hawaii50");
165         SecurityContextHolder.getContext().setAuthentication(auth);
166 
167         MockHttpServletRequest request = new MockHttpServletRequest();
168         request.addParameter(SwitchUserProcessingFilter.ACEGI_SECURITY_SWITCH_USERNAME_KEY,
169             "jacklord");
170 
171         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
172         filter.setUserDetailsService(new MockAuthenticationDaoUserJackLord());
173 
174         Authentication result = filter.attemptSwitchUser(request);
175         assertTrue(result != null);
176     }
177 
178     public void testBadConfigMissingAuthenticationDao() {
179         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
180         filter.setSwitchUserUrl("/j_acegi_switch_user");
181         filter.setExitUserUrl("/j_acegi_exit_user");
182         filter.setTargetUrl("/main.jsp");
183 
184         try {
185             filter.afterPropertiesSet();
186             fail("Expect to fail due to missing 'authenticationDao'");
187         } catch (Exception expected) {
188             // expected exception
189         }
190     }
191 
192     public void testBadConfigMissingTargetUrl() {
193         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
194         filter.setUserDetailsService(new MockAuthenticationDaoUserJackLord());
195         filter.setSwitchUserUrl("/j_acegi_switch_user");
196         filter.setExitUserUrl("/j_acegi_exit_user");
197 
198         try {
199             filter.afterPropertiesSet();
200             fail("Expect to fail due to missing 'targetUrl'");
201         } catch (Exception expected) {
202             // expected exception
203         }
204     }
205 
206     public void testDefaultProcessesFilterUrlWithPathParameter() {
207         MockHttpServletRequest request = createMockSwitchRequest();
208         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
209         filter.setSwitchUserUrl("/j_acegi_switch_user");
210 
211         request.setRequestURI(
212             "/webapp/j_acegi_switch_user;jsessionid=8JHDUD723J8");
213         assertTrue(filter.requiresSwitchUser(request));
214     }
215 
216     public void testExitRequestUserJackLordToDano() throws Exception {
217         // original user	
218         GrantedAuthority[] auths = {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
219                     "ROLE_TWO")};
220         UsernamePasswordAuthenticationToken source = new UsernamePasswordAuthenticationToken("dano",
221                 "hawaii50", auths);
222 
223         // set current user (Admin)
224         GrantedAuthority[] adminAuths = {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
225                     "ROLE_TWO"), new SwitchUserGrantedAuthority("PREVIOUS_ADMINISTRATOR",
226                     source)};
227         UsernamePasswordAuthenticationToken admin = new UsernamePasswordAuthenticationToken("jacklord",
228                 "hawaii50", adminAuths);
229 
230         SecurityContextHolder.getContext().setAuthentication(admin);
231 
232         // http request
233         MockHttpServletRequest request = createMockSwitchRequest();
234         request.setRequestURI("/j_acegi_exit_user");
235 
236         // http response
237         MockHttpServletResponse response = new MockHttpServletResponse();
238 
239         // setup filter
240         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
241         filter.setUserDetailsService(new MockAuthenticationDaoUserJackLord());
242         filter.setExitUserUrl("/j_acegi_exit_user");
243 
244         MockFilterChain chain = new MockFilterChain(true);
245 
246         // run 'exit'
247         filter.doFilter(request, response, chain);
248 
249         // check current user, should be back to original user (dano) 
250         Authentication targetAuth = SecurityContextHolder.getContext()
251                                                          .getAuthentication();
252         assertNotNull(targetAuth);
253         assertEquals("dano", targetAuth.getPrincipal());
254     }
255 
256     public void testExitUserWithNoCurrentUser() throws Exception {
257         // no current user in secure context
258         SecurityContextHolder.getContext().setAuthentication(null);
259 
260         // http request
261         MockHttpServletRequest request = createMockSwitchRequest();
262         request.setRequestURI("/j_acegi_exit_user");
263 
264         // http response
265         MockHttpServletResponse response = new MockHttpServletResponse();
266 
267         // setup filter
268         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
269         filter.setUserDetailsService(new MockAuthenticationDaoUserJackLord());
270         filter.setExitUserUrl("/j_acegi_exit_user");
271 
272         MockFilterChain chain = new MockFilterChain(true);
273 
274         // run 'exit', expect fail due to no current user 
275         try {
276             filter.doFilter(request, response, chain);
277 
278             fail("Cannot exit from a user with no current user set!");
279         } catch (AuthenticationException expected) {}
280     }
281 
282     public void testRedirectToTargetUrl() throws Exception {
283         // set current user 
284         UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("dano",
285                 "hawaii50");
286         SecurityContextHolder.getContext().setAuthentication(auth);
287 
288         MockHttpServletRequest request = createMockSwitchRequest();
289         request.addParameter(SwitchUserProcessingFilter.ACEGI_SECURITY_SWITCH_USERNAME_KEY,
290             "jacklord");
291         request.setRequestURI("/webapp/j_acegi_switch_user");
292 
293         MockHttpServletResponse response = new MockHttpServletResponse();
294         MockFilterChain chain = new MockFilterChain(true);
295 
296         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
297         filter.setSwitchUserUrl("/j_acegi_switch_user");
298         filter.setTargetUrl("/webapp/someOtherUrl");
299         filter.setUserDetailsService(new MockAuthenticationDaoUserJackLord());
300 
301         filter.doFilter(request, response, chain);
302 
303         assertEquals("/webapp/someOtherUrl", response.getRedirectedUrl());
304     }
305 
306     public void testRequiresExitUser() {
307         // filter
308         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
309         filter.setExitUserUrl("/j_acegi_exit_user");
310 
311         // request
312         MockHttpServletRequest request = new MockHttpServletRequest();
313         request.setRequestURI("/j_acegi_exit_user");
314 
315         assertTrue(filter.requiresExitUser(request));
316     }
317 
318     public void testRequiresSwitch() {
319         // filter
320         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
321         filter.setSwitchUserUrl("/j_acegi_switch_user");
322 
323         // request
324         MockHttpServletRequest request = createMockSwitchRequest();
325 
326         assertTrue(filter.requiresSwitchUser(request));
327     }
328 
329     public void testSwitchRequestFromDanoToJackLord() throws Exception {
330         // set current user 
331         UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("dano",
332                 "hawaii50");
333         SecurityContextHolder.getContext().setAuthentication(auth);
334 
335         // http request
336         MockHttpServletRequest request = new MockHttpServletRequest();
337         request.setRequestURI("/webapp/j_acegi_switch_user");
338         request.addParameter(SwitchUserProcessingFilter.ACEGI_SECURITY_SWITCH_USERNAME_KEY,
339             "jacklord");
340 
341         // http response
342         MockHttpServletResponse response = new MockHttpServletResponse();
343 
344         // setup filter
345         SwitchUserProcessingFilter filter = new SwitchUserProcessingFilter();
346         filter.setUserDetailsService(new MockAuthenticationDaoUserJackLord());
347         filter.setSwitchUserUrl("/j_acegi_switch_user");
348 
349         MockFilterChain chain = new MockFilterChain(true);
350 
351         // test updates user token and context
352         filter.doFilter(request, response, chain);
353 
354         // check current user
355         Authentication targetAuth = SecurityContextHolder.getContext()
356                                                          .getAuthentication();
357         assertNotNull(targetAuth);
358         assertTrue(targetAuth.getPrincipal() instanceof UserDetails);
359         assertEquals("jacklord", ((User)targetAuth.getPrincipal()).getUsername());        
360     }
361 
362     private MockHttpServletRequest createMockSwitchRequest() {
363         MockHttpServletRequest request = new MockHttpServletRequest();
364         request.setScheme("http");
365         request.setServerName("localhost");
366         request.setRequestURI("/j_acegi_switch_user");
367 
368         return request;
369     }
370 
371     //~ Inner Classes ==========================================================
372 
373     private class MockAuthenticationDaoUserJackLord implements UserDetailsService {
374         private String password = "hawaii50";
375 
376         public void setPassword(String password) {
377             this.password = password;
378         }
379 
380         public UserDetails loadUserByUsername(String username)
381             throws UsernameNotFoundException, DataAccessException {
382             // jacklord, dano  (active)
383             // mcgarrett (disabled)
384             // wofat (account expired)
385             // steve (credentials expired)
386             if ("jacklord".equals(username) || "dano".equals(username)) {
387                 return new User(username, password, true, true, true, true,
388                     new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
389                             "ROLE_TWO")});
390             } else if ("mcgarrett".equals(username)) {
391                 return new User(username, password, false, true, true, true,
392                     new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
393                             "ROLE_TWO")});
394             } else if ("wofat".equals(username)) {
395                 return new User(username, password, true, false, true, true,
396                     new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
397                             "ROLE_TWO")});
398             } else if ("steve".equals(username)) {
399                 return new User(username, password, true, true, false, true,
400                     new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
401                             "ROLE_TWO")});
402             } else {
403                 throw new UsernameNotFoundException("Could not find: "
404                     + username);
405             }
406         }
407     }
408 }