From c785563ee542af2adc66c7c024062151e6d8f427 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 20 Jan 2026 15:53:22 +0100 Subject: [PATCH 01/27] Use standardized cache --- .github/workflows/run-integration-tests.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml index 0d059a8c..6505c62f 100644 --- a/.github/workflows/run-integration-tests.yml +++ b/.github/workflows/run-integration-tests.yml @@ -42,7 +42,15 @@ jobs: with: distribution: temurin java-version: ${{ matrix.java }} - cache: 'maven' + + - name: Cache Maven + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-mvn-it-build-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-mvn-it-build- + ${{ runner.os }}-mvn-build- - name: Test run: | From da0eeaa2b7a2a17a679c44faece3e367d20826d8 Mon Sep 17 00:00:00 2001 From: XDEV Renovate Bot Date: Thu, 22 Jan 2026 04:30:46 +0000 Subject: [PATCH 02/27] Update actions/cache action to v5 --- .github/workflows/run-integration-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml index 6505c62f..03593311 100644 --- a/.github/workflows/run-integration-tests.yml +++ b/.github/workflows/run-integration-tests.yml @@ -44,7 +44,7 @@ jobs: java-version: ${{ matrix.java }} - name: Cache Maven - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.m2/repository key: ${{ runner.os }}-mvn-it-build-${{ hashFiles('**/pom.xml') }} From 92b2852fcbc02bc67f2950a2ffaa473b78b0670e Mon Sep 17 00:00:00 2001 From: XDEV Renovate Bot Date: Fri, 23 Jan 2026 09:05:32 +0000 Subject: [PATCH 03/27] Update com.vaadin to v25 --- demo/pom.xml | 2 +- vaadin/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/pom.xml b/demo/pom.xml index dad59aec..eeae13da 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -22,7 +22,7 @@ UTF-8 - 24.9.9 + 25.0.3 3.5.9 diff --git a/vaadin/pom.xml b/vaadin/pom.xml index c7129678..e8a56f19 100644 --- a/vaadin/pom.xml +++ b/vaadin/pom.xml @@ -62,7 +62,7 @@ com.vaadin vaadin-bom - 24.9.9 + 25.0.3 pom import From a5fcb5da86a0d7505797795eae19d487c60ca522 Mon Sep 17 00:00:00 2001 From: XDEV Renovate Bot Date: Fri, 23 Jan 2026 09:05:33 +0000 Subject: [PATCH 04/27] Update dependency org.springdoc:springdoc-openapi-starter-webmvc-ui to v3 --- demo/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/pom.xml b/demo/pom.xml index dad59aec..4d337acc 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -82,7 +82,7 @@ org.springdoc springdoc-openapi-starter-webmvc-ui - 2.8.15 + 3.0.1 From dc771693d692045681bbd760efc093bf1810d39f Mon Sep 17 00:00:00 2001 From: XDEV Renovate Bot Date: Fri, 23 Jan 2026 09:05:34 +0000 Subject: [PATCH 05/27] Update dependency software.xdev:flyway-core-slim to v2 --- demo/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/pom.xml b/demo/pom.xml index dad59aec..71f0e0ce 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -124,7 +124,7 @@ software.xdev flyway-core-slim - 1.1.0 + 2.0.0 From 27f01756d7a957629e78e83bb656ce408d1938d2 Mon Sep 17 00:00:00 2001 From: XDEV Renovate Bot Date: Fri, 23 Jan 2026 09:05:39 +0000 Subject: [PATCH 06/27] Update dependency software.xdev:spring-security-advanced-authentication-ui to v3 --- demo/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/pom.xml b/demo/pom.xml index dad59aec..a1d38e23 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -76,7 +76,7 @@ software.xdev spring-security-advanced-authentication-ui - 2.1.2 + 3.0.0 From bf79bb30f1c50a6ff5af9133b265c803d92f452f Mon Sep 17 00:00:00 2001 From: XDEV Renovate Bot Date: Fri, 23 Jan 2026 09:05:40 +0000 Subject: [PATCH 07/27] Update org.springframework.boot to v4 --- crypto-symmetric-managed/pom.xml | 2 +- csp/pom.xml | 2 +- demo/pom.xml | 2 +- metrics/pom.xml | 2 +- oauth2-oidc-remember-me/pom.xml | 2 +- oauth2-oidc/pom.xml | 2 +- vaadin/pom.xml | 2 +- web-sidecar-actuator/pom.xml | 2 +- web-sidecar-common/pom.xml | 2 +- web/pom.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crypto-symmetric-managed/pom.xml b/crypto-symmetric-managed/pom.xml index be4836f4..292c436c 100644 --- a/crypto-symmetric-managed/pom.xml +++ b/crypto-symmetric-managed/pom.xml @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.9 + 4.0.2 pom import diff --git a/csp/pom.xml b/csp/pom.xml index 5e7f36da..fbce565c 100644 --- a/csp/pom.xml +++ b/csp/pom.xml @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.9 + 4.0.2 pom import diff --git a/demo/pom.xml b/demo/pom.xml index dad59aec..9b409e86 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -24,7 +24,7 @@ 24.9.9 - 3.5.9 + 4.0.2 diff --git a/metrics/pom.xml b/metrics/pom.xml index de581f5f..14ee9f03 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.9 + 4.0.2 pom import diff --git a/oauth2-oidc-remember-me/pom.xml b/oauth2-oidc-remember-me/pom.xml index c2a46399..c2664377 100644 --- a/oauth2-oidc-remember-me/pom.xml +++ b/oauth2-oidc-remember-me/pom.xml @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.9 + 4.0.2 pom import diff --git a/oauth2-oidc/pom.xml b/oauth2-oidc/pom.xml index d4bb7bef..0af98367 100644 --- a/oauth2-oidc/pom.xml +++ b/oauth2-oidc/pom.xml @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.9 + 4.0.2 pom import diff --git a/vaadin/pom.xml b/vaadin/pom.xml index c7129678..b548d3e5 100644 --- a/vaadin/pom.xml +++ b/vaadin/pom.xml @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.9 + 4.0.2 pom import diff --git a/web-sidecar-actuator/pom.xml b/web-sidecar-actuator/pom.xml index 1295c02e..b988f2ef 100644 --- a/web-sidecar-actuator/pom.xml +++ b/web-sidecar-actuator/pom.xml @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.9 + 4.0.2 pom import diff --git a/web-sidecar-common/pom.xml b/web-sidecar-common/pom.xml index ee396744..187a9b71 100644 --- a/web-sidecar-common/pom.xml +++ b/web-sidecar-common/pom.xml @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.9 + 4.0.2 pom import diff --git a/web/pom.xml b/web/pom.xml index a3318832..63eb63ac 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.9 + 4.0.2 pom import From 00bc5fb96a7703256eeaebb4e519ee76cfd762a7 Mon Sep 17 00:00:00 2001 From: AB Date: Mon, 26 Jan 2026 12:40:45 +0100 Subject: [PATCH 08/27] Update log --- .../sidecar/errorpage/ErrorPageCompatibilityPathsProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/errorpage/ErrorPageCompatibilityPathsProvider.java b/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/errorpage/ErrorPageCompatibilityPathsProvider.java index d1e30ea4..8f5b47cc 100644 --- a/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/errorpage/ErrorPageCompatibilityPathsProvider.java +++ b/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/errorpage/ErrorPageCompatibilityPathsProvider.java @@ -84,7 +84,7 @@ protected void printInformation() } LOG.debug("Built-in error pages can be disabled with " - + "@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class}) " + + "@Application(exclude = {ErrorMvcAutoConfiguration.class}) " + "which then only shows the error page (if any) of the underlying app server."); LOG.debug("Alternatively you can also try to disable Spring's ErrorPageFilter " + "(see https://stackoverflow.com/q/30170586 for more information)."); From 36cf47ef473b072781f609e141e914646bd47fe2 Mon Sep 17 00:00:00 2001 From: AB Date: Mon, 26 Jan 2026 12:43:10 +0100 Subject: [PATCH 09/27] BUmp version --- CHANGELOG.md | 3 +++ bom/pom.xml | 26 +++++++++---------- client-storage/pom.xml | 2 +- codec-sha256/pom.xml | 2 +- crypto-symmetric-managed/pom.xml | 2 +- crypto-symmetric/pom.xml | 2 +- csp/pom.xml | 2 +- demo/entities-metamodel/pom.xml | 2 +- demo/entities/pom.xml | 2 +- demo/integration-tests/pom.xml | 14 +++++----- demo/integration-tests/tci-db/pom.xml | 2 +- .../integration-tests/tci-webapp-rest/pom.xml | 2 +- .../tci-webapp-vaadin/pom.xml | 2 +- demo/integration-tests/tci-webapp/pom.xml | 2 +- demo/integration-tests/webapp-it-base/pom.xml | 2 +- demo/integration-tests/webapp-rest-it/pom.xml | 2 +- .../webapp-vaadin-it/pom.xml | 2 +- demo/persistence/pom.xml | 2 +- demo/pom.xml | 20 +++++++------- demo/webapp-rest/pom.xml | 2 +- demo/webapp-shared/pom.xml | 2 +- demo/webapp-vaadin/pom.xml | 2 +- metrics/pom.xml | 2 +- oauth2-oidc-remember-me/pom.xml | 2 +- oauth2-oidc/pom.xml | 2 +- pom.xml | 2 +- vaadin/pom.xml | 2 +- web-sidecar-actuator/pom.xml | 2 +- web-sidecar-common/pom.xml | 2 +- web/pom.xml | 2 +- 30 files changed, 59 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05867475..e778ba12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2.0.0 +* Updated to Spring Boot 4.x + # 1.5.3 _Last expected version for Spring Boot 3.x_ * Updated dependencies diff --git a/bom/pom.xml b/bom/pom.xml index 1bc1368b..c160037d 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse bom - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT pom bom @@ -51,62 +51,62 @@ software.xdev.sse client-storage - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse crypto-symmetric - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse crypto-symmetric-managed - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse codec-sha256 - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse csp - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse metrics - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse oauth2-oidc - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse oauth2-oidc-remember-me - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse vaadin - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse web - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse web-sidecar-actuator - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse web-sidecar-common - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT diff --git a/client-storage/pom.xml b/client-storage/pom.xml index 18ccc7b6..4d1b4e91 100644 --- a/client-storage/pom.xml +++ b/client-storage/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse client-storage - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar client-storage diff --git a/codec-sha256/pom.xml b/codec-sha256/pom.xml index 9fc51429..56450355 100644 --- a/codec-sha256/pom.xml +++ b/codec-sha256/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse codec-sha256 - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar codec-sha256 diff --git a/crypto-symmetric-managed/pom.xml b/crypto-symmetric-managed/pom.xml index 292c436c..fbbc43c0 100644 --- a/crypto-symmetric-managed/pom.xml +++ b/crypto-symmetric-managed/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse crypto-symmetric-managed - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar crypto-symmetric-managed diff --git a/crypto-symmetric/pom.xml b/crypto-symmetric/pom.xml index b391f790..6f182cc3 100644 --- a/crypto-symmetric/pom.xml +++ b/crypto-symmetric/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse crypto-symmetric - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar crypto-symmetric diff --git a/csp/pom.xml b/csp/pom.xml index fbce565c..ce33be11 100644 --- a/csp/pom.xml +++ b/csp/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse csp - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar csp diff --git a/demo/entities-metamodel/pom.xml b/demo/entities-metamodel/pom.xml index fc6a5030..616c7015 100644 --- a/demo/entities-metamodel/pom.xml +++ b/demo/entities-metamodel/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT entities-metamodel diff --git a/demo/entities/pom.xml b/demo/entities/pom.xml index 39ca91ff..0dbb88c9 100644 --- a/demo/entities/pom.xml +++ b/demo/entities/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT entities diff --git a/demo/integration-tests/pom.xml b/demo/integration-tests/pom.xml index 81a1bc43..8647cdf0 100644 --- a/demo/integration-tests/pom.xml +++ b/demo/integration-tests/pom.xml @@ -7,12 +7,12 @@ software.xdev.sse.demo demo - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse.demo.it integration-tests - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT pom @@ -31,31 +31,31 @@ software.xdev.sse.demo.it tci-db - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse.demo.it tci-webapp - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse.demo.it tci-webapp-rest - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse.demo.it tci-webapp-vaadin - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse.demo.it webapp-it-base - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT diff --git a/demo/integration-tests/tci-db/pom.xml b/demo/integration-tests/tci-db/pom.xml index 0106bff0..55c826c2 100644 --- a/demo/integration-tests/tci-db/pom.xml +++ b/demo/integration-tests/tci-db/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT tci-db diff --git a/demo/integration-tests/tci-webapp-rest/pom.xml b/demo/integration-tests/tci-webapp-rest/pom.xml index 46491bec..b16f3c50 100644 --- a/demo/integration-tests/tci-webapp-rest/pom.xml +++ b/demo/integration-tests/tci-webapp-rest/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT tci-webapp-rest diff --git a/demo/integration-tests/tci-webapp-vaadin/pom.xml b/demo/integration-tests/tci-webapp-vaadin/pom.xml index de7be9e1..0a76f3f8 100644 --- a/demo/integration-tests/tci-webapp-vaadin/pom.xml +++ b/demo/integration-tests/tci-webapp-vaadin/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT tci-webapp-vaadin diff --git a/demo/integration-tests/tci-webapp/pom.xml b/demo/integration-tests/tci-webapp/pom.xml index b11aea6a..b78f136b 100644 --- a/demo/integration-tests/tci-webapp/pom.xml +++ b/demo/integration-tests/tci-webapp/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT tci-webapp diff --git a/demo/integration-tests/webapp-it-base/pom.xml b/demo/integration-tests/webapp-it-base/pom.xml index f58e3052..f8e6f93d 100644 --- a/demo/integration-tests/webapp-it-base/pom.xml +++ b/demo/integration-tests/webapp-it-base/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT webapp-it-base diff --git a/demo/integration-tests/webapp-rest-it/pom.xml b/demo/integration-tests/webapp-rest-it/pom.xml index 49c6222c..c25e9838 100644 --- a/demo/integration-tests/webapp-rest-it/pom.xml +++ b/demo/integration-tests/webapp-rest-it/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT webapp-rest-it diff --git a/demo/integration-tests/webapp-vaadin-it/pom.xml b/demo/integration-tests/webapp-vaadin-it/pom.xml index eb935c53..d20179c5 100644 --- a/demo/integration-tests/webapp-vaadin-it/pom.xml +++ b/demo/integration-tests/webapp-vaadin-it/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT webapp-vaadin-it diff --git a/demo/persistence/pom.xml b/demo/persistence/pom.xml index cccfb146..a6bf6f35 100644 --- a/demo/persistence/pom.xml +++ b/demo/persistence/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT persistence diff --git a/demo/pom.xml b/demo/pom.xml index 42be328b..a3ca3a98 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse.demo demo - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT pom @@ -43,25 +43,25 @@ software.xdev.sse.demo entities - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse.demo entities-metamodel - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse.demo persistence - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse.demo webapp-shared - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT @@ -144,27 +144,27 @@ software.xdev.sse csp - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse oauth2-oidc - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse oauth2-oidc-remember-me - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse vaadin - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT software.xdev.sse web-sidecar-actuator - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT diff --git a/demo/webapp-rest/pom.xml b/demo/webapp-rest/pom.xml index 41a2e78b..90e44f3c 100644 --- a/demo/webapp-rest/pom.xml +++ b/demo/webapp-rest/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT webapp-rest diff --git a/demo/webapp-shared/pom.xml b/demo/webapp-shared/pom.xml index 054cceb6..a3a9fb8a 100644 --- a/demo/webapp-shared/pom.xml +++ b/demo/webapp-shared/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT webapp-shared diff --git a/demo/webapp-vaadin/pom.xml b/demo/webapp-vaadin/pom.xml index 20469b64..c217e531 100644 --- a/demo/webapp-vaadin/pom.xml +++ b/demo/webapp-vaadin/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT webapp-vaadin diff --git a/metrics/pom.xml b/metrics/pom.xml index 14ee9f03..8bd272cb 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse metrics - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar metrics diff --git a/oauth2-oidc-remember-me/pom.xml b/oauth2-oidc-remember-me/pom.xml index c2664377..dd45832a 100644 --- a/oauth2-oidc-remember-me/pom.xml +++ b/oauth2-oidc-remember-me/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse oauth2-oidc-remember-me - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar oauth2-oidc-remember-me diff --git a/oauth2-oidc/pom.xml b/oauth2-oidc/pom.xml index 0af98367..da78e3c7 100644 --- a/oauth2-oidc/pom.xml +++ b/oauth2-oidc/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse oauth2-oidc - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar oauth2-oidc diff --git a/pom.xml b/pom.xml index 1db85fb5..26e012c1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse root - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT pom diff --git a/vaadin/pom.xml b/vaadin/pom.xml index 15c94fc5..b5bed168 100644 --- a/vaadin/pom.xml +++ b/vaadin/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse vaadin - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar vaadin diff --git a/web-sidecar-actuator/pom.xml b/web-sidecar-actuator/pom.xml index b988f2ef..13203df7 100644 --- a/web-sidecar-actuator/pom.xml +++ b/web-sidecar-actuator/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse web-sidecar-actuator - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar web-sidecar-actuator diff --git a/web-sidecar-common/pom.xml b/web-sidecar-common/pom.xml index 187a9b71..290d0435 100644 --- a/web-sidecar-common/pom.xml +++ b/web-sidecar-common/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse web-sidecar-common - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar web-sidecar-common diff --git a/web/pom.xml b/web/pom.xml index 63eb63ac..025a7ba7 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse web - 1.5.4-SNAPSHOT + 2.0.0-SNAPSHOT jar web From 5cbd1289e3083c3dca6b43a4a80c89a02e6cdc17 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:28:42 +0100 Subject: [PATCH 10/27] Replace deprecated `prometheus-metrics-exposition-formats-no-protobuf` --- demo/pom.xml | 6 ------ demo/webapp-shared/pom.xml | 6 +++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/demo/pom.xml b/demo/pom.xml index a3ca3a98..3fc299d1 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -85,12 +85,6 @@ 3.0.1 - - software.xdev - prometheus-metrics-exposition-formats-no-protobuf - 3.0.1 - - com.github.ben-manes.caffeine diff --git a/demo/webapp-shared/pom.xml b/demo/webapp-shared/pom.xml index a3a9fb8a..78a46e25 100644 --- a/demo/webapp-shared/pom.xml +++ b/demo/webapp-shared/pom.xml @@ -52,8 +52,8 @@ - software.xdev - prometheus-metrics-exposition-formats-no-protobuf + io.prometheus + prometheus-metrics-exposition-textformats runtime @@ -61,7 +61,7 @@ io.micrometer micrometer-registry-prometheus - + io.prometheus prometheus-metrics-exposition-formats From 4c77088817cc5eb62479a25bf73a292e36358a23 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:28:59 +0100 Subject: [PATCH 11/27] Update hibernate processor --- demo/entities-metamodel/pom.xml | 26 +-------- .../demo/entities/IdentifiableEntity_.java | 15 +++-- .../xdev/sse/demo/entities/Product_.java | 17 ++++-- .../xdev/sse/demo/entities/UserDetail_.java | 55 +++++++++++++------ .../entities/auth/AuthRememberMeSecret_.java | 7 ++- .../xdev/sse/demo/entities/auth/Secret_.java | 54 +++++++++++++----- 6 files changed, 107 insertions(+), 67 deletions(-) diff --git a/demo/entities-metamodel/pom.xml b/demo/entities-metamodel/pom.xml index 616c7015..1d8a1237 100644 --- a/demo/entities-metamodel/pom.xml +++ b/demo/entities-metamodel/pom.xml @@ -28,7 +28,7 @@ org.hibernate.orm - hibernate-jpamodelgen + hibernate-processor true @@ -83,30 +83,6 @@ - - - - org.apache.maven.plugins - maven-clean-plugin - - - - wipe-hibernate-entity-index - generate-sources - - clean - - - true - - - ${project.generated.sources}/entity - - - - - - diff --git a/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/IdentifiableEntity_.java b/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/IdentifiableEntity_.java index 79ebd9fa..091a4e91 100644 --- a/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/IdentifiableEntity_.java +++ b/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/IdentifiableEntity_.java @@ -4,21 +4,28 @@ import jakarta.persistence.metamodel.SingularAttribute; import jakarta.persistence.metamodel.StaticMetamodel; +/** + * Static metamodel for {@link software.xdev.sse.demo.entities.IdentifiableEntity} + **/ @StaticMetamodel(IdentifiableEntity.class) public abstract class IdentifiableEntity_ { + + /** + * @see #id + **/ public static final String ID = "id"; /** - * @see software.xdev.sse.demo.entities.IdentifiableEntity#id + * Static metamodel type for {@link software.xdev.sse.demo.entities.IdentifiableEntity} **/ - public static volatile SingularAttribute id; + public static volatile MappedSuperclassType class_; /** - * @see software.xdev.sse.demo.entities.IdentifiableEntity + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.IdentifiableEntity#id} **/ - public static volatile MappedSuperclassType class_; + public static volatile SingularAttribute id; } diff --git a/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/Product_.java b/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/Product_.java index 15d0904e..d0b7c11b 100644 --- a/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/Product_.java +++ b/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/Product_.java @@ -4,21 +4,28 @@ import jakarta.persistence.metamodel.SingularAttribute; import jakarta.persistence.metamodel.StaticMetamodel; +/** + * Static metamodel for {@link software.xdev.sse.demo.entities.Product} + **/ @StaticMetamodel(Product.class) -public abstract class Product_ extends software.xdev.sse.demo.entities.IdentifiableEntity_ { +public abstract class Product_ extends IdentifiableEntity_ { + + /** + * @see #name + **/ public static final String NAME = "name"; /** - * @see software.xdev.sse.demo.entities.Product#name + * Static metamodel type for {@link software.xdev.sse.demo.entities.Product} **/ - public static volatile SingularAttribute name; + public static volatile EntityType class_; /** - * @see software.xdev.sse.demo.entities.Product + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.Product#name} **/ - public static volatile EntityType class_; + public static volatile SingularAttribute name; } diff --git a/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/UserDetail_.java b/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/UserDetail_.java index c3a89b85..3a494371 100644 --- a/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/UserDetail_.java +++ b/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/UserDetail_.java @@ -5,45 +5,68 @@ import jakarta.persistence.metamodel.StaticMetamodel; import java.time.LocalDateTime; +/** + * Static metamodel for {@link software.xdev.sse.demo.entities.UserDetail} + **/ @StaticMetamodel(UserDetail.class) -public abstract class UserDetail_ extends software.xdev.sse.demo.entities.IdentifiableEntity_ { +public abstract class UserDetail_ extends IdentifiableEntity_ { - public static final String CREATED_AT = "createdAt"; - public static final String EMAIL_ADDRESS = "emailAddress"; - public static final String LAST_LOGIN_AT = "lastLoginAt"; + + /** + * @see #fullName + **/ public static final String FULL_NAME = "fullName"; - public static final String DISABLED_AT = "disabledAt"; - /** - * @see software.xdev.sse.demo.entities.UserDetail#createdAt + * @see #emailAddress **/ - public static volatile SingularAttribute createdAt; + public static final String EMAIL_ADDRESS = "emailAddress"; /** - * @see software.xdev.sse.demo.entities.UserDetail#emailAddress + * @see #createdAt **/ - public static volatile SingularAttribute emailAddress; + public static final String CREATED_AT = "createdAt"; /** - * @see software.xdev.sse.demo.entities.UserDetail#lastLoginAt + * @see #disabledAt **/ - public static volatile SingularAttribute lastLoginAt; + public static final String DISABLED_AT = "disabledAt"; /** - * @see software.xdev.sse.demo.entities.UserDetail#fullName + * @see #lastLoginAt **/ - public static volatile SingularAttribute fullName; + public static final String LAST_LOGIN_AT = "lastLoginAt"; + /** - * @see software.xdev.sse.demo.entities.UserDetail + * Static metamodel type for {@link software.xdev.sse.demo.entities.UserDetail} **/ public static volatile EntityType class_; /** - * @see software.xdev.sse.demo.entities.UserDetail#disabledAt + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.UserDetail#fullName} + **/ + public static volatile SingularAttribute fullName; + + /** + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.UserDetail#emailAddress} + **/ + public static volatile SingularAttribute emailAddress; + + /** + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.UserDetail#createdAt} + **/ + public static volatile SingularAttribute createdAt; + + /** + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.UserDetail#disabledAt} **/ public static volatile SingularAttribute disabledAt; + + /** + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.UserDetail#lastLoginAt} + **/ + public static volatile SingularAttribute lastLoginAt; } diff --git a/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/auth/AuthRememberMeSecret_.java b/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/auth/AuthRememberMeSecret_.java index b06ad76d..7155a8f9 100644 --- a/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/auth/AuthRememberMeSecret_.java +++ b/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/auth/AuthRememberMeSecret_.java @@ -3,13 +3,16 @@ import jakarta.persistence.metamodel.EntityType; import jakarta.persistence.metamodel.StaticMetamodel; +/** + * Static metamodel for {@link software.xdev.sse.demo.entities.auth.AuthRememberMeSecret} + **/ @StaticMetamodel(AuthRememberMeSecret.class) -public abstract class AuthRememberMeSecret_ extends software.xdev.sse.demo.entities.auth.Secret_ { +public abstract class AuthRememberMeSecret_ extends Secret_ { /** - * @see software.xdev.sse.demo.entities.auth.AuthRememberMeSecret + * Static metamodel type for {@link software.xdev.sse.demo.entities.auth.AuthRememberMeSecret} **/ public static volatile EntityType class_; diff --git a/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/auth/Secret_.java b/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/auth/Secret_.java index e33104bf..e692d97b 100644 --- a/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/auth/Secret_.java +++ b/demo/entities-metamodel/src/gen/java/software/xdev/sse/demo/entities/auth/Secret_.java @@ -4,47 +4,71 @@ import jakarta.persistence.metamodel.SingularAttribute; import jakarta.persistence.metamodel.StaticMetamodel; import java.time.LocalDateTime; +import software.xdev.sse.demo.entities.IdentifiableEntity_; import software.xdev.sse.demo.entities.UserDetail; +/** + * Static metamodel for {@link software.xdev.sse.demo.entities.auth.Secret} + **/ @StaticMetamodel(Secret.class) -public abstract class Secret_ extends software.xdev.sse.demo.entities.IdentifiableEntity_ { +public abstract class Secret_ extends IdentifiableEntity_ { + + /** + * @see #identifier + **/ public static final String IDENTIFIER = "identifier"; - public static final String CREATED_AT = "createdAt"; - public static final String SECRET = "secret"; - public static final String USER = "user"; + + /** + * @see #cryptoAlgorithm + **/ public static final String CRYPTO_ALGORITHM = "cryptoAlgorithm"; - /** - * @see software.xdev.sse.demo.entities.auth.Secret#identifier + * @see #secret **/ - public static volatile SingularAttribute identifier; + public static final String SECRET = "secret"; /** - * @see software.xdev.sse.demo.entities.auth.Secret#createdAt + * @see #createdAt **/ - public static volatile SingularAttribute createdAt; + public static final String CREATED_AT = "createdAt"; /** - * @see software.xdev.sse.demo.entities.auth.Secret#secret + * @see #user **/ - public static volatile SingularAttribute secret; + public static final String USER = "user"; + /** - * @see software.xdev.sse.demo.entities.auth.Secret + * Static metamodel type for {@link software.xdev.sse.demo.entities.auth.Secret} **/ public static volatile MappedSuperclassType class_; /** - * @see software.xdev.sse.demo.entities.auth.Secret#user + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.auth.Secret#identifier} **/ - public static volatile SingularAttribute user; + public static volatile SingularAttribute identifier; /** - * @see software.xdev.sse.demo.entities.auth.Secret#cryptoAlgorithm + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.auth.Secret#cryptoAlgorithm} **/ public static volatile SingularAttribute cryptoAlgorithm; + + /** + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.auth.Secret#secret} + **/ + public static volatile SingularAttribute secret; + + /** + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.auth.Secret#createdAt} + **/ + public static volatile SingularAttribute createdAt; + + /** + * Static metamodel for attribute {@link software.xdev.sse.demo.entities.auth.Secret#user} + **/ + public static volatile SingularAttribute user; } From 1d781c1c89239205bd4d8429b8c0301fc29df2e5 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:29:32 +0100 Subject: [PATCH 12/27] Remove deprecated Vaadin websecurity impl --- .../vaadin/TotalVaadinFlowWebSecurity.java | 161 ------------------ 1 file changed, 161 deletions(-) delete mode 100644 vaadin/src/main/java/software/xdev/sse/vaadin/TotalVaadinFlowWebSecurity.java diff --git a/vaadin/src/main/java/software/xdev/sse/vaadin/TotalVaadinFlowWebSecurity.java b/vaadin/src/main/java/software/xdev/sse/vaadin/TotalVaadinFlowWebSecurity.java deleted file mode 100644 index 7999128f..00000000 --- a/vaadin/src/main/java/software/xdev/sse/vaadin/TotalVaadinFlowWebSecurity.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright © 2025 XDEV Software (https://xdev.software) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package software.xdev.sse.vaadin; - -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; -import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.security.web.access.AccessDeniedHandlerImpl; -import org.springframework.security.web.authentication.HttpStatusEntryPoint; -import org.springframework.security.web.util.matcher.OrRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; - -import com.vaadin.flow.spring.security.VaadinWebSecurity; - -import software.xdev.sse.vaadin.csrf.VaadinCSRFDisableRequestMatcherProvider; -import software.xdev.sse.web.loginurl.LoginUrlStore; - - -/** - * Override of {@link VaadinWebSecurity} that doesn't allow any VaadinSession to be created without previous - * authentication. - * @deprecated Vaadin has decided that they will remove {@link VaadinWebSecurity}. - * Use {@link TotalVaadinFlowSecurityConfigurer} instead. - */ -@Deprecated(forRemoval = true) -@SuppressWarnings("java:S6813") -public abstract class TotalVaadinFlowWebSecurity extends VaadinWebSecurity -{ - protected static final Set DEFAULT_DO_NOT_RESPOND_UNAUTHORIZED_METHODS = Stream.of( - HttpMethod.GET, - HttpMethod.OPTIONS, - HttpMethod.HEAD, - HttpMethod.TRACE) - .map(HttpMethod::name) - .collect(Collectors.toSet()); - - @Autowired - protected SecureVaadinRequestCache vaadinDefaultRequestCache; - - @Autowired - protected List vaadinCSRFDisableRequestMatcherProviders; - - @Autowired(required = false) - protected LoginUrlStore loginUrlStore; - - @SuppressWarnings("java:S4502") // Vaadin brings its own CSRF - @Override - protected void configure(final HttpSecurity http) throws Exception - { - // IMPORTANT - SECURITY - // Not using "super.configure(http)" here due to security problems, see below #configureAuthorizeHttpRequests - http.exceptionHandling(cfg -> { - this.configureExceptionHandling(cfg); - this.addDefaultAuthenticationEntryPointFor(cfg); - }); - - final List vaadinCSRFDisableRequestMatchers = - this.vaadinCSRFDisableRequestMatcherProviders.stream() - .map(VaadinCSRFDisableRequestMatcherProvider::getMatcher) - .filter(Objects::nonNull) - .toList(); - - // Removed out-of-the-box Spring CSRF as Vaadin bring its own CSRF - // Otherwise sessions are created on rogue POST request - // If CSRF is required for other parts of the app, whitelist them using requireCsrfProtectionMatcher - if(vaadinCSRFDisableRequestMatchers.isEmpty()) - { - // NOTE that Springs default logout view will not show up when CSRF is disabled! - http.csrf(AbstractHttpConfigurer::disable); - } - else - { - final OrRequestMatcher matcher = new OrRequestMatcher(vaadinCSRFDisableRequestMatchers); - // Using ignoringRequestMatchers is not working as it's joined (using AND) - // with CsrfFilter.DEFAULT_CSRF_MATCHER - http.csrf(c -> c.requireCsrfProtectionMatcher(matcher::matches)); - } - - // NOTE: Creates session by default for redirect after auth - // Example: /settings -> /login -> OIDC-Server -> /login/oauth2/code/... -> /settings - // See also HttpSessionRequestCache#createSessionAllowed - http.requestCache(cfg -> cfg.requestCache(this.vaadinDefaultRequestCache)); - - http.authorizeHttpRequests(this::configureAuthorizeHttpRequests); - - this.getNavigationAccessControl().setEnabled(this.enableNavigationAccessControl()); - - this.configureLoginViewFromLoginUrlStore(); - } - - protected void configureExceptionHandling(final ExceptionHandlingConfigurer cfg) - { - // Removed support for Hilla endpoints - cfg.accessDeniedHandler(this.createAccessDeniedHandler()); - } - - protected void addDefaultAuthenticationEntryPointFor(final ExceptionHandlingConfigurer cfg) - { - // This is required for so that stuff like Vaadin's POST requests are not redirected to the - // login site (causes JavaScript crash as HTML can't be parsed). - cfg.defaultAuthenticationEntryPointFor( - new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), - request -> !DEFAULT_DO_NOT_RESPOND_UNAUTHORIZED_METHODS.contains(request.getMethod())); - } - - protected void configureAuthorizeHttpRequests( - final AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry urlRegistry) - { - // By default, Vaadin configures authorizeHttpRequests to allow anonymous requests for certain - // endpoints like /favicon.ico or /VAADIN/** - // This is a problem because: - // 1. A new (heavy) Vaadin session is created, which might cause Session leak and performance problems - // 2. There is no user associated with it which the app is not designed for - - // The registered filters may also cause a performance impact - - // ALL VAADIN REQUESTS REQUIRE AUTHENTICATION - urlRegistry.anyRequest().authenticated(); - } - - protected void configureLoginViewFromLoginUrlStore() - { - if(this.loginUrlStore != null) - { - // This is usually only needed when the authentication is anonymous - // and navigation to a view that requires non-anonymous authentication happens - this.getNavigationAccessControl().setLoginView(this.loginUrlStore.getLoginUrl()); - } - } - - // Copied from super (as it's private) and removed hilla - @SuppressWarnings("java:S2177") - protected AccessDeniedHandler createAccessDeniedHandler() - { - return new AccessDeniedHandlerImpl(); - } -} From b0d4bb465e75b25d8bc752ba4fe43ff40128682a Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:30:33 +0100 Subject: [PATCH 13/27] Rewrite OidcUserService to be closer to standard Only fork OidcUserRequestUtils#shouldRetrieveUserInfo --- .../sse/demo/security/AuthUserService.java | 13 +- .../sse/demo/security/AuthUserService.java | 13 +- .../oauth2/userinfo/OidcUserRequestUtils.java | 41 +--- .../sse/oauth2/userinfo/OidcUserService.java | 219 ------------------ .../sse/oauth2/userinfo/package-info.java | 5 +- .../OidcUserServiceCompatibilityTest.java | 95 -------- 6 files changed, 28 insertions(+), 358 deletions(-) delete mode 100644 oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/OidcUserService.java delete mode 100644 oauth2-oidc/src/test/java/software/xdev/sse/oauth2/userinfo/OidcUserServiceCompatibilityTest.java diff --git a/demo/webapp-rest/src/main/java/software/xdev/sse/demo/security/AuthUserService.java b/demo/webapp-rest/src/main/java/software/xdev/sse/demo/security/AuthUserService.java index 4aaad43c..7412ed0c 100644 --- a/demo/webapp-rest/src/main/java/software/xdev/sse/demo/security/AuthUserService.java +++ b/demo/webapp-rest/src/main/java/software/xdev/sse/demo/security/AuthUserService.java @@ -3,27 +3,32 @@ import java.util.Optional; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; +import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.stereotype.Component; -import software.xdev.sse.oauth2.userinfo.OidcUserService; +import software.xdev.sse.oauth2.userinfo.OidcUserRequestUtils; @Component public class AuthUserService extends OidcUserService { - @Override - protected boolean shouldRetrieveUserInfo(final OidcUserRequest userRequest) + public AuthUserService() + { + this.setRetrieveUserInfo(this::shouldRetrieveUserInfo); + } + + private boolean shouldRetrieveUserInfo(final OidcUserRequest userRequest) { // Check if required data is NOT already present if(Optional.ofNullable(userRequest.getIdToken()) .map(t -> t.getEmail() == null || t.getFullName() == null) .orElse(true)) { - return super.shouldRetrieveUserInfo(userRequest); + return OidcUserRequestUtils.shouldRetrieveUserInfo(userRequest); } // If data is already present don't fetch additional data return false; diff --git a/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/security/AuthUserService.java b/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/security/AuthUserService.java index dc7e782b..4b5bf85c 100644 --- a/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/security/AuthUserService.java +++ b/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/security/AuthUserService.java @@ -4,13 +4,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; +import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.stereotype.Component; import software.xdev.sse.demo.buisness.service.UserService; import software.xdev.sse.demo.entities.UserDetail; import software.xdev.sse.oauth2.userenrichment.OAuth2UserEnricher; -import software.xdev.sse.oauth2.userinfo.OidcUserService; +import software.xdev.sse.oauth2.userinfo.OidcUserRequestUtils; @Component @@ -22,15 +23,19 @@ public class AuthUserService extends OidcUserService @Autowired protected AuthUserEnricher userEnricher; - @Override - protected boolean shouldRetrieveUserInfo(final OidcUserRequest userRequest) + public AuthUserService() + { + this.setRetrieveUserInfo(this::shouldRetrieveUserInfo); + } + + private boolean shouldRetrieveUserInfo(final OidcUserRequest userRequest) { // Check if required data is NOT already present if(Optional.ofNullable(userRequest.getIdToken()) .map(t -> t.getEmail() == null || t.getFullName() == null) .orElse(true)) { - return super.shouldRetrieveUserInfo(userRequest); + return OidcUserRequestUtils.shouldRetrieveUserInfo(userRequest); } // If data is already present don't fetch additional data return false; diff --git a/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/OidcUserRequestUtils.java b/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/OidcUserRequestUtils.java index 9b54079b..44b9feec 100644 --- a/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/OidcUserRequestUtils.java +++ b/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/OidcUserRequestUtils.java @@ -15,51 +15,26 @@ */ package software.xdev.sse.oauth2.userinfo; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.OAuth2AccessToken; -import org.springframework.security.oauth2.core.oidc.OidcUserInfo; -import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; -import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority; +import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.util.StringUtils; /** - * @apiNote only contains required code for {@link OidcUserService} - * @see org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequestUtils + * Publicly available fork of {@link org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequestUtils} */ public final class OidcUserRequestUtils { - public static OidcUser getUser(final OidcUserRequest userRequest, final OidcUserInfo userInfo) + public static boolean shouldRetrieveUserInfo(final OidcUserRequest userRequest) { - final Set authorities = new LinkedHashSet<>(); + // Auto-disabled if UserInfo Endpoint URI is not provided final ClientRegistration.ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails(); - final String userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName(); - if(StringUtils.hasText(userNameAttributeName)) - { - authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo, userNameAttributeName)); - } - else - { - authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo)); - } - final OAuth2AccessToken token = userRequest.getAccessToken(); - for(final String scope : token.getScopes()) - { - authorities.add(new SimpleGrantedAuthority("SCOPE_" + scope)); - } - if(StringUtils.hasText(userNameAttributeName)) - { - return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo, userNameAttributeName); - } - return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo); + + return StringUtils.hasLength(providerDetails.getUserInfoEndpoint().getUri()) + && AuthorizationGrantType.AUTHORIZATION_CODE + .equals(userRequest.getClientRegistration().getAuthorizationGrantType()); } private OidcUserRequestUtils() diff --git a/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/OidcUserService.java b/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/OidcUserService.java deleted file mode 100644 index 53204d61..00000000 --- a/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/OidcUserService.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright © 2025 XDEV Software (https://xdev.software) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package software.xdev.sse.oauth2.userinfo; - -import java.time.Instant; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; - -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.Converter; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails; -import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; -import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.converter.ClaimConversionService; -import org.springframework.security.oauth2.core.converter.ClaimTypeConverter; -import org.springframework.security.oauth2.core.oidc.OidcScopes; -import org.springframework.security.oauth2.core.oidc.OidcUserInfo; -import org.springframework.security.oauth2.core.oidc.StandardClaimNames; -import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; -import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - - -/** - * @see org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService - */ -@SuppressWarnings({"java:S1452", "java:S1133", "unused"}) -public class OidcUserService implements OAuth2UserService -{ - protected static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = "invalid_user_info_response"; - - protected static final Converter, Map> DEFAULT_CLAIM_TYPE_CONVERTER = - new ClaimTypeConverter(createDefaultClaimTypeConverters()); - - protected Set accessibleScopes = new HashSet<>( - Arrays.asList(OidcScopes.PROFILE, OidcScopes.EMAIL, OidcScopes.ADDRESS, OidcScopes.PHONE)); - - protected OAuth2UserService oauth2UserService = new DefaultOAuth2UserService(); - - protected Function, Map>> - claimTypeConverterFactory = clientRegistration -> DEFAULT_CLAIM_TYPE_CONVERTER; - - protected Predicate retrieveUserInfo = this::shouldRetrieveUserInfo; - - protected BiFunction oidcUserMapper = OidcUserRequestUtils::getUser; - - public static Map> createDefaultClaimTypeConverters() - { - final Converter booleanConverter = getConverter(TypeDescriptor.valueOf(Boolean.class)); - final Converter instantConverter = getConverter(TypeDescriptor.valueOf(Instant.class)); - final Map> claimTypeConverters = new HashMap<>(); - claimTypeConverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter); - claimTypeConverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter); - claimTypeConverters.put(StandardClaimNames.UPDATED_AT, instantConverter); - return claimTypeConverters; - } - - protected static Converter getConverter(final TypeDescriptor targetDescriptor) - { - final TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class); - return source -> ClaimConversionService.getSharedInstance() - .convert(source, sourceDescriptor, targetDescriptor); - } - - @Override - public OidcUser loadUser(final OidcUserRequest userRequest) - { - Assert.notNull(userRequest, "userRequest cannot be null"); - OidcUserInfo userInfo = null; - if(this.shouldRetrieveUserInfo(userRequest)) - { - final OAuth2User oauth2User = this.oauth2UserService.loadUser(userRequest); - final Map claims = this.getClaims(userRequest, oauth2User); - userInfo = new OidcUserInfo(claims); - // https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse - // 1) The sub (subject) Claim MUST always be returned in the UserInfo Response - if(userInfo.getSubject() == null) - { - final OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE); - throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); - } - // 2) Due to the possibility of token substitution attacks (see Section - // 16.11), - // the UserInfo Response is not guaranteed to be about the End-User - // identified by the sub (subject) element of the ID Token. - // The sub Claim in the UserInfo Response MUST be verified to exactly match - // the sub Claim in the ID Token; if they do not match, - // the UserInfo Response values MUST NOT be used. - if(!userInfo.getSubject().equals(userRequest.getIdToken().getSubject())) - { - final OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE); - throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); - } - } - return this.oidcUserMapper.apply(userRequest, userInfo); - } - - protected Map getClaims(final OidcUserRequest userRequest, final OAuth2User oauth2User) - { - final Converter, Map> converter = this.claimTypeConverterFactory - .apply(userRequest.getClientRegistration()); - return converter != null - ? converter.convert(oauth2User.getAttributes()) - : DEFAULT_CLAIM_TYPE_CONVERTER.convert(oauth2User.getAttributes()); - } - - protected OidcUser getUser( - final OidcUserRequest userRequest, - final OidcUserInfo userInfo, - final Set authorities) - { - final ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails(); - final String userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName(); - if(StringUtils.hasText(userNameAttributeName)) - { - return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo, userNameAttributeName); - } - return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo); - } - - protected boolean shouldRetrieveUserInfo(final OidcUserRequest userRequest) - { - // Auto-disabled if UserInfo Endpoint URI is not provided - final ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails(); - if(!StringUtils.hasLength(providerDetails.getUserInfoEndpoint().getUri())) - { - return false; - } - // The Claims requested by the profile, email, address, and phone scope values - // are returned from the UserInfo Endpoint (as described in Section 5.3.2), - // when a response_type value is used that results in an Access Token being - // issued. - // However, when no Access Token is issued, which is the case for the - // response_type=id_token, - // the resulting Claims are returned in the ID Token. - // The Authorization Code Grant Flow, which is response_type=code, results in an - // Access Token being issued. - if(AuthorizationGrantType.AUTHORIZATION_CODE - .equals(userRequest.getClientRegistration().getAuthorizationGrantType())) - { - // Return true if there is at least one match between the authorized scope(s) - // and accessible scope(s) - // - // Also return true if authorized scope(s) is empty, because the provider has - // not indicated which scopes are accessible via the access token - // @formatter:off - return this.accessibleScopes.isEmpty() - || CollectionUtils.isEmpty(userRequest.getAccessToken().getScopes()) - || CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(), this.accessibleScopes); - // @formatter:on - } - return false; - } - - public void setOauth2UserService(final OAuth2UserService oauth2UserService) - { - Assert.notNull(oauth2UserService, "oauth2UserService cannot be null"); - this.oauth2UserService = oauth2UserService; - } - - public void setClaimTypeConverterFactory( - final Function, Map>> - claimTypeConverterFactory) - { - Assert.notNull(claimTypeConverterFactory, "claimTypeConverterFactory cannot be null"); - this.claimTypeConverterFactory = claimTypeConverterFactory; - } - - /** - * @deprecated Use {@link #setRetrieveUserInfo(Predicate)} instead - */ - @Deprecated(since = "6.3", forRemoval = true) - public void setAccessibleScopes(final Set accessibleScopes) - { - Assert.notNull(accessibleScopes, "accessibleScopes cannot be null"); - this.accessibleScopes = accessibleScopes; - } - - public void setRetrieveUserInfo(final Predicate retrieveUserInfo) - { - Assert.notNull(retrieveUserInfo, "retrieveUserInfo cannot be null"); - this.retrieveUserInfo = retrieveUserInfo; - } - - public void setOidcUserMapper(final BiFunction oidcUserMapper) - { - Assert.notNull(oidcUserMapper, "oidcUserMapper cannot be null"); - this.oidcUserMapper = oidcUserMapper; - } -} diff --git a/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/package-info.java b/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/package-info.java index 6cf36c8d..5fb7e498 100644 --- a/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/package-info.java +++ b/oauth2-oidc/src/main/java/software/xdev/sse/oauth2/userinfo/package-info.java @@ -14,8 +14,7 @@ * limitations under the License. */ /** - * Reimplementation of {@link org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService} because not - * everything should be final and private. + * Not everything should be final and private. *

* See also: *

* Note: Just because some issues are now "closed" because "you can now supply customizers" doesn't mean this is fixed. - * One can still not override the default methods because they are private! + * One can still not override/access the default methods because they are private! */ package software.xdev.sse.oauth2.userinfo; diff --git a/oauth2-oidc/src/test/java/software/xdev/sse/oauth2/userinfo/OidcUserServiceCompatibilityTest.java b/oauth2-oidc/src/test/java/software/xdev/sse/oauth2/userinfo/OidcUserServiceCompatibilityTest.java deleted file mode 100644 index 8acec6b2..00000000 --- a/oauth2-oidc/src/test/java/software/xdev/sse/oauth2/userinfo/OidcUserServiceCompatibilityTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright © 2025 XDEV Software (https://xdev.software) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package software.xdev.sse.oauth2.userinfo; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - - -class OidcUserServiceCompatibilityTest -{ - @Test - void superclass() - { - Assertions.assertEquals( - org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService.class.getSuperclass(), - OidcUserService.class.getSuperclass()); - } - - @Test - void interfaces() - { - final Set> actual = Arrays.stream(OidcUserService.class.getInterfaces()).collect(Collectors.toSet()); - Assertions.assertTrue(Arrays.stream( - org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService.class.getInterfaces()) - .allMatch(actual::contains)); - } - - @Test - void fields() - { - final List actual = Arrays.stream(OidcUserService.class.getDeclaredFields()).toList(); - Assertions.assertTrue(Arrays.stream( - org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService.class.getDeclaredFields()) - .allMatch(e -> actual.stream() - .anyMatch(a -> a.getName().equals(e.getName()) - && a.getType().equals(e.getType())))); - } - - @Test - void methods() - { - final Predicate filterOutMethods = m -> !m.isSynthetic(); - final List actual = Arrays.stream(OidcUserService.class.getDeclaredMethods()) - .filter(filterOutMethods) - .toList(); - Assertions.assertTrue(Arrays.stream( - org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService.class.getDeclaredMethods()) - .filter(filterOutMethods) - .allMatch(e -> actual.stream() - .anyMatch(a -> a.getName().equals(e.getName()) - && a.getReturnType().equals(e.getReturnType()) - && equalParamTypes(a.getParameterTypes(), e.getParameterTypes())))); - } - - /** - * @apiNote Again a method that somehow is private and therefore not accessible :( - * @see java.lang.reflect.Executable#equalParamTypes - */ - static boolean equalParamTypes(final Class[] params1, final Class[] params2) - { - if(params1.length == params2.length) - { - for(int i = 0; i < params1.length; i++) - { - if(params1[i] != params2[i]) - { - return false; - } - } - return true; - } - return false; - } -} From 2cbaa38e3ca40258631029f9d6b1962394862355 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:31:16 +0100 Subject: [PATCH 14/27] Adopt Spring Boot changes --- demo/webapp-shared/pom.xml | 5 +++++ .../src/main/java/software/xdev/sse/demo/DemoJPAConfig.java | 4 ++-- .../sse/demo/init/DemoFlywayConfigurationCustomizer.java | 2 +- .../src/main/java/software/xdev/sse/demo/Application.java | 6 ++---- .../xdev/sse/web/sidecar/auto/CommonSidecarsAutoConfig.java | 2 +- .../errorpage/ErrorPageCompatibilityPathsProvider.java | 4 ++-- .../software/xdev/sse/web/cookie/auto/CookieAutoConfig.java | 2 +- 7 files changed, 14 insertions(+), 11 deletions(-) diff --git a/demo/webapp-shared/pom.xml b/demo/webapp-shared/pom.xml index 78a46e25..7e11412b 100644 --- a/demo/webapp-shared/pom.xml +++ b/demo/webapp-shared/pom.xml @@ -19,6 +19,11 @@
+ + org.springframework.boot + spring-boot-starter-flyway + + org.springframework.boot spring-boot-starter-logging diff --git a/demo/webapp-shared/src/main/java/software/xdev/sse/demo/DemoJPAConfig.java b/demo/webapp-shared/src/main/java/software/xdev/sse/demo/DemoJPAConfig.java index ad6293fa..d9364661 100644 --- a/demo/webapp-shared/src/main/java/software/xdev/sse/demo/DemoJPAConfig.java +++ b/demo/webapp-shared/src/main/java/software/xdev/sse/demo/DemoJPAConfig.java @@ -6,8 +6,8 @@ import java.util.function.Function; import java.util.stream.Collectors; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer; +import org.springframework.boot.hibernate.autoconfigure.HibernatePropertiesCustomizer; +import org.springframework.boot.persistence.autoconfigure.EntityScan; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; diff --git a/demo/webapp-shared/src/main/java/software/xdev/sse/demo/init/DemoFlywayConfigurationCustomizer.java b/demo/webapp-shared/src/main/java/software/xdev/sse/demo/init/DemoFlywayConfigurationCustomizer.java index 995f7b44..e9a6ef0a 100644 --- a/demo/webapp-shared/src/main/java/software/xdev/sse/demo/init/DemoFlywayConfigurationCustomizer.java +++ b/demo/webapp-shared/src/main/java/software/xdev/sse/demo/init/DemoFlywayConfigurationCustomizer.java @@ -1,7 +1,7 @@ package software.xdev.sse.demo.init; import org.flywaydb.core.api.configuration.FluentConfiguration; -import org.springframework.boot.autoconfigure.flyway.FlywayConfigurationCustomizer; +import org.springframework.boot.flyway.autoconfigure.FlywayConfigurationCustomizer; import org.springframework.stereotype.Component; import software.xdev.sse.demo.persistence.FlywayInfo; diff --git a/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java b/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java index abbc749d..6234bc70 100644 --- a/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java +++ b/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java @@ -5,9 +5,8 @@ import java.util.stream.Stream; import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; +import org.springframework.boot.webmvc.autoconfigure.error.ErrorMvcAutoConfiguration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; @@ -15,11 +14,10 @@ @SuppressWarnings({"checkstyle:HideUtilityClassConstructor", "PMD.UseUtilityClass"}) -@SpringBootApplication +@SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class}) @EnableScheduling @EnableAsync @EnableVaadin("software.xdev.sse.demo.ui") -@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class}) public class Application { @SuppressWarnings("PMD.AvoidSystemSetterCall") diff --git a/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/auto/CommonSidecarsAutoConfig.java b/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/auto/CommonSidecarsAutoConfig.java index c3ba4855..67641d56 100644 --- a/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/auto/CommonSidecarsAutoConfig.java +++ b/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/auto/CommonSidecarsAutoConfig.java @@ -22,7 +22,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.web.server.ErrorPageRegistry; +import org.springframework.boot.web.error.ErrorPageRegistry; import org.springframework.context.annotation.Bean; import software.xdev.sse.web.sidecar.OtherWebSecurityPaths; diff --git a/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/errorpage/ErrorPageCompatibilityPathsProvider.java b/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/errorpage/ErrorPageCompatibilityPathsProvider.java index 8f5b47cc..bda59ae5 100644 --- a/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/errorpage/ErrorPageCompatibilityPathsProvider.java +++ b/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/errorpage/ErrorPageCompatibilityPathsProvider.java @@ -23,9 +23,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.boot.web.error.ErrorPage; +import org.springframework.boot.web.error.ErrorPageRegistry; import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory; -import org.springframework.boot.web.server.ErrorPage; -import org.springframework.boot.web.server.ErrorPageRegistry; import software.xdev.sse.web.sidecar.public_stateless.PublicStatelessPathsProvider; diff --git a/web/src/main/java/software/xdev/sse/web/cookie/auto/CookieAutoConfig.java b/web/src/main/java/software/xdev/sse/web/cookie/auto/CookieAutoConfig.java index 7b1371f8..ec6242b2 100644 --- a/web/src/main/java/software/xdev/sse/web/cookie/auto/CookieAutoConfig.java +++ b/web/src/main/java/software/xdev/sse/web/cookie/auto/CookieAutoConfig.java @@ -19,7 +19,7 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier; +import org.springframework.boot.web.server.servlet.CookieSameSiteSupplier; import org.springframework.context.annotation.Bean; import software.xdev.sse.web.cookie.CookieSecureService; From 0455e6177e6f47ef5d0edfe3119efae066047238 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:31:23 +0100 Subject: [PATCH 15/27] Remove unused class --- .../util/EntityManagerController.java | 178 ------------------ 1 file changed, 178 deletions(-) delete mode 100644 demo/persistence/src/main/java/software/xdev/sse/demo/persistence/util/EntityManagerController.java diff --git a/demo/persistence/src/main/java/software/xdev/sse/demo/persistence/util/EntityManagerController.java b/demo/persistence/src/main/java/software/xdev/sse/demo/persistence/util/EntityManagerController.java deleted file mode 100644 index 9741eb2a..00000000 --- a/demo/persistence/src/main/java/software/xdev/sse/demo/persistence/util/EntityManagerController.java +++ /dev/null @@ -1,178 +0,0 @@ -package software.xdev.sse.demo.persistence.util; - -import static java.util.Map.entry; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -import jakarta.persistence.Entity; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.spi.ClassTransformer; -import jakarta.persistence.spi.PersistenceUnitTransactionType; - -import org.hibernate.cfg.JdbcSettings; -import org.hibernate.jpa.HibernatePersistenceProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo; - -import software.xdev.sse.demo.persistence.config.DefaultJPAConfig; - - -/** - * Handles the creation and destruction of {@link EntityManager}s. - *

- * This should only be used when a {@link EntityManager} has to be created manually, e.g. when not running on an - * AppServer. - */ -public class EntityManagerController implements AutoCloseable -{ - private static final Logger LOG = LoggerFactory.getLogger(EntityManagerController.class); - - protected final List activeEms = Collections.synchronizedList(new ArrayList<>()); - protected final EntityManagerFactory emf; - - public EntityManagerController(final EntityManagerFactory emf) - { - this.emf = Objects.requireNonNull(emf); - } - - /** - * Creates a new {@link EntityManager} with an internal {@link EntityManagerFactory}, which can be used to load and - * save data in the database. - * - *

- * It may be a good idea to close the EntityManager, when you're finished with it. - *

- *

- * All created EntityManager are automatically cleaned up once {@link #close()} is called. - *

- * - * @return EntityManager - */ - public EntityManager createEntityManager() - { - final EntityManager em = this.emf.createEntityManager(); - this.activeEms.add(em); - - return em; - } - - @Override - public void close() - { - LOG.debug("Shutting down resources"); - this.activeEms.forEach(em -> - { - try - { - if(em.getTransaction() != null && em.getTransaction().isActive()) - { - em.getTransaction().rollback(); - } - em.close(); - } - catch(final Exception e) - { - LOG.warn("Unable to close EntityManager", e); - } - }); - - LOG.debug("Cleared {}x EntityManagers", this.activeEms.size()); - - this.activeEms.clear(); - - try - { - this.emf.close(); - LOG.debug("Released EntityManagerFactory"); - } - catch(final Exception e) - { - LOG.error("Failed to release EntityManagerFactory", e); - } - } - - public static EntityManagerController createForStandalone( - final String driverFullClassName, - final String connectionProviderClassName, - final String jdbcUrl, - final String username, - final String password - ) - { - return createForStandalone( - driverFullClassName, - connectionProviderClassName, - "Test", - jdbcUrl, - username, - password); - } - - public static EntityManagerController createForStandalone( - final String driverFullClassName, - final String connectionProviderClassName, - final String persistenceUnitName, - final String jdbcUrl, - final String username, - final String password - ) - { - final MutablePersistenceUnitInfo persistenceUnitInfo = new MutablePersistenceUnitInfo() - { - @Override - public void addTransformer(final ClassTransformer classTransformer) - { - // Do nothing - } - - @Override - public ClassLoader getNewTempClassLoader() - { - return null; - } - }; - persistenceUnitInfo.setTransactionType(PersistenceUnitTransactionType.RESOURCE_LOCAL); - persistenceUnitInfo.setPersistenceUnitName(persistenceUnitName); - persistenceUnitInfo.setPersistenceProviderClassName(HibernatePersistenceProvider.class.getName()); - AnnotatedClassFinder.find(DefaultJPAConfig.ENTITY_PACKAGE, Entity.class) - .stream() - .map(Class::getName) - .forEach(persistenceUnitInfo::addManagedClassName); - try - { - Collections.list(EntityManagerController.class - .getClassLoader() - .getResources("")) - .forEach(persistenceUnitInfo::addJarFileUrl); - } - catch(final IOException ioe) - { - throw new UncheckedIOException(ioe); - } - - final Map properties = new HashMap<>(Map.ofEntries( - entry(JdbcSettings.JAKARTA_JDBC_DRIVER, driverFullClassName), - entry(JdbcSettings.JAKARTA_JDBC_URL, jdbcUrl), - entry(JdbcSettings.JAKARTA_JDBC_USER, username), - entry(JdbcSettings.JAKARTA_JDBC_PASSWORD, password) - )); - Optional.ofNullable(connectionProviderClassName) - .ifPresent(p -> properties.put(JdbcSettings.CONNECTION_PROVIDER, connectionProviderClassName)); - properties.putAll(DisableHibernateFormatMapper.properties()); - return new EntityManagerController( - new HibernatePersistenceProvider() - .createContainerEntityManagerFactory( - persistenceUnitInfo, - properties)); - } -} From a6bb7ac595d0a6964f8cf3ba2d23552549af5ece Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:31:36 +0100 Subject: [PATCH 16/27] Import correct testcontainers dependency --- demo/integration-tests/tci-db/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/integration-tests/tci-db/pom.xml b/demo/integration-tests/tci-db/pom.xml index 55c826c2..3bb2da8b 100644 --- a/demo/integration-tests/tci-db/pom.xml +++ b/demo/integration-tests/tci-db/pom.xml @@ -39,7 +39,7 @@ org.testcontainers - mariadb + testcontainers-mariadb compile From 4f90208572cd6ef110cd31a5458d7289d9d0ce6a Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:31:50 +0100 Subject: [PATCH 17/27] Update TCI to match for Spring Boot 4.x --- demo/integration-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/integration-tests/pom.xml b/demo/integration-tests/pom.xml index 8647cdf0..d3030001 100644 --- a/demo/integration-tests/pom.xml +++ b/demo/integration-tests/pom.xml @@ -99,7 +99,7 @@ software.xdev.tci bom - 2.9.4 + 3.0.0 pom import From 9acb53c1d9d286e29178166e072c0b24f47c75b9 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:32:40 +0100 Subject: [PATCH 18/27] Adopt Vaadin / Spring Boot changes --- .../java/software/xdev/sse/demo/Application.java | 6 ++---- .../servlet/http/DummyHttpServletResponse.java | 2 +- .../vaadin/TotalVaadinFlowSecurityConfigurer.java | 12 ++++++------ 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/demo/webapp-rest/src/main/java/software/xdev/sse/demo/Application.java b/demo/webapp-rest/src/main/java/software/xdev/sse/demo/Application.java index c98f3042..0e666fd0 100644 --- a/demo/webapp-rest/src/main/java/software/xdev/sse/demo/Application.java +++ b/demo/webapp-rest/src/main/java/software/xdev/sse/demo/Application.java @@ -5,14 +5,12 @@ import java.util.stream.Stream; import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; +import org.springframework.boot.webmvc.autoconfigure.error.ErrorMvcAutoConfiguration; @SuppressWarnings({"checkstyle:HideUtilityClassConstructor", "PMD.UseUtilityClass"}) -@SpringBootApplication -@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class}) +@SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class}) public class Application { @SuppressWarnings("PMD.AvoidSystemSetterCall") diff --git a/oauth2-oidc/src/test/java/jakarta/servlet/http/DummyHttpServletResponse.java b/oauth2-oidc/src/test/java/jakarta/servlet/http/DummyHttpServletResponse.java index 9f7e6a15..351a0f38 100644 --- a/oauth2-oidc/src/test/java/jakarta/servlet/http/DummyHttpServletResponse.java +++ b/oauth2-oidc/src/test/java/jakarta/servlet/http/DummyHttpServletResponse.java @@ -61,7 +61,7 @@ public void sendError(final int sc) throws IOException } @Override - public void sendRedirect(final String location) throws IOException + public void sendRedirect(final String location, final int sc, final boolean clearBuffer) throws IOException { } diff --git a/vaadin/src/main/java/software/xdev/sse/vaadin/TotalVaadinFlowSecurityConfigurer.java b/vaadin/src/main/java/software/xdev/sse/vaadin/TotalVaadinFlowSecurityConfigurer.java index ffaca82c..92842040 100644 --- a/vaadin/src/main/java/software/xdev/sse/vaadin/TotalVaadinFlowSecurityConfigurer.java +++ b/vaadin/src/main/java/software/xdev/sse/vaadin/TotalVaadinFlowSecurityConfigurer.java @@ -144,7 +144,7 @@ public void setBuilder(final HttpSecurity builder) } @Override - public void init(final HttpSecurity http) throws Exception + public void init(final HttpSecurity http) { if(this.customizeVaadinSecurityConfigurer != null) { @@ -171,7 +171,7 @@ public void init(final HttpSecurity http) throws Exception } @Override - public void configure(final HttpSecurity http) throws Exception + public void configure(final HttpSecurity http) { this.vaadinSecurityConfigurer.configure(http); @@ -182,7 +182,7 @@ public void configure(final HttpSecurity http) throws Exception } // region Exception Handling - protected void initExceptionHandling(final HttpSecurity http) throws Exception + protected void initExceptionHandling(final HttpSecurity http) { http.exceptionHandling(cfg -> { this.configureExceptionHandler(cfg); @@ -213,7 +213,7 @@ protected AccessDeniedHandler createAccessDeniedHandler() // endregion // region CSRF - protected void initCSRF(final HttpSecurity http) throws Exception + protected void initCSRF(final HttpSecurity http) { final List vaadinCSRFDisableRequestMatchers = this.getApplicationContext() .getBeansOfType(VaadinCSRFDisableRequestMatcherProvider.class) @@ -244,7 +244,7 @@ protected void initCSRF(final HttpSecurity http) throws Exception // region RequestCache - protected void initRequestCache(final HttpSecurity http) throws Exception + protected void initRequestCache(final HttpSecurity http) { final SecureVaadinRequestCache vaadinDefaultRequestCache = Objects.requireNonNull( this.getApplicationContext().getBean(SecureVaadinRequestCache.class), @@ -260,7 +260,7 @@ protected void initRequestCache(final HttpSecurity http) throws Exception // region HTTP Requests - protected void initAuthorizeHttpRequests(final HttpSecurity http) throws Exception + protected void initAuthorizeHttpRequests(final HttpSecurity http) { http.authorizeHttpRequests(this::configureAuthorizeHttpRequests); } From c086eceec85e8e239f6e91c5c4d4e84216442a51 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:32:50 +0100 Subject: [PATCH 19/27] Rewrite serializer to use Jackson v3 --- ...tOAuth2CookieRememberMeAuthSerializer.java | 77 +++++++++-------- ...th2CookieRememberMeAuthSerializerTest.java | 82 ++++++++++--------- 2 files changed, 80 insertions(+), 79 deletions(-) diff --git a/oauth2-oidc-remember-me/src/main/java/software/xdev/sse/oauth2/rememberme/serializer/DefaultOAuth2CookieRememberMeAuthSerializer.java b/oauth2-oidc-remember-me/src/main/java/software/xdev/sse/oauth2/rememberme/serializer/DefaultOAuth2CookieRememberMeAuthSerializer.java index ccf599e1..609c1f40 100644 --- a/oauth2-oidc-remember-me/src/main/java/software/xdev/sse/oauth2/rememberme/serializer/DefaultOAuth2CookieRememberMeAuthSerializer.java +++ b/oauth2-oidc-remember-me/src/main/java/software/xdev/sse/oauth2/rememberme/serializer/DefaultOAuth2CookieRememberMeAuthSerializer.java @@ -25,6 +25,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -46,15 +47,13 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator; -import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.nimbusds.jwt.JWTClaimNames; import com.nimbusds.jwt.JWTParser; import software.xdev.sse.oauth2.util.OAuth2UserNameAttributeKeyExtractor; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator; +import tools.jackson.databind.jsontype.PolymorphicTypeValidator; /** @@ -66,46 +65,53 @@ @SuppressWarnings("java:S4544") // Handled by PolymorphicTypeValidator (see below) public class DefaultOAuth2CookieRememberMeAuthSerializer implements OAuth2CookieRememberMeAuthSerializer { - protected final ObjectMapper mapper; + protected final JsonMapper mapper; public DefaultOAuth2CookieRememberMeAuthSerializer() { - this(true); + this(true, null); } // Only here for tests - protected DefaultOAuth2CookieRememberMeAuthSerializer(final boolean withPolymorphicTypeValidator) + protected DefaultOAuth2CookieRememberMeAuthSerializer( + final boolean withPolymorphicTypeValidator, + final Consumer customizer) { - this(withPolymorphicTypeValidator - // https://cowtowncoder.medium.com/jackson-2-10-safe-default-typing-2d018f0ce2ba - ? BasicPolymorphicTypeValidator.builder() - .allowIfSubType(Instant.class) - .allowIfSubType(java.net.URL.class) - .allowIfSubType(ArrayList.class) - .build() - : null); + this( + withPolymorphicTypeValidator + // https://cowtowncoder.medium.com/jackson-2-10-safe-default-typing-2d018f0ce2ba + ? BasicPolymorphicTypeValidator.builder() + .allowIfSubType(Instant.class) + .allowIfSubType(java.net.URL.class) + .allowIfSubType(ArrayList.class) + .build() + : null, + customizer); } - protected DefaultOAuth2CookieRememberMeAuthSerializer(final PolymorphicTypeValidator polymorphicTypeValidator) + protected DefaultOAuth2CookieRememberMeAuthSerializer( + final PolymorphicTypeValidator polymorphicTypeValidator, + final Consumer customizer) { - this.mapper = new ObjectMapper() - .registerModule(new JavaTimeModule()) - .setSerializationInclusion(JsonInclude.Include.NON_NULL); - Optional.ofNullable(polymorphicTypeValidator) - .ifPresent(this.mapper::setPolymorphicTypeValidator); + final JsonMapper.Builder builder = JsonMapper.builder() + .changeDefaultPropertyInclusion(v -> v.withValueInclusion(JsonInclude.Include.NON_NULL)); + + if(polymorphicTypeValidator != null) + { + builder.polymorphicTypeValidator(polymorphicTypeValidator); + } + if(customizer != null) + { + customizer.accept(builder); + } + + this.mapper = builder.build(); } @Override public String serialize(final OAuth2AuthenticationToken token, final OAuth2AuthorizedClient client) { - try - { - return this.mapper.writeValueAsString(new SOAuth2AuthContainer(token, client)); - } - catch(final JsonProcessingException e) - { - throw new IllegalStateException("Unable to serialize", e); - } + return this.mapper.writeValueAsString(new SOAuth2AuthContainer(token, client)); } @Override @@ -113,15 +119,8 @@ public OAuth2AuthContainer deserialize( final String json, final Function clientRegistrationResolver) { - try - { - return this.mapper.readValue(json, SOAuth2AuthContainer.class) - .toOriginal(clientRegistrationResolver); - } - catch(final JsonProcessingException e) - { - throw new IllegalStateException("Unable to deserialize", e); - } + return this.mapper.readValue(json, SOAuth2AuthContainer.class) + .toOriginal(clientRegistrationResolver); } public record DefaultOAuth2AuthContainer( diff --git a/oauth2-oidc-remember-me/src/test/java/software/xdev/sse/oauth2/rememberme/serializer/DefaultOAuth2CookieRememberMeAuthSerializerTest.java b/oauth2-oidc-remember-me/src/test/java/software/xdev/sse/oauth2/rememberme/serializer/DefaultOAuth2CookieRememberMeAuthSerializerTest.java index 30523745..9165de0b 100644 --- a/oauth2-oidc-remember-me/src/test/java/software/xdev/sse/oauth2/rememberme/serializer/DefaultOAuth2CookieRememberMeAuthSerializerTest.java +++ b/oauth2-oidc-remember-me/src/test/java/software/xdev/sse/oauth2/rememberme/serializer/DefaultOAuth2CookieRememberMeAuthSerializerTest.java @@ -28,9 +28,8 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Predicate; +import java.util.function.Consumer; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -48,7 +47,17 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority; import org.springframework.security.web.authentication.WebAuthenticationDetails; +import tools.jackson.core.StreamReadFeature; +import tools.jackson.databind.exc.InvalidDefinitionException; +import tools.jackson.databind.exc.InvalidTypeIdException; +import tools.jackson.databind.json.JsonMapper; + +// NOTE: Originally designed for Jackson v2 +// Was improved in v3 so that for example Object objects are no longer deserialized into +// potentially dangerous classes. +// Please note that Jackson v3 does not cover all possible options (as it depends on the available classes) +// and therefore this test is still present class DefaultOAuth2CookieRememberMeAuthSerializerTest { private static final String ACCESS_TOKEN = "dummy"; @@ -70,11 +79,19 @@ class DefaultOAuth2CookieRememberMeAuthSerializerTest private static final String NAME = "A B"; private static final String EMAIL = "a.b@xdev-software.de"; + private static final Consumer SERIALIZER_JSON_MAPPER_CUSTOMIZER = + b -> b.enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION); + + static DefaultOAuth2CookieRememberMeAuthSerializer createSerializer(final boolean safe) + { + return new DefaultOAuth2CookieRememberMeAuthSerializer(safe, SERIALIZER_JSON_MAPPER_CUSTOMIZER); + } + @Test void defaultSerializationWorks() { Assertions.assertDoesNotThrow(() -> this.serializeAndDeserialize( - new DefaultOAuth2CookieRememberMeAuthSerializer(), + createSerializer(true), Map.of())); } @@ -82,58 +99,43 @@ void defaultSerializationWorks() static Set attackSuccessIds = new HashSet<>(); @Test - void performAttackWithoutProtectionSuccess() + void performAttackWithoutDedicatedProtection() { - final String id = "success"; - this.performAttack( - new DefaultOAuth2CookieRememberMeAuthSerializer(false), - id, - List.of( - "Unable to deserialize"::equals, - s -> s.contains(AttackPerformer.SUCCESS_INDICATOR), - AttackPerformer.SUCCESS_INDICATOR::equals) + final String id = "default"; + final var serializer = createSerializer(false); + final InvalidDefinitionException ex = Assertions.assertThrows( + InvalidDefinitionException.class, + () -> this.performAttack(serializer, id) ); - Assertions.assertTrue(attackSuccessIds.contains(id)); + Assertions.assertTrue(ex.getMessage().startsWith("Configured `PolymorphicTypeValidator`") + && ex.getMessage().contains("denies resolution of all subtypes of base type " + + "`java.lang.Object` as using too generic base type " + + "can open a security hole without checks on subtype: " + + "please configure a custom `PolymorphicTypeValidator` for this use case")); + Assertions.assertFalse(attackSuccessIds.contains(id)); } @Test void performAttackFails() { final String id = "fail"; - this.performAttack( - new DefaultOAuth2CookieRememberMeAuthSerializer(), - id, - List.of( - "Unable to deserialize"::equals, - s -> s.startsWith("Could not resolve type id") - && s.contains("$AttackPerformer' as a subtype of `java.lang.Object`: " - + "Configured `PolymorphicTypeValidator`"))); + final var serializer = createSerializer(true); + final InvalidTypeIdException ex = Assertions.assertThrows( + InvalidTypeIdException.class, + () -> this.performAttack(serializer, id) + ); + Assertions.assertTrue(ex.getMessage().startsWith("Could not resolve type id") + && ex.getMessage().contains( + "$AttackPerformer' as a subtype of `java.lang.Object`: Configured `PolymorphicTypeValidator`")); Assertions.assertFalse(attackSuccessIds.contains(id)); } void performAttack( final DefaultOAuth2CookieRememberMeAuthSerializer serializer, - final String id, - final List> exceptionCauseMessageChecks) + final String id) { final Map data = Map.of("test", new AttackPerformer(id)); - final IllegalStateException ex = Assertions.assertThrows( - IllegalStateException.class, - () -> this.serializeAndDeserialize(serializer, data)); - Throwable current = ex; - - int i = 0; - while(current != null) - { - Assertions.assertTrue( - i < exceptionCauseMessageChecks.size() - && exceptionCauseMessageChecks.get(i).test(current.getMessage()), - "Invalid exception message at nested=" + i + ": " + current.getMessage() - + "\nSOURCE EXCEPTION:\n" - + ExceptionUtils.getStackTrace(ex)); - current = current.getCause(); - i++; - } + this.serializeAndDeserialize(serializer, data); } public static class AttackPerformer From a3fdb73b30f896fbd846c4267b6840d1f7cd8ee3 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:42:32 +0100 Subject: [PATCH 20/27] Migrate Vaadin Demo to v25 --- demo/webapp-vaadin/pnpm-workspace.yaml | 25 ++++++++ demo/webapp-vaadin/pom.xml | 62 ++++--------------- .../software/xdev/sse/demo/Application.java | 3 + 3 files changed, 41 insertions(+), 49 deletions(-) create mode 100644 demo/webapp-vaadin/pnpm-workspace.yaml diff --git a/demo/webapp-vaadin/pnpm-workspace.yaml b/demo/webapp-vaadin/pnpm-workspace.yaml new file mode 100644 index 00000000..9ac0828b --- /dev/null +++ b/demo/webapp-vaadin/pnpm-workspace.yaml @@ -0,0 +1,25 @@ +# Delay install of newly released packages to prevent supply chain attacks +minimumReleaseAge: 180 # 3h +overrides: + # Remove unused packages + # glob CLI unused + "jackspeak": "npm:empty-npm-package@1.0.0" + "foreground-child": "npm:empty-npm-package@1.0.0" + "package-json-from-dist": "npm:empty-npm-package@1.0.0" + # rollup-plugin-visualizer CLI unused + "yargs": "npm:empty-npm-package@1.0.0" + "open": "npm:empty-npm-package@1.0.0" + # transform-ast test only + "nanobench": "npm:empty-npm-package@1.0.0" + # Workbox unused + "workbox-google-analytics": "npm:empty-npm-package@1.0.0" + "@surma/rollup-plugin-off-main-thread": "npm:empty-npm-package@1.0.0" + "@babel/preset-env": "npm:empty-npm-package@1.0.0" + "@babel/runtime": "npm:empty-npm-package@1.0.0" + "@rollup/plugin-replace@2.4.2": "npm:empty-npm-package@1.0.0" + "@rollup/plugin-babel": "npm:empty-npm-package@1.0.0" + "@rollup/plugin-node-resolve": "npm:empty-npm-package@1.0.0" + "@rollup/plugin-terser": "npm:empty-npm-package@1.0.0" + "tempy": "npm:empty-npm-package@1.0.0" + # Disable telemetry + "@vaadin/vaadin-usage-statistics": "npm:empty-npm-package@1.0.0" diff --git a/demo/webapp-vaadin/pom.xml b/demo/webapp-vaadin/pom.xml index c217e531..f635a6f0 100644 --- a/demo/webapp-vaadin/pom.xml +++ b/demo/webapp-vaadin/pom.xml @@ -28,45 +28,11 @@ com.vaadin vaadin-spring-boot-starter - - - com.vaadin - hilla - - - com.vaadin - hilla-dev - - - - com.vaadin - copilot - - - - com.vaadin - vaadin-material-theme - - - - com.vaadin - flow-react - - - - com.vaadin - ui-tests - com.vaadin collaboration-engine - - - com.vaadin.servletdetector - throw-if-servlet3 - @@ -91,6 +57,17 @@
+ + com.vaadin + vaadin-dev + true + + + com.vaadin + copilot + + + @@ -170,11 +147,11 @@ + + true false - - false @@ -225,19 +202,6 @@ prod - - - - com.vaadin - vaadin-core - - - com.vaadin - vaadin-dev - - - - diff --git a/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java b/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java index 6234bc70..ae12cb73 100644 --- a/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java +++ b/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java @@ -10,7 +10,9 @@ import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; +import com.vaadin.flow.component.dependency.StyleSheet; import com.vaadin.flow.spring.annotation.EnableVaadin; +import com.vaadin.flow.theme.lumo.Lumo; @SuppressWarnings({"checkstyle:HideUtilityClassConstructor", "PMD.UseUtilityClass"}) @@ -18,6 +20,7 @@ @EnableScheduling @EnableAsync @EnableVaadin("software.xdev.sse.demo.ui") +@StyleSheet(Lumo.STYLESHEET) public class Application { @SuppressWarnings("PMD.AvoidSystemSetterCall") From d66fa75799b9ac36b0e4fddf3a685858727071ee Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:44:05 +0100 Subject: [PATCH 21/27] Update minimum Java version to 21 --- .github/workflows/check-build.yml | 4 ++-- .github/workflows/release.yml | 8 ++++---- .github/workflows/test-deploy.yml | 4 ++-- client-storage/pom.xml | 2 +- codec-sha256/pom.xml | 2 +- crypto-symmetric-managed/pom.xml | 2 +- crypto-symmetric/pom.xml | 2 +- csp/pom.xml | 2 +- demo/pom.xml | 2 +- metrics/pom.xml | 2 +- oauth2-oidc-remember-me/pom.xml | 2 +- oauth2-oidc/pom.xml | 2 +- vaadin/pom.xml | 2 +- web-sidecar-actuator/pom.xml | 2 +- web-sidecar-common/pom.xml | 2 +- web/pom.xml | 2 +- 16 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 7147c235..948790af 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -25,7 +25,7 @@ jobs: timeout-minutes: 30 strategy: matrix: - java: [17, 21, 25] + java: [21, 25] distribution: [temurin] steps: - uses: actions/checkout@v6 @@ -107,7 +107,7 @@ jobs: timeout-minutes: 15 strategy: matrix: - java: [17] + java: [21] distribution: [temurin] steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e749a64d..4375f192 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v5 with: - java-version: '17' + java-version: '21' distribution: 'temurin' # Try to reuse existing cache from check-build @@ -125,7 +125,7 @@ jobs: uses: actions/setup-java@v5 with: # running setup-java overwrites the settings.xml distribution: 'temurin' - java-version: '17' + java-version: '21' server-id: github-central server-password: PACKAGES_CENTRAL_TOKEN gpg-passphrase: MAVEN_GPG_PASSPHRASE @@ -147,7 +147,7 @@ jobs: uses: actions/setup-java@v5 with: # running setup-java again overwrites the settings.xml distribution: 'temurin' - java-version: '17' + java-version: '21' server-id: sonatype-central-portal server-username: MAVEN_CENTRAL_USERNAME server-password: MAVEN_CENTRAL_TOKEN @@ -182,7 +182,7 @@ jobs: - name: Setup - Java uses: actions/setup-java@v5 with: - java-version: '17' + java-version: '21' distribution: 'temurin' # Try to reuse existing cache from check-build diff --git a/.github/workflows/test-deploy.yml b/.github/workflows/test-deploy.yml index e9cbbadc..53828e1d 100644 --- a/.github/workflows/test-deploy.yml +++ b/.github/workflows/test-deploy.yml @@ -14,7 +14,7 @@ jobs: uses: actions/setup-java@v5 with: # running setup-java overwrites the settings.xml distribution: 'temurin' - java-version: '17' + java-version: '21' server-id: github-central server-password: PACKAGES_CENTRAL_TOKEN gpg-passphrase: MAVEN_GPG_PASSPHRASE @@ -36,7 +36,7 @@ jobs: uses: actions/setup-java@v5 with: # running setup-java again overwrites the settings.xml distribution: 'temurin' - java-version: '17' + java-version: '21' server-id: sonatype-central-portal server-username: MAVEN_CENTRAL_USERNAME server-password: MAVEN_CENTRAL_TOKEN diff --git a/client-storage/pom.xml b/client-storage/pom.xml index 4d1b4e91..ce246cd6 100644 --- a/client-storage/pom.xml +++ b/client-storage/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/codec-sha256/pom.xml b/codec-sha256/pom.xml index 56450355..31fee402 100644 --- a/codec-sha256/pom.xml +++ b/codec-sha256/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/crypto-symmetric-managed/pom.xml b/crypto-symmetric-managed/pom.xml index fbbc43c0..ea52e223 100644 --- a/crypto-symmetric-managed/pom.xml +++ b/crypto-symmetric-managed/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/crypto-symmetric/pom.xml b/crypto-symmetric/pom.xml index 6f182cc3..a2b8a343 100644 --- a/crypto-symmetric/pom.xml +++ b/crypto-symmetric/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/csp/pom.xml b/csp/pom.xml index ce33be11..8e510e04 100644 --- a/csp/pom.xml +++ b/csp/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/demo/pom.xml b/demo/pom.xml index 3fc299d1..08b6a1d4 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -15,7 +15,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/metrics/pom.xml b/metrics/pom.xml index 8bd272cb..b1ecfb71 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/oauth2-oidc-remember-me/pom.xml b/oauth2-oidc-remember-me/pom.xml index dd45832a..a5f0d011 100644 --- a/oauth2-oidc-remember-me/pom.xml +++ b/oauth2-oidc-remember-me/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/oauth2-oidc/pom.xml b/oauth2-oidc/pom.xml index da78e3c7..3fefc280 100644 --- a/oauth2-oidc/pom.xml +++ b/oauth2-oidc/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/vaadin/pom.xml b/vaadin/pom.xml index b5bed168..265d2cb9 100644 --- a/vaadin/pom.xml +++ b/vaadin/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/web-sidecar-actuator/pom.xml b/web-sidecar-actuator/pom.xml index 13203df7..08ce4b75 100644 --- a/web-sidecar-actuator/pom.xml +++ b/web-sidecar-actuator/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/web-sidecar-common/pom.xml b/web-sidecar-common/pom.xml index 290d0435..4296bb1d 100644 --- a/web-sidecar-common/pom.xml +++ b/web-sidecar-common/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 diff --git a/web/pom.xml b/web/pom.xml index 025a7ba7..2254d2e6 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -42,7 +42,7 @@ - 17 + 21 ${javaVersion} UTF-8 From 4dacb50f1df6805be5bd12f239683ca676dc692f Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 10:46:17 +0100 Subject: [PATCH 22/27] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e778ba12..5f2d921f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # 2.0.0 +_Minimum required Java version: 21_ * Updated to Spring Boot 4.x + * Removed fork of `OidcUserService` to move closer to the standard implementation. Only fork the required `OidcUserRequestUtils#shouldRetrieveUserInfo` +* Update Vaadin to 25 + * Removed `TotalVaadinFlowWebSecurity` as it's no longer supported +* Updated Jackson Databind to v3 +* Migrated demos and tests +* Code cleanup # 1.5.3 _Last expected version for Spring Boot 3.x_ From 8c459a1513e93da9626261e06d462cdbbf47b7c3 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 15:26:14 +0100 Subject: [PATCH 23/27] Update tests & docs to match new behavior --- .../cases/urlmapping/BaseUrlMappingTest.java | 18 ++++++++++++++- .../urlmapping/UrlMappingBeforePatchTest.java | 19 --------------- .../urlmapping/UrlMappingPatchedTest.java | 23 ------------------- .../sidecar/httpsecurity/package-info.java | 10 ++++++++ 4 files changed, 27 insertions(+), 43 deletions(-) diff --git a/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/BaseUrlMappingTest.java b/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/BaseUrlMappingTest.java index c75e20d3..754b563a 100644 --- a/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/BaseUrlMappingTest.java +++ b/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/BaseUrlMappingTest.java @@ -1,8 +1,13 @@ package software.xdev.sse.demo.vaadin.cases.urlmapping; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.io.IOException; import java.net.URI; import java.util.Collection; +import java.util.List; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; @@ -53,5 +58,16 @@ void check() throws IOException } } - protected abstract Collection checkResponse(final ClassicHttpResponse response); + // As of Spring Boot 7.x the underlying problem is fixed out-of-the-box and both responses should now be identical + protected Collection checkResponse(final ClassicHttpResponse response) + { + return List.of( + () -> assertEquals(302, response.getCode()), + () -> assertNull(response.getHeader("Set-Cookie")), + () -> assertTrue(response.getHeader("Location") + .getValue() + .endsWith("/oauth2/authorization/local")), + () -> assertEquals("1", response.getHeader("X-Force-Reload").getValue()) + ); + } } diff --git a/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/UrlMappingBeforePatchTest.java b/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/UrlMappingBeforePatchTest.java index a49955f0..a8f6996b 100644 --- a/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/UrlMappingBeforePatchTest.java +++ b/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/UrlMappingBeforePatchTest.java @@ -1,14 +1,5 @@ package software.xdev.sse.demo.vaadin.cases.urlmapping; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.util.Collection; -import java.util.List; - -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.junit.jupiter.api.function.Executable; - import software.xdev.sse.demo.tci.webapp.containers.VaadinWebAppContainer; @@ -20,14 +11,4 @@ protected void customizeWebAppContainer(final VaadinWebAppContainer c) super.customizeWebAppContainer(c); c.withEnv("SSE_SIDECAR_HTTP-SECURITY-MATCHER_DEFAULT_CREATOR_ENABLED", "false"); } - - @Override - protected Collection checkResponse(final ClassicHttpResponse response) - { - return List.of( - () -> assertEquals(401, response.getCode()), - () -> assertNull(response.getHeader("Set-Cookie")), - () -> assertEquals("Basic realm=\"Realm\"", response.getHeader("WWW-Authenticate").getValue()) - ); - } } diff --git a/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/UrlMappingPatchedTest.java b/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/UrlMappingPatchedTest.java index 7e9ab658..677fe721 100644 --- a/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/UrlMappingPatchedTest.java +++ b/demo/integration-tests/webapp-vaadin-it/src/test/java/software/xdev/sse/demo/vaadin/cases/urlmapping/UrlMappingPatchedTest.java @@ -1,28 +1,5 @@ package software.xdev.sse.demo.vaadin.cases.urlmapping; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Collection; -import java.util.List; - -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.junit.jupiter.api.function.Executable; - - class UrlMappingPatchedTest extends BaseUrlMappingTest { - @Override - protected Collection checkResponse(final ClassicHttpResponse response) - { - return List.of( - () -> assertEquals(302, response.getCode()), - () -> assertNull(response.getHeader("Set-Cookie")), - () -> assertTrue(response.getHeader("Location") - .getValue() - .endsWith("/oauth2/authorization/local")), - () -> assertEquals("1", response.getHeader("X-Force-Reload").getValue()) - ); - } } diff --git a/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/httpsecurity/package-info.java b/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/httpsecurity/package-info.java index b0293e6f..719d44e0 100644 --- a/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/httpsecurity/package-info.java +++ b/web-sidecar-common/src/main/java/software/xdev/sse/web/sidecar/httpsecurity/package-info.java @@ -14,6 +14,15 @@ * limitations under the License. */ /** + *

Spring 7.x Update

+ * With Spring 7 the underlying root issue has been fixed - Spring now always uses a PathPatternRequestMatcher. + *

+ * However, it's still possible for frameworks or applications to use a custom implementation in + * {@link org.springframework.security.config.annotation.web.builders.HttpSecurity#securityMatcher(java.lang.String...)} + * so for now this implementation will stay available. + *

+ * + *

Original behavior - Spring 6.x

* Controls how * {@link org.springframework.security.config.annotation.web.builders.HttpSecurity * #securityMatcher(org.springframework.security.web.util.matcher.RequestMatcher)} is applied for sidecars. @@ -27,6 +36,7 @@ *

* This package is only designed to be used in Sidecars and not in the main application! *

+ * * @see #221 */ package software.xdev.sse.web.sidecar.httpsecurity; From 6778c27b21e1f457b31a8058c619997d632bc705 Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 15:26:41 +0100 Subject: [PATCH 24/27] Fix Vaadin displaying styles --- .../src/main/java/software/xdev/sse/demo/Application.java | 3 --- .../src/main/java/software/xdev/sse/demo/ui/AppShell.java | 5 ++++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java b/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java index ae12cb73..6234bc70 100644 --- a/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java +++ b/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/Application.java @@ -10,9 +10,7 @@ import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; -import com.vaadin.flow.component.dependency.StyleSheet; import com.vaadin.flow.spring.annotation.EnableVaadin; -import com.vaadin.flow.theme.lumo.Lumo; @SuppressWarnings({"checkstyle:HideUtilityClassConstructor", "PMD.UseUtilityClass"}) @@ -20,7 +18,6 @@ @EnableScheduling @EnableAsync @EnableVaadin("software.xdev.sse.demo.ui") -@StyleSheet(Lumo.STYLESHEET) public class Application { @SuppressWarnings("PMD.AvoidSystemSetterCall") diff --git a/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/ui/AppShell.java b/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/ui/AppShell.java index 5b56cc2d..16fc34b3 100644 --- a/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/ui/AppShell.java +++ b/demo/webapp-vaadin/src/main/java/software/xdev/sse/demo/ui/AppShell.java @@ -2,11 +2,14 @@ import java.util.Map; +import com.vaadin.flow.component.dependency.StyleSheet; import com.vaadin.flow.component.page.AppShellConfigurator; import com.vaadin.flow.component.page.Push; import com.vaadin.flow.server.AppShellSettings; +import com.vaadin.flow.theme.lumo.Lumo; +@StyleSheet("context://" + Lumo.STYLESHEET) @Push public class AppShell implements AppShellConfigurator { @@ -14,7 +17,7 @@ public class AppShell implements AppShellConfigurator public void configurePage(final AppShellSettings settings) { // Don't use Vaadin's PWA implementation because it can only handle PNGs - // Also it renders them for each IPhone resolution in existence, which bloats up the delivered content + // Also it renders them for each iPhone resolution in existence, which bloats up the delivered content settings.addLink( "manifest.webmanifest", Map.of( From 9887b4983bbec0be6059317726d3c5da471caf9a Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 15:34:48 +0100 Subject: [PATCH 25/27] Provide fallback for `setDefaultEntryPoint` --- .../NoErrorBasicAuthenticationEntryPoint.java | 29 ++++++++++++++++++- ...rrorBasicAuthenticationEntryPointTest.java | 12 ++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/NoErrorBasicAuthenticationEntryPoint.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/NoErrorBasicAuthenticationEntryPoint.java index 853c5258..c4d6a184 100644 --- a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/NoErrorBasicAuthenticationEntryPoint.java +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/NoErrorBasicAuthenticationEntryPoint.java @@ -16,21 +16,28 @@ package software.xdev.sse.web.sidecar.actuator; import java.lang.reflect.Field; +import java.lang.reflect.Method; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint; +import org.springframework.security.web.authentication.HttpStatusEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; +import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher; public class NoErrorBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint { + private static final Logger LOG = LoggerFactory.getLogger(NoErrorBasicAuthenticationEntryPoint.class); + protected NoErrorBasicAuthenticationEntryPoint() { } @@ -76,7 +83,27 @@ public static > HttpBasicConfigurer install( if(authenticationEntryPoint instanceof final DelegatingAuthenticationEntryPoint delegatingAuthEntryPoint) { - delegatingAuthEntryPoint.setDefaultEntryPoint(entryPoint); + try + { + final Method mSetDefaultEntryPoint = DelegatingAuthenticationEntryPoint.class.getDeclaredMethod( + "setDefaultEntryPoint", + AuthenticationEntryPoint.class); + mSetDefaultEntryPoint.setAccessible(true); + mSetDefaultEntryPoint.invoke(delegatingAuthEntryPoint, entryPoint); + } + catch(final Exception ex) + { + LOG.debug("setDefaultEntryPoint failed (Spring Boot API changed?) - Trying to use fallback", ex); + + // Replace entire implementation + httpBasicConfigurer.authenticationEntryPoint( + DelegatingAuthenticationEntryPoint.builder() + .addEntryPointFor( + new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), + new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest")) + .defaultEntryPoint(entryPoint) + .build()); + } } } catch(final NoSuchFieldException | IllegalAccessException e) diff --git a/web-sidecar-actuator/src/test/java/software/xdev/sse/web/sidecar/actuator/NoErrorBasicAuthenticationEntryPointTest.java b/web-sidecar-actuator/src/test/java/software/xdev/sse/web/sidecar/actuator/NoErrorBasicAuthenticationEntryPointTest.java index cb7f4d31..35f35979 100644 --- a/web-sidecar-actuator/src/test/java/software/xdev/sse/web/sidecar/actuator/NoErrorBasicAuthenticationEntryPointTest.java +++ b/web-sidecar-actuator/src/test/java/software/xdev/sse/web/sidecar/actuator/NoErrorBasicAuthenticationEntryPointTest.java @@ -19,6 +19,8 @@ import org.junit.jupiter.api.Test; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint; class NoErrorBasicAuthenticationEntryPointTest @@ -29,4 +31,14 @@ void checkInstall() Assertions.assertDoesNotThrow(() -> NoErrorBasicAuthenticationEntryPoint.install(new HttpBasicConfigurer())); } + + @Test + void setDefaultEntryPointPresent() + { + // setDefaultEntryPoint is deprecated + // If this fails NoErrorBasicAuthenticationEntryPoint.install needs to be modified + Assertions.assertDoesNotThrow(() -> DelegatingAuthenticationEntryPoint.class.getDeclaredMethod( + "setDefaultEntryPoint", + AuthenticationEntryPoint.class)); + } } From ed1a2a9607ce55933227df75d05a6017dd82eddf Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 15:51:02 +0100 Subject: [PATCH 26/27] Update docs --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f2d921f..1fb51e4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ _Minimum required Java version: 21_ * Removed fork of `OidcUserService` to move closer to the standard implementation. Only fork the required `OidcUserRequestUtils#shouldRetrieveUserInfo` * Update Vaadin to 25 * Removed `TotalVaadinFlowWebSecurity` as it's no longer supported + * Please note that Stylesheet [may now required being added to `PublicStatelessPathsProvider`](https://vaadin.com/docs/v25/upgrading#themes-and-styling) * Updated Jackson Databind to v3 * Migrated demos and tests * Code cleanup From 138173f67c15c19c5273ab8cabe1524ca60c1beb Mon Sep 17 00:00:00 2001 From: AB Date: Tue, 27 Jan 2026 15:55:52 +0100 Subject: [PATCH 27/27] Remove deprecated `password-sha256` --- CHANGELOG.md | 1 + .../actuator/config/ActuatorUserConfig.java | 23 ------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fb51e4f..363a1d6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ _Minimum required Java version: 21_ * Removed `TotalVaadinFlowWebSecurity` as it's no longer supported * Please note that Stylesheet [may now required being added to `PublicStatelessPathsProvider`](https://vaadin.com/docs/v25/upgrading#themes-and-styling) * Updated Jackson Databind to v3 +* `ActuatorUserConfig`: Remove deprecated `passwordSha256` - Use `passwordHash` instead * Migrated demos and tests * Code cleanup diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorUserConfig.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorUserConfig.java index 8fcdf429..bf1fd0d3 100644 --- a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorUserConfig.java +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorUserConfig.java @@ -21,8 +21,6 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import org.slf4j.LoggerFactory; - public class ActuatorUserConfig { @@ -45,27 +43,6 @@ public void setUsername(final String username) this.username = username; } - /** - * @deprecated Replaced by {@link #getPasswordHash()} - */ - @Deprecated(since = "1.3.0", forRemoval = true) - public String getPasswordSha256() - { - return this.getPasswordHash(); - } - - /** - * @deprecated Replaced by {@link #setPasswordHash(String)} - */ - @Deprecated(since = "1.3.0", forRemoval = true) - public void setPasswordSha256(final String passwordSha256) - { - LoggerFactory.getLogger(this.getClass()) - .error("Detected usage of deprecated property 'passwordSha256' that will be removed soon." - + " Use 'passwordHash' instead!"); - this.setPasswordHash(passwordSha256); - } - public String getPasswordHash() { return this.passwordHash;