Allow configuration of csrf through nested builder

Issue: gh-5557
This commit is contained in:
Eleftheria Stein 2019-06-19 16:32:44 -04:00
parent 1a31376dda
commit 6986cf3ef3
3 changed files with 175 additions and 1 deletions

View File

@ -771,6 +771,35 @@ public final class HttpSecurity extends
return getOrApply(new CsrfConfigurer<>(context));
}
/**
* Adds CSRF support. This is activated by default when using
* {@link WebSecurityConfigurerAdapter}'s default constructor. You can disable it
* using:
*
* <pre>
* &#064;Configuration
* &#064;EnableWebSecurity
* public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
*
* &#064;Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .csrf(csrf -> csrf.disable());
* }
* }
* </pre>
*
* @param csrfCustomizer the {@link Customizer} to provide more options for
* the {@link CsrfConfigurer}
* @return the {@link HttpSecurity} for further customizations
* @throws Exception
*/
public HttpSecurity csrf(Customizer<CsrfConfigurer<HttpSecurity>> csrfCustomizer) throws Exception {
ApplicationContext context = getContext();
csrfCustomizer.customize(getOrApply(new CsrfConfigurer<>(context)));
return HttpSecurity.this;
}
/**
* Provides logout support. This is automatically applied when using
* {@link WebSecurityConfigurerAdapter}. The default is that accessing the URL

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* 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.
@ -75,6 +75,36 @@ public class CsrfConfigurerIgnoringRequestMatchersTests {
}
}
@Test
public void requestWhenIgnoringRequestMatchersInLambdaThenAugmentedByConfiguredRequestMatcher()
throws Exception {
this.spring.register(IgnoringRequestInLambdaMatchers.class, BasicController.class).autowire();
this.mvc.perform(get("/path"))
.andExpect(status().isForbidden());
this.mvc.perform(post("/path"))
.andExpect(status().isOk());
}
@EnableWebSecurity
static class IgnoringRequestInLambdaMatchers extends WebSecurityConfigurerAdapter {
RequestMatcher requestMatcher =
request -> HttpMethod.POST.name().equals(request.getMethod());
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf(csrf ->
csrf
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/path"))
.ignoringRequestMatchers(this.requestMatcher)
);
// @formatter:on
}
}
@Test
public void requestWhenIgnoringRequestMatcherThenUnionsWithConfiguredIgnoringAntMatchers()
throws Exception {
@ -107,6 +137,40 @@ public class CsrfConfigurerIgnoringRequestMatchersTests {
}
}
@Test
public void requestWhenIgnoringRequestMatcherInLambdaThenUnionsWithConfiguredIgnoringAntMatchers()
throws Exception {
this.spring.register(IgnoringPathsAndMatchersInLambdaConfig.class, BasicController.class).autowire();
this.mvc.perform(put("/csrf"))
.andExpect(status().isForbidden());
this.mvc.perform(post("/csrf"))
.andExpect(status().isOk());
this.mvc.perform(put("/no-csrf"))
.andExpect(status().isOk());
}
@EnableWebSecurity
static class IgnoringPathsAndMatchersInLambdaConfig extends WebSecurityConfigurerAdapter {
RequestMatcher requestMatcher =
request -> HttpMethod.POST.name().equals(request.getMethod());
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf(csrf ->
csrf
.ignoringAntMatchers("/no-csrf")
.ignoringRequestMatchers(this.requestMatcher)
);
// @formatter:on
}
}
@RestController
public static class BasicController {
@RequestMapping("/path")

View File

@ -210,6 +210,26 @@ public class CsrfConfigurerTests {
}
}
@Test
public void postWhenCsrfDisabledInLambdaThenRespondsWithOk() throws Exception {
this.spring.register(DisableCsrfInLambdaConfig.class, BasicController.class).autowire();
this.mvc.perform(post("/"))
.andExpect(status().isOk());
}
@EnableWebSecurity
static class DisableCsrfInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf(AbstractHttpConfigurer::disable);
// @formatter:on
}
}
// SEC-2498
@Test
public void loginWhenCsrfDisabledThenRedirectsToPreviousPostRequest() throws Exception {
@ -386,6 +406,40 @@ public class CsrfConfigurerTests {
}
}
@Test
public void requireCsrfProtectionMatcherInLambdaWhenRequestDoesNotMatchThenRespondsWithOk() throws Exception {
RequireCsrfProtectionMatcherInLambdaConfig.MATCHER = mock(RequestMatcher.class);
this.spring.register(RequireCsrfProtectionMatcherInLambdaConfig.class, BasicController.class).autowire();
when(RequireCsrfProtectionMatcherInLambdaConfig.MATCHER.matches(any()))
.thenReturn(false);
this.mvc.perform(get("/"))
.andExpect(status().isOk());
}
@Test
public void requireCsrfProtectionMatcherInLambdaWhenRequestMatchesThenRespondsWithForbidden() throws Exception {
RequireCsrfProtectionMatcherInLambdaConfig.MATCHER = mock(RequestMatcher.class);
when(RequireCsrfProtectionMatcherInLambdaConfig.MATCHER.matches(any())).thenReturn(true);
this.spring.register(RequireCsrfProtectionMatcherInLambdaConfig.class, BasicController.class).autowire();
this.mvc.perform(get("/"))
.andExpect(status().isForbidden());
}
@EnableWebSecurity
static class RequireCsrfProtectionMatcherInLambdaConfig extends WebSecurityConfigurerAdapter {
static RequestMatcher MATCHER;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf(csrf -> csrf.requireCsrfProtectionMatcher(MATCHER));
// @formatter:on
}
}
@Test
public void getWhenCustomCsrfTokenRepositoryThenRepositoryIsUsed() throws Exception {
CsrfTokenRepositoryConfig.REPO = mock(CsrfTokenRepository.class);
@ -454,6 +508,33 @@ public class CsrfConfigurerTests {
}
}
@Test
public void getWhenCustomCsrfTokenRepositoryInLambdaThenRepositoryIsUsed() throws Exception {
CsrfTokenRepositoryInLambdaConfig.REPO = mock(CsrfTokenRepository.class);
when(CsrfTokenRepositoryInLambdaConfig.REPO.loadToken(any()))
.thenReturn(new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token"));
this.spring.register(CsrfTokenRepositoryInLambdaConfig.class, BasicController.class).autowire();
this.mvc.perform(get("/"))
.andExpect(status().isOk());
verify(CsrfTokenRepositoryInLambdaConfig.REPO).loadToken(any(HttpServletRequest.class));
}
@EnableWebSecurity
static class CsrfTokenRepositoryInLambdaConfig extends WebSecurityConfigurerAdapter {
static CsrfTokenRepository REPO;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.formLogin()
.and()
.csrf(csrf -> csrf.csrfTokenRepository(REPO));
// @formatter:on
}
}
@Test
public void getWhenCustomAccessDeniedHandlerThenHandlerIsUsed() throws Exception {
AccessDeniedHandlerConfig.DENIED_HANDLER = mock(AccessDeniedHandler.class);