Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
36ff669
WIP: OpenSAML 5.1.6 upgrade
strehle Apr 14, 2026
1386e60
Merge remote-tracking branch 'origin/develop' into opensaml5-1-6-update
strehle Apr 17, 2026
4453bad
rebase back to spring security
strehle Apr 17, 2026
df8f4a1
fix logout
strehle Apr 17, 2026
4daa2e2
fix logout - wrong classes
strehle Apr 18, 2026
dd224a0
cleanup reference documentations
strehle Apr 18, 2026
90a50a7
revert a agent change before and fix deprecate all toghether to final…
strehle Apr 18, 2026
c3e5cb9
cleanup
strehle Apr 18, 2026
6ce8f97
more cleanups
strehle Apr 18, 2026
fadb026
comment changes
strehle Apr 18, 2026
12b5261
Merge remote-tracking branch 'origin/opensaml5-1-6-update' into opens…
strehle Apr 18, 2026
b9907ff
Respect skipSslVerification flag in TLS hostname verification logic
duanemay Apr 16, 2026
65b7e2a
migrate to boot 4 without jackson 3
gdgenchev Mar 25, 2026
daed215
fix dependencies
gdgenchev Mar 25, 2026
5b2a089
fix OAuth2RestTemplate incomatibilities
gdgenchev Mar 25, 2026
c5baff1
Replace AntPathRequestMatcher with PathPatternRequestMatcher for Spri…
gdgenchev Mar 25, 2026
c61c453
Replace removed/moved Spring classes for Spring Boot 4
gdgenchev Mar 25, 2026
c345dfa
Update OpenSaml4* classes to OpenSaml5* for Spring Security 7
gdgenchev Mar 25, 2026
dd91fa4
Fix AuthorizationManager API for Spring Security 7
gdgenchev Mar 25, 2026
cb9d1d2
Fix multiple Spring Boot 4 / Spring Security 7 API changes
gdgenchev Mar 25, 2026
b2f6a12
Fix remaining Spring 6/7 API signature changes
gdgenchev Mar 25, 2026
0123ac5
Fix MediaType sorting to use MimeTypeUtils.sortBySpecificity
gdgenchev Mar 25, 2026
6207ce2
Fix final compilation errors for Spring Boot 4
gdgenchev Mar 25, 2026
d989aaa
WIP: Attempt to fix ErrorPage import in uaa module
gdgenchev Mar 25, 2026
af020a8
Fix ErrorPage and connector configuration for Spring Boot 4
gdgenchev Mar 25, 2026
87e43c5
fix: add explicit version for spring-retry dependency
gdgenchev Mar 25, 2026
94c734d
fix: update ResponseErrorHandler anonymous implementations for Spring 6
gdgenchev Mar 25, 2026
bbd9f87
fix: batch update test files for Spring Security 7 API changes
gdgenchev Mar 25, 2026
b04fa93
fix: update test method signatures for Spring 6 API changes
gdgenchev Mar 25, 2026
7511f43
fix: correct Map.containsKey and remove deprecated Spring MVC method
gdgenchev Mar 25, 2026
0868781
fix: update test assertions for Spring Security 7 and AssertJ
gdgenchev Mar 25, 2026
e9feda4
fix: complete server module test compilation for Spring Boot 4
gdgenchev Mar 25, 2026
a8060c7
fix: complete UAA module test compilation for Spring Boot 4
gdgenchev Mar 25, 2026
b8384bd
fix: downgrade thymeleaf-layout-dialect to 3.3.0 for compatibility
gdgenchev Mar 25, 2026
fd5274f
fix: update ResponseErrorHandler signatures in OAuth2ErrorHandlerTests
gdgenchev Mar 25, 2026
a471d7e
fix circular dependency between DataSource and Flyway in Spring Boot 4
gdgenchev Mar 25, 2026
3e6206c
fix Mockito nested test class issue in IdentityZoneHolderTest
gdgenchev Mar 25, 2026
7c063fe
fix: update CSRF redirect URL assertions for Spring Security 7
gdgenchev Mar 26, 2026
6f17f50
fix: adapt tests for Spring Framework 7 MockHttpServletResponse changes
gdgenchev Mar 26, 2026
5dc8ba2
fix: upgrade thymeleaf-layout-dialect to 4.0.1
gdgenchev Mar 26, 2026
21c6fd6
migrate bc to non-fips to just check..
gdgenchev Mar 27, 2026
4dd81fe
fix: correct malformed PEM certificate in KeyWithCertTest
gdgenchev Mar 27, 2026
e3edabe
fix: change @Nullable boolean params to Boolean to fix Spring Boot 4 …
gdgenchev Mar 30, 2026
0ccfda5
fix: use RedirectAttributes for Spring 6 redirect model passing
gdgenchev Mar 30, 2026
6b39d09
fix: handle servlet context path in UaaMetricsFilter path matching
gdgenchev Mar 30, 2026
c6bbf56
fix: use single braces for PathPattern variable capture
gdgenchev Mar 30, 2026
c85cf4d
fix missing begin slash in request matcher
gdgenchev Mar 31, 2026
ceb8a2c
fix: complete servlet context path handling in UaaMetricsFilter
gdgenchev Mar 31, 2026
d123214
fix: set requestURI in PasswordChangeUiRequiredFilterTest
gdgenchev Mar 31, 2026
0dde80f
fix: handle empty password validation in Spring Security 7
gdgenchev Mar 31, 2026
d712b23
fix: set requestURI for PathPatternRequestMatcher in UaaRelyingPartyR…
gdgenchev Mar 31, 2026
f142b62
fix some Nonnulls
gdgenchev Apr 18, 2026
48657ee
fix http client timeout
gdgenchev Apr 18, 2026
08a0963
Revert "migrate bc to non-fips to just check.."
gdgenchev Apr 18, 2026
43ae15f
fix classpath error
gdgenchev Apr 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ subprojects {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'org.opensaml' && details.requested.name.startsWith("opensaml-")) {
details.useVersion "${versions.opensaml}"
details.because 'Spring Security 5.8.x allows OpenSAML 3 or 4. OpenSAML 3 has reached its end-of-life. Spring Security 6 drops support for 3, using 4.'
details.because 'Pinning all opensaml modules to the same version for OpenSAML 5 migration.'
}
}
}
Expand Down
29 changes: 24 additions & 5 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ versions.apacheDsVersion = "2.0.0.AM27"
versions.bouncyCastleFipsVersion = "2.1.2"
versions.bouncyCastlePkixFipsVersion = "2.1.11"
versions.bouncyCastleTlsFipsVersion = "2.1.23"
versions.springBootVersion = "3.5.13"
versions.springBootVersion = "4.0.4"
versions.guavaVersion = "33.6.0-jre"
versions.seleniumVersion = "4.43.0"
versions.braveVersion = "6.3.1"
versions.opensaml = "4.3.2"
// OpenSAML 5.2.x pulls non-FIPS classes; stay on 5.1.x until resolved
versions.opensaml = "5.1.6"

// Versions we're overriding from the Spring Boot Bom (Dependabot does not issue PRs to bump these versions, so we need to manually bump them)
ext["selenium.version"] = "${versions.seleniumVersion}" // Selenium for integration tests only
Expand Down Expand Up @@ -73,19 +74,37 @@ libraries.slf4jImpl = "org.apache.logging.log4j:log4j-slf4j2-impl"
libraries.snakeyaml = "org.yaml:snakeyaml"
libraries.springBeans = "org.springframework:spring-beans"
libraries.springBootBom = "org.springframework.boot:spring-boot-dependencies:${versions.springBootVersion}"
libraries.springBootJackson2 = "org.springframework.boot:spring-boot-jackson2"
libraries.springBootStarter = "org.springframework.boot:spring-boot-starter"
libraries.springBootStarterAspectj = "org.springframework.boot:spring-boot-starter-aspectj"
libraries.springBootStarterFlyway = "org.springframework.boot:spring-boot-starter-flyway"
libraries.springBootStarterFlywayTest = "org.springframework.boot:spring-boot-starter-flyway-test"
libraries.springBootStarterLdap = "org.springframework.boot:spring-boot-starter-ldap"
libraries.springBootStarterLdapTest = "org.springframework.boot:spring-boot-starter-ldap-test"
libraries.springBootStarterLog4j2 = "org.springframework.boot:spring-boot-starter-log4j2"
libraries.springBootStarterMail = "org.springframework.boot:spring-boot-starter-mail"
libraries.springBootStarterMailTest = "org.springframework.boot:spring-boot-starter-mail-test"
libraries.springBootStarterRestclient = "org.springframework.boot:spring-boot-starter-restclient"
libraries.springBootStarterRestclientTest = "org.springframework.boot:spring-boot-starter-restclient-test"
libraries.springBootStarterSecurity = "org.springframework.boot:spring-boot-starter-security"
libraries.springBootStarterSecuritySaml2 = "org.springframework.boot:spring-boot-starter-security-saml2"
libraries.springBootStarterSecuritySaml2Test = "org.springframework.boot:spring-boot-starter-security-saml2-test"
libraries.springBootStarterSecurityTest = "org.springframework.boot:spring-boot-starter-security-test"
libraries.springBootStarterSessionJdbc = "org.springframework.boot:spring-boot-starter-session-jdbc"
libraries.springBootStarterTest = "org.springframework.boot:spring-boot-starter-test"
libraries.springBootStarterThymeleaf = "org.springframework.boot:spring-boot-starter-thymeleaf"
libraries.springBootStarterThymeleafTest = "org.springframework.boot:spring-boot-starter-thymeleaf-test"
libraries.springBootStarterTomcat = "org.springframework.boot:spring-boot-starter-tomcat"
libraries.springBootStarterValidation = "org.springframework.boot:spring-boot-starter-validation"
libraries.springBootStarterValidationTest = "org.springframework.boot:spring-boot-starter-validation-test"
libraries.springBootStarterWeb = "org.springframework.boot:spring-boot-starter-web"
libraries.springBootStarterMail = "org.springframework.boot:spring-boot-starter-mail"
libraries.springContext = "org.springframework:spring-context"
libraries.springContextSupport = "org.springframework:spring-context-support"
libraries.springJdbc = "org.springframework:spring-jdbc"
libraries.springLdapCore = "org.springframework.ldap:spring-ldap-core"
libraries.springRestdocs = "org.springframework.restdocs:spring-restdocs-mockmvc"
libraries.springdocOpenapi = "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.17"
libraries.springRetry = "org.springframework.retry:spring-retry"
libraries.springRetry = "org.springframework.retry:spring-retry:2.0.12"
libraries.springSecurityConfig = "org.springframework.security:spring-security-config"
libraries.springSecurityCore = "org.springframework.security:spring-security-core"
libraries.springSecurityLdap = "org.springframework.security:spring-security-ldap"
Expand All @@ -98,7 +117,7 @@ libraries.springTx = "org.springframework:spring-tx"
libraries.springWeb = "org.springframework:spring-web"
libraries.springWebMvc = "org.springframework:spring-webmvc"
libraries.statsdClient = "com.timgroup:java-statsd-client:3.1.0"
libraries.thymeleafDialect = "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
libraries.thymeleafDialect = "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:4.0.1"
libraries.thymeleafExtrasSpringSecurity = "org.thymeleaf.extras:thymeleaf-extras-springsecurity6"
libraries.thymeLeaf = "org.thymeleaf:thymeleaf"
libraries.thymeleafSpring = "org.thymeleaf:thymeleaf-spring6"
Expand Down
3 changes: 3 additions & 0 deletions metrics-data/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ processResources {
//https://www.pivotaltracker.com/story/show/74344574
filter { line -> line.contains('${project.artifactId}') ? line.replace('${project.artifactId}', 'cloudfoundry-identity-metrics-data') : line }
}
tasks.withType(Test).configureEach {
useJUnitPlatform()
}
14 changes: 14 additions & 0 deletions model/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,19 @@ dependencies {
testImplementation(libraries.junit5JupiterParams)

testImplementation(libraries.jsonAssert)
testImplementation(libraries.springBootStarterRestclientTest) {
exclude(group: "org.springframework.boot", module: "spring-boot-starter-jackson")
}
testImplementation(libraries.springBootStarterSecurityTest)

implementation(libraries.nimbusJwt)
implementation "org.jspecify:jspecify:1.0.0"
implementation(libraries.springBootStarterRestclient) {
exclude(group: "org.springframework.boot", module: "spring-boot-starter-jackson")
}
implementation(libraries.springBootStarterSecurity)
implementation(libraries.springBootStarterValidation)
implementation(libraries.springBootJackson2)
}

apply(from: file("build_properties.gradle"))
Expand All @@ -40,3 +51,6 @@ processResources {
integrationTest {}.onlyIf { //disable since we don't have any
false
}
tasks.withType(Test).configureEach {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import java.util.Optional;

import org.springframework.lang.Nullable;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.jspecify.annotations.Nullable;

/**
* An entity that can have an alias in another identity zone.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,12 @@ protected ClientHttpRequest createRequest(URI uri, HttpMethod method) throws IOE
}

@Override
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
protected <T> T doExecute(URI url, String uriTemplate, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
OAuth2AccessToken accessToken = context.getAccessToken();
RuntimeException rethrow = null;
try {
return super.doExecute(url, method, requestCallback, responseExtractor);
return super.doExecute(url, uriTemplate, method, requestCallback, responseExtractor);
}
catch (AccessTokenRequiredException | OAuth2AccessDeniedException e) {
rethrow = e;
Expand All @@ -146,7 +146,7 @@ protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCal
if (accessToken != null && retryBadAccessTokens) {
context.setAccessToken(null);
try {
return super.doExecute(url, method, requestCallback, responseExtractor);
return super.doExecute(url, uriTemplate, method, requestCallback, responseExtractor);
}
catch (InvalidTokenException e) {
// Don't reveal the token value in case it is logged
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public String obtainAuthorizationCode(OAuth2ProtectedResourceDetails details, Ac
ResponseExtractor<ResponseEntity<Void>> extractor = new ResponseExtractor<>() {
@Override
public ResponseEntity<Void> extractData(ClientHttpResponse response) throws IOException {
if (response.getHeaders().containsKey("Set-Cookie")) {
if (response.getHeaders().containsHeader("Set-Cookie")) {
copy.setCookie(response.getHeaders().getFirst("Set-Cookie"));
}
return delegate.extractData(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.cloudfoundry.identity.uaa.oauth.client.resource.OAuth2ProtectedResourceDetails;
import org.cloudfoundry.identity.uaa.oauth.common.OAuth2AccessToken;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageConversionException;
Expand All @@ -21,6 +22,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -70,11 +72,12 @@ public boolean hasError(ClientHttpResponse response) throws IOException {
|| this.errorHandler.hasError(response);
}

public void handleError(final ClientHttpResponse response) throws IOException {
public void handleError(URI url, HttpMethod method, final ClientHttpResponse response) throws IOException {
if (!HttpStatus.Series.CLIENT_ERROR.equals(HttpStatus.resolve(response.getStatusCode().value()).series())) {
// We should only care about 400 level errors. Ex: A 500 server error shouldn't
// be an oauth related error.
errorHandler.handleError(response);
// TODO: Replace (URI) null and (HttpMethod) null with actual values
errorHandler.handleError((URI) null, (HttpMethod) null, response);
} else {
// Need to use buffered response because input stream may need to be consumed multiple times.
ClientHttpResponse bufferedResponse = new ClientHttpResponse() {
Expand Down Expand Up @@ -141,7 +144,8 @@ public int getRawStatusCode() throws IOException {
}

// then delegate to the custom handler
errorHandler.handleError(bufferedResponse);
// TODO: Replace (URI) null and (HttpMethod) null with actual values
errorHandler.handleError((URI) null, (HttpMethod) null, bufferedResponse);
}
catch (InvalidTokenException ex) {
// Special case: an invalid token can be renewed so tell the caller what to do
Expand All @@ -155,7 +159,8 @@ public int getRawStatusCode() throws IOException {
}
// This is not an exception that is really understood, so allow our delegate
// to handle it in a non-oauth way
errorHandler.handleError(bufferedResponse);
// TODO: Replace (URI) null and (HttpMethod) null with actual values
errorHandler.handleError((URI) null, (HttpMethod) null, bufferedResponse);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
Expand Down Expand Up @@ -132,7 +133,7 @@ protected OAuth2AccessToken retrieveToken(AccessTokenRequest request, OAuth2Prot
ResponseExtractor<OAuth2AccessToken> extractor = new ResponseExtractor<>() {
@Override
public OAuth2AccessToken extractData(ClientHttpResponse response) throws IOException {
if (response.getHeaders().containsKey("Set-Cookie")) {
if (response.getHeaders().containsHeader("Set-Cookie")) {
copy.setCookie(response.getHeaders().getFirst("Set-Cookie"));
}
return delegate.extractData(response);
Expand Down Expand Up @@ -232,7 +233,7 @@ private class AccessTokenErrorHandler extends DefaultResponseErrorHandler {

@SuppressWarnings("unchecked")
@Override
public void handleError(ClientHttpResponse response) throws IOException {
public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
for (HttpMessageConverter<?> converter : messageConverters) {
if (converter.canRead(OAuth2Exception.class, response.getHeaders().getContentType())) {
OAuth2Exception ex;
Expand All @@ -246,7 +247,8 @@ public void handleError(ClientHttpResponse response) throws IOException {
throw ex;
}
}
super.handleError(response);
// TODO: Replace (URI) null and (HttpMethod) null with actual values
super.handleError((URI) null, (HttpMethod) null, response);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import org.cloudfoundry.identity.uaa.approval.Approval;
import org.cloudfoundry.identity.uaa.impl.JsonDateSerializer;
import org.cloudfoundry.identity.uaa.scim.impl.ScimUserJsonDeserializer;
import org.springframework.lang.NonNull;
import org.jspecify.annotations.NonNull;
import org.springframework.util.Assert;

import com.fasterxml.jackson.annotation.JsonIgnore;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.jspecify.annotations.Nullable;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.lang.Nullable;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ void noRetryAccessDeniedExceptionForNoExistingToken() {
throw new AccessTokenRequiredException(resource);
});
assertThatExceptionOfType(AccessTokenRequiredException.class).isThrownBy(() ->
restTemplate.doExecute(new URI("https://foo"), HttpMethod.GET, new NullRequestCallback(),
restTemplate.doExecute(new URI("https://foo"), null, HttpMethod.GET, new NullRequestCallback(),
new SimpleResponseExtractor()));
}

Expand All @@ -183,7 +183,7 @@ public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
return request;
}
});
Boolean result = restTemplate.doExecute(new URI("https://foo"), HttpMethod.GET, new NullRequestCallback(),
Boolean result = restTemplate.doExecute(new URI("https://foo"), null, HttpMethod.GET, new NullRequestCallback(),
new SimpleResponseExtractor());
assertThat(result).isTrue();
}
Expand Down
Loading
Loading