Various NamespaceHttp*Tests groovy->java

Issue: gh-4939
This commit is contained in:
Josh Cummings 2019-03-26 17:44:51 -06:00
parent b1195e7789
commit cf0c5f9026
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
18 changed files with 1458 additions and 1347 deletions

View File

@ -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 <anonymous> 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;
}
}
}

View File

@ -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 <http-firewall> 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)
}
}
}

View File

@ -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 <anonymous> 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 {}
}

View File

@ -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 <headers> 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"))
}
}
}

View File

@ -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 <intercept-url> 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:
// <intercept-url pattern="/users**" access="hasRole('ROLE_ADMIN')"/>
// <intercept-url pattern="/sessions/**" access="hasRole('ROLE_ADMIN')"/>
.antMatchers("/users**","/sessions/**").hasRole("ADMIN")
// the line below is similar to intercept-url@method:
// <intercept-url pattern="/admin/post" access="hasRole('ROLE_ADMIN')" method="POST"/>
// <intercept-url pattern="/admin/another-post/**" access="hasRole('ROLE_ADMIN')" method="POST"/>
.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":
// <intercept-url pattern="/login" requires-channel="https"/>
// <intercept-url pattern="/secured/**" requires-channel="https"/>
.antMatchers("/login","/secured/**").requiresSecure()
// the line below is similar to intercept-url@requires-channel="http":
// <intercept-url pattern="/**" 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")
}
}
}

View File

@ -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 <jee> 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<PreAuthenticatedAuthenticationToken> {
public UserDetails loadUserDetails(
PreAuthenticatedAuthenticationToken token)
throws UsernameNotFoundException {
return null;
}
}
}

View File

@ -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 <port-mappings> 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)
}
}

View File

@ -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 <request-cache> 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) {
}
}
}

View File

@ -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 <access-denied-handler> 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 {
}
}
}
}

View File

@ -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 <custom-filter> 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<Class<?>> assertThatFilters() {
FilterChainProxy filterChain = this.spring.getContext().getBean(FilterChainProxy.class);
List<Class<?>> filters = filterChain.getFilters("/").stream()
.map(Object::getClass).collect(Collectors.toList());
return assertThat(filters);
}
}

View File

@ -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 <http-firewall> 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);
}
}
}

View File

@ -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 <form-login> 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> T verifyBean(Class<T> beanClass) {
return verify(this.spring.getContext().getBean(beanClass));
}
}

View File

@ -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 <headers> attributes is present
*
* @author Rob Winch
* @author Josh Cummings
*
*/
public class NamespaceHttpHeadersTests {
static final Map<String, String> 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<String, String> headers) {
return includes(headers, headers.keySet().toArray(new String[headers.size()]));
}
private static ResultMatcher includes(Map<String, String> headers, String... headerNames) {
return result -> {
assertThat(result.getResponse().getHeaderNames()).hasSameSizeAs(headerNames);
for (String headerName : headerNames) {
header().string(headerName, headers.get(headerName)).match(result);
}
};
}
}

View File

@ -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 <intercept-url> 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:
// <intercept-url pattern="/users**" access="hasRole('ROLE_ADMIN')"/>
// <intercept-url pattern="/sessions/**" access="hasRole('ROLE_ADMIN')"/>
.antMatchers("/users**", "/sessions/**").hasRole("ADMIN")
// the line below is similar to intercept-url@method:
// <intercept-url pattern="/admin/post" access="hasRole('ROLE_ADMIN')" method="POST"/>
// <intercept-url pattern="/admin/another-post/**" access="hasRole('ROLE_ADMIN')" method="POST"/>
.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":
// <intercept-url pattern="/login" requires-channel="https"/>
// <intercept-url pattern="/secured/**" requires-channel="https"/>
.antMatchers("/login", "/secured/**").requiresSecure()
// the line below is similar to intercept-url@requires-channel="http":
// <intercept-url pattern="/**" 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));
}
}

View File

@ -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 <jee> 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> T bean(Class<T> beanClass) {
return this.spring.getContext().getBean(beanClass);
}
private <T> T verifyBean(Class<T> beanClass) {
return verify(this.spring.getContext().getBean(beanClass));
}
}

View File

@ -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 <port-mappings> 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");
}
}
}

View File

@ -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 <request-cache> 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> T verifyBean(Class<T> beanClass) {
return verify(this.spring.getContext().getBean(beanClass));
}
}

View File

@ -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 <access-denied-handler> 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> T verifyBean(Class<T> beanClass) {
return verify(this.spring.getContext().getBean(beanClass));
}
}