From 7215c72373f9e108549649d2c5d25ca5a6361865 Mon Sep 17 00:00:00 2001 From: Steve Riesenberg <5248162+sjohnr@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:09:36 -0500 Subject: [PATCH 01/18] Migrate slack notifications to GChat Issue gh-15503 [skip ci] --- .../continuous-integration-workflow.yml | 45 +++++-------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 8ca59fb92c..d9e1c0e335 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -9,7 +9,6 @@ on: workflow_dispatch: # Manual trigger env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} GRADLE_ENTERPRISE_CACHE_USER: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }} GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }} @@ -241,26 +240,11 @@ jobs: export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY" echo "Tagging and publishing $REPO@$VERSION release on GitHub." ./gradlew createGitHubRelease -PnextVersion=$VERSION -Pbranch=$BRANCH -PcreateRelease=true -PgitHubAccessToken=$TOKEN - - name: Announce Release on Slack - id: spring-security-announcing - uses: slackapi/slack-github-action@v1.19.0 - with: - payload: | - { - "text": "spring-security-announcing `${{ env.VERSION }}` is available now", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "spring-security-announcing `${{ env.VERSION }}` is available now" - } - } - ] - } + - name: Announce Release on GChat env: - SLACK_WEBHOOK_URL: ${{ secrets.SPRING_RELEASE_SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + WEBHOOK_URL: ${{ secrets.SPRING_RELEASE_GCHAT_WEBHOOK_URL }} + run: | + curl -X POST '${{ env.WEBHOOK_URL }}' -H 'Content-Type: application/json' -d '{ "text": "spring-security-announcing `${{ env.VERSION }}` is available now" }' || true - name: Setup git config run: | git config user.name 'github-actions[bot]' @@ -295,20 +279,13 @@ jobs: distribution: 'adopt' - name: Schedule next release (if not already scheduled) run: ./gradlew scheduleNextRelease -PnextVersion=$VERSION -PgitHubAccessToken=$TOKEN - notify_result: - name: Check for failures - needs: [perform_release, perform_post_release] - if: failure() + send-notification: + name: Send Notification + needs: [ perform_release, perform_post_release ] + if: ${{ failure() || cancelled() }} runs-on: ubuntu-latest - permissions: - actions: read steps: - - name: Send Slack message - # Workaround while waiting for Gamesight/slack-workflow-status#38 to be fixed - # See https://github.com/Gamesight/slack-workflow-status/issues/38 - uses: sjohnr/slack-workflow-status@v1-beta + - name: Send Notification + uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1 with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} - channel: '#spring-security-ci' - name: 'CI Notifier' + webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }} From c552366a7847f09bdadcdbf50e2beb5d41de36fc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 21 Oct 2024 17:23:29 +0000 Subject: [PATCH 02/18] Release 5.7.13 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 70b5473345..903b5c8959 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ springJavaformatVersion=0.0.31 springBootVersion=2.4.2 springFrameworkVersion=5.3.29 openSamlVersion=3.4.6 -version=5.7.13-SNAPSHOT +version=5.7.13 kotlinVersion=1.6.21 samplesBranch=5.7.x org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError From dd3c6892e946f80cdd6e1a6890282fa2920067f1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 21 Oct 2024 17:58:56 +0000 Subject: [PATCH 03/18] Next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 903b5c8959..6adbe22ca5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ springJavaformatVersion=0.0.31 springBootVersion=2.4.2 springFrameworkVersion=5.3.29 openSamlVersion=3.4.6 -version=5.7.13 +version=5.7.14-SNAPSHOT kotlinVersion=1.6.21 samplesBranch=5.7.x org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError From e48d6b039baf104a6c56f4e62e0c2ff4d35a22bb Mon Sep 17 00:00:00 2001 From: Rob Winch <362503+rwinch@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:36:06 -0500 Subject: [PATCH 04/18] Support ServerWebExchangeFirewall @Bean Closes gh-15974 --- .../WebFluxSecurityConfiguration.java | 8 +++- .../WebFluxSecurityConfigurationTests.java | 46 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java index 07119e8ee0..9796e9349e 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java @@ -19,6 +19,7 @@ package org.springframework.security.config.annotation.web.reactive; import java.util.Arrays; import java.util.List; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.context.ApplicationContext; @@ -30,6 +31,7 @@ import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.WebFilterChainProxy; +import org.springframework.security.web.server.firewall.ServerWebExchangeFirewall; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.web.reactive.result.view.AbstractView; @@ -65,8 +67,10 @@ class WebFluxSecurityConfiguration { @Bean(SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME) @Order(WEB_FILTER_CHAIN_FILTER_ORDER) - WebFilterChainProxy springSecurityWebFilterChainFilter() { - return new WebFilterChainProxy(getSecurityWebFilterChains()); + WebFilterChainProxy springSecurityWebFilterChainFilter(ObjectProvider firewall) { + WebFilterChainProxy webFilterChainProxy = new WebFilterChainProxy(getSecurityWebFilterChains()); + firewall.ifUnique(webFilterChainProxy::setFirewall); + return webFilterChainProxy; } @Bean(name = AbstractView.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME) diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java index 3fb9548e4b..9c49fb7a82 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java @@ -16,14 +16,24 @@ package org.springframework.security.config.annotation.web.reactive; +import java.util.Collections; + +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import reactor.core.publisher.Mono; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration; import org.springframework.security.web.server.WebFilterChainProxy; +import org.springframework.security.web.server.firewall.ServerWebExchangeFirewall; +import org.springframework.web.server.handler.DefaultWebFilterChain; import static org.assertj.core.api.Assertions.assertThat; @@ -45,6 +55,28 @@ public class WebFluxSecurityConfigurationTests { assertThat(webFilterChainProxy).isNotNull(); } + @Test + void loadConfigWhenDefaultThenFirewalled() throws Exception { + this.spring.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class, + WebFluxSecurityConfiguration.class).autowire(); + WebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class); + MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/;/").build()); + DefaultWebFilterChain chain = emptyChain(); + webFilterChainProxy.filter(exchange, chain).block(); + assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + } + + @Test + void loadConfigWhenFirewallBeanThenCustomized() throws Exception { + this.spring.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class, + WebFluxSecurityConfiguration.class, NoOpFirewallConfig.class).autowire(); + WebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class); + MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/;/").build()); + DefaultWebFilterChain chain = emptyChain(); + webFilterChainProxy.filter(exchange, chain).block(); + assertThat(exchange.getResponse().getStatusCode()).isNotEqualTo(HttpStatus.BAD_REQUEST); + } + @Test public void loadConfigWhenBeanProxyingEnabledAndSubclassThenWebFilterChainProxyExists() { this.spring.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class, @@ -53,6 +85,20 @@ public class WebFluxSecurityConfigurationTests { assertThat(webFilterChainProxy).isNotNull(); } + private static @NotNull DefaultWebFilterChain emptyChain() { + return new DefaultWebFilterChain((webExchange) -> Mono.empty(), Collections.emptyList()); + } + + @Configuration + static class NoOpFirewallConfig { + + @Bean + ServerWebExchangeFirewall noOpFirewall() { + return ServerWebExchangeFirewall.INSECURE_NOOP; + } + + } + @Configuration static class SubclassConfig extends WebFluxSecurityConfiguration { From 5c2106b22eded588e15f160c92289b37eec27354 Mon Sep 17 00:00:00 2001 From: Rob Winch <362503+rwinch@users.noreply.github.com> Date: Fri, 25 Oct 2024 12:24:54 -0500 Subject: [PATCH 05/18] Format --- .../reactive/WebFluxSecurityConfigurationTests.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java index 0711b74ba1..4c8c249d7e 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java @@ -59,8 +59,10 @@ public class WebFluxSecurityConfigurationTests { @Test void loadConfigWhenDefaultThenFirewalled() throws Exception { - this.spring.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class, - WebFluxSecurityConfiguration.class).autowire(); + this.spring + .register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class, + WebFluxSecurityConfiguration.class) + .autowire(); WebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class); MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/;/").build()); DefaultWebFilterChain chain = emptyChain(); @@ -70,8 +72,10 @@ public class WebFluxSecurityConfigurationTests { @Test void loadConfigWhenFirewallBeanThenCustomized() throws Exception { - this.spring.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class, - WebFluxSecurityConfiguration.class, NoOpFirewallConfig.class).autowire(); + this.spring + .register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class, + WebFluxSecurityConfiguration.class, NoOpFirewallConfig.class) + .autowire(); WebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class); MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/;/").build()); DefaultWebFilterChain chain = emptyChain(); From 421430330afc2706c8fbe7181b1fe03ca12b5dc9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 00:18:43 +0000 Subject: [PATCH 06/18] Bump @antora/collector-extension in /docs --- updated-dependencies: - dependency-name: "@antora/collector-extension" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/package.json b/docs/package.json index 1c8155de05..cff679751e 100644 --- a/docs/package.json +++ b/docs/package.json @@ -2,7 +2,7 @@ "dependencies": { "antora": "3.2.0-alpha.6", "@antora/atlas-extension": "1.0.0-alpha.2", - "@antora/collector-extension": "1.0.0-beta.3", + "@antora/collector-extension": "1.0.0-beta.4", "@asciidoctor/tabs": "1.0.0-beta.6", "@springio/antora-extensions": "1.14.2", "@springio/asciidoctor-extensions": "1.0.0-alpha.14" From f28c26fd54b380f0bceee995b8d5c9e475a4926c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 03:32:26 +0000 Subject: [PATCH 07/18] Bump org.hsqldb:hsqldb from 2.7.3 to 2.7.4 Bumps org.hsqldb:hsqldb from 2.7.3 to 2.7.4. --- updated-dependencies: - dependency-name: org.hsqldb:hsqldb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 680a1958bd..24934e9029 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -69,7 +69,7 @@ org-eclipse-jetty-jetty-servlet = { module = "org.eclipse.jetty:jetty-servlet", org-eclipse-persistence-javax-persistence = "org.eclipse.persistence:javax.persistence:2.2.1" org-hamcrest = "org.hamcrest:hamcrest:2.2" org-hibernate-hibernate-entitymanager = "org.hibernate:hibernate-entitymanager:5.6.15.Final" -org-hsqldb = "org.hsqldb:hsqldb:2.7.3" +org-hsqldb = "org.hsqldb:hsqldb:2.7.4" org-jasig-cas-client-cas-client-core = "org.jasig.cas.client:cas-client-core:3.6.4" org-jetbrains-kotlin-kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "org-jetbrains-kotlin" } org-jetbrains-kotlin-kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "org-jetbrains-kotlin" } From fd900c288e1473023dc3e68b3399ff6b43135e76 Mon Sep 17 00:00:00 2001 From: Rob Winch <362503+rwinch@users.noreply.github.com> Date: Fri, 8 Nov 2024 15:10:31 -0600 Subject: [PATCH 08/18] checkExpectedBranchVersion trim version --- .../security/CheckExpectedBranchVersionPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/security/CheckExpectedBranchVersionPlugin.java b/buildSrc/src/main/java/org/springframework/security/CheckExpectedBranchVersionPlugin.java index 9266f7c4d5..1f7357fb15 100644 --- a/buildSrc/src/main/java/org/springframework/security/CheckExpectedBranchVersionPlugin.java +++ b/buildSrc/src/main/java/org/springframework/security/CheckExpectedBranchVersionPlugin.java @@ -51,7 +51,7 @@ public class CheckExpectedBranchVersionPlugin implements Plugin { String version = (String) project.getVersion(); String branchVersion = getBranchVersion(project); if (!branchVersion.matches("^[0-9]+\\.[0-9]+\\.x$")) { - System.out.println("Branch version does not match *.x, ignoring"); + System.out.println("Branch version '" + branchVersion + "' does not match *.x, ignoring"); return; } if (!versionsMatch(version, branchVersion)) { @@ -67,7 +67,7 @@ public class CheckExpectedBranchVersionPlugin implements Plugin { exec.setErrorOutput(System.err); exec.setStandardOutput(baos); }); - return baos.toString(); + return baos.toString().trim(); } } From c24b5ebe987a9101f0501477b3fb72efc3ff68af Mon Sep 17 00:00:00 2001 From: Rob Winch <362503+rwinch@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:17:15 -0500 Subject: [PATCH 09/18] Support ServerExchangeRejectedHandler @Bean Closes gh-15975 --- .../WebFluxSecurityConfiguration.java | 5 ++- .../WebFluxSecurityConfigurationTests.java | 28 ++++++++++++++++ .../pages/reactive/exploits/firewall.adoc | 32 +++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java index 3285c2cfd6..0594f73b83 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java @@ -31,6 +31,7 @@ import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.WebFilterChainProxy; +import org.springframework.security.web.server.firewall.ServerExchangeRejectedHandler; import org.springframework.security.web.server.firewall.ServerWebExchangeFirewall; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -67,9 +68,11 @@ class WebFluxSecurityConfiguration { @Bean(SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME) @Order(WEB_FILTER_CHAIN_FILTER_ORDER) - WebFilterChainProxy springSecurityWebFilterChainFilter(ObjectProvider firewall) { + WebFilterChainProxy springSecurityWebFilterChainFilter(ObjectProvider firewall, + ObjectProvider rejectedHandler) { WebFilterChainProxy webFilterChainProxy = new WebFilterChainProxy(getSecurityWebFilterChains()); firewall.ifUnique(webFilterChainProxy::setFirewall); + rejectedHandler.ifUnique(webFilterChainProxy::setExchangeRejectedHandler); return webFilterChainProxy; } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java index 4c8c249d7e..d8be15886b 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java @@ -32,6 +32,8 @@ import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration; import org.springframework.security.web.server.WebFilterChainProxy; +import org.springframework.security.web.server.firewall.HttpStatusExchangeRejectedHandler; +import org.springframework.security.web.server.firewall.ServerExchangeRejectedHandler; import org.springframework.security.web.server.firewall.ServerWebExchangeFirewall; import org.springframework.web.server.handler.DefaultWebFilterChain; @@ -70,6 +72,20 @@ public class WebFluxSecurityConfigurationTests { assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); } + @Test + void loadConfigWhenCustomRejectedHandler() throws Exception { + this.spring + .register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class, + WebFluxSecurityConfiguration.class, CustomServerExchangeRejectedHandlerConfig.class) + .autowire(); + WebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class); + MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/;/").build()); + DefaultWebFilterChain chain = emptyChain(); + webFilterChainProxy.filter(exchange, chain).block(); + assertThat(exchange.getResponse().getStatusCode()) + .isEqualTo(CustomServerExchangeRejectedHandlerConfig.EXPECTED_STATUS); + } + @Test void loadConfigWhenFirewallBeanThenCustomized() throws Exception { this.spring @@ -107,6 +123,18 @@ public class WebFluxSecurityConfigurationTests { } + @Configuration + static class CustomServerExchangeRejectedHandlerConfig { + + static HttpStatus EXPECTED_STATUS = HttpStatus.I_AM_A_TEAPOT; + + @Bean + ServerExchangeRejectedHandler rejectedHandler() { + return new HttpStatusExchangeRejectedHandler(EXPECTED_STATUS); + } + + } + @Configuration static class SubclassConfig extends WebFluxSecurityConfiguration { diff --git a/docs/modules/ROOT/pages/reactive/exploits/firewall.adoc b/docs/modules/ROOT/pages/reactive/exploits/firewall.adoc index dce7fb05f7..f359ebdcf9 100644 --- a/docs/modules/ROOT/pages/reactive/exploits/firewall.adoc +++ b/docs/modules/ROOT/pages/reactive/exploits/firewall.adoc @@ -200,3 +200,35 @@ firewall.setAllowedHeaderValues { } ---- ====== + +The `ServerExchangeRejectedHandler` interface is used to handle `ServerExchangeRejectedException` throw by Spring Security's `ServerWebExchangeFirewall`. +By default `HttpStatusExchangeRejectedHandler` is used to send an HTTP 400 response to clients when a request is rejected. +To customize the behavior, users can expose a `ServerExchangeRejectedHandler` Bean. +For example, the following will send an HTTP 404 when the request is rejected: + + +.Send 404 on Request Rejected +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Bean +ServerExchangeRejectedHandler rejectedHandler() { + return new HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND); +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Bean +fun rejectedHandler(): ServerExchangeRejectedHandler { + return HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND) +} +---- +====== + +Handling can be completely customized by creating a custom `ServerExchangeRejectedHandler` implementation. From d985d044bc00310e69db6ebc483d36e004ad97a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 03:25:53 +0000 Subject: [PATCH 10/18] Bump org.springframework.ldap:spring-ldap-core from 2.4.2 to 2.4.4 Bumps [org.springframework.ldap:spring-ldap-core](https://github.com/spring-projects/spring-ldap) from 2.4.2 to 2.4.4. - [Release notes](https://github.com/spring-projects/spring-ldap/releases) - [Changelog](https://github.com/spring-projects/spring-ldap/blob/main/changelog.txt) - [Commits](https://github.com/spring-projects/spring-ldap/compare/2.4.2...2.4.4) --- updated-dependencies: - dependency-name: org.springframework.ldap:spring-ldap-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 24934e9029..28ce8e0464 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -91,7 +91,7 @@ org-skyscreamer-jsonassert = "org.skyscreamer:jsonassert:1.5.3" org-slf4j-log4j-over-slf4j = { module = "org.slf4j:log4j-over-slf4j", version.ref = "org-slf4j" } org-slf4j-slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "org-slf4j" } org-springframework-data-spring-data-bom = "org.springframework.data:spring-data-bom:2021.2.18" -org-springframework-ldap-spring-ldap-core = "org.springframework.ldap:spring-ldap-core:2.4.2" +org-springframework-ldap-spring-ldap-core = "org.springframework.ldap:spring-ldap-core:2.4.4" org-springframework-spring-framework-bom = { module = "org.springframework:spring-framework-bom", version.ref = "org-springframework" } org-synchronoss-cloud-nio-multipart-parser = "org.synchronoss.cloud:nio-multipart-parser:1.1.0" From 451fbf022764266431d4add0f08287470a48558e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 00:44:53 +0000 Subject: [PATCH 11/18] Bump @antora/collector-extension in /docs --- updated-dependencies: - dependency-name: "@antora/collector-extension" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/package.json b/docs/package.json index cff679751e..2054ec1c59 100644 --- a/docs/package.json +++ b/docs/package.json @@ -2,7 +2,7 @@ "dependencies": { "antora": "3.2.0-alpha.6", "@antora/atlas-extension": "1.0.0-alpha.2", - "@antora/collector-extension": "1.0.0-beta.4", + "@antora/collector-extension": "1.0.0-beta.5", "@asciidoctor/tabs": "1.0.0-beta.6", "@springio/antora-extensions": "1.14.2", "@springio/asciidoctor-extensions": "1.0.0-alpha.14" From a7bf8f7cc68d95ce30f18722f5d441ea3fa798f4 Mon Sep 17 00:00:00 2001 From: Joe Grandja <10884212+jgrandja@users.noreply.github.com> Date: Mon, 28 Oct 2024 07:38:45 -0400 Subject: [PATCH 12/18] Require Locale argument for toLower/toUpperCase usage --- ...romAssertionAttributesUserDetailsService.java | 4 +++- .../config/http/HttpConfigurationBuilder.java | 5 +++-- ...impleAttributes2GrantedAuthoritiesMapper.java | 6 +++--- .../authority/mapping/SimpleAuthorityMapper.java | 7 ++++--- .../MapReactiveUserDetailsService.java | 5 +++-- .../userdetails/memory/UserAttributeEditor.java | 5 +++-- .../provisioning/InMemoryUserDetailsManager.java | 15 ++++++++------- .../crypto/password/LdapShaPasswordEncoder.java | 7 ++++--- etc/checkstyle/checkstyle-suppressions.xml | 4 ++++ etc/checkstyle/checkstyle.xml | 16 ++++++++++++++++ .../security/ldap/LdapEncoder.java | 6 ++++-- .../ldap/authentication/LdapEncoder.java | 6 ++++-- ...ctiveDirectoryLdapAuthenticationProvider.java | 11 ++++++----- .../DefaultLdapAuthoritiesPopulator.java | 3 ++- .../ldap/userdetails/LdapUserDetailsManager.java | 7 ++++--- .../ldap/userdetails/LdapUserDetailsMapper.java | 3 ++- .../NestedLdapAuthoritiesPopulator.java | 5 +++-- ...h2AuthorizedClientExchangeFilterFunction.java | 5 +++-- ...h2AuthorizedClientExchangeFilterFunction.java | 5 +++-- .../taglibs/authz/AbstractAuthorizeTag.java | 5 +++-- .../security/web/PortResolverImpl.java | 4 +++- .../security/web/util/UrlUtils.java | 3 ++- .../web/util/matcher/AntPathRequestMatcher.java | 7 ++++--- 23 files changed, 94 insertions(+), 50 deletions(-) diff --git a/cas/src/main/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsService.java b/cas/src/main/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsService.java index 0e47d1c57f..f32c58d388 100644 --- a/cas/src/main/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsService.java +++ b/cas/src/main/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsService.java @@ -18,6 +18,7 @@ package org.springframework.security.cas.userdetails; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import org.jasig.cas.client.validation.Assertion; @@ -73,7 +74,8 @@ public final class GrantedAuthorityFromAssertionAttributesUserDetailsService } private SimpleGrantedAuthority createSimpleGrantedAuthority(Object o) { - return new SimpleGrantedAuthority(this.convertToUpperCase ? o.toString().toUpperCase() : o.toString()); + return new SimpleGrantedAuthority( + this.convertToUpperCase ? o.toString().toUpperCase(Locale.ROOT) : o.toString()); } /** diff --git a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java index 4d7992d554..fa83133f7c 100644 --- a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java +++ b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -18,6 +18,7 @@ package org.springframework.security.config.http; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import javax.servlet.ServletRequest; @@ -301,7 +302,7 @@ class HttpConfigurationBuilder { // Needed to account for placeholders static String createPath(String path, boolean lowerCase) { - return lowerCase ? path.toLowerCase() : path; + return lowerCase ? path.toLowerCase(Locale.ENGLISH) : path; } BeanMetadataElement getSecurityContextHolderStrategyForAuthenticationFilters() { diff --git a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAttributes2GrantedAuthoritiesMapper.java b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAttributes2GrantedAuthoritiesMapper.java index c07137bb8f..3e907f8920 100755 --- a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAttributes2GrantedAuthoritiesMapper.java +++ b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAttributes2GrantedAuthoritiesMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2024 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. @@ -79,10 +79,10 @@ public class SimpleAttributes2GrantedAuthoritiesMapper */ private GrantedAuthority getGrantedAuthority(String attribute) { if (isConvertAttributeToLowerCase()) { - attribute = attribute.toLowerCase(Locale.getDefault()); + attribute = attribute.toLowerCase(Locale.ROOT); } else if (isConvertAttributeToUpperCase()) { - attribute = attribute.toUpperCase(Locale.getDefault()); + attribute = attribute.toUpperCase(Locale.ROOT); } if (isAddPrefixIfAlreadyExisting() || !attribute.startsWith(getAttributePrefix())) { return new SimpleGrantedAuthority(getAttributePrefix() + attribute); diff --git a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java index 2bb66a73b6..18af306e72 100644 --- a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java +++ b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2024 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. @@ -18,6 +18,7 @@ package org.springframework.security.core.authority.mapping; import java.util.Collection; import java.util.HashSet; +import java.util.Locale; import java.util.Set; import org.springframework.beans.factory.InitializingBean; @@ -71,10 +72,10 @@ public final class SimpleAuthorityMapper implements GrantedAuthoritiesMapper, In private GrantedAuthority mapAuthority(String name) { if (this.convertToUpperCase) { - name = name.toUpperCase(); + name = name.toUpperCase(Locale.ROOT); } else if (this.convertToLowerCase) { - name = name.toLowerCase(); + name = name.toLowerCase(Locale.ROOT); } if (this.prefix.length() > 0 && !name.startsWith(this.prefix)) { name = this.prefix + name; diff --git a/core/src/main/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsService.java b/core/src/main/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsService.java index 10712776a7..ecb459bc3c 100644 --- a/core/src/main/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsService.java +++ b/core/src/main/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2024 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. @@ -18,6 +18,7 @@ package org.springframework.security.core.userdetails; import java.util.Arrays; import java.util.Collection; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -91,7 +92,7 @@ public class MapReactiveUserDetailsService implements ReactiveUserDetailsService } private String getKey(String username) { - return username.toLowerCase(); + return username.toLowerCase(Locale.ROOT); } } diff --git a/core/src/main/java/org/springframework/security/core/userdetails/memory/UserAttributeEditor.java b/core/src/main/java/org/springframework/security/core/userdetails/memory/UserAttributeEditor.java index 09f10c18d0..2b6422e2cb 100644 --- a/core/src/main/java/org/springframework/security/core/userdetails/memory/UserAttributeEditor.java +++ b/core/src/main/java/org/springframework/security/core/userdetails/memory/UserAttributeEditor.java @@ -19,6 +19,7 @@ package org.springframework.security.core.userdetails.memory; import java.beans.PropertyEditorSupport; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import org.springframework.util.StringUtils; @@ -45,10 +46,10 @@ public class UserAttributeEditor extends PropertyEditorSupport { userAttrib.setPassword(currentToken); } else { - if (currentToken.toLowerCase().equals("enabled")) { + if (currentToken.toLowerCase(Locale.ENGLISH).equals("enabled")) { userAttrib.setEnabled(true); } - else if (currentToken.toLowerCase().equals("disabled")) { + else if (currentToken.toLowerCase(Locale.ENGLISH).equals("disabled")) { userAttrib.setEnabled(false); } else { diff --git a/core/src/main/java/org/springframework/security/provisioning/InMemoryUserDetailsManager.java b/core/src/main/java/org/springframework/security/provisioning/InMemoryUserDetailsManager.java index 9faac275e0..6381b8b762 100644 --- a/core/src/main/java/org/springframework/security/provisioning/InMemoryUserDetailsManager.java +++ b/core/src/main/java/org/springframework/security/provisioning/InMemoryUserDetailsManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -19,6 +19,7 @@ package org.springframework.security.provisioning; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Properties; @@ -96,23 +97,23 @@ public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetai @Override public void createUser(UserDetails user) { Assert.isTrue(!userExists(user.getUsername()), "user should not exist"); - this.users.put(user.getUsername().toLowerCase(), new MutableUser(user)); + this.users.put(user.getUsername().toLowerCase(Locale.ROOT), new MutableUser(user)); } @Override public void deleteUser(String username) { - this.users.remove(username.toLowerCase()); + this.users.remove(username.toLowerCase(Locale.ROOT)); } @Override public void updateUser(UserDetails user) { Assert.isTrue(userExists(user.getUsername()), "user should exist"); - this.users.put(user.getUsername().toLowerCase(), new MutableUser(user)); + this.users.put(user.getUsername().toLowerCase(Locale.ROOT), new MutableUser(user)); } @Override public boolean userExists(String username) { - return this.users.containsKey(username.toLowerCase()); + return this.users.containsKey(username.toLowerCase(Locale.ROOT)); } @Override @@ -143,14 +144,14 @@ public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetai @Override public UserDetails updatePassword(UserDetails user, String newPassword) { String username = user.getUsername(); - MutableUserDetails mutableUser = this.users.get(username.toLowerCase()); + MutableUserDetails mutableUser = this.users.get(username.toLowerCase(Locale.ROOT)); mutableUser.setPassword(newPassword); return mutableUser; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - UserDetails user = this.users.get(username.toLowerCase()); + UserDetails user = this.users.get(username.toLowerCase(Locale.ROOT)); if (user == null) { throw new UsernameNotFoundException(username); } diff --git a/crypto/src/main/java/org/springframework/security/crypto/password/LdapShaPasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/password/LdapShaPasswordEncoder.java index eb9687c0fc..35f7224a7c 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/password/LdapShaPasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/password/LdapShaPasswordEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2024 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. @@ -18,6 +18,7 @@ package org.springframework.security.crypto.password; import java.security.MessageDigest; import java.util.Base64; +import java.util.Locale; import org.springframework.security.crypto.codec.Utf8; import org.springframework.security.crypto.keygen.BytesKeyGenerator; @@ -50,11 +51,11 @@ public class LdapShaPasswordEncoder implements PasswordEncoder { private static final String SSHA_PREFIX = "{SSHA}"; - private static final String SSHA_PREFIX_LC = SSHA_PREFIX.toLowerCase(); + private static final String SSHA_PREFIX_LC = SSHA_PREFIX.toLowerCase(Locale.ENGLISH); private static final String SHA_PREFIX = "{SHA}"; - private static final String SHA_PREFIX_LC = SHA_PREFIX.toLowerCase(); + private static final String SHA_PREFIX_LC = SHA_PREFIX.toLowerCase(Locale.ENGLISH); private BytesKeyGenerator saltGenerator; diff --git a/etc/checkstyle/checkstyle-suppressions.xml b/etc/checkstyle/checkstyle-suppressions.xml index 23b77adb50..94b245f36a 100644 --- a/etc/checkstyle/checkstyle-suppressions.xml +++ b/etc/checkstyle/checkstyle-suppressions.xml @@ -56,4 +56,8 @@ + + + + diff --git a/etc/checkstyle/checkstyle.xml b/etc/checkstyle/checkstyle.xml index e0a8b9d743..a240dd3407 100644 --- a/etc/checkstyle/checkstyle.xml +++ b/etc/checkstyle/checkstyle.xml @@ -29,5 +29,21 @@ + + + + + + + + + + + + + + diff --git a/ldap/src/main/java/org/springframework/security/ldap/LdapEncoder.java b/ldap/src/main/java/org/springframework/security/ldap/LdapEncoder.java index a3911aa180..57d45a444c 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/LdapEncoder.java +++ b/ldap/src/main/java/org/springframework/security/ldap/LdapEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2005-2024 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. @@ -16,6 +16,8 @@ package org.springframework.security.ldap; +import java.util.Locale; + import org.springframework.ldap.BadLdapGrammarException; /** @@ -72,7 +74,7 @@ final class LdapEncoder { } protected static String toTwoCharHex(char c) { - String raw = Integer.toHexString(c).toUpperCase(); + String raw = Integer.toHexString(c).toUpperCase(Locale.ENGLISH); return (raw.length() > 1) ? raw : "0" + raw; } diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/LdapEncoder.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/LdapEncoder.java index f79f4843ae..e037e3d2ef 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/authentication/LdapEncoder.java +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/LdapEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2005-2024 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. @@ -16,6 +16,8 @@ package org.springframework.security.ldap.authentication; +import java.util.Locale; + import org.springframework.ldap.BadLdapGrammarException; /** @@ -72,7 +74,7 @@ final class LdapEncoder { } protected static String toTwoCharHex(char c) { - String raw = Integer.toHexString(c).toUpperCase(); + String raw = Integer.toHexString(c).toUpperCase(Locale.ENGLISH); return (raw.length() > 1) ? raw : "0" + raw; } diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java index d69868b0ed..ba44a840fd 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2024 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. @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Hashtable; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -144,9 +145,9 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda */ public ActiveDirectoryLdapAuthenticationProvider(String domain, String url, String rootDn) { Assert.isTrue(StringUtils.hasText(url), "Url cannot be empty"); - this.domain = StringUtils.hasText(domain) ? domain.toLowerCase() : null; + this.domain = StringUtils.hasText(domain) ? domain.toLowerCase(Locale.ROOT) : null; this.url = url; - this.rootDn = StringUtils.hasText(rootDn) ? rootDn.toLowerCase() : null; + this.rootDn = StringUtils.hasText(rootDn) ? rootDn.toLowerCase(Locale.ROOT) : null; } /** @@ -155,7 +156,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda */ public ActiveDirectoryLdapAuthenticationProvider(String domain, String url) { Assert.isTrue(StringUtils.hasText(url), "Url cannot be empty"); - this.domain = StringUtils.hasText(domain) ? domain.toLowerCase() : null; + this.domain = StringUtils.hasText(domain) ? domain.toLowerCase(Locale.ROOT) : null; this.url = url; this.rootDn = (this.domain != null) ? rootDnFromDomain(this.domain) : null; } @@ -364,7 +365,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda } String createBindPrincipal(String username) { - if (this.domain == null || username.toLowerCase().endsWith(this.domain)) { + if (this.domain == null || username.toLowerCase(Locale.ROOT).endsWith(this.domain)) { return username; } return username + "@" + this.domain; diff --git a/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java b/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java index b69985d2b4..a57fd4ca8e 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java +++ b/ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -179,7 +180,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator return null; } if (this.convertToUpperCase) { - role = role.toUpperCase(); + role = role.toUpperCase(Locale.ROOT); } return new SimpleGrantedAuthority(this.rolePrefix + role); }; diff --git a/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManager.java b/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManager.java index a1487b6665..0c7a39718c 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManager.java +++ b/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.Locale; import javax.naming.Context; import javax.naming.NameNotFoundException; @@ -124,7 +125,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { NamingEnumeration ne = roleAttr.getAll(); Object group = ne.next(); String role = group.toString(); - return new SimpleGrantedAuthority(this.rolePrefix + role.toUpperCase()); + return new SimpleGrantedAuthority(this.rolePrefix + role.toUpperCase(Locale.ROOT)); }; private String[] attributesToRetrieve; @@ -287,7 +288,7 @@ public class LdapUserDetailsManager implements UserDetailsManager { */ protected DistinguishedName buildGroupDn(String group) { DistinguishedName dn = new DistinguishedName(this.groupSearchBase); - dn.add(this.groupRoleAttributeName, group.toLowerCase()); + dn.add(this.groupRoleAttributeName, group.toLowerCase(Locale.ROOT)); return dn; } diff --git a/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapper.java b/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapper.java index 470c8d7842..4ee85ee446 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapper.java +++ b/ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapper.java @@ -17,6 +17,7 @@ package org.springframework.security.ldap.userdetails; import java.util.Collection; +import java.util.Locale; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -127,7 +128,7 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper { protected GrantedAuthority createAuthority(Object role) { if (role instanceof String) { if (this.convertToUpperCase) { - role = ((String) role).toUpperCase(); + role = ((String) role).toUpperCase(Locale.ROOT); } return new SimpleGrantedAuthority(this.rolePrefix + role); } diff --git a/ldap/src/main/java/org/springframework/security/ldap/userdetails/NestedLdapAuthoritiesPopulator.java b/ldap/src/main/java/org/springframework/security/ldap/userdetails/NestedLdapAuthoritiesPopulator.java index b61068ec8f..5726513d30 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/userdetails/NestedLdapAuthoritiesPopulator.java +++ b/ldap/src/main/java/org/springframework/security/ldap/userdetails/NestedLdapAuthoritiesPopulator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2024 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. @@ -18,6 +18,7 @@ package org.springframework.security.ldap.userdetails; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -193,7 +194,7 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula } for (String role : roles) { if (isConvertToUpperCase()) { - role = role.toUpperCase(); + role = role.toUpperCase(Locale.ROOT); } role = getRolePrefix() + role; // if the group already exist, we will not search for it's parents again. diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java index b657293c95..5f5d59a9ec 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -19,6 +19,7 @@ package org.springframework.security.oauth2.client.web.reactive.function.client; import java.time.Duration; import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.function.Consumer; @@ -753,7 +754,7 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements // @formatter:off return Stream.of(wwwAuthenticateHeader) .filter((header) -> !StringUtils.isEmpty(header)) - .filter((header) -> header.toLowerCase().startsWith("bearer")) + .filter((header) -> header.toLowerCase(Locale.ENGLISH).startsWith("bearer")) .map((header) -> header.substring("bearer".length())) .map((header) -> header.split(",")) .flatMap(Stream::of) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java index 7eeef11a52..303d02ad85 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -19,6 +19,7 @@ package org.springframework.security.oauth2.client.web.reactive.function.client; import java.time.Duration; import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -735,7 +736,7 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement private Map parseAuthParameters(String wwwAuthenticateHeader) { // @formatter:off return Stream.of(wwwAuthenticateHeader).filter((header) -> !StringUtils.isEmpty(header)) - .filter((header) -> header.toLowerCase().startsWith("bearer")) + .filter((header) -> header.toLowerCase(Locale.ENGLISH).startsWith("bearer")) .map((header) -> header.substring("bearer".length())) .map((header) -> header.split(",")) .flatMap(Stream::of) diff --git a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java index e9c9673ae4..2f12c50c23 100644 --- a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java +++ b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2022 the original author or authors. + * Copyright 2004-2024 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. @@ -17,6 +17,7 @@ package org.springframework.security.taglibs.authz; import java.io.IOException; +import java.util.Locale; import java.util.Map; import javax.servlet.ServletContext; @@ -169,7 +170,7 @@ public abstract class AbstractAuthorizeTag { } public void setMethod(String method) { - this.method = (method != null) ? method.toUpperCase() : null; + this.method = (method != null) ? method.toUpperCase(Locale.ENGLISH) : null; } private SecurityContext getContext() { diff --git a/web/src/main/java/org/springframework/security/web/PortResolverImpl.java b/web/src/main/java/org/springframework/security/web/PortResolverImpl.java index faa01d83c3..58a0ebb421 100644 --- a/web/src/main/java/org/springframework/security/web/PortResolverImpl.java +++ b/web/src/main/java/org/springframework/security/web/PortResolverImpl.java @@ -16,6 +16,8 @@ package org.springframework.security.web; +import java.util.Locale; + import javax.servlet.ServletRequest; import org.springframework.util.Assert; @@ -45,7 +47,7 @@ public class PortResolverImpl implements PortResolver { @Override public int getServerPort(ServletRequest request) { int serverPort = request.getServerPort(); - String scheme = request.getScheme().toLowerCase(); + String scheme = request.getScheme().toLowerCase(Locale.ENGLISH); Integer mappedPort = getMappedPort(serverPort, scheme); return (mappedPort != null) ? mappedPort : serverPort; } diff --git a/web/src/main/java/org/springframework/security/web/util/UrlUtils.java b/web/src/main/java/org/springframework/security/web/util/UrlUtils.java index 98f4bbf5c8..69f7b81d57 100644 --- a/web/src/main/java/org/springframework/security/web/util/UrlUtils.java +++ b/web/src/main/java/org/springframework/security/web/util/UrlUtils.java @@ -16,6 +16,7 @@ package org.springframework.security.web.util; +import java.util.Locale; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; @@ -49,7 +50,7 @@ public final class UrlUtils { */ public static String buildFullRequestUrl(String scheme, String serverName, int serverPort, String requestURI, String queryString) { - scheme = scheme.toLowerCase(); + scheme = scheme.toLowerCase(Locale.ENGLISH); StringBuilder url = new StringBuilder(); url.append(scheme).append("://").append(serverName); // Only add port if not default diff --git a/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java b/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java index 1c5c8e650c..dc6eaeb1c5 100644 --- a/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java +++ b/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2024 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. @@ -17,6 +17,7 @@ package org.springframework.security.web.util.matcher; import java.util.Collections; +import java.util.Locale; import java.util.Map; import javax.servlet.http.HttpServletRequest; @@ -304,7 +305,7 @@ public final class AntPathRequestMatcher implements RequestMatcher, RequestVaria private SubpathMatcher(String subpath, boolean caseSensitive) { Assert.isTrue(!subpath.contains("*"), "subpath cannot contain \"*\""); - this.subpath = caseSensitive ? subpath : subpath.toLowerCase(); + this.subpath = caseSensitive ? subpath : subpath.toLowerCase(Locale.ROOT); this.length = subpath.length(); this.caseSensitive = caseSensitive; } @@ -312,7 +313,7 @@ public final class AntPathRequestMatcher implements RequestMatcher, RequestVaria @Override public boolean matches(String path) { if (!this.caseSensitive) { - path = path.toLowerCase(); + path = path.toLowerCase(Locale.ROOT); } return path.startsWith(this.subpath) && (path.length() == this.length || path.charAt(this.length) == '/'); } From a5cd7ce12228fd7eec0efaf52d68a87366da0cd5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 18 Nov 2024 15:23:13 +0000 Subject: [PATCH 13/18] Release 6.2.8 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 466af0d495..eff45b9978 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ springBootVersion=3.1.1 -version=6.2.8-SNAPSHOT +version=6.2.8 samplesBranch=6.2.x org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true From 506e5b7f11fe10e6bf6405bfcf3b2a1a447354cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 18 Nov 2024 15:23:30 +0000 Subject: [PATCH 14/18] Release 5.8.16 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 0261aeb396..f66a5c8253 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ springBootVersion=2.7.12 -version=5.8.16-SNAPSHOT +version=5.8.16 samplesBranch=5.8.x org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true From 96c47b30d3c7b94a6faa2c3d8b32ab1d35d67107 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 18 Nov 2024 15:23:35 +0000 Subject: [PATCH 15/18] Release 6.3.5 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 307d1bf9cf..dcabe58c67 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ springBootVersion=3.1.1 -version=6.3.5-SNAPSHOT +version=6.3.5 samplesBranch=6.3.x org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true From 7881660ca021791d3948ba505ad34762c89eeba1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 18 Nov 2024 16:01:43 +0000 Subject: [PATCH 16/18] Next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index f66a5c8253..f15852e67d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ springBootVersion=2.7.12 -version=5.8.16 +version=5.8.17-SNAPSHOT samplesBranch=5.8.x org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true From d224dbe3347053f88be0ae10fa56c40816c92853 Mon Sep 17 00:00:00 2001 From: Joe Grandja <10884212+jgrandja@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:14:18 -0500 Subject: [PATCH 17/18] Next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index eff45b9978..3e9551ad38 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ springBootVersion=3.1.1 -version=6.2.8 +version=6.2.9-SNAPSHOT samplesBranch=6.2.x org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true From d34bd346c05825cdb14b93d44ab9d017aa240103 Mon Sep 17 00:00:00 2001 From: Joe Grandja <10884212+jgrandja@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:04:36 -0500 Subject: [PATCH 18/18] Next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index dcabe58c67..2b893bd441 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ springBootVersion=3.1.1 -version=6.3.5 +version=6.3.6-SNAPSHOT samplesBranch=6.3.x org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true