From cf0c5f90260dd5e7766285aa48e7933d7fd914f1 Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Tue, 26 Mar 2019 17:44:51 -0600 Subject: [PATCH] Various NamespaceHttp*Tests groovy->java Issue: gh-4939 --- .../NamespaceHttpCustomFilterTests.groovy | 197 ------------ .../NamespaceHttpFirewallTests.groovy | 134 -------- .../NamespaceHttpFormLoginTests.groovy | 170 ---------- .../NamespaceHttpHeadersTests.groovy | 267 ---------------- .../NamespaceHttpInterceptUrlTests.groovy | 169 ---------- .../configurers/NamespaceHttpJeeTests.groovy | 142 --------- .../NamespaceHttpPortMappingsTests.groovy | 115 ------- .../NamespaceHttpRequestCacheTests.groovy | 73 ----- ...eHttpServerAccessDeniedHandlerTests.groovy | 80 ----- .../NamespaceHttpCustomFilterTests.java | 194 ++++++++++++ .../NamespaceHttpFirewallTests.java | 104 +++++++ .../NamespaceHttpFormLoginTests.java | 197 ++++++++++++ .../NamespaceHttpHeadersTests.java | 293 ++++++++++++++++++ .../NamespaceHttpInterceptUrlTests.java | 187 +++++++++++ .../configurers/NamespaceHttpJeeTests.java | 159 ++++++++++ .../NamespaceHttpPortMappingsTests.java | 86 +++++ .../NamespaceHttpRequestCacheTests.java | 123 ++++++++ ...aceHttpServerAccessDeniedHandlerTests.java | 115 +++++++ 18 files changed, 1458 insertions(+), 1347 deletions(-) delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.groovy delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFirewallTests.groovy delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.groovy delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.groovy delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpInterceptUrlTests.groovy delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpJeeTests.groovy delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpPortMappingsTests.groovy delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpRequestCacheTests.groovy delete mode 100644 config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.groovy create mode 100644 config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.java create mode 100644 config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFirewallTests.java create mode 100644 config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java create mode 100644 config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.java create mode 100644 config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpInterceptUrlTests.java create mode 100644 config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpJeeTests.java create mode 100644 config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpPortMappingsTests.java create mode 100644 config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpRequestCacheTests.java create mode 100644 config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.groovy deleted file mode 100644 index 53f1c821e0..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.groovy +++ /dev/null @@ -1,197 +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 - * - * https://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.configurers; - -import java.io.IOException; - -import javax.servlet.FilterChain -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.security.access.AccessDecisionManager -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.access.ConfigAttribute -import org.springframework.security.authentication.AnonymousAuthenticationToken -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -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.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.web.AuthenticationEntryPoint -import org.springframework.security.web.FilterInvocation -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.security.web.access.AccessDeniedHandlerImpl; -import org.springframework.security.web.access.ExceptionTranslationFilter -import org.springframework.security.web.access.intercept.FilterSecurityInterceptor -import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; -import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; -import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -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.servletapi.SecurityContextHolderAwareRequestFilter; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher -import org.springframework.security.web.util.matcher.AnyRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher -import org.springframework.web.filter.OncePerRequestFilter - -import spock.lang.Ignore; - -/** - * Tests to verify that all the functionality of attributes is present - * - * @author Rob Winch - * - */ -public class NamespaceHttpCustomFilterTests extends BaseSpringSpec { - def "http/custom-filter@before"() { - when: - loadConfig(CustomFilterBeforeConfig) - then: - filterChain().filters[0].class == CustomFilter - } - - @Configuration - static class CustomFilterBeforeConfig extends BaseWebConfig { - CustomFilterBeforeConfig() { - // do not add the default filters to make testing easier - super(true) - } - - protected void configure(HttpSecurity http) { - http - .addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class) - .formLogin() - } - } - - def "http/custom-filter@after"() { - when: - loadConfig(CustomFilterAfterConfig) - then: - filterChain().filters[1].class == CustomFilter - } - - @Configuration - static class CustomFilterAfterConfig extends BaseWebConfig { - CustomFilterAfterConfig() { - // do not add the default filters to make testing easier - super(true) - } - - protected void configure(HttpSecurity http) { - http - .addFilterAfter(new CustomFilter(), UsernamePasswordAuthenticationFilter.class) - .formLogin() - } - } - - def "http/custom-filter@position"() { - when: - loadConfig(CustomFilterPositionConfig) - then: - filterChain().filters.collect { it.class } == [CustomFilter] - } - - @Configuration - static class CustomFilterPositionConfig extends BaseWebConfig { - CustomFilterPositionConfig() { - // do not add the default filters to make testing easier - super(true) - } - - protected void configure(HttpSecurity http) { - http - // this works so long as the CustomFilter extends one of the standard filters - // if not, use addFilterBefore or addFilterAfter - .addFilter(new CustomFilter()) - } - } - - def "http/custom-filter@position at"() { - when: - loadConfig(CustomFilterPositionAtConfig) - then: - filterChain().filters.collect { it.class } == [OtherCustomFilter] - } - - @Configuration - static class CustomFilterPositionAtConfig extends BaseWebConfig { - CustomFilterPositionAtConfig() { - // do not add the default filters to make testing easier - super(true) - } - - protected void configure(HttpSecurity http) { - http - .addFilterAt(new OtherCustomFilter(), UsernamePasswordAuthenticationFilter.class) - } - } - - def "http/custom-filter no AuthenticationManager in HttpSecurity"() { - when: - loadConfig(NoAuthenticationManagerInHtppConfigurationConfig) - then: - filterChain().filters[0].class == CustomFilter - } - - @EnableWebSecurity - static class NoAuthenticationManagerInHtppConfigurationConfig extends WebSecurityConfigurerAdapter { - NoAuthenticationManagerInHtppConfigurationConfig() { - super(true) - } - - protected AuthenticationManager authenticationManager() - throws Exception { - return new CustomAuthenticationManager(); - } - - @Override - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class) - } - } - - static class CustomFilter extends UsernamePasswordAuthenticationFilter {} - static class OtherCustomFilter extends OncePerRequestFilter { - protected void doFilterInternal( - HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - filterChain.doFilter(request,response); - } - } - - static class CustomAuthenticationManager implements AuthenticationManager { - public Authentication authenticate(Authentication authentication) - throws AuthenticationException { - return null; - } - } -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFirewallTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFirewallTests.groovy deleted file mode 100644 index eab27e4805..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFirewallTests.groovy +++ /dev/null @@ -1,134 +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 - * - * https://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.configurers - -import org.springframework.context.annotation.Bean; - -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - -import org.springframework.context.annotation.Configuration -import org.springframework.mock.web.MockFilterChain -import org.springframework.mock.web.MockHttpServletRequest -import org.springframework.mock.web.MockHttpServletResponse -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.builders.WebSecurity -import org.springframework.security.config.annotation.web.configuration.BaseWebConfig -import org.springframework.security.web.FilterChainProxy -import org.springframework.security.web.firewall.DefaultHttpFirewall -import org.springframework.security.web.firewall.FirewalledRequest -import org.springframework.security.web.firewall.RequestRejectedException - -/** - * Tests to verify that all the functionality of attributes is present - * - * @author Rob Winch - * - */ -public class NamespaceHttpFirewallTests extends BaseSpringSpec { - FilterChainProxy springSecurityFilterChain - MockHttpServletRequest request - MockHttpServletResponse response - MockFilterChain chain - - def setup() { - request = new MockHttpServletRequest("GET", "") - response = new MockHttpServletResponse() - chain = new MockFilterChain() - } - - def "http-firewall"() { - setup: - loadConfig(HttpFirewallConfig) - springSecurityFilterChain = context.getBean(FilterChainProxy) - request.setPathInfo("/public/../private/") - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: "the default firewall is used" - thrown(RequestRejectedException) - } - - @Configuration - static class HttpFirewallConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) { - } - } - - def "http-firewall@ref"() { - setup: - loadConfig(CustomHttpFirewallConfig) - springSecurityFilterChain = context.getBean(FilterChainProxy) - request.setParameter("deny", "true") - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: "the custom firewall is used" - thrown(RequestRejectedException) - } - - @Configuration - static class CustomHttpFirewallConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { } - - @Override - public void configure(WebSecurity builder) throws Exception { - builder - .httpFirewall(new CustomHttpFirewall()) - } - } - - def "http-firewall bean"() { - setup: - loadConfig(CustomHttpFirewallBeanConfig) - springSecurityFilterChain = context.getBean(FilterChainProxy) - request.setParameter("deny", "true") - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: "the custom firewall is used" - thrown(RequestRejectedException) - } - - @Configuration - static class CustomHttpFirewallBeanConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { } - - @Bean - CustomHttpFirewall firewall() { - return new CustomHttpFirewall(); - } - } - - static class CustomHttpFirewall extends DefaultHttpFirewall { - - @Override - public FirewalledRequest getFirewalledRequest(HttpServletRequest request) - throws RequestRejectedException { - if(request.getParameter("deny")) { - throw new RequestRejectedException("custom rejection") - } - return super.getFirewalledRequest(request) - } - - @Override - public HttpServletResponse getFirewalledResponse( - HttpServletResponse response) { - return super.getFirewalledRequest(response) - } - - } -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.groovy deleted file mode 100644 index e8f3025022..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.groovy +++ /dev/null @@ -1,170 +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 - * - * https://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.configurers - -import org.springframework.context.annotation.Configuration -import org.springframework.mock.web.MockFilterChain -import org.springframework.mock.web.MockHttpServletRequest -import org.springframework.mock.web.MockHttpServletResponse -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.builders.WebSecurity -import org.springframework.security.config.annotation.web.configuration.BaseWebConfig -import org.springframework.security.web.FilterChainProxy -import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler -import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource - -/** - * Tests to verify that all the functionality of attributes is present - * - * @author Rob Winch - * - */ -public class NamespaceHttpFormLoginTests extends BaseSpringSpec { - FilterChainProxy springSecurityFilterChain - - def "http/form-login"() { - setup: - loadConfig(FormLoginConfig) - springSecurityFilterChain = context.getBean(FilterChainProxy) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.getRedirectedUrl() == "http://localhost/login" - when: "fail to log in" - super.setup() - request.servletPath = "/login" - request.method = "POST" - springSecurityFilterChain.doFilter(request,response,chain) - then: "sent to login error page" - response.getRedirectedUrl() == "/login?error" - when: "login success" - super.setup() - request.servletPath = "/login" - request.method = "POST" - request.parameters.username = ["user"] as String[] - request.parameters.password = ["password"] as String[] - springSecurityFilterChain.doFilter(request,response,chain) - then: "sent to default succes page" - response.getRedirectedUrl() == "/" - } - - @Configuration - static class FormLoginConfig extends BaseWebConfig { - - @Override - public void configure(WebSecurity web) throws Exception { - web - .ignoring() - .antMatchers("/resources/**"); - } - - @Override - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .formLogin() - } - } - - def "http/form-login custom"() { - setup: - loadConfig(FormLoginCustomConfig) - springSecurityFilterChain = context.getBean(FilterChainProxy) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.getRedirectedUrl() == "http://localhost/authentication/login" - when: "fail to log in" - super.setup() - request.servletPath = "/authentication/login/process" - request.method = "POST" - springSecurityFilterChain.doFilter(request,response,chain) - then: "sent to login error page" - response.getRedirectedUrl() == "/authentication/login?failed" - when: "login success" - super.setup() - request.servletPath = "/authentication/login/process" - request.method = "POST" - request.parameters.username = ["user"] as String[] - request.parameters.password = ["password"] as String[] - springSecurityFilterChain.doFilter(request,response,chain) - then: "sent to default succes page" - response.getRedirectedUrl() == "/default" - } - - @Configuration - static class FormLoginCustomConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - boolean alwaysUseDefaultSuccess = true; - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .formLogin() - .usernameParameter("username") // form-login@username-parameter - .passwordParameter("password") // form-login@password-parameter - .loginPage("/authentication/login") // form-login@login-page - .failureUrl("/authentication/login?failed") // form-login@authentication-failure-url - .loginProcessingUrl("/authentication/login/process") // form-login@login-processing-url - .defaultSuccessUrl("/default", alwaysUseDefaultSuccess) // form-login@default-target-url / form-login@always-use-default-target - } - } - - def "http/form-login custom refs"() { - when: - loadConfig(FormLoginCustomRefsConfig) - springSecurityFilterChain = context.getBean(FilterChainProxy) - then: "CustomWebAuthenticationDetailsSource is used" - findFilter(UsernamePasswordAuthenticationFilter).authenticationDetailsSource.class == CustomWebAuthenticationDetailsSource - when: "fail to log in" - request.servletPath = "/login" - request.method = "POST" - springSecurityFilterChain.doFilter(request,response,chain) - then: "sent to login error page" - response.getRedirectedUrl() == "/custom/failure" - when: "login success" - super.setup() - request.servletPath = "/login" - request.method = "POST" - request.parameters.username = ["user"] as String[] - request.parameters.password = ["password"] as String[] - springSecurityFilterChain.doFilter(request,response,chain) - then: "sent to default succes page" - response.getRedirectedUrl() == "/custom/targetUrl" - } - - @Configuration - static class FormLoginCustomRefsConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) throws Exception { - http - .formLogin() - .loginPage("/login") - .failureHandler(new SimpleUrlAuthenticationFailureHandler("/custom/failure")) // form-login@authentication-failure-handler-ref - .successHandler(new SavedRequestAwareAuthenticationSuccessHandler( defaultTargetUrl : "/custom/targetUrl" )) // form-login@authentication-success-handler-ref - .authenticationDetailsSource(new CustomWebAuthenticationDetailsSource()) // form-login@authentication-details-source-ref - .and(); - } - } - - static class CustomWebAuthenticationDetailsSource extends WebAuthenticationDetailsSource {} -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.groovy deleted file mode 100644 index 34d0e4d611..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.groovy +++ /dev/null @@ -1,267 +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 - * - * https://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.configurers; - -import org.springframework.context.annotation.Configuration -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.configuration.BaseWebConfig -import org.springframework.security.web.header.writers.CacheControlHeadersWriter -import org.springframework.security.web.header.writers.HstsHeaderWriter -import org.springframework.security.web.header.writers.StaticHeadersWriter -import org.springframework.security.web.header.writers.XContentTypeOptionsHeaderWriter -import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter -import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy -import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter -import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode -import org.springframework.security.web.util.matcher.AnyRequestMatcher -import org.springframework.security.web.util.matcher.RequestMatcher; - -/** - * Tests to verify that all the functionality of attributes is present - * - * @author Rob Winch - * - */ -public class NamespaceHttpHeadersTests extends BaseSpringSpec { - - def "http/headers"() { - setup: - loadConfig(HeadersDefaultConfig) - request.secure = true - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - responseHeaders == ['X-Content-Type-Options':'nosniff', - 'X-Frame-Options':'DENY', - 'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains', - 'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', - 'Pragma':'no-cache', - 'Expires' : '0', - 'X-XSS-Protection' : '1; mode=block'] - } - - @Configuration - static class HeadersDefaultConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { - http - .headers() - } - } - - def "http/headers/cache-control"() { - setup: - loadConfig(HeadersCacheControlConfig) - request.secure = true - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - responseHeaders == ['Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', - 'Expires' : '0', - 'Pragma':'no-cache'] - } - - @Configuration - static class HeadersCacheControlConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { - http - .headers() - .defaultsDisabled() - .cacheControl() - } - } - - def "http/headers/hsts"() { - setup: - loadConfig(HstsConfig) - request.secure = true - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - responseHeaders == ['Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains'] - } - - @Configuration - static class HstsConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { - http - .headers() - .defaultsDisabled() - .httpStrictTransportSecurity() - } - } - - def "http/headers/hsts custom"() { - setup: - loadConfig(HstsCustomConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - responseHeaders == ['Strict-Transport-Security': 'max-age=15768000'] - } - - @Configuration - static class HstsCustomConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { - http - .headers() - // hsts@request-matcher-ref, hsts@max-age-seconds, hsts@include-subdomains - .defaultsDisabled() - .httpStrictTransportSecurity() - .requestMatcher(AnyRequestMatcher.INSTANCE) - .maxAgeInSeconds(15768000) - .includeSubDomains(false) - } - } - - def "http/headers/frame-options@policy=SAMEORIGIN"() { - setup: - loadConfig(FrameOptionsSameOriginConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - responseHeaders == ['X-Frame-Options': 'SAMEORIGIN'] - } - - @Configuration - static class FrameOptionsSameOriginConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { - http - .headers() - // frame-options@policy=SAMEORIGIN - .defaultsDisabled() - .frameOptions() - .sameOrigin() - } - } - - // frame-options@strategy, frame-options@value, frame-options@parameter are not provided instead use frame-options@ref - - def "http/headers/frame-options"() { - setup: - loadConfig(FrameOptionsAllowFromConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - responseHeaders == ['X-Frame-Options': 'ALLOW-FROM https://example.com'] - } - - - @Configuration - static class FrameOptionsAllowFromConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { - http - .headers() - // frame-options@ref - .defaultsDisabled() - .addHeaderWriter(new XFrameOptionsHeaderWriter(new StaticAllowFromStrategy(new URI("https://example.com")))) - } - } - - def "http/headers/xss-protection"() { - setup: - loadConfig(XssProtectionConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - responseHeaders == ['X-XSS-Protection': '1; mode=block'] - } - - @Configuration - static class XssProtectionConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { - http - .headers() - // xss-protection - .defaultsDisabled() - .xssProtection() - } - } - - def "http/headers/xss-protection custom"() { - setup: - loadConfig(XssProtectionCustomConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - responseHeaders == ['X-XSS-Protection': '1'] - } - - @Configuration - static class XssProtectionCustomConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { - http - .headers() - // xss-protection@enabled and xss-protection@block - .defaultsDisabled() - .xssProtection() - .xssProtectionEnabled(true) - .block(false) - } - } - - def "http/headers/content-type-options"() { - setup: - loadConfig(ContentTypeOptionsConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - responseHeaders == ['X-Content-Type-Options': 'nosniff'] - } - - @Configuration - static class ContentTypeOptionsConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { - http - .headers() - // content-type-options - .defaultsDisabled() - .contentTypeOptions() - } - } - - // header@name / header@value are not provided instead use header@ref - - def "http/headers/header@ref"() { - setup: - loadConfig(HeaderRefConfig) - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - responseHeaders == ['customHeaderName': 'customHeaderValue'] - } - - @Configuration - static class HeaderRefConfig extends BaseWebConfig { - @Override - protected void configure(HttpSecurity http) { - http - .headers() - .defaultsDisabled() - .addHeaderWriter(new StaticHeadersWriter("customHeaderName", "customHeaderValue")) - } - } - -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpInterceptUrlTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpInterceptUrlTests.groovy deleted file mode 100644 index fc822e3147..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpInterceptUrlTests.groovy +++ /dev/null @@ -1,169 +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 - * - * https://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.configurers - -import javax.servlet.http.HttpServletResponse - -import org.springframework.context.annotation.Configuration -import org.springframework.http.HttpMethod; -import org.springframework.mock.web.MockFilterChain -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContextImpl -import org.springframework.security.web.FilterChainProxy -import org.springframework.security.web.context.HttpRequestResponseHolder -import org.springframework.security.web.context.HttpSessionSecurityContextRepository - -/** - * Tests to verify that all the functionality of attributes is present - * - * @author Rob Winch - * - */ -public class NamespaceHttpInterceptUrlTests extends BaseSpringSpec { - - def "http/intercept-url denied when not logged in"() { - setup: - loadConfig(HttpInterceptUrlConfig) - request.servletPath == "/users" - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_FORBIDDEN - } - - def "http/intercept-url denied when logged in"() { - setup: - loadConfig(HttpInterceptUrlConfig) - login() - request.setServletPath("/users") - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_FORBIDDEN - } - - def "http/intercept-url allowed when logged in"() { - setup: - loadConfig(HttpInterceptUrlConfig) - login("admin","ROLE_ADMIN") - request.setServletPath("/users") - when: - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - !response.isCommitted() - } - - def "http/intercept-url@method=POST"() { - setup: - loadConfig(HttpInterceptUrlConfig) - when: - login() - request.setServletPath("/admin/post") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - !response.isCommitted() - when: - super.setup() - login() - request.setServletPath("/admin/post") - request.setMethod("POST") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_FORBIDDEN - when: - super.setup() - login("admin","ROLE_ADMIN") - request.setServletPath("/admin/post") - request.setMethod("POST") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.status == HttpServletResponse.SC_OK - !response.committed - } - - def "http/intercept-url@requires-channel"() { - setup: - loadConfig(HttpInterceptUrlConfig) - when: - request.setServletPath("/login") - request.setRequestURI("/login") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.redirectedUrl == "https://localhost/login" - when: - super.setup() - request.setServletPath("/secured/a") - request.setRequestURI("/secured/a") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.redirectedUrl == "https://localhost/secured/a" - when: - super.setup() - request.setSecure(true) - request.setScheme("https") - request.setServletPath("/user") - request.setRequestURI("/user") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.redirectedUrl == "http://localhost/user" - } - - @EnableWebSecurity - static class HttpInterceptUrlConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - // the line below is similar to intercept-url@pattern: - // - // - .antMatchers("/users**","/sessions/**").hasRole("ADMIN") - // the line below is similar to intercept-url@method: - // - // - .antMatchers(HttpMethod.POST, "/admin/post","/admin/another-post/**").hasRole("ADMIN") - .antMatchers("/signup").permitAll() - .anyRequest().hasRole("USER") - .and() - .requiresChannel() - // NOTE: channel security is configured separately of authorization (i.e. intercept-url@access - // the line below is similar to intercept-url@requires-channel="https": - // - // - .antMatchers("/login","/secured/**").requiresSecure() - // the line below is similar to intercept-url@requires-channel="http": - // - .anyRequest().requiresInsecure() - } - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER").and() - .withUser("admin").password("password").roles("USER", "ADMIN") - } - } -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpJeeTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpJeeTests.groovy deleted file mode 100644 index 6215e67cce..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpJeeTests.groovy +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2002-2017 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 - * - * https://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.configurers; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.mock.web.MockFilterChain -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse -import org.springframework.security.access.AccessDecisionManager -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.access.ConfigAttribute -import org.springframework.security.authentication.AnonymousAuthenticationToken -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.web.AuthenticationEntryPoint -import org.springframework.security.web.FilterChainProxy -import org.springframework.security.web.FilterInvocation -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.security.web.access.AccessDeniedHandlerImpl; -import org.springframework.security.web.access.ExceptionTranslationFilter -import org.springframework.security.web.access.intercept.FilterSecurityInterceptor -import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; -import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; -import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler -import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService; -import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource; -import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter; -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.servletapi.SecurityContextHolderAwareRequestFilter; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher -import org.springframework.security.web.util.matcher.AnyRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher -import org.springframework.test.util.ReflectionTestUtils; - -/** - * Tests to verify that all the functionality of attributes is present - * - * @author Rob Winch - * - */ -public class NamespaceHttpJeeTests extends BaseSpringSpec { - - def "http/jee@mappable-roles"() { - when: - loadConfig(JeeMappableRolesConfig) - J2eePreAuthenticatedProcessingFilter filter = findFilter(J2eePreAuthenticatedProcessingFilter) - AuthenticationManager authenticationManager = ReflectionTestUtils.getField(filter,"authenticationManager") - then: - authenticationManager - filter.authenticationDetailsSource.class == J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource - filter.authenticationDetailsSource.j2eeMappableRoles == ["ROLE_USER", "ROLE_ADMIN"] as Set - authenticationManager.providers.find { it instanceof PreAuthenticatedAuthenticationProvider }.preAuthenticatedUserDetailsService.class == PreAuthenticatedGrantedAuthoritiesUserDetailsService - } - - @EnableWebSecurity - public static class JeeMappableRolesConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .jee() - .mappableRoles("USER","ADMIN"); - } - } - - def "http/jee@user-service-ref"() { - when: - loadConfig(JeeUserServiceRefConfig) - J2eePreAuthenticatedProcessingFilter filter = findFilter(J2eePreAuthenticatedProcessingFilter) - AuthenticationManager authenticationManager = ReflectionTestUtils.getField(filter,"authenticationManager") - then: - authenticationManager - filter.authenticationDetailsSource.class == J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource - filter.authenticationDetailsSource.j2eeMappableRoles == ["ROLE_USER", "ROLE_ADMIN"] as Set - authenticationManager.providers.find { it instanceof PreAuthenticatedAuthenticationProvider }.preAuthenticatedUserDetailsService.class == CustomUserService - } - - @EnableWebSecurity - public static class JeeUserServiceRefConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .jee() - .mappableAuthorities("ROLE_USER","ROLE_ADMIN") - .authenticatedUserDetailsService(new CustomUserService()); - } - } - - static class CustomUserService implements AuthenticationUserDetailsService { - public UserDetails loadUserDetails( - PreAuthenticatedAuthenticationToken token) - throws UsernameNotFoundException { - return null; - } - } -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpPortMappingsTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpPortMappingsTests.groovy deleted file mode 100644 index a0e7e0dd91..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpPortMappingsTests.groovy +++ /dev/null @@ -1,115 +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 - * - * https://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.configurers - -import org.springframework.context.annotation.Configuration -import org.springframework.mock.web.MockFilterChain -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken -import org.springframework.security.config.annotation.BaseSpringSpec -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContextImpl -import org.springframework.security.web.FilterChainProxy -import org.springframework.security.web.context.HttpRequestResponseHolder -import org.springframework.security.web.context.HttpSessionSecurityContextRepository - -/** - * Tests to verify that all the functionality of attributes is present - * - * @author Rob Winch - * - */ -public class NamespaceHttpPortMappingsTests extends BaseSpringSpec { - FilterChainProxy springSecurityFilterChain - MockHttpServletRequest request - MockHttpServletResponse response - MockFilterChain chain - - def setup() { - request = new MockHttpServletRequest("GET", "") - request.setMethod("GET") - response = new MockHttpServletResponse() - chain = new MockFilterChain() - } - - def "http/port-mapper works with http/intercept-url@requires-channel"() { - setup: - loadConfig(HttpInterceptUrlWithPortMapperConfig) - springSecurityFilterChain = context.getBean(FilterChainProxy) - when: - request.setServletPath("/login") - request.setRequestURI("/login") - request.setServerPort(9080); - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.redirectedUrl == "https://localhost:9443/login" - when: - setup() - request.setServletPath("/secured/a") - request.setRequestURI("/secured/a") - request.setServerPort(9080); - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.redirectedUrl == "https://localhost:9443/secured/a" - when: - setup() - request.setSecure(true) - request.setScheme("https") - request.setServerPort(9443); - request.setServletPath("/user") - request.setRequestURI("/user") - springSecurityFilterChain.doFilter(request,response,chain) - then: - response.redirectedUrl == "http://localhost:9080/user" - } - - @EnableWebSecurity - static class HttpInterceptUrlWithPortMapperConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().hasRole("USER") - .and() - .portMapper() - .http(9080).mapsTo(9443) - .and() - .requiresChannel() - .antMatchers("/login","/secured/**").requiresSecure() - .anyRequest().requiresInsecure() - } - - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER").and() - .withUser("admin").password("password").roles("USER", "ADMIN") - } - } - - def login(String username="user", String role="ROLE_USER") { - HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository() - HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response) - repo.loadContext(requestResponseHolder) - repo.saveContext(new SecurityContextImpl(authentication: new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.createAuthorityList(role))), requestResponseHolder.request, requestResponseHolder.response) - } -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpRequestCacheTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpRequestCacheTests.groovy deleted file mode 100644 index 3cff0bf684..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpRequestCacheTests.groovy +++ /dev/null @@ -1,73 +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 - * - * https://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.configurers; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.config.annotation.BaseSpringSpec; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.BaseWebConfig; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.security.web.access.ExceptionTranslationFilter; -import org.springframework.security.web.savedrequest.HttpSessionRequestCache; -import org.springframework.security.web.savedrequest.RequestCache; - -/** - * Tests to verify that all the functionality of attributes is present - * - * @author Rob Winch - * - */ -public class NamespaceHttpRequestCacheTests extends BaseSpringSpec { - def "http/request-cache@ref"() { - setup: - RequestCacheRefConfig.REQUEST_CACHE = Mock(RequestCache) - when: - loadConfig(RequestCacheRefConfig) - then: - findFilter(ExceptionTranslationFilter).requestCache == RequestCacheRefConfig.REQUEST_CACHE - } - - @Configuration - static class RequestCacheRefConfig extends BaseWebConfig { - static RequestCache REQUEST_CACHE - protected void configure(HttpSecurity http) { - http. - requestCache() - .requestCache(REQUEST_CACHE) - } - } - - def "http/request-cache@ref defaults to HttpSessionRequestCache"() { - when: - loadConfig(DefaultRequestCacheRefConfig) - then: - findFilter(ExceptionTranslationFilter).requestCache.class == HttpSessionRequestCache - } - - @Configuration - static class DefaultRequestCacheRefConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) { - } - } -} diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.groovy deleted file mode 100644 index 445a5c0799..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.groovy +++ /dev/null @@ -1,80 +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 - * - * https://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.configurers; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.config.annotation.BaseSpringSpec; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.BaseWebConfig; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.security.web.access.ExceptionTranslationFilter; - -/** - * Tests to verify that all the functionality of attributes is present - * - * @author Rob Winch - * - */ -public class NamespaceHttpServerAccessDeniedHandlerTests extends BaseSpringSpec { - def "http/access-denied-handler@error-page"() { - when: - loadConfig(AccessDeniedPageConfig) - then: - findFilter(ExceptionTranslationFilter).accessDeniedHandler.errorPage == "/AccessDeniedPageConfig" - } - - @Configuration - static class AccessDeniedPageConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) { - http. - exceptionHandling() - .accessDeniedPage("/AccessDeniedPageConfig") - } - } - - def "http/access-denied-handler@ref"() { - when: - loadConfig(AccessDeniedHandlerRefConfig) - then: - findFilter(ExceptionTranslationFilter).accessDeniedHandler.class == AccessDeniedHandlerRefConfig.CustomAccessDeniedHandler - } - - @Configuration - static class AccessDeniedHandlerRefConfig extends BaseWebConfig { - protected void configure(HttpSecurity http) { - CustomAccessDeniedHandler accessDeniedHandler = new CustomAccessDeniedHandler() - http. - exceptionHandling() - .accessDeniedHandler(accessDeniedHandler) - } - - static class CustomAccessDeniedHandler implements AccessDeniedHandler { - public void handle(HttpServletRequest request, - HttpServletResponse response, - AccessDeniedException accessDeniedException) - throws IOException, ServletException { - } - } - } -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.java new file mode 100644 index 0000000000..ee6f7f06ed --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.java @@ -0,0 +1,194 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.configurers; + + +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.assertj.core.api.ListAssert; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.filter.OncePerRequestFilter; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests to verify that all the functionality of attributes is present + * + * @author Rob Winch + * @author Josh Cummings + * + */ +public class NamespaceHttpCustomFilterTests { + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Test + public void getFiltersWhenFilterAddedBeforeThenBehaviorMatchesNamespace() { + this.spring.register(CustomFilterBeforeConfig.class, UserDetailsServiceConfig.class).autowire(); + assertThatFilters().containsSubsequence(CustomFilter.class, UsernamePasswordAuthenticationFilter.class); + } + + @EnableWebSecurity + static class CustomFilterBeforeConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class) + .formLogin(); + } + } + + @Test + public void getFiltersWhenFilterAddedAfterThenBehaviorMatchesNamespace() { + this.spring.register(CustomFilterAfterConfig.class, UserDetailsServiceConfig.class).autowire(); + assertThatFilters().containsSubsequence(UsernamePasswordAuthenticationFilter.class, CustomFilter.class); + } + + @EnableWebSecurity + static class CustomFilterAfterConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .addFilterAfter(new CustomFilter(), UsernamePasswordAuthenticationFilter.class) + .formLogin(); + } + } + + @Test + public void getFiltersWhenFilterAddedThenBehaviorMatchesNamespace() { + this.spring.register(CustomFilterPositionConfig.class, UserDetailsServiceConfig.class).autowire(); + assertThatFilters().containsExactly(CustomFilter.class); + } + + @EnableWebSecurity + static class CustomFilterPositionConfig extends WebSecurityConfigurerAdapter { + CustomFilterPositionConfig() { + // do not add the default filters to make testing easier + super(true); + } + + protected void configure(HttpSecurity http) throws Exception { + http + // this works so long as the CustomFilter extends one of the standard filters + // if not, use addFilterBefore or addFilterAfter + .addFilter(new CustomFilter()); + } + } + + + @Test + public void getFiltersWhenFilterAddedAtPositionThenBehaviorMatchesNamespace() { + this.spring.register(CustomFilterPositionAtConfig.class, UserDetailsServiceConfig.class).autowire(); + assertThatFilters().containsExactly(OtherCustomFilter.class); + } + + @EnableWebSecurity + static class CustomFilterPositionAtConfig extends WebSecurityConfigurerAdapter { + CustomFilterPositionAtConfig() { + // do not add the default filters to make testing easier + super(true); + } + + protected void configure(HttpSecurity http) throws Exception { + http + .addFilterAt(new OtherCustomFilter(), UsernamePasswordAuthenticationFilter.class); + } + } + + @Test + public void getFiltersWhenCustomAuthenticationManagerThenBehaviorMatchesNamespace() { + this.spring.register(NoAuthenticationManagerInHttpConfigurationConfig.class).autowire(); + assertThatFilters().startsWith(CustomFilter.class); + } + + @EnableWebSecurity + static class NoAuthenticationManagerInHttpConfigurationConfig extends WebSecurityConfigurerAdapter { + NoAuthenticationManagerInHttpConfigurationConfig() { + super(true); + } + + protected AuthenticationManager authenticationManager() + throws Exception { + return new CustomAuthenticationManager(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class); + } + } + + @Configuration + static class UserDetailsServiceConfig { + @Bean + public UserDetailsService userDetailsService() { + return new InMemoryUserDetailsManager( + User.withDefaultPasswordEncoder() + .username("user") + .password("password") + .roles("USER") + .build()); + } + } + + static class CustomFilter extends UsernamePasswordAuthenticationFilter {} + static class OtherCustomFilter extends OncePerRequestFilter { + protected void doFilterInternal( + HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + filterChain.doFilter(request, response); + } + } + + static class CustomAuthenticationManager implements AuthenticationManager { + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { + return null; + } + } + + private ListAssert> assertThatFilters() { + FilterChainProxy filterChain = this.spring.getContext().getBean(FilterChainProxy.class); + List> filters = filterChain.getFilters("/").stream() + .map(Object::getClass).collect(Collectors.toList()); + return assertThat(filters); + } +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFirewallTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFirewallTests.java new file mode 100644 index 0000000000..5be01cee2f --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFirewallTests.java @@ -0,0 +1,104 @@ +/* + * 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 + * + * https://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.configurers; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.web.firewall.DefaultHttpFirewall; +import org.springframework.security.web.firewall.FirewalledRequest; +import org.springframework.security.web.firewall.HttpFirewall; +import org.springframework.security.web.firewall.RequestRejectedException; +import org.springframework.test.web.servlet.MockMvc; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; + +/** + * Tests to verify that all the functionality of attributes is present + * + * @author Rob Winch + * @author Josh Cummings + */ +public class NamespaceHttpFirewallTests { + + @Rule + public final SpringTestRule rule = new SpringTestRule(); + + @Autowired + MockMvc mvc; + + @Test + public void requestWhenPathContainsDoubleDotsThenBehaviorMatchesNamespace() throws Exception { + this.rule.register(HttpFirewallConfig.class).autowire(); + assertThatCode(() -> this.mvc.perform(get("/public/../private/"))) + .isInstanceOf(RequestRejectedException.class); + } + + @EnableWebSecurity + static class HttpFirewallConfig {} + + @Test + public void requestWithCustomFirewallThenBehaviorMatchesNamespace() { + this.rule.register(CustomHttpFirewallConfig.class).autowire(); + assertThatCode(() -> this.mvc.perform(get("/").param("deny", "true"))) + .isInstanceOf(RequestRejectedException.class); + } + + @EnableWebSecurity + static class CustomHttpFirewallConfig extends WebSecurityConfigurerAdapter { + @Override + public void configure(WebSecurity web) throws Exception { + web + .httpFirewall(new CustomHttpFirewall()); + } + } + + @Test + public void requestWithCustomFirewallBeanThenBehaviorMatchesNamespace() { + this.rule.register(CustomHttpFirewallBeanConfig.class).autowire(); + assertThatCode(() -> this.mvc.perform(get("/").param("deny", "true"))) + .isInstanceOf(RequestRejectedException.class); + } + + @EnableWebSecurity + static class CustomHttpFirewallBeanConfig { + @Bean + HttpFirewall firewall() { + return new CustomHttpFirewall(); + } + } + + static class CustomHttpFirewall extends DefaultHttpFirewall { + + @Override + public FirewalledRequest getFirewalledRequest(HttpServletRequest request) + throws RequestRejectedException { + if (request.getParameter("deny") != null) { + throw new RequestRejectedException("custom rejection"); + } + return super.getFirewalledRequest(request); + } + } +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java new file mode 100644 index 0000000000..81fa1e4b07 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java @@ -0,0 +1,197 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.configurers; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.test.web.servlet.MockMvc; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; + +/** + * Tests to verify that all the functionality of attributes is present + * + * @author Rob Winch + * @author Josh Cummings + * + */ +public class NamespaceHttpFormLoginTests { + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + MockMvc mvc; + + + @Test + public void formLoginWhenDefaultConfigurationThenMatchesNamespace() throws Exception { + this.spring.register(FormLoginConfig.class, UserDetailsServiceConfig.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(redirectedUrl("http://localhost/login")); + + this.mvc.perform(post("/login") + .with(csrf())) + .andExpect(redirectedUrl("/login?error")); + + this.mvc.perform(post("/login") + .param("username", "user") + .param("password", "password") + .with(csrf())) + .andExpect(redirectedUrl("/")); + } + + @EnableWebSecurity + static class FormLoginConfig extends WebSecurityConfigurerAdapter { + + @Override + public void configure(WebSecurity web) throws Exception { + web + .ignoring() + .antMatchers("/resources/**"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .formLogin(); + } + } + + @Test + public void formLoginWithCustomEndpointsThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(FormLoginCustomConfig.class, UserDetailsServiceConfig.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(redirectedUrl("http://localhost/authentication/login")); + + this.mvc.perform(post("/authentication/login/process") + .with(csrf())) + .andExpect(redirectedUrl("/authentication/login?failed")); + + this.mvc.perform(post("/authentication/login/process") + .param("username", "user") + .param("password", "password") + .with(csrf())) + .andExpect(redirectedUrl("/default")); + } + + @EnableWebSecurity + static class FormLoginCustomConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + boolean alwaysUseDefaultSuccess = true; + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .formLogin() + .usernameParameter("username") // form-login@username-parameter + .passwordParameter("password") // form-login@password-parameter + .loginPage("/authentication/login") // form-login@login-page + .failureUrl("/authentication/login?failed") // form-login@authentication-failure-url + .loginProcessingUrl("/authentication/login/process") // form-login@login-processing-url + .defaultSuccessUrl("/default", alwaysUseDefaultSuccess); // form-login@default-target-url / form-login@always-use-default-target + } + } + + @Test + public void formLoginWithCustomHandlersThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(FormLoginCustomRefsConfig.class, UserDetailsServiceConfig.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(redirectedUrl("http://localhost/login")); + + this.mvc.perform(post("/login") + .with(csrf())) + .andExpect(redirectedUrl("/custom/failure")); + verifyBean(WebAuthenticationDetailsSource.class).buildDetails(any(HttpServletRequest.class)); + + this.mvc.perform(post("/login") + .param("username", "user") + .param("password", "password") + .with(csrf())) + .andExpect(redirectedUrl("/custom/targetUrl")); + } + + @EnableWebSecurity + static class FormLoginCustomRefsConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + SavedRequestAwareAuthenticationSuccessHandler successHandler = + new SavedRequestAwareAuthenticationSuccessHandler(); + successHandler.setDefaultTargetUrl("/custom/targetUrl"); + + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .formLogin() + .loginPage("/login") + .failureHandler(new SimpleUrlAuthenticationFailureHandler("/custom/failure")) // form-login@authentication-failure-handler-ref + .successHandler(successHandler) // form-login@authentication-success-handler-ref + .authenticationDetailsSource(authenticationDetailsSource()) // form-login@authentication-details-source-ref + .and(); + } + + @Bean + WebAuthenticationDetailsSource authenticationDetailsSource() { + return spy(WebAuthenticationDetailsSource.class); + } + } + + @Configuration + static class UserDetailsServiceConfig { + @Bean + public UserDetailsService userDetailsService() { + return new InMemoryUserDetailsManager( + User.withDefaultPasswordEncoder() + .username("user") + .password("password") + .roles("USER") + .build()); + } + } + + private T verifyBean(Class beanClass) { + return verify(this.spring.getContext().getBean(beanClass)); + } +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.java new file mode 100644 index 0000000000..6496668b7d --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.java @@ -0,0 +1,293 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.configurers; + +import java.net.URI; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.web.header.writers.StaticHeadersWriter; +import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy; +import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter; +import org.springframework.security.web.util.matcher.AnyRequestMatcher; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultMatcher; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; + +/** + * Tests to verify that all the functionality of attributes is present + * + * @author Rob Winch + * @author Josh Cummings + * + */ +public class NamespaceHttpHeadersTests { + static final Map defaultHeaders = new LinkedHashMap<>(); + + static { + defaultHeaders.put("X-Content-Type-Options", "nosniff"); + defaultHeaders.put("X-Frame-Options", "DENY"); + defaultHeaders.put("Strict-Transport-Security", "max-age=31536000 ; includeSubDomains"); + defaultHeaders.put("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); + defaultHeaders.put("Expires", "0"); + defaultHeaders.put("Pragma", "no-cache"); + defaultHeaders.put("X-XSS-Protection", "1; mode=block"); + } + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + MockMvc mvc; + + @Test + public void secureRequestWhenDefaultConfigThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HeadersDefaultConfig.class).autowire(); + + this.mvc.perform(get("/").secure(true)) + .andExpect(includesDefaults()); + } + + @EnableWebSecurity + static class HeadersDefaultConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers(); + } + } + + @Test + public void secureRequestWhenCacheControlOnlyThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HeadersCacheControlConfig.class).autowire(); + + this.mvc.perform(get("/").secure(true)) + .andExpect(includes("Cache-Control", "Expires", "Pragma")); + } + + @EnableWebSecurity + static class HeadersCacheControlConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers() + .defaultsDisabled() + .cacheControl(); + } + } + + @Test + public void secureRequestWhenHstsOnlyThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HstsConfig.class).autowire(); + + this.mvc.perform(get("/").secure(true)) + .andExpect(includes("Strict-Transport-Security")); + } + + @EnableWebSecurity + static class HstsConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers() + .defaultsDisabled() + .httpStrictTransportSecurity(); + } + } + + @Test + public void requestWhenHstsCustomThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HstsCustomConfig.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(includes(Collections.singletonMap("Strict-Transport-Security", "max-age=15768000"))); + } + + @EnableWebSecurity + static class HstsCustomConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers() + // hsts@request-matcher-ref, hsts@max-age-seconds, hsts@include-subdomains + .defaultsDisabled() + .httpStrictTransportSecurity() + .requestMatcher(AnyRequestMatcher.INSTANCE) + .maxAgeInSeconds(15768000) + .includeSubDomains(false); + } + } + + @Test + public void requestWhenFrameOptionsSameOriginThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(FrameOptionsSameOriginConfig.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(includes(Collections.singletonMap("X-Frame-Options", "SAMEORIGIN"))); + } + + @EnableWebSecurity + static class FrameOptionsSameOriginConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers() + // frame-options@policy=SAMEORIGIN + .defaultsDisabled() + .frameOptions() + .sameOrigin(); + } + } + + // frame-options@strategy, frame-options@value, frame-options@parameter are not provided instead use frame-options@ref + + @Test + public void requestWhenFrameOptionsAllowFromThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(FrameOptionsAllowFromConfig.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(includes(Collections.singletonMap("X-Frame-Options", "ALLOW-FROM https://example.com"))); + } + + @EnableWebSecurity + static class FrameOptionsAllowFromConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers() + // frame-options@ref + .defaultsDisabled() + .addHeaderWriter(new XFrameOptionsHeaderWriter( + new StaticAllowFromStrategy(URI.create("https://example.com")))); + } + } + + @Test + public void requestWhenXssOnlyThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(XssProtectionConfig.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(includes("X-XSS-Protection")); + } + + @EnableWebSecurity + static class XssProtectionConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers() + // xss-protection + .defaultsDisabled() + .xssProtection(); + } + } + + @Test + public void requestWhenXssCustomThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(XssProtectionCustomConfig.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(includes(Collections.singletonMap("X-XSS-Protection", "1"))); + } + + @EnableWebSecurity + static class XssProtectionCustomConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers() + // xss-protection@enabled and xss-protection@block + .defaultsDisabled() + .xssProtection() + .xssProtectionEnabled(true) + .block(false); + } + } + + @Test + public void requestWhenXContentTypeOptionsOnlyThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(ContentTypeOptionsConfig.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(includes("X-Content-Type-Options")); + } + + @EnableWebSecurity + static class ContentTypeOptionsConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers() + // content-type-options + .defaultsDisabled() + .contentTypeOptions(); + } + } + + // header@name / header@value are not provided instead use header@ref + + @Test + public void requestWhenCustomHeaderOnlyThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HeaderRefConfig.class).autowire(); + + this.mvc.perform(get("/")) + .andExpect(includes(Collections.singletonMap("customHeaderName", "customHeaderValue"))); + } + + @EnableWebSecurity + static class HeaderRefConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers() + .defaultsDisabled() + .addHeaderWriter(new StaticHeadersWriter("customHeaderName", "customHeaderValue")); + } + } + + private static ResultMatcher includesDefaults() { + return includes(defaultHeaders); + } + + private static ResultMatcher includes(String... headerNames) { + return includes(defaultHeaders, headerNames); + } + + private static ResultMatcher includes(Map headers) { + return includes(headers, headers.keySet().toArray(new String[headers.size()])); + } + + private static ResultMatcher includes(Map headers, String... headerNames) { + return result -> { + assertThat(result.getResponse().getHeaderNames()).hasSameSizeAs(headerNames); + for (String headerName : headerNames) { + header().string(headerName, headers.get(headerName)).match(result); + } + }; + } +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpInterceptUrlTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpInterceptUrlTests.java new file mode 100644 index 0000000000..26fcdfcac6 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpInterceptUrlTests.java @@ -0,0 +1,187 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.configurers; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests to verify that all the functionality of attributes is present + * + * @author Rob Winch + * @author Josh Cummings + * + */ +public class NamespaceHttpInterceptUrlTests { + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + MockMvc mvc; + + @Test + public void unauthenticatedRequestWhenUrlRequiresAuthenticationThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HttpInterceptUrlConfig.class).autowire(); + + this.mvc.perform(get("/users")) + .andExpect(status().isForbidden()); + } + + @Test + public void authenticatedRequestWhenUrlRequiresElevatedPrivilegesThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HttpInterceptUrlConfig.class).autowire(); + + + this.mvc.perform(get("/users") + .with(authentication(user("ROLE_USER")))) + .andExpect(status().isForbidden()); + } + + @Test + public void authenticatedRequestWhenAuthorizedThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HttpInterceptUrlConfig.class, BaseController.class).autowire(); + + this.mvc.perform(get("/users") + .with(authentication(user("ROLE_ADMIN")))) + .andExpect(status().isOk()) + .andReturn(); + } + + @Test + public void requestWhenMappedByPostInterceptUrlThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HttpInterceptUrlConfig.class, BaseController.class).autowire(); + + this.mvc.perform(get("/admin/post") + .with(authentication(user("ROLE_USER")))) + .andExpect(status().isOk()); + + this.mvc.perform(post("/admin/post") + .with(authentication(user("ROLE_USER")))) + .andExpect(status().isForbidden()); + + this.mvc.perform(post("/admin/post") + .with(csrf()) + .with(authentication(user("ROLE_ADMIN")))) + .andExpect(status().isOk()); + } + + @Test + public void requestWhenRequiresChannelThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HttpInterceptUrlConfig.class).autowire(); + + this.mvc.perform(get("/login")) + .andExpect(redirectedUrl("https://localhost/login")); + + this.mvc.perform(get("/secured/a")) + .andExpect(redirectedUrl("https://localhost/secured/a")); + + this.mvc.perform(get("https://localhost/user")) + .andExpect(redirectedUrl("http://localhost/user")); + } + + @EnableWebSecurity + static class HttpInterceptUrlConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + // the line below is similar to intercept-url@pattern: + // + // + .antMatchers("/users**", "/sessions/**").hasRole("ADMIN") + // the line below is similar to intercept-url@method: + // + // + .antMatchers(HttpMethod.POST, "/admin/post", "/admin/another-post/**").hasRole("ADMIN") + .antMatchers("/signup").permitAll() + .anyRequest().hasRole("USER") + .and() + .requiresChannel() + // NOTE: channel security is configured separately of authorization (i.e. intercept-url@access + // the line below is similar to intercept-url@requires-channel="https": + // + // + .antMatchers("/login", "/secured/**").requiresSecure() + // the line below is similar to intercept-url@requires-channel="http": + // + .anyRequest().requiresInsecure(); + } + + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER").and() + .withUser("admin").password("password").roles("USER", "ADMIN"); + } + } + + @RestController + static class BaseController { + @GetMapping("/users") + public String users() { + return "ok"; + } + + @GetMapping("/sessions") + public String sessions() { + return "sessions"; + } + + @RequestMapping("/admin/post") + public String adminPost() { + return "adminPost"; + } + + @GetMapping("/admin/another-post") + public String adminAnotherPost() { + return "adminAnotherPost"; + } + + @GetMapping("/signup") + public String signup() { + return "signup"; + } + } + + private static Authentication user(String role) { + return new UsernamePasswordAuthenticationToken("user", null, AuthorityUtils.createAuthorityList(role)); + } + +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpJeeTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpJeeTests.java new file mode 100644 index 0000000000..ed06b41183 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpJeeTests.java @@ -0,0 +1,159 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.configurers; + + +import java.security.Principal; +import java.util.stream.Collectors; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; +import org.springframework.security.core.userdetails.User; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +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.status; + +/** + * Tests to verify that all the functionality of attributes is present + * + * @author Rob Winch + * @author Josh Cummings + * + */ +public class NamespaceHttpJeeTests { + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + MockMvc mvc; + + @Test + public void requestWhenJeeUserThenBehaviorDiffersFromNamespaceForRoleNames() throws Exception { + this.spring.register(JeeMappableRolesConfig.class, BaseController.class).autowire(); + + Principal user = mock(Principal.class); + when(user.getName()).thenReturn("joe"); + + this.mvc.perform(get("/roles") + .principal(user) + .with(request -> { + request.addUserRole("ROLE_admin"); + request.addUserRole("ROLE_user"); + request.addUserRole("ROLE_unmapped"); + return request; + })) + .andExpect(status().isOk()) + .andExpect(content().string("ROLE_admin,ROLE_user")); + } + + @EnableWebSecurity + public static class JeeMappableRolesConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().hasRole("user") + .and() + .jee() + .mappableRoles("user", "admin"); + } + } + + @Test + public void requestWhenCustomAuthenticatedUserDetailsServiceThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(JeeUserServiceRefConfig.class, BaseController.class).autowire(); + + Principal user = mock(Principal.class); + when(user.getName()).thenReturn("joe"); + + User result = new User(user.getName(), "N/A", true, true, true, true, + AuthorityUtils.createAuthorityList("ROLE_user")); + + when(bean(AuthenticationUserDetailsService.class).loadUserDetails(any())) + .thenReturn(result); + + this.mvc.perform(get("/roles") + .principal(user)) + .andExpect(status().isOk()) + .andExpect(content().string("ROLE_user")); + + verifyBean(AuthenticationUserDetailsService.class).loadUserDetails(any()); + } + + @EnableWebSecurity + public static class JeeUserServiceRefConfig extends WebSecurityConfigurerAdapter { + private final AuthenticationUserDetailsService authenticationUserDetailsService = + mock(AuthenticationUserDetailsService.class); + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().hasRole("user") + .and() + .jee() + .mappableAuthorities("ROLE_user", "ROLE_admin") + .authenticatedUserDetailsService(this.authenticationUserDetailsService); + } + + @Bean + public AuthenticationUserDetailsService authenticationUserDetailsService() { + return this.authenticationUserDetailsService; + } + } + + @RestController + static class BaseController { + @GetMapping("/authenticated") + public String authenticated(Authentication authentication) { + return authentication.getName(); + } + + @GetMapping("/roles") + public String roles(Authentication authentication) { + return authentication.getAuthorities().stream() + .map(Object::toString).collect(Collectors.joining(",")); + } + } + + private T bean(Class beanClass) { + return this.spring.getContext().getBean(beanClass); + } + + private T verifyBean(Class beanClass) { + return verify(this.spring.getContext().getBean(beanClass)); + } +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpPortMappingsTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpPortMappingsTests.java new file mode 100644 index 0000000000..cd246b87f7 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpPortMappingsTests.java @@ -0,0 +1,86 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.configurers; + + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; + +/** + * Tests to verify that all the functionality of attributes is present + * + * @author Rob Winch + * @author Josh Cummings + * + */ +public class NamespaceHttpPortMappingsTests { + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + MockMvc mvc; + + @Test + public void portMappingWhenRequestRequiresChannelThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(HttpInterceptUrlWithPortMapperConfig.class).autowire(); + + this.mvc.perform(get("http://localhost:9080/login")) + .andExpect(redirectedUrl("https://localhost:9443/login")); + + this.mvc.perform(get("http://localhost:9080/secured/a")) + .andExpect(redirectedUrl("https://localhost:9443/secured/a")); + + this.mvc.perform(get("https://localhost:9443/user")) + .andExpect(redirectedUrl("http://localhost:9080/user")); + } + + @EnableWebSecurity + static class HttpInterceptUrlWithPortMapperConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .portMapper() + .http(9080).mapsTo(9443) + .and() + .requiresChannel() + .antMatchers("/login", "/secured/**").requiresSecure() + .anyRequest().requiresInsecure(); + } + + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER").and() + .withUser("admin").password("password").roles("USER", "ADMIN"); + } + } +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpRequestCacheTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpRequestCacheTests.java new file mode 100644 index 0000000000..9d1cf59ef4 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpRequestCacheTests.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.configurers; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.userdetails.PasswordEncodedUser; +import org.springframework.security.web.savedrequest.RequestCache; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests to verify that all the functionality of attributes is present + * + * @author Rob Winch + * @author Josh Cummings + * + */ +public class NamespaceHttpRequestCacheTests { + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + MockMvc mvc; + + @Test + public void requestWhenCustomRequestCacheThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(RequestCacheRefConfig.class).autowire(); + this.mvc.perform(get("/")) + .andExpect(status().isForbidden()); + verifyBean(RequestCache.class).saveRequest(any(HttpServletRequest.class), any(HttpServletResponse.class)); + } + + @EnableWebSecurity + static class RequestCacheRefConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .requestCache() + .requestCache(requestCache()); + } + + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()) + .withUser(PasswordEncodedUser.admin()); + } + + @Bean + public RequestCache requestCache() { + return mock(RequestCache.class); + } + } + + @Test + public void requestWhenDefaultConfigurationThenUsesHttpSessionRequestCache() throws Exception { + this.spring.register(DefaultRequestCacheRefConfig.class).autowire(); + + MvcResult result = this.mvc.perform(get("/")) + .andExpect(status().isForbidden()) + .andReturn(); + + HttpSession session = result.getRequest().getSession(false); + assertThat(session).isNotNull(); + assertThat(session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")).isNotNull(); + } + + @EnableWebSecurity + static class DefaultRequestCacheRefConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated(); + } + + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()) + .withUser(PasswordEncodedUser.admin()); + } + } + + private T verifyBean(Class beanClass) { + return verify(this.spring.getContext().getBean(beanClass)); + } +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java new file mode 100644 index 0000000000..eb4763bd55 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java @@ -0,0 +1,115 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.configurers; + + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.test.web.servlet.MockMvc; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests to verify that all the functionality of attributes is present + * + * @author Rob Winch + * @author Josh Cummings + * + */ +public class NamespaceHttpServerAccessDeniedHandlerTests { + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + MockMvc mvc; + + @Test + public void requestWhenCustomAccessDeniedPageThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(AccessDeniedPageConfig.class).autowire(); + this.mvc.perform(get("/") + .with(authentication(user()))) + .andExpect(status().isForbidden()) + .andExpect(forwardedUrl("/AccessDeniedPageConfig")); + } + + @EnableWebSecurity + static class AccessDeniedPageConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().denyAll() + .and() + .exceptionHandling() + .accessDeniedPage("/AccessDeniedPageConfig"); + } + } + + private static Authentication user() { + return new UsernamePasswordAuthenticationToken("user", null, AuthorityUtils.NO_AUTHORITIES); + } + + @Test + public void requestWhenCustomAccessDeniedHandlerThenBehaviorMatchesNamespace() throws Exception { + this.spring.register(AccessDeniedHandlerRefConfig.class).autowire(); + this.mvc.perform(get("/") + .with(authentication(user()))); + verifyBean(AccessDeniedHandler.class) + .handle(any(HttpServletRequest.class), any(HttpServletResponse.class), any(AccessDeniedException.class)); + } + + @EnableWebSecurity + static class AccessDeniedHandlerRefConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().denyAll() + .and() + .exceptionHandling() + .accessDeniedHandler(accessDeniedHandler()); + } + + @Bean + AccessDeniedHandler accessDeniedHandler() { + return mock(AccessDeniedHandler.class); + } + } + + private T verifyBean(Class beanClass) { + return verify(this.spring.getContext().getBean(beanClass)); + } +}