From d2aa70707152276d757167ed034ba40433977926 Mon Sep 17 00:00:00 2001 From: John Niang Date: Tue, 25 Oct 2022 10:56:11 +0800 Subject: [PATCH] Bump Spring Boot to 3.0.0-RC1 (#2620) #### What type of PR is this? /kind improvement /area core /milestone 2.0 #### What this PR does / why we need it: - See https://github.com/spring-projects/spring-boot/releases/tag/v3.0.0-RC1 for more. - Due to [Default to Xor CSRF protection](https://github.com/spring-projects/spring-security/issues/11960), we have to implement a XOR algorithm in console project to generate a XORed token. Please be aware of source code of Spring Security at [here](https://github.com/spring-projects/spring-security/blob/9cb668aec2ad14f91c122c66b7d7d4a8b6e133f7/web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java#L94-L115), @halo-dev/sig-halo-console #### Special notes for reviewers We have removed `ThemeJava8TimeDialect` due to removal of `thymeleaf-extras-java8time` module in https://github.com/thymeleaf/thymeleaf/issues/912 #### Does this PR introduce a user-facing change? ```release-note None ``` --- build.gradle | 10 +- settings.gradle | 2 - .../run/halo/app/security/CsrfConfigurer.java | 4 + .../pat/PatAuthenticationConverter.java | 4 +- .../halo/app/theme/ThemeConfiguration.java | 7 - .../DefaultJava8TimeExpressionFactory.java | 28 ---- .../theme/dialect/ThemeJava8TimeDialect.java | 18 --- .../ThemeJava8TimeDialectIntegrationTest.java | 147 ------------------ 8 files changed, 9 insertions(+), 211 deletions(-) delete mode 100644 src/main/java/run/halo/app/theme/dialect/DefaultJava8TimeExpressionFactory.java delete mode 100644 src/main/java/run/halo/app/theme/dialect/ThemeJava8TimeDialect.java delete mode 100644 src/test/java/run/halo/app/theme/dialect/ThemeJava8TimeDialectIntegrationTest.java diff --git a/build.gradle b/build.gradle index 0b3c607f2..fc87f6d2b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'org.springframework.boot' version '3.0.0-M5' - id 'io.spring.dependency-management' version '1.0.14.RELEASE' + id 'org.springframework.boot' version '3.0.0-RC1' + id 'io.spring.dependency-management' version '1.1.0' id "checkstyle" id 'java' } @@ -15,13 +15,9 @@ checkstyle { } repositories { - maven { url 'https://maven.aliyun.com/repository/public/' } - maven { url 'https://maven.aliyun.com/repository/spring/' } - maven { url 'https://repo.spring.io/milestone' } - - mavenLocal() mavenCentral() + maven { url 'https://repo.spring.io/milestone' } } diff --git a/settings.gradle b/settings.gradle index eeb1ee70c..da157adfe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,5 @@ pluginManagement { repositories { - maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } - maven { url 'https://maven.aliyun.com/repository/spring-plugin' } maven { url 'https://repo.spring.io/milestone' } gradlePluginPortal() } diff --git a/src/main/java/run/halo/app/security/CsrfConfigurer.java b/src/main/java/run/halo/app/security/CsrfConfigurer.java index 6f2bf4cd4..6161556ff 100644 --- a/src/main/java/run/halo/app/security/CsrfConfigurer.java +++ b/src/main/java/run/halo/app/security/CsrfConfigurer.java @@ -5,6 +5,7 @@ import static org.springframework.security.web.server.util.matcher.ServerWebExch import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository; import org.springframework.security.web.server.csrf.CsrfWebFilter; +import org.springframework.security.web.server.csrf.ServerCsrfTokenRequestAttributeHandler; import org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher; import org.springframework.stereotype.Component; @@ -21,6 +22,9 @@ public class CsrfConfigurer implements SecurityConfigurer { )); http.csrf().csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()) + // TODO Use XorServerCsrfTokenRequestAttributeHandler instead when console implements + // the algorithm + .csrfTokenRequestHandler(new ServerCsrfTokenRequestAttributeHandler()) .requireCsrfProtectionMatcher(csrfMatcher); } diff --git a/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationConverter.java b/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationConverter.java index c55bdc609..205a23ade 100644 --- a/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationConverter.java +++ b/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationConverter.java @@ -1,8 +1,8 @@ package run.halo.app.security.authentication.pat; import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; -import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter; +import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken; +import org.springframework.security.oauth2.server.resource.web.server.authentication.ServerBearerTokenAuthenticationConverter; import org.springframework.security.web.server.authentication.ServerAuthenticationConverter; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; diff --git a/src/main/java/run/halo/app/theme/ThemeConfiguration.java b/src/main/java/run/halo/app/theme/ThemeConfiguration.java index 520d2445f..88fc83e5e 100644 --- a/src/main/java/run/halo/app/theme/ThemeConfiguration.java +++ b/src/main/java/run/halo/app/theme/ThemeConfiguration.java @@ -11,11 +11,9 @@ import org.springframework.http.MediaType; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerResponse; -import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect; import run.halo.app.infra.properties.HaloProperties; import run.halo.app.infra.utils.FilePathUtils; import run.halo.app.theme.dialect.LinkExpressionObjectDialect; -import run.halo.app.theme.dialect.ThemeJava8TimeDialect; /** * @author guqing @@ -49,11 +47,6 @@ public class ThemeConfiguration { "themes", themeName, "templates", "assets", resource); } - @Bean - Java8TimeDialect java8TimeDialect() { - return new ThemeJava8TimeDialect(); - } - @Bean LinkExpressionObjectDialect linkExpressionObjectDialect() { return new LinkExpressionObjectDialect(); diff --git a/src/main/java/run/halo/app/theme/dialect/DefaultJava8TimeExpressionFactory.java b/src/main/java/run/halo/app/theme/dialect/DefaultJava8TimeExpressionFactory.java deleted file mode 100644 index 2cbc8e8a7..000000000 --- a/src/main/java/run/halo/app/theme/dialect/DefaultJava8TimeExpressionFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -package run.halo.app.theme.dialect; - -import static run.halo.app.theme.ThemeLocaleContextResolver.TIME_ZONE_REQUEST_ATTRIBUTE_NAME; - -import java.util.TimeZone; -import org.thymeleaf.context.IExpressionContext; -import org.thymeleaf.extras.java8time.dialect.Java8TimeExpressionFactory; -import org.thymeleaf.extras.java8time.expression.Temporals; - -/** - * @author guqing - * @since 2.0.0 - */ -public class DefaultJava8TimeExpressionFactory extends Java8TimeExpressionFactory { - private static final String TEMPORAL_EVALUATION_VARIABLE_NAME = "temporals"; - - @Override - public Object buildObject(IExpressionContext context, String expressionObjectName) { - TimeZone timeZone = (TimeZone) context.getVariable(TIME_ZONE_REQUEST_ATTRIBUTE_NAME); - if (timeZone == null) { - timeZone = TimeZone.getDefault(); - } - if (TEMPORAL_EVALUATION_VARIABLE_NAME.equals(expressionObjectName)) { - return new Temporals(context.getLocale(), timeZone.toZoneId()); - } - return null; - } -} diff --git a/src/main/java/run/halo/app/theme/dialect/ThemeJava8TimeDialect.java b/src/main/java/run/halo/app/theme/dialect/ThemeJava8TimeDialect.java deleted file mode 100644 index 87829ef86..000000000 --- a/src/main/java/run/halo/app/theme/dialect/ThemeJava8TimeDialect.java +++ /dev/null @@ -1,18 +0,0 @@ -package run.halo.app.theme.dialect; - -import org.thymeleaf.expression.IExpressionObjectFactory; -import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect; - -/** - * @author guqing - * @since 2.0.0 - */ -public class ThemeJava8TimeDialect extends Java8TimeDialect { - private final IExpressionObjectFactory expressionObjectFactory = - new DefaultJava8TimeExpressionFactory(); - - @Override - public IExpressionObjectFactory getExpressionObjectFactory() { - return expressionObjectFactory; - } -} diff --git a/src/test/java/run/halo/app/theme/dialect/ThemeJava8TimeDialectIntegrationTest.java b/src/test/java/run/halo/app/theme/dialect/ThemeJava8TimeDialectIntegrationTest.java deleted file mode 100644 index 64374da63..000000000 --- a/src/test/java/run/halo/app/theme/dialect/ThemeJava8TimeDialectIntegrationTest.java +++ /dev/null @@ -1,147 +0,0 @@ -package run.halo.app.theme.dialect; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; -import static run.halo.app.theme.ThemeLocaleContextResolver.TIME_ZONE_COOKIE_NAME; - -import java.io.FileNotFoundException; -import java.net.URL; -import java.nio.file.Paths; -import java.time.Instant; -import java.time.ZoneId; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -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.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.context.annotation.Bean; -import org.springframework.http.MediaType; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.util.ResourceUtils; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; -import org.thymeleaf.extras.java8time.expression.Temporals; -import reactor.core.publisher.Mono; -import run.halo.app.theme.ThemeContext; -import run.halo.app.theme.ThemeResolver; - -/** - * Tests for {@link ThemeJava8TimeDialect}. - * - * @author guqing - * @since 2.0.0 - */ -@SpringBootTest -@AutoConfigureWebTestClient -class ThemeJava8TimeDialectIntegrationTest { - private static final Instant INSTANT = Instant.now(); - - // @Autowired - @SpyBean - private ThemeResolver themeResolver; - - private URL defaultThemeUrl; - - @Autowired - private WebTestClient webTestClient; - - private TimeZone defaultTimeZone; - - @BeforeEach - void setUp() throws FileNotFoundException { - defaultThemeUrl = ResourceUtils.getURL("classpath:themes/default"); - when(themeResolver.getTheme(any(ServerHttpRequest.class))) - .thenReturn(Mono.just(createDefaultContext())); - defaultTimeZone = TimeZone.getDefault(); - } - - @AfterEach - void tearDown() { - TimeZone.setDefault(defaultTimeZone); - } - - @Test - void temporalsInAmerica() { - TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles"); - TimeZone.setDefault(timeZone); - - assertTemporals(timeZone); - } - - @Test - void temporalsInChina() { - TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); - TimeZone.setDefault(timeZone); - - assertTemporals(timeZone); - } - - - @Test - void timeZoneFromCookie() { - TimeZone timeZone = TimeZone.getTimeZone("Africa/Accra"); - String formatTime = timeZoneTemporalFormat(timeZone.toZoneId()); - - webTestClient.get() - .uri("/timezone?language=zh") - .cookie(TIME_ZONE_COOKIE_NAME, timeZone.toZoneId().toString()) - .exchange() - .expectStatus() - .isOk() - .expectBody(String.class) - .isEqualTo(String.format("

%s

\n", formatTime)); - } - - @Test - void invalidTimeZone() { - TimeZone timeZone = TimeZone.getTimeZone("invalid/timezone"); - //the GMT zone if the given ID cannot be understood. - assertThat(timeZone.toZoneId().toString()).isEqualTo("GMT"); - } - - private void assertTemporals(TimeZone timeZone) { - String formatTime = timeZoneTemporalFormat(timeZone.toZoneId()); - webTestClient.get() - .uri("/timezone?language=zh") - .exchange() - .expectStatus() - .isOk() - .expectBody(String.class) - .isEqualTo(String.format("

%s

\n", formatTime)); - } - - private String timeZoneTemporalFormat(ZoneId zoneId) { - Temporals temporals = new Temporals(Locale.CHINESE, zoneId); - return temporals.format(INSTANT, "yyyy-MM-dd HH:mm:ss"); - } - - ThemeContext createDefaultContext() { - return ThemeContext.builder() - .name("default") - .path(Paths.get(defaultThemeUrl.getPath())) - .active(true) - .build(); - } - - @TestConfiguration - static class MessageResolverConfig { - - @Bean - RouterFunction routeTestIndex() { - return RouterFunctions - .route(RequestPredicates.GET("/timezone") - .and(RequestPredicates.accept(MediaType.TEXT_HTML)), - request -> ServerResponse.ok().render("timezone", Map.of("instants", INSTANT))); - } - } -} \ No newline at end of file