From ea6ce05662338054feee6c85fac48890d8a23133 Mon Sep 17 00:00:00 2001 From: Steve Riesenberg Date: Fri, 18 Nov 2022 11:14:41 -0600 Subject: [PATCH] Add configurer tests for CookieCsrfTokenRepository Issue gh-12236 --- .../web/configurers/CsrfConfigurerTests.java | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java index 51e1737d17..738c1a23e5 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java @@ -17,7 +17,10 @@ package org.springframework.security.config.annotation.web.configurers; import java.net.URI; +import java.util.Arrays; +import java.util.List; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -28,6 +31,7 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.mock.web.MockHttpSession; import org.springframework.security.config.Customizer; @@ -42,6 +46,7 @@ import org.springframework.security.core.userdetails.PasswordEncodedUser; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; @@ -509,6 +514,86 @@ public class CsrfConfigurerTests { verifyNoMoreInteractions(csrfTokenRepository); } + @Test + public void loginWhenFormLoginAndCookieCsrfTokenRepositorySetAndExistingTokenThenRemovesAndGeneratesNewToken() + throws Exception { + CsrfToken csrfToken = new DefaultCsrfToken("X-XSRF-TOKEN", "_csrf", "token"); + Cookie existingCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken()); + CookieCsrfTokenRepository csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse(); + csrfTokenRepository.setCookieName(existingCookie.getName()); + CsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository; + CsrfTokenRequestHandlerConfig.HANDLER = new CsrfTokenRequestAttributeHandler(); + this.spring.register(CsrfTokenRequestHandlerConfig.class, BasicController.class).autowire(); + + // @formatter:off + MockHttpServletRequestBuilder loginRequest = post("/login") + .cookie(existingCookie) + .header(csrfToken.getHeaderName(), csrfToken.getToken()) + .param("username", "user") + .param("password", "password"); + // @formatter:on + MvcResult mvcResult = this.mvc.perform(loginRequest).andExpect(redirectedUrl("/")).andReturn(); + List cookies = Arrays.asList(mvcResult.getResponse().getCookies()); + cookies.removeIf((cookie) -> !cookie.getName().equalsIgnoreCase(existingCookie.getName())); + assertThat(cookies).hasSize(2); + assertThat(cookies.get(0).getValue()).isEmpty(); + assertThat(cookies.get(1).getValue()).isNotEmpty(); + } + + @Test + public void postWhenHttpBasicAndCookieCsrfTokenRepositorySetAndExistingTokenThenRemovesAndGeneratesNewToken() + throws Exception { + CsrfToken csrfToken = new DefaultCsrfToken("X-XSRF-TOKEN", "_csrf", "token"); + Cookie existingCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken()); + CookieCsrfTokenRepository csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse(); + csrfTokenRepository.setCookieName(existingCookie.getName()); + HttpBasicCsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository; + HttpBasicCsrfTokenRequestHandlerConfig.HANDLER = new CsrfTokenRequestAttributeHandler(); + this.spring.register(HttpBasicCsrfTokenRequestHandlerConfig.class, BasicController.class).autowire(); + + HttpHeaders headers = new HttpHeaders(); + headers.set(csrfToken.getHeaderName(), csrfToken.getToken()); + headers.setBasicAuth("user", "password"); + // @formatter:off + MvcResult mvcResult = this.mvc.perform(post("/") + .cookie(existingCookie) + .headers(headers)) + .andExpect(status().isOk()) + .andReturn(); + // @formatter:on + List cookies = Arrays.asList(mvcResult.getResponse().getCookies()); + cookies.removeIf((cookie) -> !cookie.getName().equalsIgnoreCase(existingCookie.getName())); + assertThat(cookies).hasSize(2); + assertThat(cookies.get(0).getValue()).isEmpty(); + assertThat(cookies.get(1).getValue()).isNotEmpty(); + } + + @Test + public void getWhenHttpBasicAndCookieCsrfTokenRepositorySetAndNoExistingCookieThenGeneratesNewToken() + throws Exception { + CsrfToken csrfToken = new DefaultCsrfToken("X-XSRF-TOKEN", "_csrf", "token"); + Cookie expectedCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken()); + CookieCsrfTokenRepository csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse(); + csrfTokenRepository.setCookieName(expectedCookie.getName()); + HttpBasicCsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository; + HttpBasicCsrfTokenRequestHandlerConfig.HANDLER = new CsrfTokenRequestAttributeHandler(); + this.spring.register(HttpBasicCsrfTokenRequestHandlerConfig.class, BasicController.class).autowire(); + + HttpHeaders headers = new HttpHeaders(); + headers.set(csrfToken.getHeaderName(), csrfToken.getToken()); + headers.setBasicAuth("user", "password"); + // @formatter:off + MvcResult mvcResult = this.mvc.perform(get("/") + .headers(headers)) + .andExpect(status().isOk()) + .andReturn(); + // @formatter:on + List cookies = Arrays.asList(mvcResult.getResponse().getCookies()); + cookies.removeIf((cookie) -> !cookie.getName().equalsIgnoreCase(expectedCookie.getName())); + assertThat(cookies).hasSize(1); + assertThat(cookies.get(0).getValue()).isNotEmpty(); + } + @Configuration static class AllowHttpMethodsFirewallConfig { @@ -886,6 +971,42 @@ public class CsrfConfigurerTests { } + @Configuration + @EnableWebSecurity + static class HttpBasicCsrfTokenRequestHandlerConfig { + + static CsrfTokenRepository REPO; + + static CsrfTokenRequestHandler HANDLER; + + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().authenticated() + ) + .httpBasic(Customizer.withDefaults()) + .csrf((csrf) -> csrf + .csrfTokenRepository(REPO) + .csrfTokenRequestHandler(HANDLER) + ); + // @formatter:on + + return http.build(); + } + + @Autowired + void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()); + // @formatter:on + } + + } + @RestController static class BasicController {