/* * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.security.web.authentication.www; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.AdditionalMatchers.not; import static org.mockito.Mockito.*; import javax.servlet.FilterChain; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.commons.codec.binary.Base64; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpSession; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.web.util.WebUtils; /** * Tests {@link BasicAuthenticationFilter}. * * @author Ben Alex */ public class BasicAuthenticationFilterTests { // ~ Instance fields // ================================================================================================ private BasicAuthenticationFilter filter; private AuthenticationManager manager; // ~ Methods // ======================================================================================================== @Before public void setUp() throws Exception { SecurityContextHolder.clearContext(); UsernamePasswordAuthenticationToken rodRequest = new UsernamePasswordAuthenticationToken( "rod", "koala"); rodRequest.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest())); Authentication rod = new UsernamePasswordAuthenticationToken("rod", "koala", AuthorityUtils.createAuthorityList("ROLE_1")); manager = mock(AuthenticationManager.class); when(manager.authenticate(rodRequest)).thenReturn(rod); when(manager.authenticate(not(eq(rodRequest)))).thenThrow( new BadCredentialsException("")); filter = new BasicAuthenticationFilter(manager, new BasicAuthenticationEntryPoint()); } @After public void clearContext() throws Exception { SecurityContextHolder.clearContext(); } @Test public void testFilterIgnoresRequestsContainingNoAuthorizationHeader() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.setServletPath("/some_file.html"); final MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, response, chain); verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); // Test assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); } @Test public void testGettersSetters() { assertThat(filter.getAuthenticationManager()).isNotNull(); assertThat(filter.getAuthenticationEntryPoint()).isNotNull(); } @Test public void testInvalidBasicAuthorizationTokenIsIgnored() throws Exception { String token = "NOT_A_VALID_TOKEN_AS_MISSING_COLON"; MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.setServletPath("/some_file.html"); request.setSession(new MockHttpSession()); final MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, response, chain); verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); assertThat(response.getStatus()).isEqualTo(401); } @Test public void invalidBase64IsIgnored() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Authorization", "Basic NOT_VALID_BASE64"); request.setServletPath("/some_file.html"); request.setSession(new MockHttpSession()); final MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, response, chain); // The filter chain shouldn't proceed verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); assertThat(response.getStatus()).isEqualTo(401); } @Test public void testNormalOperation() throws Exception { String token = "rod:koala"; MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.setServletPath("/some_file.html"); // Test assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, new MockHttpServletResponse(), chain); verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); assertThat(SecurityContextHolder.getContext().getAuthentication().getName()) .isEqualTo("rod"); } @Test public void testOtherAuthorizationSchemeIsIgnored() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Authorization", "SOME_OTHER_AUTHENTICATION_SCHEME"); request.setServletPath("/some_file.html"); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, new MockHttpServletResponse(), chain); verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); } @Test(expected = IllegalArgumentException.class) public void testStartupDetectsMissingAuthenticationEntryPoint() throws Exception { new BasicAuthenticationFilter(manager, null); } @Test(expected = IllegalArgumentException.class) public void testStartupDetectsMissingAuthenticationManager() throws Exception { BasicAuthenticationFilter filter = new BasicAuthenticationFilter(null); } @Test public void testSuccessLoginThenFailureLoginResultsInSessionLosingToken() throws Exception { String token = "rod:koala"; MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.setServletPath("/some_file.html"); final MockHttpServletResponse response1 = new MockHttpServletResponse(); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, response1, chain); verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); // Test assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); assertThat(SecurityContextHolder.getContext().getAuthentication().getName()) .isEqualTo("rod"); // NOW PERFORM FAILED AUTHENTICATION token = "otherUser:WRONG_PASSWORD"; request = new MockHttpServletRequest(); request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); final MockHttpServletResponse response2 = new MockHttpServletResponse(); chain = mock(FilterChain.class); filter.doFilter(request, response2, chain); verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); request.setServletPath("/some_file.html"); // Test - the filter chain will not be invoked, as we get a 401 forbidden response MockHttpServletResponse response = response2; assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); assertThat(response.getStatus()).isEqualTo(401); } @Test public void testWrongPasswordContinuesFilterChainIfIgnoreFailureIsTrue() throws Exception { String token = "rod:WRONG_PASSWORD"; MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.setServletPath("/some_file.html"); request.setSession(new MockHttpSession()); filter = new BasicAuthenticationFilter(manager); assertThat(filter.isIgnoreFailure()).isTrue(); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, new MockHttpServletResponse(), chain); verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); // Test - the filter chain will be invoked, as we've set ignoreFailure = true assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); } @Test public void testWrongPasswordReturnsForbiddenIfIgnoreFailureIsFalse() throws Exception { String token = "rod:WRONG_PASSWORD"; MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.setServletPath("/some_file.html"); request.setSession(new MockHttpSession()); assertThat(filter.isIgnoreFailure()).isFalse(); final MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, response, chain); // Test - the filter chain will not be invoked, as we get a 401 forbidden response verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); assertThat(response.getStatus()).isEqualTo(401); } // SEC-2054 @Test public void skippedOnErrorDispatch() throws Exception { String token = "bad:credentials"; MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes()))); request.setServletPath("/some_file.html"); request.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, "/error"); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, response, chain); assertThat(response.getStatus()).isEqualTo(200); } }