1   /* Copyright 2004, 2005, 2006 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;
17  
18  import junit.framework.TestCase;
19  
20  import org.acegisecurity.AccessDeniedException;
21  import org.acegisecurity.BadCredentialsException;
22  import org.acegisecurity.GrantedAuthority;
23  import org.acegisecurity.GrantedAuthorityImpl;
24  import org.acegisecurity.MockAuthenticationEntryPoint;
25  import org.acegisecurity.MockPortResolver;
26  
27  import org.acegisecurity.context.SecurityContextHolder;
28  
29  import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
30  
31  import org.springframework.mock.web.MockHttpServletRequest;
32  import org.springframework.mock.web.MockHttpServletResponse;
33  
34  import java.io.IOException;
35  
36  import javax.servlet.FilterChain;
37  import javax.servlet.ServletException;
38  import javax.servlet.ServletRequest;
39  import javax.servlet.ServletResponse;
40  
41  /**
42   * Tests {@link ExceptionTranslationFilter}.
43   * 
44   * @author Ben Alex
45   * @version $Id: ExceptionTranslationFilterTests.java 1496 2006-05-23 13:38:33Z
46   * benalex $
47   */
48  public class ExceptionTranslationFilterTests extends TestCase {
49  	// ~ Constructors
50  	// ===================================================================================================
51  
52  	public ExceptionTranslationFilterTests() {
53  		super();
54  	}
55  
56  	public ExceptionTranslationFilterTests(String arg0) {
57  		super(arg0);
58  	}
59  
60  	// ~ Methods
61  	// ========================================================================================================
62  
63  	public static void main(String[] args) {
64  		junit.textui.TestRunner.run(ExceptionTranslationFilterTests.class);
65  	}
66  
67  	public final void setUp() throws Exception {
68  		super.setUp();
69  	}
70  
71  	protected void tearDown() throws Exception {
72  		super.tearDown();
73  		SecurityContextHolder.clearContext();
74  	}
75  
76  	public void testAccessDeniedWhenAnonymous() throws Exception {
77  		// Setup our HTTP request
78  		MockHttpServletRequest request = new MockHttpServletRequest();
79  		request.setServletPath("/secure/page.html");
80  		request.setServerPort(80);
81  		request.setScheme("http");
82  		request.setServerName("www.example.com");
83  		request.setContextPath("/mycontext");
84  		request.setRequestURI("/mycontext/secure/page.html");
85  
86  		// Setup the FilterChain to thrown an access denied exception
87  		MockFilterChain chain = new MockFilterChain(true, false, false, false);
88  
89  		// Setup SecurityContextHolder, as filter needs to check if user is
90  		// anonymous
91  		SecurityContextHolder.getContext().setAuthentication(
92  				new AnonymousAuthenticationToken("ignored", "ignored",
93  						new GrantedAuthority[] { new GrantedAuthorityImpl("IGNORED") }));
94  
95  		// Test
96  		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
97  		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
98  
99  		MockHttpServletResponse response = new MockHttpServletResponse();
100 		filter.doFilter(request, response, chain);
101 		assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
102 		assertEquals("http://www.example.com/mycontext/secure/page.html", AbstractProcessingFilter
103 				.obtainFullRequestUrl(request));
104 	}
105 
106 	public void testAccessDeniedWhenNonAnonymous() throws Exception {
107 		// Setup our HTTP request
108 		MockHttpServletRequest request = new MockHttpServletRequest();
109 		request.setServletPath("/secure/page.html");
110 
111 		// Setup the FilterChain to thrown an access denied exception
112 		MockFilterChain chain = new MockFilterChain(true, false, false, false);
113 
114 		// Setup SecurityContextHolder, as filter needs to check if user is
115 		// anonymous
116 		SecurityContextHolder.getContext().setAuthentication(null);
117 
118 		// Setup a new AccessDeniedHandlerImpl that will do a "forward"
119 		AccessDeniedHandlerImpl adh = new AccessDeniedHandlerImpl();
120 		adh.setErrorPage("/error.jsp");
121 
122 		// Test
123 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
124 		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
125 		filter.setAccessDeniedHandler(adh);
126 
127 		MockHttpServletResponse response = new MockHttpServletResponse();
128 		filter.doFilter(request, response, chain);
129 		assertEquals(403, response.getStatus());
130 		assertEquals(AccessDeniedException.class, request.getAttribute(
131 				AccessDeniedHandlerImpl.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY).getClass());
132 	}
133 
134 	public void testDoFilterWithNonHttpServletRequestDetected() throws Exception {
135 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
136 
137 		try {
138 			filter.doFilter(null, new MockHttpServletResponse(), new MockFilterChain(false, false, false, false));
139 			fail("Should have thrown ServletException");
140 		}
141 		catch (ServletException expected) {
142 			assertEquals("HttpServletRequest required", expected.getMessage());
143 		}
144 	}
145 
146 	public void testDoFilterWithNonHttpServletResponseDetected() throws Exception {
147 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
148 
149 		try {
150 			filter.doFilter(new MockHttpServletRequest(null, null), null, new MockFilterChain(false, false, false,
151 					false));
152 			fail("Should have thrown ServletException");
153 		}
154 		catch (ServletException expected) {
155 			assertEquals("HttpServletResponse required", expected.getMessage());
156 		}
157 	}
158 
159 	public void testGettersSetters() {
160 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
161 
162 		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
163 		assertTrue(filter.getAuthenticationEntryPoint() != null);
164 
165 		filter.setPortResolver(new MockPortResolver(80, 443));
166 		assertTrue(filter.getPortResolver() != null);
167 	}
168 
169 	public void testRedirectedToLoginFormAndSessionShowsOriginalTargetWhenAuthenticationException() throws Exception {
170 		// Setup our HTTP request
171 		MockHttpServletRequest request = new MockHttpServletRequest();
172 		request.setServletPath("/secure/page.html");
173 		request.setServerPort(80);
174 		request.setScheme("http");
175 		request.setServerName("www.example.com");
176 		request.setContextPath("/mycontext");
177 		request.setRequestURI("/mycontext/secure/page.html");
178 
179 		// Setup the FilterChain to thrown an authentication failure exception
180 		MockFilterChain chain = new MockFilterChain(false, true, false, false);
181 
182 		// Test
183 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
184 		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
185 		filter.setPortResolver(new MockPortResolver(80, 443));
186 		/*
187 		 * Disabled the call to afterPropertiesSet as it requires
188 		 * applicationContext to be injected before it is invoked. We do not
189 		 * have this filter configured in IOC for this test hence no
190 		 * ApplicationContext
191 		 */
192 		// filter.afterPropertiesSet();
193 		MockHttpServletResponse response = new MockHttpServletResponse();
194 		filter.doFilter(request, response, chain);
195 		assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
196 		assertEquals("http://www.example.com/mycontext/secure/page.html", AbstractProcessingFilter
197 				.obtainFullRequestUrl(request));
198 	}
199 
200 	public void testRedirectedToLoginFormAndSessionShowsOriginalTargetWithExoticPortWhenAuthenticationException()
201 			throws Exception {
202 		// Setup our HTTP request
203 		MockHttpServletRequest request = new MockHttpServletRequest();
204 		request.setServletPath("/secure/page.html");
205 		request.setServerPort(8080);
206 		request.setScheme("http");
207 		request.setServerName("www.example.com");
208 		request.setContextPath("/mycontext");
209 		request.setRequestURI("/mycontext/secure/page.html");
210 
211 		// Setup the FilterChain to thrown an authentication failure exception
212 		MockFilterChain chain = new MockFilterChain(false, true, false, false);
213 
214 		// Test
215 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
216 		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
217 		filter.setPortResolver(new MockPortResolver(8080, 8443));
218 		/*
219 		 * Disabled the call to afterPropertiesSet as it requires
220 		 * applicationContext to be injected before it is invoked. We do not
221 		 * have this filter configured in IOC for this test hence no
222 		 * ApplicationContext
223 		 */
224 		// filter.afterPropertiesSet();
225 		MockHttpServletResponse response = new MockHttpServletResponse();
226 		filter.doFilter(request, response, chain);
227 		assertEquals("/mycontext/login.jsp", response.getRedirectedUrl());
228 		assertEquals("http://www.example.com:8080/mycontext/secure/page.html", AbstractProcessingFilter
229 				.obtainFullRequestUrl(request));
230 	}
231 
232 	public void testStartupDetectsMissingAuthenticationEntryPoint() throws Exception {
233 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
234 
235 		try {
236 			filter.afterPropertiesSet();
237 			fail("Should have thrown IllegalArgumentException");
238 		}
239 		catch (IllegalArgumentException expected) {
240 			assertEquals("authenticationEntryPoint must be specified", expected.getMessage());
241 		}
242 	}
243 
244 	public void testStartupDetectsMissingPortResolver() throws Exception {
245 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
246 		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
247 		filter.setPortResolver(null);
248 
249 		try {
250 			filter.afterPropertiesSet();
251 			fail("Should have thrown IllegalArgumentException");
252 		}
253 		catch (IllegalArgumentException expected) {
254 			assertEquals("portResolver must be specified", expected.getMessage());
255 		}
256 	}
257 
258 	public void testSuccessfulAccessGrant() throws Exception {
259 		// Setup our HTTP request
260 		MockHttpServletRequest request = new MockHttpServletRequest();
261 		request.setServletPath("/secure/page.html");
262 
263 		// Setup the FilterChain to thrown no exceptions
264 		MockFilterChain chain = new MockFilterChain(false, false, false, false);
265 
266 		// Test
267 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
268 		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint("/login.jsp"));
269 
270 		MockHttpServletResponse response = new MockHttpServletResponse();
271 		filter.doFilter(request, response, chain);
272 	}
273 
274 	public void testSuccessfulStartupAndShutdownDown() throws Exception {
275 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
276 
277 		filter.init(null);
278 		filter.destroy();
279 		assertTrue(true);
280 	}
281 
282 	public void testThrowIOException() throws Exception {
283 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
284 
285 		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(""));
286 		/*
287 		 * Disabled the call to afterPropertiesSet as it requires
288 		 * applicationContext to be injected before it is invoked. We do not
289 		 * have this filter configured in IOC for this test hence no
290 		 * ApplicationContext
291 		 */
292 		// filter.afterPropertiesSet();
293 		try {
294 			filter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), new MockFilterChain(false,
295 					false, false, true));
296 			fail("Should have thrown IOException");
297 		}
298 		catch (IOException e) {
299 			assertNull("The IOException thrown should not have been wrapped", e.getCause());
300 		}
301 	}
302 
303 	public void testThrowServletException() throws Exception {
304 		ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
305 
306 		filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(""));
307 		/*
308 		 * Disabled the call to afterPropertiesSet as it requires
309 		 * applicationContext to be injected before it is invoked. We do not
310 		 * have this filter configured in IOC for this test hence no
311 		 * ApplicationContext
312 		 */
313 		// filter.afterPropertiesSet();
314 		try {
315 			filter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), new MockFilterChain(false,
316 					false, true, false));
317 			fail("Should have thrown ServletException");
318 		}
319 		catch (ServletException e) {
320 			assertNull("The ServletException thrown should not have been wrapped", e.getCause());
321 		}
322 	}
323 
324 	// ~ Inner Classes
325 	// ==================================================================================================
326 
327 	private class MockFilterChain implements FilterChain {
328 		private boolean throwAccessDenied;
329 
330 		private boolean throwAuthenticationFailure;
331 
332 		private boolean throwIOException;
333 
334 		private boolean throwServletException;
335 
336 		public MockFilterChain(boolean throwAccessDenied, boolean throwAuthenticationFailure,
337 				boolean throwServletException, boolean throwIOException) {
338 			this.throwAccessDenied = throwAccessDenied;
339 			this.throwAuthenticationFailure = throwAuthenticationFailure;
340 			this.throwServletException = throwServletException;
341 			this.throwIOException = throwIOException;
342 		}
343 
344 		public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
345 			if (throwAccessDenied) {
346 				throw new AccessDeniedException("As requested");
347 			}
348 
349 			if (throwAuthenticationFailure) {
350 				throw new BadCredentialsException("As requested");
351 			}
352 
353 			if (throwServletException) {
354 				throw new ServletException("As requested");
355 			}
356 
357 			if (throwIOException) {
358 				throw new IOException("As requested");
359 			}
360 		}
361 	}
362 }