diff --git a/config/src/test/groovy/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.groovy b/config/src/test/groovy/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.groovy deleted file mode 100644 index de68710f25..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.groovy +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2002-2012 the original author or authors. - * - * 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.config.http - -import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_XML - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - -import org.springframework.mock.web.MockFilterChain -import org.springframework.mock.web.MockHttpServletRequest -import org.springframework.mock.web.MockHttpServletResponse -import org.springframework.security.authentication.TestingAuthenticationToken -import org.springframework.security.core.context.SecurityContext -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.web.access.ExceptionTranslationFilter -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter -import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler -import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler -import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter -import org.springframework.security.web.context.HttpSessionSecurityContextRepository -import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter - -/** - * - * @author Rob Winch - */ -class SecurityContextHolderAwareRequestConfigTests extends AbstractHttpConfigTests { - - def withAutoConfig() { - httpAutoConfig () { - csrf(disabled:true) - } - createAppContext(AUTH_PROVIDER_XML) - - def securityContextAwareFilter = getFilter(SecurityContextHolderAwareRequestFilter) - - expect: - securityContextAwareFilter.authenticationEntryPoint.loginFormUrl == getFilter(ExceptionTranslationFilter).authenticationEntryPoint.loginFormUrl - securityContextAwareFilter.authenticationManager == getFilter(UsernamePasswordAuthenticationFilter).authenticationManager - securityContextAwareFilter.logoutHandlers.size() == 1 - securityContextAwareFilter.logoutHandlers[0].class == SecurityContextLogoutHandler - } - - def explicitEntryPoint() { - xml.http() { - 'http-basic'('entry-point-ref': 'ep') - } - bean('ep', BasicAuthenticationEntryPoint.class.name, ['realmName':'whocares'],[:]) - createAppContext(AUTH_PROVIDER_XML) - - def securityContextAwareFilter = getFilter(SecurityContextHolderAwareRequestFilter) - - expect: - securityContextAwareFilter.authenticationEntryPoint == getFilter(ExceptionTranslationFilter).authenticationEntryPoint - securityContextAwareFilter.authenticationManager == getFilter(BasicAuthenticationFilter).authenticationManager - securityContextAwareFilter.logoutHandlers == null - } - - def formLogin() { - xml.http() { - 'form-login'() - } - createAppContext(AUTH_PROVIDER_XML) - - def securityContextAwareFilter = getFilter(SecurityContextHolderAwareRequestFilter) - - expect: - securityContextAwareFilter.authenticationEntryPoint.loginFormUrl == getFilter(ExceptionTranslationFilter).authenticationEntryPoint.loginFormUrl - securityContextAwareFilter.authenticationManager == getFilter(UsernamePasswordAuthenticationFilter).authenticationManager - securityContextAwareFilter.logoutHandlers == null - } - - def multiHttp() { - xml.http('authentication-manager-ref' : 'authManager', 'pattern' : '/first/**') { - 'form-login'('login-page' : '/login') - 'logout'('invalidate-session' : 'true') - csrf(disabled:true) - } - xml.http('authentication-manager-ref' : 'authManager2') { - 'form-login'('login-page' : '/login2') - 'logout'('invalidate-session' : 'false') - csrf(disabled:true) - } - - String secondAuthManager = AUTH_PROVIDER_XML.replace("alias='authManager'", "id='authManager2'") - createAppContext(AUTH_PROVIDER_XML + secondAuthManager) - - def securityContextAwareFilter = getFilters('/first/filters').find { it instanceof SecurityContextHolderAwareRequestFilter } - def secondSecurityContextAwareFilter = getFilter(SecurityContextHolderAwareRequestFilter) - - expect: - securityContextAwareFilter.authenticationEntryPoint.loginFormUrl == '/login' - securityContextAwareFilter.authenticationManager == getFilters('/first/filters').find { it instanceof UsernamePasswordAuthenticationFilter}.authenticationManager - securityContextAwareFilter.authenticationManager.parent == appContext.getBean('authManager') - securityContextAwareFilter.logoutHandlers.size() == 1 - securityContextAwareFilter.logoutHandlers[0].class == SecurityContextLogoutHandler - securityContextAwareFilter.logoutHandlers[0].invalidateHttpSession == true - - secondSecurityContextAwareFilter.authenticationEntryPoint.loginFormUrl == '/login2' - secondSecurityContextAwareFilter.authenticationManager == getFilter(UsernamePasswordAuthenticationFilter).authenticationManager - secondSecurityContextAwareFilter.authenticationManager.parent == appContext.getBean('authManager2') - securityContextAwareFilter.logoutHandlers.size() == 1 - secondSecurityContextAwareFilter.logoutHandlers[0].class == SecurityContextLogoutHandler - secondSecurityContextAwareFilter.logoutHandlers[0].invalidateHttpSession == false - } - - def logoutCustom() { - xml.http() { - 'form-login'('login-page' : '/login') - 'logout'('invalidate-session' : 'false', 'logout-success-url' : '/login?logout', 'delete-cookies' : 'JSESSIONID') - csrf(disabled:true) - } - createAppContext(AUTH_PROVIDER_XML) - - def securityContextAwareFilter = getFilter(SecurityContextHolderAwareRequestFilter) - - expect: - securityContextAwareFilter.authenticationEntryPoint.loginFormUrl == getFilter(ExceptionTranslationFilter).authenticationEntryPoint.loginFormUrl - securityContextAwareFilter.authenticationManager == getFilter(UsernamePasswordAuthenticationFilter).authenticationManager - securityContextAwareFilter.logoutHandlers.size() == 2 - securityContextAwareFilter.logoutHandlers[0].class == SecurityContextLogoutHandler - securityContextAwareFilter.logoutHandlers[0].invalidateHttpSession == false - securityContextAwareFilter.logoutHandlers[1].class == CookieClearingLogoutHandler - securityContextAwareFilter.logoutHandlers[1].cookiesToClear == ['JSESSIONID'] - } - - def 'SEC-2926: Role Prefix is set'() { - setup: - httpAutoConfig () { - - } - createAppContext(AUTH_PROVIDER_XML) - - MockFilterChain chain = new MockFilterChain() { - public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { - assert request.isUserInRole("USER") - - super.doFilter(request,response) - } - } - MockHttpServletRequest request = new MockHttpServletRequest(method:'GET') - SecurityContext context = SecurityContextHolder.createEmptyContext() - context.setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_USER")) - request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context) - - when: - springSecurityFilterChain.doFilter(request, new MockHttpServletResponse(), chain) - then: - chain.request != null - } -} diff --git a/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java b/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java new file mode 100644 index 0000000000..af22e07fc4 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java @@ -0,0 +1,302 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * 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.config.http; + +import org.apache.http.HttpHeaders; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.core.StringContains.containsString; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * + * @author Rob Winch + * @author Josh Cummings + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SecurityTestExecutionListeners +public class SecurityContextHolderAwareRequestConfigTests { + + private static final String CONFIG_LOCATION_PREFIX = + "classpath:org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests"; + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + private MockMvc mvc; + + @Test + public void servletLoginWhenUsingDefaultConfigurationThenUsesSpringSecurity() + throws Exception { + + this.spring.configLocations(this.xml("Simple")).autowire(); + + this.mvc.perform(get("/good-login")) + .andExpect(status().isOk()) + .andExpect(content().string("user")); + } + + @Test + public void servletAuthenticateWhenUsingDefaultConfigurationThenUsesSpringSecurity() + throws Exception { + + this.spring.configLocations(this.xml("Simple")).autowire(); + + this.mvc.perform(get("/authenticate")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("http://localhost/login")); + } + + @Test + public void servletLogoutWhenUsingDefaultConfigurationThenUsesSpringSecurity() + throws Exception { + + this.spring.configLocations(this.xml("Simple")).autowire(); + + MvcResult result = this.mvc.perform(get("/good-login")).andReturn(); + + MockHttpSession session = (MockHttpSession) result.getRequest().getSession(false); + + assertThat(session).isNotNull(); + + result = this.mvc.perform(get("/do-logout").session(session)) + .andExpect(status().isOk()) + .andExpect(content().string("")) + .andReturn(); + + session = (MockHttpSession) result.getRequest().getSession(false); + + assertThat(session).isNull(); + } + + @Test + public void servletAuthenticateWhenUsingHttpBasicThenUsesSpringSecurity() + throws Exception { + + this.spring.configLocations(this.xml("HttpBasic")).autowire(); + + this.mvc.perform(get("/authenticate")) + .andExpect(status().isUnauthorized()) + .andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString("discworld"))); + } + + @Test + public void servletAuthenticateWhenUsingFormLoginThenUsesSpringSecurity() + throws Exception { + + this.spring.configLocations(this.xml("FormLogin")).autowire(); + + this.mvc.perform(get("/authenticate")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("http://localhost/login")); + } + + @Test + public void servletLoginWhenUsingMultipleHttpConfigsThenUsesSpringSecurity() + throws Exception { + + this.spring.configLocations(this.xml("MultiHttp")).autowire(); + + this.mvc.perform(get("/good-login")) + .andExpect(status().isOk()) + .andExpect(content().string("user")); + + this.mvc.perform(get("/v2/good-login")) + .andExpect(status().isOk()) + .andExpect(content().string("user2")); + } + + @Test + public void servletAuthenticateWhenUsingMultipleHttpConfigsThenUsesSpringSecurity() + throws Exception { + + this.spring.configLocations(this.xml("MultiHttp")).autowire(); + + this.mvc.perform(get("/authenticate")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("http://localhost/login")); + + this.mvc.perform(get("/v2/authenticate")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("http://localhost/login2")); + + } + + @Test + public void servletLogoutWhenUsingMultipleHttpConfigsThenUsesSpringSecurity() + throws Exception { + + this.spring.configLocations(this.xml("MultiHttp")).autowire(); + + MvcResult result = this.mvc.perform(get("/good-login")).andReturn(); + + MockHttpSession session = (MockHttpSession) result.getRequest().getSession(false); + + assertThat(session).isNotNull(); + + result = this.mvc.perform(get("/do-logout").session(session)) + .andExpect(status().isOk()) + .andExpect(content().string("")) + .andReturn(); + + session = (MockHttpSession) result.getRequest().getSession(false); + + assertThat(session).isNotNull(); + + result = this.mvc.perform(get("/v2/good-login")).andReturn(); + + session = (MockHttpSession) result.getRequest().getSession(false); + + assertThat(session).isNotNull(); + + result = this.mvc.perform(get("/v2/do-logout").session(session)) + .andExpect(status().isOk()) + .andExpect(content().string("")) + .andReturn(); + + session = (MockHttpSession) result.getRequest().getSession(false); + + assertThat(session).isNull(); + } + + @Test + public void servletLogoutWhenUsingCustomLogoutThenUsesSpringSecurity() + throws Exception { + + this.spring.configLocations(this.xml("Logout")).autowire(); + + this.mvc.perform(get("/authenticate")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("http://localhost/signin")); + + MvcResult result = this.mvc.perform(get("/good-login")).andReturn(); + + MockHttpSession session = (MockHttpSession) result.getRequest().getSession(false); + + assertThat(session).isNotNull(); + + result = this.mvc.perform(get("/do-logout").session(session)) + .andExpect(status().isOk()) + .andExpect(content().string("")) + .andExpect(cookie().maxAge("JSESSIONID", 0)) + .andReturn(); + + session = (MockHttpSession) result.getRequest().getSession(false); + + assertThat(session).isNotNull(); + } + + /** + * SEC-2926: Role Prefix is set + */ + @Test + @WithMockUser + public void servletIsUserInRoleWhenUsingDefaultConfigThenRoleIsSet() + throws Exception { + + this.spring.configLocations(this.xml("Simple")).autowire(); + + this.mvc.perform(get("/role")).andExpect(content().string("true")); + } + + @RestController + public static class ServletAuthenticatedController { + @GetMapping("/v2/good-login") + public String v2Login(HttpServletRequest request) throws ServletException { + + request.login("user2", "password2"); + + return this.principal(); + } + + @GetMapping("/good-login") + public String login(HttpServletRequest request) throws ServletException { + + request.login("user", "password"); + + return this.principal(); + } + + @GetMapping("/v2/authenticate") + public String v2Authenticate(HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + + return this.authenticate(request, response); + } + + @GetMapping("/authenticate") + public String authenticate(HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + + request.authenticate(response); + + return this.principal(); + } + + @GetMapping("/v2/do-logout") + public String v2Logout(HttpServletRequest request) throws ServletException { + return this.logout(request); + } + + @GetMapping("/do-logout") + public String logout(HttpServletRequest request) throws ServletException { + request.logout(); + + return this.principal(); + } + + @GetMapping("/role") + public String role(HttpServletRequest request) { + return String.valueOf(request.isUserInRole("USER")); + } + + private String principal() { + if ( SecurityContextHolder.getContext().getAuthentication() != null ) { + return SecurityContextHolder.getContext().getAuthentication().getName(); + } + return null; + } + } + + private String xml(String configName) { + return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml"; + } +} diff --git a/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-FormLogin.xml b/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-FormLogin.xml new file mode 100644 index 0000000000..0caf67fac9 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-FormLogin.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-HttpBasic.xml b/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-HttpBasic.xml new file mode 100644 index 0000000000..b89d96a67a --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-HttpBasic.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-Logout.xml b/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-Logout.xml new file mode 100644 index 0000000000..d2b0532fe1 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-Logout.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-MultiHttp.xml b/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-MultiHttp.xml new file mode 100644 index 0000000000..38d74133a7 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-MultiHttp.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-Simple.xml b/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-Simple.xml new file mode 100644 index 0000000000..8d8e2473b1 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-Simple.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + +