From 9fa699ed8371960e9f2e5e57cb0378a398b95522 Mon Sep 17 00:00:00 2001
From: Andreas Svanberg <andreass@dsv.su.se>
Date: Thu, 20 Mar 2025 06:44:13 +0100
Subject: [PATCH 1/3] Upgrade Spring Boot (#140)

The new version has upgraded `json-smart` so the override is no longer necessary.

## How to test
1. Log in and click around as different users
2. Enable Daisy integration (both `DEV` and `DAISY-INTEGRATION` Maven profiles)
3. Go to "Admin / Users / Import" and import someone (verify JSON parsing)

Reviewed-on: https://gitea.dsv.su.se/DMC/scipro/pulls/140
Reviewed-by: Nico Athanassiadis <nico@dsv.su.se>
Co-authored-by: Andreas Svanberg <andreass@dsv.su.se>
Co-committed-by: Andreas Svanberg <andreass@dsv.su.se>
---
 pom.xml | 22 +---------------------
 1 file changed, 1 insertion(+), 21 deletions(-)

diff --git a/pom.xml b/pom.xml
index 8567faf79d..617362a163 100755
--- a/pom.xml
+++ b/pom.xml
@@ -28,12 +28,7 @@
         <querydsl.version>5.0.0</querydsl.version>
         <poi.version>5.4.0</poi.version>
 
-        <!--
-            When updating spring-boot check if the transitive dependency on json-smart has been
-            updated to 2.5.2 or later.
-            If so, remove the dependency managed version of json-smart
-        -->
-        <spring.boot.version>3.4.1</spring.boot.version>
+        <spring.boot.version>3.4.3</spring.boot.version>
         <springdoc.openapi.version>2.8.3</springdoc.openapi.version>
 
         <!-- Database stuff -->
@@ -115,21 +110,6 @@
                 <version>32.0.1-jre</version>
             </dependency>
 
-            <dependency>
-                <!--
-                2.5.1 is brought in transitively by
-                  spring-boot-starter-oauth2-client
-                    spring-security-oauth2-client
-                      oauth2-oidc-sdk
-                        json-smart
-                it has a known security vulnerability that's fixed in 2.5.2
-                should be removed when spring-boot-starter-oauth2-client is updated
-                -->
-                <groupId>net.minidev</groupId>
-                <artifactId>json-smart</artifactId>
-                <version>2.5.2</version>
-            </dependency>
-
             <dependency>
                 <groupId>org.apache.poi</groupId>
                 <artifactId>poi</artifactId>

From e95421b8f2f6cb232f8ce735d9be963a07e0be60 Mon Sep 17 00:00:00 2001
From: Andreas Svanberg <andreass@dsv.su.se>
Date: Tue, 25 Mar 2025 08:45:25 +0100
Subject: [PATCH 2/3] Use OAuth 2.0 Token Introspection during log in (#141)

Currently, it uses an endpoint similar to OpenID Connect UserInfo but with some differences. The endpoint does not require the "openid" scope for example. There is an ongoing effort to replace the OAuth 2.0 authorization server with a more standard compliant one which would break the endpoint (since it would require the "openid" scope). It is currently not possible to request the "openid" scope to future-proof since Spring would act differently if that scope is present and assume full OpenID Connect. That leads to requiring an id token to have been issued which the current authorization server does not do.

To get around this the implementation is changed to use a standard compliant Token Introspection endpoint to get access to the subject of the access token (which is the only part that's necessary right now). Since the endpoint is standard compliant it will work with any future authorization server.

It may be necessary to run `docker compose up --build` to get the latest version of the Toker containers.

Reviewed-on: https://gitea.dsv.su.se/DMC/scipro/pulls/141
Reviewed-by: Nico Athanassiadis <nico@dsv.su.se>
Co-authored-by: Andreas Svanberg <andreass@dsv.su.se>
Co-committed-by: Andreas Svanberg <andreass@dsv.su.se>
---
 compose-branch-deploy.yaml                    |  2 +-
 ...enIntrospectionRequestEntityConverter.java | 42 +++++++++++++++++++
 .../dsv/scipro/war/WicketConfiguration.java   | 16 +++++++
 war/src/main/resources/application.properties |  2 +-
 4 files changed, 60 insertions(+), 2 deletions(-)
 create mode 100644 war/src/main/java/se/su/dsv/scipro/war/TokenIntrospectionRequestEntityConverter.java

diff --git a/compose-branch-deploy.yaml b/compose-branch-deploy.yaml
index 05ba4bd181..68694cc0b6 100644
--- a/compose-branch-deploy.yaml
+++ b/compose-branch-deploy.yaml
@@ -15,7 +15,7 @@ services:
       - JDBC_DATABASE_PASSWORD=scipro
       - OAUTH2_AUTHORIZATION_URI=https://oauth2-${VHOST}/authorize
       - OAUTH2_TOKEN_URI=https://oauth2-${VHOST}/exchange
-      - OAUTH2_USER_INFO_URI=https://oauth2-${VHOST}/verify
+      - OAUTH2_USER_INFO_URI=https://oauth2-${VHOST}/introspect
       - OAUTH2_CLIENT_ID=scipro_client
       - OAUTH2_CLIENT_SECRET=scipro_secret
       - OAUTH2_RESOURCE_SERVER_ID=scipro_api_client
diff --git a/war/src/main/java/se/su/dsv/scipro/war/TokenIntrospectionRequestEntityConverter.java b/war/src/main/java/se/su/dsv/scipro/war/TokenIntrospectionRequestEntityConverter.java
new file mode 100644
index 0000000000..7b5cfa9e96
--- /dev/null
+++ b/war/src/main/java/se/su/dsv/scipro/war/TokenIntrospectionRequestEntityConverter.java
@@ -0,0 +1,42 @@
+package se.su.dsv.scipro.war;
+
+import java.net.URI;
+import java.util.Collections;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.RequestEntity;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.util.UriComponentsBuilder;
+
+public class TokenIntrospectionRequestEntityConverter implements Converter<OAuth2UserRequest, RequestEntity<?>> {
+
+    private static final MediaType FORM_URL_ENCODED = MediaType.valueOf(
+        MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8"
+    );
+
+    @Override
+    public RequestEntity<?> convert(OAuth2UserRequest userRequest) {
+        ClientRegistration clientRegistration = userRequest.getClientRegistration();
+
+        URI uri = UriComponentsBuilder.fromUriString(
+            clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri()
+        )
+            .build()
+            .toUri();
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());
+        headers.setAccept(Collections.singletonList(MediaType.ALL));
+        headers.setContentType(FORM_URL_ENCODED);
+
+        MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
+        formParameters.add(OAuth2ParameterNames.TOKEN, userRequest.getAccessToken().getTokenValue());
+        return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri);
+    }
+}
diff --git a/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java b/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java
index 90b0b70a61..064728fcbc 100644
--- a/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java
+++ b/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java
@@ -14,6 +14,7 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.core.annotation.Order;
 import org.springframework.security.config.Customizer;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
 import org.springframework.security.web.SecurityFilterChain;
 import se.su.dsv.scipro.SciProApplication;
 import se.su.dsv.scipro.crosscutting.ForwardPhase2Feedback;
@@ -67,6 +68,21 @@ public class WicketConfiguration {
         return http.build();
     }
 
+    // Stop gap measure to switch to Token Introspection instead of OIDC UserInfo
+    // endpoint. This is necessary because the UserInfo endpoint will in soon require
+    // the "openid" scope, which is not granted to our clients. Unfortunately we can't
+    // request the scope because that makes Spring require an id token in the token
+    // exchange which is not granted at the moment.
+    //
+    // Once a new authorization server is in place we can remove this bean and use
+    // straight up id tokens with "openid" scope.
+    @Bean
+    public DefaultOAuth2UserService defaultOAuth2UserService() {
+        DefaultOAuth2UserService defaultOAuth2UserService = new DefaultOAuth2UserService();
+        defaultOAuth2UserService.setRequestEntityConverter(new TokenIntrospectionRequestEntityConverter());
+        return defaultOAuth2UserService;
+    }
+
     @Bean
     public CurrentUserFromSpringSecurity currentUserFromSpringSecurity(
         UserService userService,
diff --git a/war/src/main/resources/application.properties b/war/src/main/resources/application.properties
index 0df6b375e6..7b0b3f2105 100644
--- a/war/src/main/resources/application.properties
+++ b/war/src/main/resources/application.properties
@@ -22,7 +22,7 @@ spring.security.oauth2.resourceserver.opaquetoken.client-secret=${OAUTH2_RESOURC
 spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=${OAUTH2_RESOURCE_SERVER_INTROSPECTION_URI:http://localhost:59733/introspect}
 
 # Log in via local OAuth 2 authorization server
-spring.security.oauth2.client.provider.docker.user-info-uri=${OAUTH2_USER_INFO_URI:http://localhost:59734/verify}
+spring.security.oauth2.client.provider.docker.user-info-uri=${OAUTH2_USER_INFO_URI:http://localhost:59734/introspect}
 spring.security.oauth2.client.provider.docker.user-name-attribute=sub
 spring.security.oauth2.client.provider.docker.token-uri=${OAUTH2_TOKEN_URI:http://localhost:59734/exchange}
 spring.security.oauth2.client.provider.docker.authorization-uri=${OAUTH2_AUTHORIZATION_URI:http://localhost:59734/authorize}

From d2e5043c959504faad7fd307e9312e89c3c1a96d Mon Sep 17 00:00:00 2001
From: Andreas Svanberg <andreass@dsv.su.se>
Date: Fri, 28 Mar 2025 07:18:16 +0100
Subject: [PATCH 3/3] Fix CVE-2025-22228 (#143)

See https://spring.io/security/cve-2025-22228

Fixes #142

Reviewed-on: https://gitea.dsv.su.se/DMC/scipro/pulls/143
Reviewed-by: Nico Athanassiadis <nico@dsv.su.se>
Co-authored-by: Andreas Svanberg <andreass@dsv.su.se>
Co-committed-by: Andreas Svanberg <andreass@dsv.su.se>
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 617362a163..4d7f8a5d4a 100755
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
         <querydsl.version>5.0.0</querydsl.version>
         <poi.version>5.4.0</poi.version>
 
-        <spring.boot.version>3.4.3</spring.boot.version>
+        <spring.boot.version>3.4.4</spring.boot.version>
         <springdoc.openapi.version>2.8.3</springdoc.openapi.version>
 
         <!-- Database stuff -->