From 222e955a66392bac01a386f9486304e33f1b8f5b Mon Sep 17 00:00:00 2001 From: John Niang Date: Sun, 20 Apr 2025 16:18:45 +0800 Subject: [PATCH] Disable CSRF check for PAT authentication (#7353) #### What type of PR is this? /kind improvement /area core /milestone 2.20.x #### What this PR does / why we need it: This PR disables CSRF check for PAT authentication because the authentication won't pass any cookies to server. #### Does this PR introduce a user-facing change? ```release-note None ``` --- .../run/halo/app/security/CsrfConfigurer.java | 13 ++++++++- .../pat/PatAuthenticationConverter.java | 2 +- .../halo/app/security/CsrfSecurityTest.java | 29 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 application/src/test/java/run/halo/app/security/CsrfSecurityTest.java diff --git a/application/src/main/java/run/halo/app/security/CsrfConfigurer.java b/application/src/main/java/run/halo/app/security/CsrfConfigurer.java index 2d86cc31f..1dcceb26a 100644 --- a/application/src/main/java/run/halo/app/security/CsrfConfigurer.java +++ b/application/src/main/java/run/halo/app/security/CsrfConfigurer.java @@ -9,8 +9,11 @@ import org.springframework.security.web.server.csrf.CsrfWebFilter; import org.springframework.security.web.server.csrf.XorServerCsrfTokenRequestAttributeHandler; import org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; import run.halo.app.security.authentication.SecurityConfigurer; +import run.halo.app.security.authentication.pat.PatAuthenticationConverter; @Component @Order(0) @@ -25,7 +28,8 @@ class CsrfConfigurer implements SecurityConfigurer { "/apis/**", "/actuator/**", "/system/setup" - )) + )), + new NegatedServerWebExchangeMatcher(patAuthMatcher()) ); http.csrf(csrfSpec -> csrfSpec .csrfTokenRepository(new CookieServerCsrfTokenRepository()) @@ -33,4 +37,11 @@ class CsrfConfigurer implements SecurityConfigurer { .requireCsrfProtectionMatcher(csrfMatcher)); } + private static ServerWebExchangeMatcher patAuthMatcher() { + var patConverter = new PatAuthenticationConverter(); + return exchange -> patConverter.convert(exchange) + .flatMap(a -> ServerWebExchangeMatcher.MatchResult.match()) + .switchIfEmpty(Mono.defer(ServerWebExchangeMatcher.MatchResult::notMatch)); + } + } diff --git a/application/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationConverter.java b/application/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationConverter.java index d8cb72381..65829450a 100644 --- a/application/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationConverter.java +++ b/application/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationConverter.java @@ -15,7 +15,7 @@ import reactor.core.publisher.Mono; * @author johnniang * @since 2.20.4 */ -class PatAuthenticationConverter extends ServerBearerTokenAuthenticationConverter { +public class PatAuthenticationConverter extends ServerBearerTokenAuthenticationConverter { @Override public Mono convert(ServerWebExchange exchange) { diff --git a/application/src/test/java/run/halo/app/security/CsrfSecurityTest.java b/application/src/test/java/run/halo/app/security/CsrfSecurityTest.java new file mode 100644 index 000000000..0c7ae8cec --- /dev/null +++ b/application/src/test/java/run/halo/app/security/CsrfSecurityTest.java @@ -0,0 +1,29 @@ +package run.halo.app.security; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.web.reactive.server.WebTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +class CsrfSecurityTest { + + @Autowired + WebTestClient webClient; + + @Test + void shouldNotCheckCsrfForPatAuthentication() { + webClient.post() + .uri("/fake") + .headers(headers -> headers.setBearerAuth("pat_invalid")) + .exchange() + .expectStatus() + .isUnauthorized() + .expectHeader() + .exists(HttpHeaders.WWW_AUTHENTICATE); + } + +}