diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/builders/DisableUseExpressionsConfig.java b/config/src/test/groovy/org/springframework/security/config/annotation/web/builders/DisableUseExpressionsConfig.java deleted file mode 100644 index 622aa3ebf4..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/builders/DisableUseExpressionsConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2002-2013 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.annotation.web.builders; - -import org.springframework.security.config.annotation.web.configuration.BaseWebConfig; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer; - -@EnableWebSecurity -public class DisableUseExpressionsConfig extends BaseWebConfig { - // @formatter:off - protected void configure(HttpSecurity http) throws Exception { - // This config is also on UrlAuthorizationConfigurer javadoc - http - .apply(new UrlAuthorizationConfigurer<>(getApplicationContext())).getRegistry() - .antMatchers("/users**", "/sessions/**").hasRole("USER") - .antMatchers("/signup").hasRole("ANONYMOUS") - .anyRequest().hasRole("USER"); - } - // @formatter:on -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.groovy deleted file mode 100644 index 4c1902b4fe..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.groovy +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Copyright 2002-2013 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.annotation.web.builders - -import javax.servlet.http.HttpServletRequest - -import org.springframework.context.annotation.Configuration -import org.springframework.security.access.AccessDecisionManager -import org.springframework.security.access.ConfigAttribute -import org.springframework.security.access.vote.AuthenticatedVoter -import org.springframework.security.access.vote.RoleVoter -import org.springframework.security.authentication.AuthenticationManager -import org.springframework.security.authentication.BadCredentialsException -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.web.builders.NamespaceHttpTests.AuthenticationManagerRefConfig.CustomAuthenticationManager -import org.springframework.security.config.annotation.web.builders.NamespaceHttpTests.RequestMatcherRefConfig.MyRequestMatcher -import org.springframework.security.config.annotation.web.configuration.BaseWebConfig -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity -import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.Authentication -import org.springframework.security.core.AuthenticationException -import org.springframework.security.web.FilterInvocation -import org.springframework.security.web.access.ExceptionTranslationFilter -import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource -import org.springframework.security.web.access.expression.WebExpressionVoter -import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource -import org.springframework.security.web.access.intercept.FilterSecurityInterceptor -import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter -import org.springframework.security.web.context.HttpSessionSecurityContextRepository -import org.springframework.security.web.context.NullSecurityContextRepository -import org.springframework.security.web.context.SecurityContextPersistenceFilter -import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter -import org.springframework.security.web.savedrequest.HttpSessionRequestCache -import org.springframework.security.web.savedrequest.NullRequestCache -import org.springframework.security.web.savedrequest.RequestCacheAwareFilter -import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter -import org.springframework.security.web.session.SessionManagementFilter -import org.springframework.security.web.util.matcher.RegexRequestMatcher -import org.springframework.security.web.util.matcher.RequestMatcher - -/** - * Tests to verify that all the functionality of attributes is present - * - * @author Rob Winch - * - */ -public class NamespaceHttpTests extends BaseSpringSpec { - def "http@access-decision-manager-ref"() { - setup: - AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR = Mock(AccessDecisionManager) - AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR.supports(FilterInvocation) >> true - AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR.supports(_ as ConfigAttribute) >> true - when: - loadConfig(AccessDecisionManagerRefConfig) - then: - findFilter(FilterSecurityInterceptor).accessDecisionManager == AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR - } - - @Configuration - static class AccessDecisionManagerRefConfig extends BaseWebConfig { - static AccessDecisionManager ACCESS_DECISION_MGR - - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().permitAll() - .accessDecisionManager(ACCESS_DECISION_MGR) - } - } - - def "http@access-denied-page"() { - when: - loadConfig(AccessDeniedPageConfig) - then: - findFilter(ExceptionTranslationFilter).accessDeniedHandler.errorPage == "/AccessDeniedPageConfig" - } - - @Configuration - static class AccessDeniedPageConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .exceptionHandling() - .accessDeniedPage("/AccessDeniedPageConfig") - } - } - - def "http@authentication-manager-ref"() { - when: "Specify AuthenticationManager" - loadConfig(AuthenticationManagerRefConfig) - then: "Populates the AuthenticationManager" - findFilter(FilterSecurityInterceptor).authenticationManager.parent.class == CustomAuthenticationManager - } - - @Configuration - static class AuthenticationManagerRefConfig extends BaseWebConfig { - // demo authentication-manager-ref (could be any value) - - @Override - protected AuthenticationManager authenticationManager() throws Exception { - return new CustomAuthenticationManager(); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().hasRole("USER"); - } - - static class CustomAuthenticationManager implements AuthenticationManager { - public Authentication authenticate(Authentication authentication) - throws AuthenticationException { - throw new BadCredentialsException("This always fails"); - } - } - } - - // Note: There is no http@auto-config equivalent in Java Config - - def "http@create-session=always"() { - when: - loadConfig(IfRequiredConfig) - then: - findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false - findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true - findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true - findFilter(ExceptionTranslationFilter).requestCache.class == HttpSessionRequestCache - } - - @Configuration - static class CreateSessionAlwaysConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.ALWAYS); - } - } - - def "http@create-session=stateless"() { - when: - loadConfig(CreateSessionStatelessConfig) - then: - findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false - findFilter(SecurityContextPersistenceFilter).repo.class == NullSecurityContextRepository - findFilter(SessionManagementFilter).securityContextRepository.class == NullSecurityContextRepository - findFilter(ExceptionTranslationFilter).requestCache.class == NullRequestCache - findFilter(RequestCacheAwareFilter).requestCache.class == NullRequestCache - } - - @Configuration - static class CreateSessionStatelessConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS); - } - } - - def "http@create-session=ifRequired"() { - when: - loadConfig(IfRequiredConfig) - then: - findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false - findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true - findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true - } - - @Configuration - static class IfRequiredConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED); - } - } - - def "http@create-session defaults to ifRequired"() { - when: - loadConfig(IfRequiredConfig) - then: - findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false - findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true - findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true - } - - def "http@create-session=never"() { - when: - loadConfig(CreateSessionNeverConfig) - then: - findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false - findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == false - findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == false - } - - @Configuration - static class CreateSessionNeverConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.NEVER); - } - } - - @Configuration - static class DefaultCreateSessionConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - } - } - - def "http@disable-url-rewriting = true (default for Java Config)"() { - when: - loadConfig(DefaultUrlRewritingConfig) - then: - findFilter(SecurityContextPersistenceFilter).repo.disableUrlRewriting - } - - @Configuration - static class DefaultUrlRewritingConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - } - } - - // http@disable-url-rewriting is on by default to disable it create a custom HttpSecurityContextRepository and use security-context-repository-ref - - def "http@disable-url-rewriting = false"() { - when: - loadConfig(EnableUrlRewritingConfig) - then: - findFilter(SecurityContextPersistenceFilter).repo.disableUrlRewriting == false - } - - @Configuration - static class EnableUrlRewritingConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - HttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository() - repository.disableUrlRewriting = false // explicitly configured - - http. - securityContext() - .securityContextRepository(repository) - } - } - - def "http@entry-point-ref"() { - when: - loadConfig(EntryPointRefConfig) - then: - findFilter(ExceptionTranslationFilter).authenticationEntryPoint.loginFormUrl == "/EntryPointRefConfig" - } - - @Configuration - static class EntryPointRefConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .exceptionHandling() - .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/EntryPointRefConfig")) - } - } - - def "http@jaas-api-provision"() { - when: - loadConfig(JaasApiProvisionConfig) - then: - findFilter(JaasApiIntegrationFilter) - } - - @Configuration - static class JaasApiProvisionConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .addFilter(new JaasApiIntegrationFilter()) - } - } - - // http@name is not available since it can be done w/ standard bean configuration easily - - def "http@once-per-request=true"() { - when: - loadConfig(OncePerRequestConfig) - then: - findFilter(FilterSecurityInterceptor).observeOncePerRequest - } - - @Configuration - static class OncePerRequestConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().hasRole("USER"); - } - } - - def "http@once-per-request=false"() { - when: - loadConfig(OncePerRequestFalseConfig) - then: - !findFilter(FilterSecurityInterceptor).observeOncePerRequest - } - - @Configuration - static class OncePerRequestFalseConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) throws Exception { - http. - authorizeRequests() - .filterSecurityInterceptorOncePerRequest(false) - .antMatchers("/users**","/sessions/**").hasRole("ADMIN") - .antMatchers("/signup").permitAll() - .anyRequest().hasRole("USER"); - } - } - - def "http@realm"() { - setup: - loadConfig(RealmConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.getHeader("WWW-Authenticate") == 'Basic realm="RealmConfig"' - } - - @Configuration - static class RealmConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().authenticated() - .and() - .httpBasic().realmName("RealmConfig") - } - } - - // http@request-matcher is not available (instead request securityMatcher instances are used) - - def "http@request-matcher-ref ant"() { - when: - loadConfig(RequestMatcherAntConfig) - then: - filterChain(0).requestMatcher.pattern == "/api/**" - } - - @Configuration - static class RequestMatcherAntConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .antMatcher("/api/**") - } - } - - def "http@request-matcher-ref regex"() { - when: - loadConfig(RequestMatcherRegexConfig) - then: - filterChain(0).requestMatcher.class == RegexRequestMatcher - filterChain(0).requestMatcher.pattern.matcher("/regex/a") - filterChain(0).requestMatcher.pattern.matcher("/regex/b") - !filterChain(0).requestMatcher.pattern.matcher("/regex1/b") - } - - @Configuration - static class RequestMatcherRegexConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .regexMatcher("/regex/.*") - } - } - - def "http@request-matcher-ref"() { - when: - loadConfig(RequestMatcherRefConfig) - then: - filterChain(0).requestMatcher.class == MyRequestMatcher - } - - @Configuration - static class RequestMatcherRefConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .requestMatcher(new MyRequestMatcher()); - } - static class MyRequestMatcher implements RequestMatcher { - public boolean matches(HttpServletRequest request) { - return true; - } - } - } - - def "http@security=none"() { - when: - loadConfig(SecurityNoneConfig) - then: - filterChain(0).requestMatcher.pattern == "/resources/**" - filterChain(0).filters.empty - filterChain(1).requestMatcher.pattern == "/public/**" - filterChain(1).filters.empty - } - - @Configuration - static class SecurityNoneConfig extends BaseWebConfig { - - @Override - public void configure(WebSecurity web) - throws Exception { - web - .ignoring() - .antMatchers("/resources/**","/public/**") - } - - @Override - protected void configure(HttpSecurity http) throws Exception {} - - } - - def "http@security-context-repository-ref"() { - when: - loadConfig(SecurityContextRepoConfig) - then: - findFilter(SecurityContextPersistenceFilter).repo.class == NullSecurityContextRepository - } - - @Configuration - static class SecurityContextRepoConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .securityContext() - .securityContextRepository(new NullSecurityContextRepository()) // security-context-repository-ref - } - } - - def "http@servlet-api-provision=false"() { - when: - loadConfig(ServletApiProvisionConfig) - then: - findFilter(SecurityContextHolderAwareRequestFilter) == null - } - - @Configuration - static class ServletApiProvisionConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http.servletApi().disable() - } - } - - def "http@servlet-api-provision defaults to true"() { - when: - loadConfig(ServletApiProvisionDefaultsConfig) - then: - findFilter(SecurityContextHolderAwareRequestFilter) != null - } - - @Configuration - static class ServletApiProvisionDefaultsConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - } - } - - def "http@use-expressions=true"() { - when: - loadConfig(UseExpressionsConfig) - then: - findFilter(FilterSecurityInterceptor).securityMetadataSource.class == ExpressionBasedFilterInvocationSecurityMetadataSource - findFilter(FilterSecurityInterceptor).accessDecisionManager.decisionVoters.collect { it.class } == [WebExpressionVoter] - } - - @EnableWebSecurity - static class UseExpressionsConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .antMatchers("/users**","/sessions/**").hasRole("USER") - .antMatchers("/signup").permitAll() - .anyRequest().hasRole("USER") - } - } - - def "http@use-expressions=false"() { - when: - loadConfig(DisableUseExpressionsConfig) - then: - findFilter(FilterSecurityInterceptor).securityMetadataSource.class == DefaultFilterInvocationSecurityMetadataSource - findFilter(FilterSecurityInterceptor).accessDecisionManager.decisionVoters.collect { it.class } == [RoleVoter, AuthenticatedVoter] - } -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java new file mode 100644 index 0000000000..4fed401271 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java @@ -0,0 +1,582 @@ +/* + * 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.annotation.web.builders; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDecisionManager; +import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.jaas.JaasAuthenticationToken; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.PasswordEncodedUser; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource; +import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource; +import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; +import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; +import org.springframework.security.web.context.NullSecurityContextRepository; +import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter; +import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RegexRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.stereotype.Controller; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.web.bind.annotation.GetMapping; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests to verify that all the functionality of attributes are present in Java Config. + * + * @author Rob Winch + * @author Joe Grandja + */ +public class NamespaceHttpTests { + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + private MockMvc mockMvc; + + @Test // http@access-decision-manager-ref + public void configureWhenAccessDecisionManagerSetThenVerifyUse() throws Exception { + AccessDecisionManagerRefConfig.ACCESS_DECISION_MANAGER = mock(AccessDecisionManager.class); + when(AccessDecisionManagerRefConfig.ACCESS_DECISION_MANAGER.supports(FilterInvocation.class)).thenReturn(true); + when(AccessDecisionManagerRefConfig.ACCESS_DECISION_MANAGER.supports(any(ConfigAttribute.class))).thenReturn(true); + + this.spring.register(AccessDecisionManagerRefConfig.class).autowire(); + + this.mockMvc.perform(get("/")); + + verify(AccessDecisionManagerRefConfig.ACCESS_DECISION_MANAGER, times(1)).decide(any(Authentication.class), any(), anyCollection()); + } + + @EnableWebSecurity + static class AccessDecisionManagerRefConfig extends WebSecurityConfigurerAdapter { + static AccessDecisionManager ACCESS_DECISION_MANAGER; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().permitAll() + .accessDecisionManager(ACCESS_DECISION_MANAGER); + } + } + + @Test // http@access-denied-page + public void configureWhenAccessDeniedPageSetAndRequestForbiddenThenForwardedToAccessDeniedPage() throws Exception { + this.spring.register(AccessDeniedPageConfig.class).autowire(); + + this.mockMvc.perform(get("/admin").with(user(PasswordEncodedUser.user()))) + .andExpect(status().isForbidden()) + .andExpect(forwardedUrl("/AccessDeniedPage")); + } + + @EnableWebSecurity + static class AccessDeniedPageConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/admin").hasRole("ADMIN") + .anyRequest().authenticated() + .and() + .exceptionHandling() + .accessDeniedPage("/AccessDeniedPage"); + } + } + + @Test // http@authentication-manager-ref + public void configureWhenAuthenticationManagerProvidedThenVerifyUse() throws Exception { + AuthenticationManagerRefConfig.AUTHENTICATION_MANAGER = mock(AuthenticationManager.class); + this.spring.register(AuthenticationManagerRefConfig.class).autowire(); + + this.mockMvc.perform(formLogin()); + + verify(AuthenticationManagerRefConfig.AUTHENTICATION_MANAGER, times(1)).authenticate(any(Authentication.class)); + } + + @EnableWebSecurity + static class AuthenticationManagerRefConfig extends WebSecurityConfigurerAdapter { + static AuthenticationManager AUTHENTICATION_MANAGER; + + @Override + protected AuthenticationManager authenticationManager() throws Exception { + return AUTHENTICATION_MANAGER; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .formLogin(); + } + } + + @Test // http@create-session=always + public void configureWhenSessionCreationPolicyAlwaysThenSessionCreatedOnRequest() throws Exception { + this.spring.register(CreateSessionAlwaysConfig.class).autowire(); + + MvcResult mvcResult = this.mockMvc.perform(get("/")).andReturn(); + HttpSession session = mvcResult.getRequest().getSession(false); + + assertThat(session).isNotNull(); + assertThat(session.isNew()).isTrue(); + } + + @EnableWebSecurity + static class CreateSessionAlwaysConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().permitAll() + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.ALWAYS); + } + } + + @Test // http@create-session=stateless + public void configureWhenSessionCreationPolicyStatelessThenSessionNotCreatedOnRequest() throws Exception { + this.spring.register(CreateSessionStatelessConfig.class).autowire(); + + MvcResult mvcResult = this.mockMvc.perform(get("/")).andReturn(); + HttpSession session = mvcResult.getRequest().getSession(false); + + assertThat(session).isNull(); + } + + @EnableWebSecurity + static class CreateSessionStatelessConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().permitAll() + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS); + } + } + + @Test // http@create-session=ifRequired + public void configureWhenSessionCreationPolicyIfRequiredThenSessionCreatedWhenRequiredOnRequest() throws Exception { + this.spring.register(IfRequiredConfig.class).autowire(); + + MvcResult mvcResult = this.mockMvc.perform(get("/unsecure")).andReturn(); + HttpSession session = mvcResult.getRequest().getSession(false); + + assertThat(session).isNull(); + + mvcResult = this.mockMvc.perform(formLogin()).andReturn(); + session = mvcResult.getRequest().getSession(false); + + assertThat(session).isNotNull(); + assertThat(session.isNew()).isTrue(); + } + + @EnableWebSecurity + static class IfRequiredConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/unsecure").permitAll() + .anyRequest().authenticated() + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) + .and() + .formLogin(); + } + } + + @Test // http@create-session=never + public void configureWhenSessionCreationPolicyNeverThenSessionNotCreatedOnRequest() throws Exception { + this.spring.register(CreateSessionNeverConfig.class).autowire(); + + MvcResult mvcResult = this.mockMvc.perform(get("/")).andReturn(); + HttpSession session = mvcResult.getRequest().getSession(false); + + assertThat(session).isNull(); + } + + @EnableWebSecurity + static class CreateSessionNeverConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().anonymous() + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.NEVER); + } + } + + @Test // http@entry-point-ref + public void configureWhenAuthenticationEntryPointSetAndRequestUnauthorizedThenRedirectedToAuthenticationEntryPoint() throws Exception { + this.spring.register(EntryPointRefConfig.class).autowire(); + + this.mockMvc.perform(get("/")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("**/entry-point")); + } + + @EnableWebSecurity + static class EntryPointRefConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .exceptionHandling() + .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/entry-point")) + .and() + .formLogin(); + } + } + + @Test // http@jaas-api-provision + public void configureWhenJaasApiIntegrationFilterAddedThenJaasSubjectObtained() throws Exception { + LoginContext loginContext = mock(LoginContext.class); + when(loginContext.getSubject()).thenReturn(new Subject()); + + JaasAuthenticationToken authenticationToken = mock(JaasAuthenticationToken.class); + when(authenticationToken.isAuthenticated()).thenReturn(true); + when(authenticationToken.getLoginContext()).thenReturn(loginContext); + + this.spring.register(JaasApiProvisionConfig.class).autowire(); + + this.mockMvc.perform(get("/").with(authentication(authenticationToken))); + + verify(loginContext, times(1)).getSubject(); + } + + @EnableWebSecurity + static class JaasApiProvisionConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .addFilter(new JaasApiIntegrationFilter()); + } + } + + @Test // http@realm + public void configureWhenHttpBasicAndRequestUnauthorizedThenReturnWWWAuthenticateWithRealm() throws Exception { + this.spring.register(RealmConfig.class).autowire(); + + this.mockMvc.perform(get("/")) + .andExpect(status().isUnauthorized()) + .andExpect(header().string("WWW-Authenticate", "Basic realm=\"RealmConfig\"")); + } + + @EnableWebSecurity + static class RealmConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .httpBasic() + .realmName("RealmConfig"); + } + } + + @Test // http@request-matcher-ref ant + public void configureWhenAntPatternMatchingThenAntPathRequestMatcherUsed() throws Exception { + this.spring.register(RequestMatcherAntConfig.class).autowire(); + + FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class); + + assertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class); + DefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(0); + assertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(AntPathRequestMatcher.class); + } + + @EnableWebSecurity + static class RequestMatcherAntConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .antMatcher("/api/**"); + } + } + + @Test // http@request-matcher-ref regex + public void configureWhenRegexPatternMatchingThenRegexRequestMatcherUsed() throws Exception { + this.spring.register(RequestMatcherRegexConfig.class).autowire(); + + FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class); + + assertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class); + DefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(0); + assertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(RegexRequestMatcher.class); + } + + @EnableWebSecurity + static class RequestMatcherRegexConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .regexMatcher("/regex/.*"); + } + } + + @Test // http@request-matcher-ref + public void configureWhenRequestMatcherProvidedThenRequestMatcherUsed() throws Exception { + this.spring.register(RequestMatcherRefConfig.class).autowire(); + + FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class); + + assertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class); + DefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(0); + assertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(RequestMatcherRefConfig.MyRequestMatcher.class); + } + + @EnableWebSecurity + static class RequestMatcherRefConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .requestMatcher(new MyRequestMatcher()); + } + + static class MyRequestMatcher implements RequestMatcher { + public boolean matches(HttpServletRequest request) { + return true; + } + } + } + + @Test // http@security=none + public void configureWhenIgnoredAntPatternsThenAntPathRequestMatcherUsedWithNoFilters() throws Exception { + this.spring.register(SecurityNoneConfig.class).autowire(); + + FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class); + + assertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class); + DefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(0); + assertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(AntPathRequestMatcher.class); + assertThat(((AntPathRequestMatcher) securityFilterChain.getRequestMatcher()).getPattern()).isEqualTo("/resources/**"); + assertThat(securityFilterChain.getFilters()).isEmpty(); + + assertThat(filterChainProxy.getFilterChains().get(1)).isInstanceOf(DefaultSecurityFilterChain.class); + securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(1); + assertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(AntPathRequestMatcher.class); + assertThat(((AntPathRequestMatcher) securityFilterChain.getRequestMatcher()).getPattern()).isEqualTo("/public/**"); + assertThat(securityFilterChain.getFilters()).isEmpty(); + } + + @EnableWebSecurity + static class SecurityNoneConfig extends WebSecurityConfigurerAdapter { + + @Override + public void configure(WebSecurity web) throws Exception { + web + .ignoring() + .antMatchers("/resources/**", "/public/**"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + } + } + + @Test // http@security-context-repository-ref + public void configureWhenNullSecurityContextRepositoryThenSecurityContextNotSavedInSession() throws Exception { + this.spring.register(SecurityContextRepoConfig.class).autowire(); + + MvcResult mvcResult = this.mockMvc.perform(formLogin()).andReturn(); + HttpSession session = mvcResult.getRequest().getSession(false); + assertThat(session).isNull(); + } + + @EnableWebSecurity + static class SecurityContextRepoConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .securityContext() + .securityContextRepository(new NullSecurityContextRepository()) + .and() + .formLogin(); + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()); + } + } + + @Test // http@servlet-api-provision=false + public void configureWhenServletApiDisabledThenRequestNotServletApiWrapper() throws Exception { + this.spring.register(ServletApiProvisionConfig.class, MainController.class).autowire(); + + this.mockMvc.perform(get("/")); + + assertThat(MainController.HTTP_SERVLET_REQUEST_TYPE).isNotInstanceOf(SecurityContextHolderAwareRequestWrapper.class); + } + + @EnableWebSecurity + static class ServletApiProvisionConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().permitAll() + .and() + .servletApi() + .disable(); + } + } + + @Test // http@servlet-api-provision defaults to true + public void configureWhenServletApiDefaultThenRequestIsServletApiWrapper() throws Exception { + this.spring.register(ServletApiProvisionDefaultsConfig.class, MainController.class).autowire(); + + this.mockMvc.perform(get("/")); + + assertThat(SecurityContextHolderAwareRequestWrapper.class).isAssignableFrom(MainController.HTTP_SERVLET_REQUEST_TYPE); + } + + @EnableWebSecurity + static class ServletApiProvisionDefaultsConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().permitAll(); + } + } + + @Controller + static class MainController { + static Class HTTP_SERVLET_REQUEST_TYPE; + + @GetMapping("/") + public String index(HttpServletRequest request) { + HTTP_SERVLET_REQUEST_TYPE = request.getClass(); + return "index"; + } + } + + @Test // http@use-expressions=true + public void configureWhenUseExpressionsEnabledThenExpressionBasedSecurityMetadataSource() throws Exception { + this.spring.register(UseExpressionsConfig.class).autowire(); + + UseExpressionsConfig config = this.spring.getContext().getBean(UseExpressionsConfig.class); + + assertThat(ExpressionBasedFilterInvocationSecurityMetadataSource.class) + .isAssignableFrom(config.filterInvocationSecurityMetadataSourceType); + } + + @EnableWebSecurity + static class UseExpressionsConfig extends WebSecurityConfigurerAdapter { + private Class filterInvocationSecurityMetadataSourceType; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/users**", "/sessions/**").hasRole("USER") + .antMatchers("/signup").permitAll() + .anyRequest().hasRole("USER"); + } + + @Override + public void init(final WebSecurity web) throws Exception { + super.init(web); + final HttpSecurity http = this.getHttp(); + web.postBuildAction(() -> { + FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class); + UseExpressionsConfig.this.filterInvocationSecurityMetadataSourceType = + securityInterceptor.getSecurityMetadataSource().getClass(); + }); + } + } + + @Test // http@use-expressions=false + public void configureWhenUseExpressionsDisabledThenDefaultSecurityMetadataSource() throws Exception { + this.spring.register(DisableUseExpressionsConfig.class).autowire(); + + DisableUseExpressionsConfig config = this.spring.getContext().getBean(DisableUseExpressionsConfig.class); + + assertThat(DefaultFilterInvocationSecurityMetadataSource.class) + .isAssignableFrom(config.filterInvocationSecurityMetadataSourceType); + } + + @EnableWebSecurity + static class DisableUseExpressionsConfig extends WebSecurityConfigurerAdapter { + private Class filterInvocationSecurityMetadataSourceType; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .apply(new UrlAuthorizationConfigurer<>(getApplicationContext())).getRegistry() + .antMatchers("/users**", "/sessions/**").hasRole("USER") + .antMatchers("/signup").hasRole("ANONYMOUS") + .anyRequest().hasRole("USER"); + } + + @Override + public void init(final WebSecurity web) throws Exception { + super.init(web); + final HttpSecurity http = this.getHttp(); + web.postBuildAction(() -> { + FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class); + DisableUseExpressionsConfig.this.filterInvocationSecurityMetadataSourceType = + securityInterceptor.getSecurityMetadataSource().getClass(); + }); + } + } +}