diff --git a/build.gradle b/build.gradle index 14b06caf377..1ef4d677833 100644 --- a/build.gradle +++ b/build.gradle @@ -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.' } } } diff --git a/dependencies.gradle b/dependencies.gradle index f8f8c63a93a..8b0bb596845 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -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 @@ -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" @@ -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" diff --git a/metrics-data/build.gradle b/metrics-data/build.gradle index c44596e7fe7..3b065a9814a 100644 --- a/metrics-data/build.gradle +++ b/metrics-data/build.gradle @@ -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() +} diff --git a/model/build.gradle b/model/build.gradle index 086b4838728..9fd3b92fd9f 100644 --- a/model/build.gradle +++ b/model/build.gradle @@ -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")) @@ -40,3 +51,6 @@ processResources { integrationTest {}.onlyIf { //disable since we don't have any false } +tasks.withType(Test).configureEach { + useJUnitPlatform() +} diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/EntityWithAlias.java b/model/src/main/java/org/cloudfoundry/identity/uaa/EntityWithAlias.java index 59fbd6af5bc..b0e1007afdf 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/EntityWithAlias.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/EntityWithAlias.java @@ -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. diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/OAuth2RestTemplate.java b/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/OAuth2RestTemplate.java index eb34639aa73..9e4ffe5088a 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/OAuth2RestTemplate.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/OAuth2RestTemplate.java @@ -129,12 +129,12 @@ protected ClientHttpRequest createRequest(URI uri, HttpMethod method) throws IOE } @Override - protected T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, + protected T doExecute(URI url, String uriTemplate, HttpMethod method, RequestCallback requestCallback, ResponseExtractor 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; @@ -146,7 +146,7 @@ protected 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 diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/grant/AuthorizationCodeAccessTokenProvider.java b/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/grant/AuthorizationCodeAccessTokenProvider.java index f257bd17f0f..1f3954f0913 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/grant/AuthorizationCodeAccessTokenProvider.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/grant/AuthorizationCodeAccessTokenProvider.java @@ -80,7 +80,7 @@ public String obtainAuthorizationCode(OAuth2ProtectedResourceDetails details, Ac ResponseExtractor> extractor = new ResponseExtractor<>() { @Override public ResponseEntity 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); diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/http/OAuth2ErrorHandler.java b/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/http/OAuth2ErrorHandler.java index 4bd4a604461..7e382b3d921 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/http/OAuth2ErrorHandler.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/client/http/OAuth2ErrorHandler.java @@ -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; @@ -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; @@ -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() { @@ -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 @@ -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); } } } diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/OAuth2AccessTokenSupport.java b/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/OAuth2AccessTokenSupport.java index 939ce95c958..e2a75936df3 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/OAuth2AccessTokenSupport.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/OAuth2AccessTokenSupport.java @@ -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; @@ -132,7 +133,7 @@ protected OAuth2AccessToken retrieveToken(AccessTokenRequest request, OAuth2Prot ResponseExtractor 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); @@ -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; @@ -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); } } diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUser.java b/model/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUser.java index 7801586de59..dd5f3e0042f 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUser.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUser.java @@ -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; diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/zone/UserConfig.java b/model/src/main/java/org/cloudfoundry/identity/uaa/zone/UserConfig.java index 0b132a077d3..81c5c8117e8 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/zone/UserConfig.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/zone/UserConfig.java @@ -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 { diff --git a/model/src/test/java/org/cloudfoundry/identity/uaa/oauth/client/OAuth2RestTemplateTests.java b/model/src/test/java/org/cloudfoundry/identity/uaa/oauth/client/OAuth2RestTemplateTests.java index 330a6dfb7d3..7adf608b425 100644 --- a/model/src/test/java/org/cloudfoundry/identity/uaa/oauth/client/OAuth2RestTemplateTests.java +++ b/model/src/test/java/org/cloudfoundry/identity/uaa/oauth/client/OAuth2RestTemplateTests.java @@ -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())); } @@ -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(); } diff --git a/model/src/test/java/org/cloudfoundry/identity/uaa/oauth/client/http/OAuth2ErrorHandlerTests.java b/model/src/test/java/org/cloudfoundry/identity/uaa/oauth/client/http/OAuth2ErrorHandlerTests.java index 04ab7af14b9..c2de09243d6 100644 --- a/model/src/test/java/org/cloudfoundry/identity/uaa/oauth/client/http/OAuth2ErrorHandlerTests.java +++ b/model/src/test/java/org/cloudfoundry/identity/uaa/oauth/client/http/OAuth2ErrorHandlerTests.java @@ -10,6 +10,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpOutputMessage; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -27,6 +28,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; @@ -105,7 +108,7 @@ void handleErrorClientHttpResponse() { HttpHeaders headers = new HttpHeaders(); headers.set("www-authenticate", "Bearer error=foo"); ClientHttpResponse response = new TestClientHttpResponse(headers, 401); - assertThatThrownBy(() -> handler.handleError(response)) + assertThatThrownBy(() -> handler.handleError(URI.create("http://test"), HttpMethod.GET, response)) .isInstanceOf(HttpClientErrorException.class) .hasMessageContaining("401 Unauthorized"); } @@ -115,7 +118,7 @@ void handleErrorWithInvalidToken() { HttpHeaders headers = new HttpHeaders(); headers.set("www-authenticate", "Bearer error=\"invalid_token\", description=\"foo\""); ClientHttpResponse response = new TestClientHttpResponse(headers, 401); - assertThatThrownBy(() -> handler.handleError(response)) + assertThatThrownBy(() -> handler.handleError(URI.create("http://test"), HttpMethod.GET, response)) .isInstanceOf(AccessTokenRequiredException.class) .hasMessageContaining("OAuth2 access denied"); } @@ -128,13 +131,13 @@ public boolean hasError(ClientHttpResponse response) throws IOException { return true; } - public void handleError(ClientHttpResponse response) throws IOException { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) throws IOException { throw new RuntimeException("planned"); } }, resource); HttpHeaders headers = new HttpHeaders(); ClientHttpResponse response = new TestClientHttpResponse(headers, 401); - assertThatThrownBy(() -> handler.handleError(response)) + assertThatThrownBy(() -> handler.handleError(URI.create("http://test"), HttpMethod.GET, response)) .isInstanceOf(RuntimeException.class) .hasMessageContaining("planned"); } @@ -144,7 +147,7 @@ void handle500Error() { HttpHeaders headers = new HttpHeaders(); ClientHttpResponse response = new TestClientHttpResponse(headers, 500); assertThatExceptionOfType(HttpServerErrorException.class).isThrownBy(() -> - handler.handleError(response)); + handler.handleError(URI.create("http://test"), HttpMethod.GET, response)); } @Test @@ -152,7 +155,7 @@ void handleGeneric400Error() { HttpHeaders headers = new HttpHeaders(); ClientHttpResponse response = new TestClientHttpResponse(headers, 400); assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> - handler.handleError(response)); + handler.handleError(URI.create("http://test"), HttpMethod.GET, response)); } @Test @@ -160,7 +163,7 @@ void handleGeneric403Error() { HttpHeaders headers = new HttpHeaders(); ClientHttpResponse response = new TestClientHttpResponse(headers, 403); assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> - handler.handleError(response)); + handler.handleError(URI.create("http://test"), HttpMethod.GET, response)); } // See https://github.com/spring-projects/spring-security-oauth/issues/387 @@ -172,7 +175,7 @@ void handleGeneric403ErrorWithBody() { new ByteArrayInputStream("{}".getBytes())); handler = new OAuth2ErrorHandler(new DefaultResponseErrorHandler(), resource); assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> - handler.handleError(response)); + handler.handleError(URI.create("http://test"), HttpMethod.GET, response)); } @Test @@ -183,7 +186,7 @@ public boolean hasError(ClientHttpResponse response) throws IOException { return true; } - public void handleError(ClientHttpResponse response) throws IOException { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) throws IOException { InputStream body = response.getBody(); byte[] buf = new byte[appSpecificBodyContent.length()]; int readResponse = body.read(buf); @@ -197,7 +200,7 @@ public void handleError(ClientHttpResponse response) throws IOException { headers.set("Content-Type", "application/json"); InputStream appSpecificErrorBody = new ByteArrayInputStream(appSpecificBodyContent.getBytes("UTF-8")); ClientHttpResponse response = new TestClientHttpResponse(headers, 400, appSpecificErrorBody); - assertThatThrownBy(() -> handler.handleError(response)) + assertThatThrownBy(() -> handler.handleError(URI.create("http://test"), HttpMethod.GET, response)) .isInstanceOf(RuntimeException.class) .hasMessageContaining("planned"); } @@ -210,7 +213,7 @@ void handleErrorWithMissingHeader() throws IOException { when(response.getBody()).thenReturn(new ByteArrayInputStream(new byte[0])); when(response.getStatusText()).thenReturn(HttpStatus.BAD_REQUEST.toString()); assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> - handler.handleError(response)); + handler.handleError(URI.create("http://test"), HttpMethod.GET, response)); } // gh-875 @@ -222,7 +225,7 @@ void handleErrorWhenAccessDeniedMessageAndStatus400ThenThrowsUserDeniedAuthoriza headers.setContentType(MediaType.APPLICATION_JSON); ClientHttpResponse response = new TestClientHttpResponse(headers, 400, messageBody); assertThatExceptionOfType(UserDeniedAuthorizationException.class).isThrownBy(() -> - handler.handleError(response)); + handler.handleError(URI.create("http://test"), HttpMethod.GET, response)); } // gh-875 @@ -234,7 +237,7 @@ void handleErrorWhenAccessDeniedMessageAndStatus403ThenThrowsOAuth2AccessDeniedE headers.setContentType(MediaType.APPLICATION_JSON); ClientHttpResponse response = new TestClientHttpResponse(headers, 403, messageBody); assertThatExceptionOfType(OAuth2AccessDeniedException.class).isThrownBy(() -> - handler.handleError(response)); + handler.handleError(URI.create("http://test"), HttpMethod.GET, response)); } @Test @@ -273,6 +276,6 @@ public void write(Object o, MediaType contentType, HttpOutputMessage outputMessa InputStream appSpecificErrorBody = new ByteArrayInputStream(appSpecificBodyContent.getBytes("UTF-8")); ClientHttpResponse response = new TestClientHttpResponse(headers, 401, appSpecificErrorBody); assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> - handler.handleError(response)); + handler.handleError(URI.create("http://test"), HttpMethod.GET, response)); } } diff --git a/server/build.gradle b/server/build.gradle index cfe490910c4..ab1c9e670fb 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -108,8 +108,32 @@ dependencies { testImplementation(libraries.guavaTestLib) testImplementation(libraries.xmlUnit) testImplementation(libraries.awaitility) + testImplementation(libraries.springBootStarterFlywayTest) + testImplementation(libraries.springBootStarterLdapTest) + testImplementation(libraries.springBootStarterMailTest) + testImplementation(libraries.springBootStarterRestclientTest) { + exclude(group: "org.springframework.boot", module: "spring-boot-starter-jackson") + } + testImplementation(libraries.springBootStarterSecuritySaml2Test) + testImplementation(libraries.springBootStarterSecurityTest) + testImplementation(libraries.springBootStarterThymeleafTest) + testImplementation(libraries.springBootStarterValidationTest) implementation(libraries.commonsIo) + implementation(libraries.springBootStarterAspectj) + implementation(libraries.springBootStarterFlyway) + implementation(libraries.springBootStarterLdap) + implementation(libraries.springBootStarterRestclient) { + exclude(group: "org.springframework.boot", module: "spring-boot-starter-jackson") + } + implementation(libraries.springBootStarterSecurity) + implementation(libraries.springBootStarterSecuritySaml2) + implementation(libraries.springBootStarterSessionJdbc) + implementation(libraries.springBootStarterThymeleaf) + implementation(libraries.springBootStarterTomcat) + implementation(libraries.springBootStarterValidation) + implementation "org.springframework.security:spring-security-access:7.0.4" + implementation(libraries.springBootJackson2) } configurations.all { @@ -151,3 +175,6 @@ task tomcatListenerJar(type: Jar) { artifacts { archives(tomcatListenerJar) } +tasks.withType(Test).configureEach { + useJUnitPlatform() +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlBeansConfiguration.java b/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlBeansConfiguration.java index 86bee119cb8..adcd3bd73f5 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlBeansConfiguration.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlBeansConfiguration.java @@ -101,7 +101,6 @@ YamlConfigurationValidator uaaConfigValidation(@Value("#{environment['environmen @Primary ContentNegotiationManagerFactoryBean contentNegotiationManager() { ContentNegotiationManagerFactoryBean bean = new ContentNegotiationManagerFactoryBean(); - bean.setFavorPathExtension(false); bean.setFavorParameter(true); bean.addMediaType("json", MediaType.APPLICATION_JSON); bean.addMediaType("xml", MediaType.APPLICATION_XML); @@ -115,7 +114,7 @@ RequestMappingHandlerMapping requestMappingHandlerMapping( ) { RequestMappingHandlerMapping bean = new RequestMappingHandlerMapping(); bean.setContentNegotiationManager(contentNegotiationManagerFactoryBean.build()); - bean.setUseSuffixPatternMatch(false); + // Note: setUseSuffixPatternMatch() removed in Spring 6 - suffix pattern matching is now always disabled bean.setOrder(1); return bean; } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlFiltersConfiguration.java b/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlFiltersConfiguration.java index d43462c0ff8..997bb7e56b1 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlFiltersConfiguration.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlFiltersConfiguration.java @@ -227,7 +227,7 @@ public FilterRegistrationBean httpHeaderSecurityFilter filter.setHstsEnabled(false); filter.setAntiClickJackingEnabled(false); filter.setBlockContentTypeSniffingEnabled(true); - filter.setXssProtectionEnabled(false); + // Note: setXssProtectionEnabled() removed in Spring Security 7 - XSS protection header no longer supported FilterRegistrationBean bean = new FilterRegistrationBean<>(filter); bean.setEnabled(false); return bean; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/UaaProperties.java b/server/src/main/java/org/cloudfoundry/identity/uaa/UaaProperties.java index 32992470846..36e431fe5c3 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/UaaProperties.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/UaaProperties.java @@ -1,9 +1,9 @@ package org.cloudfoundry.identity.uaa; import org.cloudfoundry.identity.uaa.util.UaaStringUtils; +import org.jspecify.annotations.Nullable; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.bind.DefaultValue; -import org.springframework.lang.Nullable; import java.util.Collections; import java.util.List; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/AccountsController.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/AccountsController.java index 6957f26234c..ba18ff0b293 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/AccountsController.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/AccountsController.java @@ -8,6 +8,7 @@ import org.cloudfoundry.identity.uaa.util.DomainFilter; import org.cloudfoundry.identity.uaa.zone.BrandingInformation; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; +import org.jspecify.annotations.Nullable; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -41,8 +42,8 @@ public AccountsController( @GetMapping("/create_account") public String activationEmail(Model model, - @RequestParam(value = "client_id", required = false) String clientId, - @RequestParam(value = "redirect_uri", required = false) String redirectUri, + @Nullable @RequestParam("client_id") String clientId, + @Nullable @RequestParam("redirect_uri") String redirectUri, HttpServletResponse response) { if (!IdentityZoneHolder.get().getConfig().getLinks().getSelfService().isSelfServiceLinksEnabled()) { return handleSelfServiceDisabled(model, response, "error_message_code", "self_service_disabled"); @@ -56,15 +57,15 @@ public String activationEmail(Model model, @PostMapping("/create_account.do") public String sendActivationEmail(Model model, HttpServletResponse response, - @RequestParam(value = "client_id", required = false) String clientId, - @RequestParam(value = "redirect_uri", required = false) String redirectUri, + @Nullable @RequestParam("client_id") String clientId, + @Nullable @RequestParam("redirect_uri") String redirectUri, @Valid @ModelAttribute ValidEmail email, BindingResult result, @RequestParam String password, @RequestParam("password_confirmation") String passwordConfirmation, - @RequestParam(value = "does_user_consent", required = false) boolean doesUserConsent) { + @Nullable @RequestParam("does_user_consent") Boolean doesUserConsent) { BrandingInformation zoneBranding = IdentityZoneHolder.get().getConfig().getBranding(); - if (zoneBranding != null && zoneBranding.getConsent() != null && !doesUserConsent) { + if (zoneBranding != null && zoneBranding.getConsent() != null && !Boolean.TRUE.equals(doesUserConsent)) { return handleUnprocessableEntity(model, response, "error_message_code", "missing_consent"); } if (!IdentityZoneHolder.get().getConfig().getLinks().getSelfService().isSelfServiceLinksEnabled()) { @@ -115,7 +116,7 @@ public String verifyUser(Model model, accountCreation = accountCreationService.completeActivation(code); } catch (HttpClientErrorException e) { model.addAttribute("error_message_code", "code_expired"); - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + response.setStatus(HttpStatus.UNPROCESSABLE_CONTENT.value()); return "accounts/link_prompt"; } @@ -130,7 +131,7 @@ public String verifyUser(Model model, private String handleUnprocessableEntity(Model model, HttpServletResponse response, String attributeKey, String attributeValue) { model.addAttribute(attributeKey, attributeValue); updateModelWithConsentAttributes(model); - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + response.setStatus(HttpStatus.UNPROCESSABLE_CONTENT.value()); return "accounts/new_activation_email"; } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ChangeEmailController.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ChangeEmailController.java index 6be1cf79163..43353f5d41a 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ChangeEmailController.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ChangeEmailController.java @@ -7,6 +7,7 @@ import org.cloudfoundry.identity.uaa.error.UaaException; import org.cloudfoundry.identity.uaa.user.UaaUser; import org.cloudfoundry.identity.uaa.user.UaaUserDatabase; +import org.jspecify.annotations.Nullable; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.context.SecurityContext; @@ -41,8 +42,8 @@ public ChangeEmailController( } @GetMapping("/change_email") - public String changeEmailPage(Model model, @RequestParam(value = "client_id", required = false) String clientId, - @RequestParam(value = "redirect_uri", required = false) String redirectUri) { + public String changeEmailPage(Model model, @Nullable @RequestParam("client_id") String clientId, + @Nullable @RequestParam("redirect_uri") String redirectUri) { SecurityContext securityContext = SecurityContextHolder.getContext(); model.addAttribute("email", ((UaaPrincipal) securityContext.getAuthentication().getPrincipal()).getEmail()); model.addAttribute("client_id", clientId); @@ -52,15 +53,15 @@ public String changeEmailPage(Model model, @RequestParam(value = "client_id", re @PostMapping("/change_email.do") public String changeEmail(Model model, @Valid @ModelAttribute ValidEmail newEmail, BindingResult result, - @RequestParam(required = false, value = "client_id") String clientId, - @RequestParam(required = false, value = "redirect_uri") String redirectUri, + @Nullable @RequestParam("client_id") String clientId, + @Nullable @RequestParam("redirect_uri") String redirectUri, RedirectAttributes redirectAttributes, HttpServletResponse response) { SecurityContext securityContext = SecurityContextHolder.getContext(); if (result.hasErrors()) { model.addAttribute("error_message_code", "invalid_email"); model.addAttribute("email", ((UaaPrincipal) securityContext.getAuthentication().getPrincipal()).getEmail()); - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + response.setStatus(HttpStatus.UNPROCESSABLE_CONTENT.value()); return "change_email"; } String origin = ((UaaPrincipal) securityContext.getAuthentication().getPrincipal()).getOrigin(); @@ -78,7 +79,7 @@ public String changeEmail(Model model, @Valid @ModelAttribute ValidEmail newEmai if (e.getHttpStatus() == 409) { model.addAttribute("error_message_code", "username_exists"); model.addAttribute("email", ((UaaPrincipal) securityContext.getAuthentication().getPrincipal()).getEmail()); - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + response.setStatus(HttpStatus.UNPROCESSABLE_CONTENT.value()); return "change_email"; } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ChangePasswordController.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ChangePasswordController.java index 87427ad2b12..09e218e70fe 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ChangePasswordController.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ChangePasswordController.java @@ -42,7 +42,7 @@ public String changePassword( PasswordConfirmationValidation validation = new PasswordConfirmationValidation(newPassword, confirmPassword); if (!validation.valid()) { model.addAttribute("message_code", validation.getMessageCode()); - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + response.setStatus(HttpStatus.UNPROCESSABLE_CONTENT.value()); return "change_password"; } @@ -66,7 +66,7 @@ public String changePassword( } catch (InvalidPasswordException e) { model.addAttribute("message", e.getMessagesAsOneString()); } - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + response.setStatus(HttpStatus.UNPROCESSABLE_CONTENT.value()); return "change_password"; } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/PasswordChangeEndpoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/PasswordChangeEndpoint.java index da0b6c4e00e..05375a6af89 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/PasswordChangeEndpoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/PasswordChangeEndpoint.java @@ -23,7 +23,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.View; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static org.springframework.http.HttpStatus.UNPROCESSABLE_CONTENT; @Controller public class PasswordChangeEndpoint { @@ -58,7 +58,7 @@ public ActionResult changePassword(@PathVariable String userId, @RequestBody Pas throwIfPasswordChangeNotPermitted(userId, oldPassword, zoneId); if (scimUserProvisioning.checkPasswordMatches(userId, newPassword, zoneId)) { - throw new InvalidPasswordException("Your new password cannot be the same as the old password.", UNPROCESSABLE_ENTITY); + throw new InvalidPasswordException("Your new password cannot be the same as the old password.", UNPROCESSABLE_CONTENT); } passwordValidator.validate(newPassword); scimUserProvisioning.changePassword(userId, oldPassword, newPassword, zoneId); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/PasswordResetEndpoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/PasswordResetEndpoint.java index edc49655c87..4313c7332d7 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/PasswordResetEndpoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/PasswordResetEndpoint.java @@ -16,6 +16,7 @@ import org.cloudfoundry.identity.uaa.web.ExceptionReport; import org.cloudfoundry.identity.uaa.web.ExceptionReportHttpMessageConverter; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.Nullable; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -33,14 +34,7 @@ import java.util.HashMap; import java.util.Map; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.CONFLICT; -import static org.springframework.http.HttpStatus.CREATED; -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; -import static org.springframework.http.HttpStatus.NOT_FOUND; -import static org.springframework.http.HttpStatus.OK; -import static org.springframework.http.HttpStatus.UNAUTHORIZED; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static org.springframework.http.HttpStatus.*; @Controller public class PasswordResetEndpoint { @@ -65,8 +59,8 @@ public PasswordResetEndpoint( @PostMapping({"/password_resets", "/password_resets/"}) public ResponseEntity resetPassword(@RequestBody String email, - @RequestParam(required = false, value = "client_id") String clientId, - @RequestParam(required = false, value = "redirect_uri") String redirectUri) { + @Nullable @RequestParam("client_id") String clientId, + @Nullable @RequestParam("redirect_uri") String redirectUri) { if (clientId == null) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication instanceof OAuth2Authentication oAuth2Authentication) { @@ -137,14 +131,14 @@ private ExpiringCode getCode(String id, String username, String clientId) { @ExceptionHandler(InvalidPasswordException.class) public View handleException(InvalidPasswordException t) throws ScimException { return new ConvertingExceptionView(new ResponseEntity<>(new ExceptionReport( - t, false), UNPROCESSABLE_ENTITY), + t, false), UNPROCESSABLE_CONTENT), messageConverters); } @ExceptionHandler(InvalidCodeException.class) public View handleCodeException(InvalidCodeException t) throws ScimException { return new ConvertingExceptionView(new ResponseEntity<>(new ExceptionReport( - t, false), UNPROCESSABLE_ENTITY), + t, false), UNPROCESSABLE_CONTENT), messageConverters); } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ProfileController.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ProfileController.java index b85e85ae0fc..974ec71b463 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ProfileController.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ProfileController.java @@ -10,6 +10,7 @@ import org.cloudfoundry.identity.uaa.security.beans.SecurityContextAccessor; import org.cloudfoundry.identity.uaa.zone.MultitenantClientServices; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; @@ -71,10 +72,10 @@ public String get(Authentication authentication, Model model) { * Handle form post for revoking chosen approvals */ @PostMapping({"/profile", "/profile/"}) - public String post(@RequestParam(required = false) Collection checkedScopes, - @RequestParam(required = false) String update, - @RequestParam(required = false) String delete, - @RequestParam(required = false) String clientId) { + public String post(@Nullable @RequestParam Collection checkedScopes, + @Nullable @RequestParam String update, + @Nullable @RequestParam String delete, + @Nullable @RequestParam String clientId) { String userId = getCurrentUserId(); if (null != update) { Map> approvalsByClientId = getCurrentApprovalsForUser(userId); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationEntryPoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationEntryPoint.java index 01439ce0833..ea87ae4e2c8 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationEntryPoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationEntryPoint.java @@ -34,7 +34,7 @@ public class ResetPasswordAuthenticationEntryPoint implements AuthenticationEntr @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { Throwable cause = authException.getCause(); - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + response.setStatus(HttpStatus.UNPROCESSABLE_CONTENT.value()); HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) { @Override diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationFilter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationFilter.java index 33ea75f3db9..ada97ea1cbc 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationFilter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationFilter.java @@ -21,10 +21,11 @@ import org.cloudfoundry.identity.uaa.scim.exception.InvalidPasswordException; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpMethod; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @@ -42,7 +43,7 @@ public class ResetPasswordAuthenticationFilter extends OncePerRequestFilter { private final AuthenticationEntryPoint entryPoint; private final ExpiringCodeStore expiringCodeStore; public static final String RESET_PASSWORD_URL = "/reset_password.do"; - private static final RequestMatcher matcher = new AntPathRequestMatcher(RESET_PASSWORD_URL, "POST"); + private static final RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.valueOf("POST"), RESET_PASSWORD_URL); public ResetPasswordAuthenticationFilter( ResetPasswordService service, diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordController.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordController.java index 6325801ec5c..c3580454871 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordController.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/ResetPasswordController.java @@ -13,6 +13,7 @@ import org.cloudfoundry.identity.uaa.util.UaaUrlUtils; import org.cloudfoundry.identity.uaa.zone.MergedZoneBrandingInformation; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -73,8 +74,8 @@ public ResetPasswordController( @GetMapping("/forgot_password") public String forgotPasswordPage(Model model, - @RequestParam(required = false, value = "client_id") String clientId, - @RequestParam(required = false, value = "redirect_uri") String redirectUri, + @Nullable @RequestParam("client_id") String clientId, + @Nullable @RequestParam("redirect_uri") String redirectUri, HttpServletResponse response) { if (!identityZoneManager.getCurrentIdentityZone().getConfig().getLinks().getSelfService().isSelfServiceLinksEnabled()) { return handleSelfServiceDisabled(model, response, "error_message_code", "self_service_disabled"); @@ -232,7 +233,7 @@ public void resetPassword(Model model, private String handleUnprocessableEntity(Model model, HttpServletResponse response, String attributeKey, String attributeValue) { model.addAttribute(attributeKey, attributeValue); - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + response.setStatus(HttpStatus.UNPROCESSABLE_CONTENT.value()); return "forgot_password"; } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaChangePasswordService.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaChangePasswordService.java index c9e818b90dd..b8fadaaca3c 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaChangePasswordService.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaChangePasswordService.java @@ -34,7 +34,7 @@ import java.util.List; import static org.cloudfoundry.identity.uaa.constants.OriginKeys.UAA; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static org.springframework.http.HttpStatus.UNPROCESSABLE_CONTENT; @Service public class UaaChangePasswordService implements ChangePasswordService, ApplicationEventPublisherAware { @@ -63,7 +63,7 @@ public void changePassword(String username, String currentPassword, String newPa Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); try { if (scimUserProvisioning.checkPasswordMatches(user.getId(), newPassword, IdentityZoneHolder.get().getId())) { - throw new InvalidPasswordException("Your new password cannot be the same as the old password.", UNPROCESSABLE_ENTITY); + throw new InvalidPasswordException("Your new password cannot be the same as the old password.", UNPROCESSABLE_CONTENT); } scimUserProvisioning.changePassword(user.getId(), currentPassword, newPassword, IdentityZoneHolder.get().getId()); publish(new PasswordChangeEvent("Password changed", uaaUser, authentication, IdentityZoneHolder.getCurrentZoneId())); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java b/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java index 3f34284f2df..cecc8face36 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java @@ -37,8 +37,8 @@ import java.util.Set; import static java.util.Collections.emptyList; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; import static org.cloudfoundry.identity.uaa.util.UaaStringUtils.isEmpty; +import static org.springframework.http.HttpStatus.UNPROCESSABLE_CONTENT; @Service("resetPasswordService") public class UaaResetPasswordService implements ResetPasswordService, ApplicationEventPublisherAware { @@ -78,7 +78,7 @@ public ResetPasswordResponse resetPassword(ExpiringCode code, String newPassword @Override public void resetUserPassword(String userId, String password) { if (scimUserProvisioning.checkPasswordMatches(userId, password, identityZoneManager.getCurrentIdentityZoneId())) { - throw new InvalidPasswordException(resourcePropertySource != null && resourcePropertySource.getProperty(FORCE_PASSWORD_CHANGE_SAME_AS_OLD) != null ? resourcePropertySource.getProperty(FORCE_PASSWORD_CHANGE_SAME_AS_OLD).toString() : FORCE_PASSWORD_CHANGE_SAME_AS_OLD, UNPROCESSABLE_ENTITY); + throw new InvalidPasswordException(resourcePropertySource != null && resourcePropertySource.getProperty(FORCE_PASSWORD_CHANGE_SAME_AS_OLD) != null ? resourcePropertySource.getProperty(FORCE_PASSWORD_CHANGE_SAME_AS_OLD).toString() : FORCE_PASSWORD_CHANGE_SAME_AS_OLD, UNPROCESSABLE_CONTENT); } passwordValidator.validate(password); ScimUser user = scimUserProvisioning.retrieve(userId, identityZoneManager.getCurrentIdentityZoneId()); @@ -110,7 +110,7 @@ private ResetPasswordResponse changePasswordCodeAuthenticated(ExpiringCode expir Authentication authentication = constructAuthentication(uaaUser); try { if (scimUserProvisioning.checkPasswordMatches(userId, newPassword, identityZoneManager.getCurrentIdentityZoneId())) { - throw new InvalidPasswordException("Your new password cannot be the same as the old password.", UNPROCESSABLE_ENTITY); + throw new InvalidPasswordException("Your new password cannot be the same as the old password.", UNPROCESSABLE_CONTENT); } if (isUserModified(user, userName, passwordLastModified)) { throw new UaaException("Invalid password reset request."); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandler.java b/server/src/main/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandler.java index 5870e1426d6..e720d4d8b55 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandler.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandler.java @@ -9,11 +9,11 @@ import org.cloudfoundry.identity.uaa.EntityWithAlias; import org.cloudfoundry.identity.uaa.zone.IdentityZoneProvisioning; import org.cloudfoundry.identity.uaa.zone.ZoneDoesNotExistsException; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; public abstract class EntityAliasHandler { private static final Logger LOGGER = LoggerFactory.getLogger(EntityAliasHandler.class); @@ -170,7 +170,7 @@ public final T ensureConsistencyOfAliasEntity( final String errorMessage = "Could not create alias for %s, as alias zone does not exist.".formatted( originalEntity.getAliasDescription() ); - throw new EntityAliasFailedException(errorMessage, HttpStatus.UNPROCESSABLE_ENTITY.value(), e); + throw new EntityAliasFailedException(errorMessage, HttpStatus.UNPROCESSABLE_CONTENT.value(), e); } // create new alias entity in alias zid diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/approval/ApprovalsAdminEndpoints.java b/server/src/main/java/org/cloudfoundry/identity/uaa/approval/ApprovalsAdminEndpoints.java index 6bd274cf44d..e0e9bf3a68e 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/approval/ApprovalsAdminEndpoints.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/approval/ApprovalsAdminEndpoints.java @@ -15,6 +15,7 @@ import org.cloudfoundry.identity.uaa.web.ExceptionReportHttpMessageConverter; import org.cloudfoundry.identity.uaa.zone.MultitenantClientServices; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; @@ -79,9 +80,9 @@ public class ApprovalsAdminEndpoints implements InitializingBean { @GetMapping("/approvals") @ResponseBody - public List getApprovals(@RequestParam(required = false, defaultValue = "user_id pr") String ignored, - @RequestParam(required = false, defaultValue = "1") int startIndex, - @RequestParam(required = false, defaultValue = "100") int count) { + public List getApprovals(@Nullable @RequestParam(defaultValue = "user_id pr") String ignored, + @Nullable @RequestParam(defaultValue = "1") int startIndex, + @Nullable @RequestParam(defaultValue = "100") int count) { String userId = getCurrentUserId(); logger.debug("Fetching all approvals for user: {}", userId); List input = approvalStore.getApprovalsForUser(userId, identityZoneManager.getCurrentIdentityZoneId()); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/BackwardsCompatibleTokenEndpointAuthenticationFilter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/BackwardsCompatibleTokenEndpointAuthenticationFilter.java index ec8453866d3..5e3e0055672 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/BackwardsCompatibleTokenEndpointAuthenticationFilter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/BackwardsCompatibleTokenEndpointAuthenticationFilter.java @@ -51,7 +51,7 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; @@ -76,7 +76,7 @@ */ @Slf4j public class BackwardsCompatibleTokenEndpointAuthenticationFilter implements Filter { - public static final String DEFAULT_FILTER_PROCESSES_URI = "/oauth/token/alias/{{registrationId}}"; + public static final String DEFAULT_FILTER_PROCESSES_URI = "/oauth/token/alias/{registrationId}"; private final AuthenticationManager tokenExchangeAuthenticationManager; /** @@ -100,7 +100,7 @@ public class BackwardsCompatibleTokenEndpointAuthenticationFilter implements Fil private final ExternalOAuthAuthenticationManager externalOAuthAuthenticationManager; - private final AntPathRequestMatcher requestMatcher; + private final PathPatternRequestMatcher requestMatcher; public BackwardsCompatibleTokenEndpointAuthenticationFilter(AuthenticationManager authenticationManager, OAuth2RequestFactory oAuth2RequestFactory) { @@ -133,7 +133,7 @@ public BackwardsCompatibleTokenEndpointAuthenticationFilter( super(); Assert.isTrue(requestMatcherUrl.contains("{registrationId}"), "filterProcessesUrl must contain a {registrationId} match variable"); - requestMatcher = new AntPathRequestMatcher(requestMatcherUrl); + requestMatcher = PathPatternRequestMatcher.withDefaults().matcher(requestMatcherUrl); this.authenticationManager = authenticationManager; this.oAuth2RequestFactory = oAuth2RequestFactory; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/ClientDetailsAuthenticationProvider.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/ClientDetailsAuthenticationProvider.java index b3e74ef4f4e..2814bc223cd 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/ClientDetailsAuthenticationProvider.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/ClientDetailsAuthenticationProvider.java @@ -52,8 +52,7 @@ public class ClientDetailsAuthenticationProvider extends DaoAuthenticationProvid private final JwtClientAuthentication jwtClientAuthentication; public ClientDetailsAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder encoder, JwtClientAuthentication jwtClientAuthentication) { - super(); - setUserDetailsService(userDetailsService); + super(userDetailsService); setPasswordEncoder(encoder); this.jwtClientAuthentication = jwtClientAuthentication; } @@ -94,7 +93,20 @@ protected void additionalAuthenticationChecks(UserDetails userDetails, UsernameP error = new BadCredentialsException("Missing credentials"); break; } - super.additionalAuthenticationChecks(uaaClient, authentication); + // Spring Security 7 rejects empty passwords before validation. + // Handle empty credentials by bypassing parent's empty check + if (ObjectUtils.isEmpty(authentication.getCredentials())) { + String presentedPassword = authentication.getCredentials() == null ? "" : authentication.getCredentials().toString(); + String storedPassword = uaaClient.getPassword(); + // Handle {noop} encoded empty password: "{noop}" means empty password with noop encoding + if ("{noop}".equals(storedPassword) && presentedPassword.isEmpty()) { + error = null; + } else if (!getPasswordEncoder().matches(presentedPassword, storedPassword)) { + throw new BadCredentialsException("Bad credentials"); + } + } else { + super.additionalAuthenticationChecks(uaaClient, authentication); + } error = null; break; } catch (AuthenticationException e) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/PasswordChangeUiRequiredFilter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/PasswordChangeUiRequiredFilter.java index 14d9c720b4e..c73f199b29c 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/PasswordChangeUiRequiredFilter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/PasswordChangeUiRequiredFilter.java @@ -2,11 +2,11 @@ import org.cloudfoundry.identity.uaa.util.SessionUtils; import org.cloudfoundry.identity.uaa.web.UaaSavedRequestCache; -import org.springframework.lang.NonNull; +import org.jspecify.annotations.NonNull; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.savedrequest.SavedRequest; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; @@ -21,19 +21,19 @@ public class PasswordChangeUiRequiredFilter extends OncePerRequestFilter { private static final String MATCH_PATH = "/force_password_change"; private static final String COMPLETED_PATH = "/force_password_change_completed"; - private final AntPathRequestMatcher matchPath; - private final AntPathRequestMatcher completedPath; + private final PathPatternRequestMatcher matchPath; + private final PathPatternRequestMatcher completedPath; private final UaaSavedRequestCache cache; public PasswordChangeUiRequiredFilter() { this(new UaaSavedRequestCache()); - this.cache.setRequestMatcher(new AntPathRequestMatcher("/oauth/authorize**")); + this.cache.setRequestMatcher(PathPatternRequestMatcher.withDefaults().matcher("/oauth/authorize**")); } public PasswordChangeUiRequiredFilter(UaaSavedRequestCache cache) { this.cache = cache; - this.matchPath = new AntPathRequestMatcher(MATCH_PATH); - this.completedPath = new AntPathRequestMatcher(COMPLETED_PATH); + this.matchPath = PathPatternRequestMatcher.withDefaults().matcher(MATCH_PATH); + this.completedPath = PathPatternRequestMatcher.withDefaults().matcher(COMPLETED_PATH); } @Override @@ -89,4 +89,4 @@ private boolean needsPasswordReset(HttpServletRequest request) { authentication.isAuthenticated(); } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/RemoteAuthenticationEndpoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/RemoteAuthenticationEndpoint.java index 74f2779a40c..847f8678cb0 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/RemoteAuthenticationEndpoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/RemoteAuthenticationEndpoint.java @@ -4,6 +4,7 @@ import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.login.AuthenticationResponse; import org.cloudfoundry.identity.uaa.oauth.provider.OAuth2Authentication; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -82,7 +83,7 @@ public HttpEntity authenticate(HttpServletRequest reques public HttpEntity authenticate(HttpServletRequest request, @RequestParam String username, @RequestParam(value = OriginKeys.ORIGIN) String origin, - @RequestParam(required = false) String email) { + @Nullable @RequestParam String email) { AuthenticationResponse response = new AuthenticationResponse(); HttpStatus status = HttpStatus.UNAUTHORIZED; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/SamlLogoutRequestValidator.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/SamlLogoutRequestValidator.java index cf3ce218f97..20ff4fec1e0 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/SamlLogoutRequestValidator.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/SamlLogoutRequestValidator.java @@ -1,7 +1,7 @@ package org.cloudfoundry.identity.uaa.authentication; import org.springframework.security.saml2.core.Saml2Error; -import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlLogoutRequestValidator; +import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutValidatorResult; @@ -9,7 +9,7 @@ import java.util.Collection; /** - * Delegates SAML logout request validation to {@link OpenSamlLogoutRequestValidator}, + * Delegates SAML logout request validation to {@link OpenSaml5LogoutRequestValidator}, * but ignores errors due to missing signatures. */ public class SamlLogoutRequestValidator implements Saml2LogoutRequestValidator { @@ -17,7 +17,7 @@ public class SamlLogoutRequestValidator implements Saml2LogoutRequestValidator { private final Saml2LogoutRequestValidator delegate; public SamlLogoutRequestValidator() { - this.delegate = new OpenSamlLogoutRequestValidator(); + this.delegate = new OpenSaml5LogoutRequestValidator(); } public SamlLogoutRequestValidator(Saml2LogoutRequestValidator delegate) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/SamlLogoutResponseValidator.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/SamlLogoutResponseValidator.java index 1cce7c85a86..23e0f679182 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/SamlLogoutResponseValidator.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/SamlLogoutResponseValidator.java @@ -1,7 +1,7 @@ package org.cloudfoundry.identity.uaa.authentication; import org.springframework.security.saml2.core.Saml2Error; -import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlLogoutResponseValidator; +import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutResponseValidator; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidatorParameters; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutValidatorResult; @@ -9,7 +9,7 @@ import java.util.Collection; /** - * Delegates SAML logout responses validation to {@link OpenSamlLogoutResponseValidator} + * Delegates SAML logout responses validation to {@link OpenSaml5LogoutResponseValidator} * but ignores errors due to missing signatures. */ @@ -18,7 +18,7 @@ public class SamlLogoutResponseValidator implements Saml2LogoutResponseValidator private final Saml2LogoutResponseValidator delegate; public SamlLogoutResponseValidator() { - this.delegate = new OpenSamlLogoutResponseValidator(); + this.delegate = new OpenSaml5LogoutResponseValidator(); } public SamlLogoutResponseValidator(Saml2LogoutResponseValidator delegate) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/event/UserNotFoundEvent.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/event/UserNotFoundEvent.java index 7f70271b33b..909d1c81f62 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/event/UserNotFoundEvent.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/event/UserNotFoundEvent.java @@ -13,14 +13,14 @@ *******************************************************************************/ package org.cloudfoundry.identity.uaa.authentication.event; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Base64; import org.cloudfoundry.identity.uaa.audit.AuditEvent; import org.cloudfoundry.identity.uaa.audit.AuditEventType; import org.springframework.security.core.Authentication; -import org.springframework.security.crypto.codec.Base64; -import org.springframework.security.crypto.codec.Utf8; /** * Event which indicates that someone tried to authenticate as a non-existent @@ -42,7 +42,8 @@ public AuditEvent getAuditEvent() { try { // Store hash of name, to conceal accidental entry of sensitive info // (e.g. password) - name = Utf8.decode(Base64.encode(MessageDigest.getInstance("SHA-1").digest(Utf8.encode(name)))); + byte[] hash = MessageDigest.getInstance("SHA-1").digest(name.getBytes(StandardCharsets.UTF_8)); + name = Base64.getEncoder().encodeToString(hash); } catch (NoSuchAlgorithmException shouldNeverHappen) { name = "NOSHA"; } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/listener/BadCredentialsListener.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/listener/BadCredentialsListener.java index 327e1253ee8..3b52aa0b747 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/listener/BadCredentialsListener.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/listener/BadCredentialsListener.java @@ -4,10 +4,10 @@ import org.cloudfoundry.identity.uaa.authentication.event.PrincipalAuthenticationFailureEvent; import org.cloudfoundry.identity.uaa.authentication.event.PrincipalNotFoundEvent; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.NonNull; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ApplicationListener; -import org.springframework.lang.NonNull; import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/AutologinRequestConverter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/AutologinRequestConverter.java index 6a45efb78d3..6e7deb4858a 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/AutologinRequestConverter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/AutologinRequestConverter.java @@ -17,6 +17,7 @@ import org.cloudfoundry.identity.uaa.login.AutologinRequest; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.util.LinkedMaskingMultiValueMap; +import org.jspecify.annotations.Nullable; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; @@ -26,7 +27,6 @@ import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.http.converter.StringHttpMessageConverter; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; import org.springframework.util.StreamUtils; @@ -131,4 +131,4 @@ else if (contentType.isCompatibleWith(MediaType.APPLICATION_JSON) || Assert.state(charset != null, "No default charset"); return charset; } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAdminEndpoints.java b/server/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAdminEndpoints.java index f0be04fea6b..280ae96f013 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAdminEndpoints.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAdminEndpoints.java @@ -38,6 +38,7 @@ import org.cloudfoundry.identity.uaa.zone.InvalidClientSecretException; import org.cloudfoundry.identity.uaa.zone.MultitenantClientServices; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -453,12 +454,12 @@ protected void deleteApprovals(String clientId) { @GetMapping({"/oauth/clients", "/oauth/clients/"}) @ResponseBody public SearchResults listClientDetails( - @RequestParam(value = "attributes", required = false) String attributesCommaSeparated, - @RequestParam(required = false, defaultValue = "client_id pr") String filter, - @RequestParam(required = false, defaultValue = "client_id") String sortBy, - @RequestParam(required = false, defaultValue = "ascending") String sortOrder, - @RequestParam(required = false, defaultValue = "1") int startIndex, - @RequestParam(required = false, defaultValue = "100") int count) { + @Nullable @RequestParam("attributes") String attributesCommaSeparated, + @Nullable @RequestParam(defaultValue = "client_id pr") String filter, + @Nullable @RequestParam(defaultValue = "client_id") String sortBy, + @Nullable @RequestParam(defaultValue = "ascending") String sortOrder, + @Nullable @RequestParam(defaultValue = "1") int startIndex, + @Nullable @RequestParam(defaultValue = "100") int count) { if (count > clientMaxCount) { count = clientMaxCount; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAdminSecurityConfiguration.java b/server/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAdminSecurityConfiguration.java index 95d9c0a4fe6..12a81f0681e 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAdminSecurityConfiguration.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAdminSecurityConfiguration.java @@ -47,7 +47,7 @@ UaaFilterChain clientAdminSecret( auth.requestMatchers(HttpMethod.PUT, "/oauth/clients/tx/**").access(isAdminOrHasScopes()); auth.requestMatchers(HttpMethod.DELETE, "/oauth/clients/tx/**").access(isAdminOrHasScopes()); - auth.requestMatchers(HttpMethod.GET, "oauth/clients/meta", "/oauth/clients/*/meta").fullyAuthenticated(); + auth.requestMatchers(HttpMethod.GET, "/oauth/clients/meta", "/oauth/clients/*/meta").fullyAuthenticated(); auth.requestMatchers(HttpMethod.GET, "/oauth/clients/**").access(isAdminOrHasScopes("clients.read")); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/db/beans/DatabaseConfiguration.java b/server/src/main/java/org/cloudfoundry/identity/uaa/db/beans/DatabaseConfiguration.java index 9063e7e4fcc..8c9b8c2a172 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/db/beans/DatabaseConfiguration.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/db/beans/DatabaseConfiguration.java @@ -6,10 +6,11 @@ import org.cloudfoundry.identity.uaa.resources.jdbc.MySqlLimitSqlAdapter; import org.cloudfoundry.identity.uaa.resources.jdbc.PostgresLimitSqlAdapter; import org.jspecify.annotations.NonNull; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; -import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration; +import org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitialization; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/LdapGroupsConfig.java b/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/LdapGroupsConfig.java index abf864df848..daa51dc78c8 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/LdapGroupsConfig.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/impl/config/LdapGroupsConfig.java @@ -2,6 +2,7 @@ import org.cloudfoundry.identity.uaa.provider.ldap.extension.NestedLdapAuthoritiesPopulator; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.ldap.core.ContextSource; import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; @@ -11,6 +12,7 @@ import static java.util.Optional.ofNullable; +@Configuration public class LdapGroupsConfig { @Bean public LdapAuthoritiesPopulator nestedLdapAuthoritiesPopulator(ContextSource contextSource, Environment environment, String configuredGroupRoleAttribute) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/invitations/InvitationsController.java b/server/src/main/java/org/cloudfoundry/identity/uaa/invitations/InvitationsController.java index 398f8afef98..d58ce10c65f 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/invitations/InvitationsController.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/invitations/InvitationsController.java @@ -31,6 +31,7 @@ import org.cloudfoundry.identity.uaa.util.UaaHttpRequestUtils; import org.cloudfoundry.identity.uaa.zone.BrandingInformation; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.http.HttpStatus; @@ -40,7 +41,6 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.PortResolverImpl; import org.springframework.security.web.savedrequest.DefaultSavedRequest; import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.stereotype.Controller; @@ -52,6 +52,7 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -195,7 +196,7 @@ private void setRequestAttributes(HttpServletRequest request, String newCode, Ua RequestContextHolder.getRequestAttributes().setAttribute("user_id", user.getId(), RequestAttributes.SCOPE_SESSION); HttpServletRequestWrapper wrapper = getNewCodeWrapper(request, newCode); - SavedRequest savedRequest = new DefaultSavedRequest(wrapper, new PortResolverImpl()); + SavedRequest savedRequest = new DefaultSavedRequest(wrapper); RequestContextHolder.getRequestAttributes().setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest, RequestAttributes.SCOPE_SESSION); } @@ -242,8 +243,9 @@ public String getQueryString() { public String acceptInvitation(@RequestParam("password") String password, @RequestParam("password_confirmation") String passwordConfirmation, @RequestParam("code") String code, - @RequestParam(value = "does_user_consent", required = false) boolean doesUserConsent, + @Nullable @RequestParam("does_user_consent") Boolean doesUserConsent, Model model, + RedirectAttributes redirectAttributes, HttpServletResponse response) { PasswordConfirmationValidation validation = new PasswordConfirmationValidation(password, passwordConfirmation); @@ -267,16 +269,16 @@ public String acceptInvitation(@RequestParam("password") String password, final String newCode = expiringCodeStore.generateCode(expiringCode.getData(), new Timestamp(System.currentTimeMillis() + (10 * 60 * 1000)), expiringCode.getIntent(), identityZoneManager.getCurrentIdentityZoneId()).getCode(); BrandingInformation zoneBranding = identityZoneManager.getCurrentIdentityZone().getConfig().getBranding(); - if (zoneBranding != null && zoneBranding.getConsent() != null && !doesUserConsent) { - return processErrorReload(newCode, model, response, "error_message_code", "missing_consent"); + if (zoneBranding != null && zoneBranding.getConsent() != null && !Boolean.TRUE.equals(doesUserConsent)) { + return processErrorReload(newCode, model, redirectAttributes, response, "error_message_code", "missing_consent"); } if (!validation.valid()) { - return processErrorReload(newCode, model, response, "error_message_code", validation.getMessageCode()); + return processErrorReload(newCode, model, redirectAttributes, response, "error_message_code", validation.getMessageCode()); } try { passwordValidator.validate(password); } catch (InvalidPasswordException e) { - return processErrorReload(newCode, model, response, "error_message", e.getMessagesAsOneString()); + return processErrorReload(newCode, model, redirectAttributes, response, "error_message", e.getMessagesAsOneString()); } AcceptedInvitation invitation; try { @@ -291,15 +293,16 @@ public String acceptInvitation(@RequestParam("password") String password, return res; } - private String processErrorReload(String code, Model model, HttpServletResponse response, String errorCode, String error) { + private String processErrorReload(String code, Model model, RedirectAttributes redirectAttributes, HttpServletResponse response, String errorCode, String error) { ExpiringCode expiringCode = expiringCodeStore.retrieveCode(code, identityZoneManager.getCurrentIdentityZoneId()); Map codeData = JsonUtils.readValue(expiringCode.getData(), new TypeReference<>() { }); try { String newCode = expiringCodeStore.generateCode(expiringCode.getData(), new Timestamp(System.currentTimeMillis() + (10 * 60 * 1000)), expiringCode.getIntent(), identityZoneManager.getCurrentIdentityZoneId()).getCode(); - model.addAttribute(errorCode, error); - model.addAttribute("code", newCode); + // Use redirect attributes (which show up in model() for MockMvc in Spring 6) + redirectAttributes.addAttribute(errorCode, error); + redirectAttributes.addAttribute("code", newCode); //return an absolute, within the app, link return "redirect:/invitations/accept"; } catch (EmptyResultDataAccessException noProviderFound) { @@ -371,7 +374,7 @@ public String acceptLdapInvitation(@RequestParam("enterprise_username") String u private String handleUnprocessableEntity(Model model, HttpServletResponse response, String attributeKey, String attributeValue, String view) { model.addAttribute(attributeKey, attributeValue); - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + response.setStatus(HttpStatus.UNPROCESSABLE_CONTENT.value()); return view; } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/invitations/InvitationsEndpoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/invitations/InvitationsEndpoint.java index 183dae0dd00..3ad2367f25c 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/invitations/InvitationsEndpoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/invitations/InvitationsEndpoint.java @@ -13,6 +13,7 @@ import org.cloudfoundry.identity.uaa.util.UaaUrlUtils; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.MultitenantClientServices; +import org.jspecify.annotations.Nullable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -70,7 +71,7 @@ public InvitationsEndpoint(final ScimUserProvisioning scimUserProvisioning, @PostMapping(value = {"/invite_users", "/invite_users/"}, consumes = "application/json") public ResponseEntity inviteUsers(@RequestBody InvitationsRequest invitations, - @RequestParam(value = "client_id", required = false) String clientId, + @Nullable @RequestParam("client_id") String clientId, @RequestParam(value = "redirect_uri") String redirectUri) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/logging/LogSanitizerUtil.java b/server/src/main/java/org/cloudfoundry/identity/uaa/logging/LogSanitizerUtil.java index 8c6651532c9..403dff3f63d 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/logging/LogSanitizerUtil.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/logging/LogSanitizerUtil.java @@ -1,6 +1,6 @@ package org.cloudfoundry.identity.uaa.logging; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; public final class LogSanitizerUtil { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/login/ForcePasswordChangeController.java b/server/src/main/java/org/cloudfoundry/identity/uaa/login/ForcePasswordChangeController.java index 975ba488652..8560bb75962 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/login/ForcePasswordChangeController.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/login/ForcePasswordChangeController.java @@ -71,7 +71,7 @@ public String handleForcePasswordChange(Model model, private String handleUnprocessableEntity(Model model, HttpServletResponse response, String email, String message) { model.addAttribute("message", message); model.addAttribute("email", email); - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + response.setStatus(HttpStatus.UNPROCESSABLE_CONTENT.value()); return "force_password_change"; } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpoint.java index 3af256c3d22..e94de960e44 100755 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpoint.java @@ -37,6 +37,7 @@ import org.cloudfoundry.identity.uaa.zone.Links; import org.cloudfoundry.identity.uaa.zone.LoginConsent; import org.cloudfoundry.identity.uaa.zone.MultitenantClientServices; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.support.PropertiesLoaderUtils; @@ -828,7 +829,7 @@ private String extractUrlFromString(String s) { } @PostMapping(value = "/origin-chooser") - public String loginUsingOrigin(@RequestParam(required = false, name = LOGIN_HINT_ATTRIBUTE) String loginHint) { + public String loginUsingOrigin(@Nullable @RequestParam(name = LOGIN_HINT_ATTRIBUTE) String loginHint) { if (!StringUtils.hasText(loginHint)) { return "redirect:/login?discoveryPerformed=true"; } @@ -837,7 +838,7 @@ public String loginUsingOrigin(@RequestParam(required = false, name = LOGIN_HINT } @PostMapping(value = "/login/idp_discovery") - public String discoverIdentityProvider(@RequestParam String email, @RequestParam(required = false) String skipDiscovery, @RequestParam(required = false, name = LOGIN_HINT_ATTRIBUTE) String loginHint, @RequestParam(required = false, name = USERNAME_PARAMETER) String username, Model model, HttpSession session, HttpServletRequest request) { + public String discoverIdentityProvider(@RequestParam String email, @Nullable @RequestParam String skipDiscovery, @Nullable @RequestParam(name = LOGIN_HINT_ATTRIBUTE) String loginHint, @Nullable @RequestParam(name = USERNAME_PARAMETER) String username, Model model, HttpSession session, HttpServletRequest request) { ClientDetails clientDetails = null; if (hasSavedOauthAuthorizeRequest(session)) { SavedRequest savedRequest = SessionUtils.getSavedRequestSession(session); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginSecurityConfiguration.java b/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginSecurityConfiguration.java index c4b665a2ea1..9ed10948888 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginSecurityConfiguration.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginSecurityConfiguration.java @@ -28,6 +28,7 @@ import org.cloudfoundry.identity.uaa.web.FilterChainOrder; import org.cloudfoundry.identity.uaa.web.UaaFilterChain; import org.cloudfoundry.identity.uaa.web.UaaSavedRequestCache; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; @@ -36,7 +37,6 @@ import org.springframework.core.io.support.ResourcePropertySource; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; -import org.springframework.lang.Nullable; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.Customizer; @@ -61,8 +61,8 @@ import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.csrf.CsrfLogoutHandler; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; +import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.security.web.session.DisableEncodeUrlFilter; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.io.IOException; @@ -511,7 +511,7 @@ UaaFilterChain uiSecurity( ) throws Exception { ReAuthenticationRequiredFilter reAuthenticationRequiredFilter = new ReAuthenticationRequiredFilter(samlEntityID); var clientRedirectStateCache = new UaaSavedRequestCache(); - clientRedirectStateCache.setRequestMatcher(new AntPathRequestMatcher("/oauth/authorize**")); + clientRedirectStateCache.setRequestMatcher(PathPatternRequestMatcher.withDefaults().matcher("/oauth/authorize**")); // See: https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html#migrating-to-spring-security-6 var csrfRequestHandler = new CsrfTokenRequestAttributeHandler(); @@ -581,7 +581,7 @@ FilterRegistrationBean logoutFilter( csrfLogoutHandler, cookieClearingLogoutHandlerWithHandler ); - logoutFilter.setLogoutRequestMatcher(new AntPathRequestMatcher("/logout.do")); + logoutFilter.setLogoutRequestMatcher(PathPatternRequestMatcher.withDefaults().matcher("/logout.do")); FilterRegistrationBean bean = new FilterRegistrationBean<>(logoutFilter); bean.setEnabled(false); return bean; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/metrics/UaaMetricsFilter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/metrics/UaaMetricsFilter.java index 1679d7c1c0f..2489d6d9865 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/metrics/UaaMetricsFilter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/metrics/UaaMetricsFilter.java @@ -3,13 +3,15 @@ import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.util.TimeService; import org.cloudfoundry.identity.uaa.util.UaaYamlUtils; +import org.jspecify.annotations.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.jmx.export.notification.NotificationPublisher; import org.springframework.jmx.export.notification.NotificationPublisherAware; -import org.springframework.lang.NonNull; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import org.yaml.snakeyaml.Yaml; @@ -41,7 +43,7 @@ public class UaaMetricsFilter extends OncePerRequestFilter implements UaaMetrics private final TimeService timeService; private final IdleTimer inflight; private final Map perUriMetrics; - private final LinkedHashMap urlGroups; + private final LinkedHashMap urlGroups; private boolean enabled = true; private boolean perRequestMetrics; @@ -60,7 +62,7 @@ public UaaMetricsFilter( this.urlGroups = new LinkedHashMap<>(); List groups = getUrlGroups(); groups.forEach( - group -> urlGroups.put(new AntPathRequestMatcher(group.getPattern()), group) + group -> urlGroups.put(PathPatternRequestMatcher.pathPattern(group.getPattern()), group) ); this.inflight = new IdleTimer(); } @@ -115,8 +117,34 @@ protected MetricsQueue getMetricsQueue(String uri) { protected UrlGroup getUriGroup(final HttpServletRequest request) { if (urlGroups != null) { String uri = request.getRequestURI(); - for (Map.Entry entry : urlGroups.entrySet()) { - if (entry.getKey().matches(request)) { + // Create a wrapper request that returns the path without context for matching + HttpServletRequest matchRequest = new jakarta.servlet.http.HttpServletRequestWrapper(request) { + @Override + public String getRequestURI() { + String contextPath = request.getContextPath(); + String fullUri = request.getRequestURI(); + if (StringUtils.hasText(contextPath) && fullUri.startsWith(contextPath)) { + return fullUri.substring(contextPath.length()); + } + return fullUri; + } + + @Override + public String getContextPath() { + // Return empty context path since we've already stripped it from the URI + // This is required for Spring Boot 4's PathPatternRequestMatcher validation + return ""; + } + + @Override + public String getServletPath() { + // Return the stripped URI as servlet path for proper path resolution + return getRequestURI(); + } + }; + + for (Map.Entry entry : urlGroups.entrySet()) { + if (entry.getKey().matches(matchRequest)) { UrlGroup group = entry.getValue(); if (logger.isDebugEnabled()) { logger.debug("Successfully matched URI: %s to a group: %s".formatted(uri, group.getGroup())); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/CheckTokenEndpoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/CheckTokenEndpoint.java index 61637252c26..76e70fca94e 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/CheckTokenEndpoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/CheckTokenEndpoint.java @@ -12,6 +12,7 @@ import org.cloudfoundry.identity.uaa.oauth.token.Claims; import org.cloudfoundry.identity.uaa.util.TimeService; import org.cloudfoundry.identity.uaa.util.UaaTokenUtils; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; @@ -83,8 +84,8 @@ public void afterPropertiesSet() { @PostMapping("/check_token") @ResponseBody @Deprecated - public Claims checkToken(@RequestParam(name = "token", required = false, defaultValue = "") String value, - @RequestParam(required = false, defaultValue = "") List scopes, + public Claims checkToken(@Nullable @RequestParam(name = "token", defaultValue = "") String value, + @Nullable @RequestParam(defaultValue = "") List scopes, HttpServletRequest request) throws HttpRequestMethodNotSupportedException { if (!hadParsedAllArgs(request)) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/RemoteTokenServices.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/RemoteTokenServices.java index 6db0202a3f7..e3a05702037 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/RemoteTokenServices.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/RemoteTokenServices.java @@ -42,6 +42,7 @@ import org.springframework.web.client.RestTemplate; import java.io.IOException; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Collections; @@ -79,9 +80,9 @@ public RemoteTokenServices() { ((RestTemplate) restTemplate).setErrorHandler(new DefaultResponseErrorHandler() { @Override // Ignore 400 - public void handleError(ClientHttpResponse response) throws IOException { + public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { if (response.getStatusCode().value() != 400) { - super.handleError(response); + super.handleError(url, method, response); } } }); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/client/OAuth2ClientContextFilter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/client/OAuth2ClientContextFilter.java index 27a7a0f9e44..fbc2495cd47 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/client/OAuth2ClientContextFilter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/client/OAuth2ClientContextFilter.java @@ -8,7 +8,6 @@ import org.springframework.security.web.util.ThrowableAnalyzer; import org.springframework.util.Assert; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.NestedServletException; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; @@ -75,7 +74,7 @@ public void doFilter(ServletRequest servletRequest, if (ex instanceof RuntimeException runtimeException) { throw runtimeException; } - throw new NestedServletException("Unhandled exception", ex); + throw new ServletException("Unhandled exception", ex); } } } @@ -96,7 +95,7 @@ protected void redirectUser(UserRedirectRequiredException e, String redirectUri = e.getRedirectUri(); UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(redirectUri); + .fromUriString(redirectUri); Map requestParams = e.getRequestParams(); for (Map.Entry param : requestParams.entrySet()) { builder.queryParam(param.getKey(), param.getValue()); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/error/DefaultOAuth2ExceptionRenderer.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/error/DefaultOAuth2ExceptionRenderer.java index f0ce2f63181..580dd11db15 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/error/DefaultOAuth2ExceptionRenderer.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/error/DefaultOAuth2ExceptionRenderer.java @@ -10,6 +10,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpResponse; +import org.springframework.util.MimeTypeUtils; +import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.web.HttpMediaTypeNotAcceptableException; @@ -72,7 +74,7 @@ private void writeWithMessageConverters(Object returnValue, HttpInputMessage inp if (acceptedMediaTypes.isEmpty()) { acceptedMediaTypes = Collections.singletonList(MediaType.ALL); } - MediaType.sortByQualityValue(acceptedMediaTypes); + MimeTypeUtils.sortBySpecificity(acceptedMediaTypes); Class returnValueType = returnValue.getClass(); List allSupportedMediaTypes = new ArrayList<>(); for (MediaType acceptedMediaType : acceptedMediaTypes) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/error/OAuth2AuthenticationEntryPoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/error/OAuth2AuthenticationEntryPoint.java index 1e09dc8c091..d47bbaa976f 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/error/OAuth2AuthenticationEntryPoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/error/OAuth2AuthenticationEntryPoint.java @@ -45,7 +45,7 @@ public void commence(HttpServletRequest request, HttpServletResponse response, A protected ResponseEntity enhanceResponse(ResponseEntity response, Exception exception) { HttpHeaders headers = response.getHeaders(); String existing = null; - if (headers.containsKey(WWW_AUTHENTICATE)) { + if (headers.containsHeader(WWW_AUTHENTICATE)) { existing = extractTypePrefix(headers.getFirst(WWW_AUTHENTICATE)); } StringBuilder builder = new StringBuilder(); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/TokenExchangeGranter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/TokenExchangeGranter.java index 38fd303545b..d03801c4bed 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/TokenExchangeGranter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/TokenExchangeGranter.java @@ -87,7 +87,7 @@ protected Authentication validateRequest(TokenRequest request) { throw new InvalidGrantException("Invalid audience"); } String requiredImpersonationAuthority = - String.format(TOKEN_EXCHANGE_IMPERSONATE_CLIENT_PERMISSION, audience); + TOKEN_EXCHANGE_IMPERSONATE_CLIENT_PERMISSION.formatted(audience); long count = client.getAuthorities()==null ? 0l : client .getAuthorities() .stream() diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderAliasHandler.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderAliasHandler.java index 40da5581fd1..e7d3fe374d7 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderAliasHandler.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderAliasHandler.java @@ -10,12 +10,12 @@ import org.cloudfoundry.identity.uaa.alias.EntityAliasFailedException; import org.cloudfoundry.identity.uaa.alias.EntityAliasHandler; import org.cloudfoundry.identity.uaa.zone.IdentityZoneProvisioning; +import org.jspecify.annotations.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.http.HttpStatus; -import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; @Component diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpoints.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpoints.java index b34e1f01c2d..cdebb86c1c0 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpoints.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpoints.java @@ -26,6 +26,7 @@ import org.cloudfoundry.identity.uaa.util.ObjectUtils; import org.cloudfoundry.identity.uaa.util.UaaStringUtils; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -66,13 +67,7 @@ import static org.cloudfoundry.identity.uaa.constants.OriginKeys.SAML; import static org.cloudfoundry.identity.uaa.constants.OriginKeys.UAA; import static org.cloudfoundry.identity.uaa.util.UaaStringUtils.getCleanedUserControlString; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.CONFLICT; -import static org.springframework.http.HttpStatus.CREATED; -import static org.springframework.http.HttpStatus.EXPECTATION_FAILED; -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; -import static org.springframework.http.HttpStatus.OK; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static org.springframework.http.HttpStatus.*; import static org.springframework.util.StringUtils.hasText; @RequestMapping("/identity-providers") @@ -122,7 +117,7 @@ public IdentityProviderEndpoints( } @PostMapping({"/", ""}) - public ResponseEntity createIdentityProvider(@RequestBody IdentityProvider body, @RequestParam(required = false, defaultValue = "false") boolean rawConfig) { + public ResponseEntity createIdentityProvider(@RequestBody IdentityProvider body, @Nullable @RequestParam(defaultValue = "false") boolean rawConfig) { body.setSerializeConfigRaw(rawConfig); String zoneId = identityZoneManager.getCurrentIdentityZoneId(); body.setIdentityZoneId(zoneId); @@ -130,7 +125,7 @@ public ResponseEntity createIdentityProvider(@RequestBody Iden configValidator.validate(body); } catch (IllegalArgumentException e) { logger.debug("IdentityProvider[origin=%s; zone=%s] - Configuration validation error.".formatted(body.getOriginKey(), body.getIdentityZoneId()), e); - return new ResponseEntity<>(body, UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(body, UNPROCESSABLE_CONTENT); } if (SAML.equals(body.getType())) { SamlIdentityProviderDefinition definition = ObjectUtils.castInstance(body.getConfig(), SamlIdentityProviderDefinition.class); @@ -141,7 +136,7 @@ public ResponseEntity createIdentityProvider(@RequestBody Iden } if (!idpAliasHandler.aliasPropertiesAreValid(body, null)) { - return new ResponseEntity<>(body, UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(body, UNPROCESSABLE_CONTENT); } // persist IdP and create alias if necessary @@ -150,20 +145,20 @@ public ResponseEntity createIdentityProvider(@RequestBody Iden @DeleteMapping(value = "{id}") @Transactional - public ResponseEntity deleteIdentityProvider(@PathVariable String id, @RequestParam(required = false, defaultValue = "false") boolean rawConfig) { + public ResponseEntity deleteIdentityProvider(@PathVariable String id, @Nullable @RequestParam(defaultValue = "false") boolean rawConfig) { String identityZoneId = identityZoneManager.getCurrentIdentityZoneId(); IdentityProvider existing = identityProviderProvisioning.retrieve(id, identityZoneId); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (publisher == null || existing == null) { - return new ResponseEntity<>(UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(UNPROCESSABLE_CONTENT); } // reject deletion if the IdP has an alias, but alias feature is disabled final boolean idpHasAlias = hasText(existing.getAliasZid()); if (idpHasAlias && !aliasEntitiesEnabled) { - return new ResponseEntity<>(UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(UNPROCESSABLE_CONTENT); } // delete the IdP @@ -194,7 +189,7 @@ public ResponseEntity deleteIdentityProvider(@PathVariable Str } @PutMapping(value = "{id}") - public ResponseEntity updateIdentityProvider(@PathVariable String id, @RequestBody IdentityProvider body, @RequestParam(required = false, defaultValue = "false") boolean rawConfig) { + public ResponseEntity updateIdentityProvider(@PathVariable String id, @RequestBody IdentityProvider body, @Nullable @RequestParam(defaultValue = "false") boolean rawConfig) { body.setSerializeConfigRaw(rawConfig); String zoneId = identityZoneManager.getCurrentIdentityZoneId(); IdentityProvider existing = identityProviderProvisioning.retrieve(id, zoneId); @@ -205,7 +200,7 @@ public ResponseEntity updateIdentityProvider(@PathVariable Str configValidator.validate(body); } catch (IllegalArgumentException e) { logger.debug("IdentityProvider[origin=%s; zone=%s] - Configuration validation error for update.".formatted(body.getOriginKey(), body.getIdentityZoneId()), e); - return new ResponseEntity<>(body, UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(body, UNPROCESSABLE_CONTENT); } if (!idpAliasHandler.aliasPropertiesAreValid(body, existing)) { @@ -213,7 +208,7 @@ public ResponseEntity updateIdentityProvider(@PathVariable Str logger.warn("IdentityProvider[origin={}; zone={}] - Alias ID and/or ZID changed during update of IdP with alias.", getCleanedUserControlString(body.getOriginKey()), getCleanedUserControlString(body.getIdentityZoneId())); } - return new ResponseEntity<>(body, UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(body, UNPROCESSABLE_CONTENT); } if (SAML.equals(body.getType())) { @@ -254,7 +249,7 @@ private ResponseEntity persistIdentityProviderChange(IdentityP getCleanedUserControlString(body.getOriginKey()), getCleanedUserControlString(body.getIdentityZoneId()), status == CREATED ? "creating" : "updating"); } - return new ResponseEntity<>(body, UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(body, UNPROCESSABLE_CONTENT); } updatedIdp.setSerializeConfigRaw(rawConfig); setAuthMethod(updatedIdp); @@ -269,16 +264,16 @@ public ResponseEntity updateIdentityProviderStatus(@Path IdentityProvider existing = identityProviderProvisioning.retrieve(id, zoneId); if (body.getRequirePasswordChange() == null || !body.getRequirePasswordChange()) { logger.debug("Invalid payload. The property requirePasswordChangeRequired needs to be set"); - return new ResponseEntity<>(body, UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(body, UNPROCESSABLE_CONTENT); } if (!UAA.equals(existing.getType())) { logger.debug("Invalid operation. This operation is not supported on external IDP"); - return new ResponseEntity<>(body, UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(body, UNPROCESSABLE_CONTENT); } UaaIdentityProviderDefinition uaaIdentityProviderDefinition = ObjectUtils.castInstance(existing.getConfig(), UaaIdentityProviderDefinition.class); if (uaaIdentityProviderDefinition == null || uaaIdentityProviderDefinition.getPasswordPolicy() == null) { logger.debug("IDP does not have an existing PasswordPolicy. Operation not supported"); - return new ResponseEntity<>(body, UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(body, UNPROCESSABLE_CONTENT); } uaaIdentityProviderDefinition.getPasswordPolicy().setPasswordNewerThan(new Date(System.currentTimeMillis())); identityProviderProvisioning.update(existing, zoneId); @@ -293,9 +288,9 @@ public ResponseEntity updateIdentityProviderStatus(@Path @GetMapping({"/", ""}) public ResponseEntity> retrieveIdentityProviders( - @RequestParam(value = "active_only", required = false) String activeOnly, - @RequestParam(required = false, defaultValue = "false") boolean rawConfig, - @RequestParam(required = false, defaultValue = "") String originKey) { + @Nullable @RequestParam("active_only") String activeOnly, + @Nullable @RequestParam(defaultValue = "false") boolean rawConfig, + @Nullable @RequestParam(defaultValue = "") String originKey) { boolean retrieveActiveOnly = Boolean.parseBoolean(activeOnly); List identityProviderList; if (UaaStringUtils.isNotEmpty(originKey)) { @@ -312,7 +307,7 @@ public ResponseEntity> retrieveIdentityProviders( } @GetMapping(value = "{id}") - public ResponseEntity retrieveIdentityProvider(@PathVariable String id, @RequestParam(required = false, defaultValue = "false") boolean rawConfig) { + public ResponseEntity retrieveIdentityProvider(@PathVariable String id, @Nullable @RequestParam(defaultValue = "false") boolean rawConfig) { IdentityProvider identityProvider = identityProviderProvisioning.retrieve(id, identityZoneManager.getCurrentIdentityZoneId()); identityProvider.setSerializeConfigRaw(rawConfig); setAuthMethod(identityProvider); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/ProcessLdapProperties.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/ProcessLdapProperties.java index 1ee642ae415..55797e70f08 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/ProcessLdapProperties.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/ProcessLdapProperties.java @@ -23,6 +23,8 @@ import org.springframework.ldap.core.support.DirContextAuthenticationStrategy; import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy; +import javax.net.ssl.HttpsURLConnection; + import javax.net.ssl.SSLSocketFactory; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -107,7 +109,13 @@ public DirContextAuthenticationStrategy getAuthenticationStrategy() throws Class default: throw new IllegalArgumentException(tlsConfig); } - tlsStrategy.setHostnameVerifier(new NoopHostnameVerifier()); + // - When skipSslVerification=false (default): Uses JDK default hostname verification (secure) + // - When skipSslVerification=true: Disables hostname verification via NoopHostnameVerifier + if (isDisableSslVerification()) { + tlsStrategy.setHostnameVerifier(new NoopHostnameVerifier()); + } else { + tlsStrategy.setHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier()); + } tlsStrategy.setSslSocketFactory(getSSLSocketFactory()); return tlsStrategy; } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/BootstrapSamlIdentityProviderData.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/BootstrapSamlIdentityProviderData.java index dec15bf8b8d..639598ced18 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/BootstrapSamlIdentityProviderData.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/BootstrapSamlIdentityProviderData.java @@ -185,7 +185,7 @@ public void setIdentityProviders(Map> providers) { IdentityProvider provider = parseSamlProvider(def); if (def.getType() == SamlIdentityProviderDefinition.MetadataLocation.DATA) { RelyingPartyRegistration metadataDelegate = samlConfigurator.getExtendedMetadataDelegate(def); - def.setIdpEntityId(metadataDelegate.getAssertingPartyDetails().getEntityId()); + def.setIdpEntityId(metadataDelegate.getAssertingPartyMetadata().getEntityId()); } IdentityProviderWrapper wrapper = new IdentityProviderWrapper<>(provider); wrapper.setOverride(override == null || override); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProvider.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5AuthenticationProvider.java similarity index 96% rename from server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProvider.java rename to server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5AuthenticationProvider.java index 4f33625a1f0..27825d0d969 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProvider.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5AuthenticationProvider.java @@ -16,13 +16,15 @@ package org.cloudfoundry.identity.uaa.provider.saml; -import jakarta.annotation.Nonnull; +import javax.annotation.Nonnull; + import lombok.Getter; -import net.shibboleth.utilities.java.support.xml.ParserPool; +import net.shibboleth.shared.xml.ParserPool; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.ZoneAware; +import org.jspecify.annotations.NonNull; import org.opensaml.core.config.ConfigurationService; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.config.XMLObjectProviderRegistry; @@ -103,13 +105,17 @@ import static org.cloudfoundry.identity.uaa.util.UaaUrlUtils.normalizeUrlForPortComparison; /** - * This was copied from Spring Security, and modified to work with Open SAML 4.0.x - * The original class only works with Open SAML 4.1.x+ + * This was originally copied from Spring Security, and modified to work with Open SAML 4.0.x, + * then further updated for Open SAML 5.x compatibility. *

- * Once we can move to the spring-security version of OpenSaml4AuthenticationProvider, - * this class should be removed, along with OpenSamlDecryptionUtils and OpenSamlVerificationUtils. + * Key changes from OpenSAML 4 to 5: + * - {@code net.shibboleth.utilities.java.support} packages moved to {@code net.shibboleth.shared} + * - {@code SAML20AssertionValidator} constructor gains a 4th {@code AssertionValidator} parameter + * - {@code ValidationContext.getValidationFailureMessage()} renamed to {@code getValidationFailureMessages()} + * - {@code ConditionValidator.validate()} now throws {@code AssertionValidationException} + * - {@code BearerSubjectConfirmationValidator.validateAddress()} removed (address validation dropped) */ -public final class OpenSaml4AuthenticationProvider implements AuthenticationProvider, ZoneAware { +public final class OpenSaml5AuthenticationProvider implements AuthenticationProvider, ZoneAware { static { SamlConfiguration.setupOpenSaml(); @@ -144,9 +150,9 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv private Converter responseAuthenticationConverter = createDefaultResponseAuthenticationConverter(); /** - * Creates an {@link OpenSaml4AuthenticationProvider} + * Creates an {@link OpenSaml5AuthenticationProvider} */ - public OpenSaml4AuthenticationProvider() { + public OpenSaml5AuthenticationProvider() { XMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class); this.responseUnmarshaller = (ResponseUnmarshaller) registry.getUnmarshallerFactory() .getUnmarshaller(Response.DEFAULT_ELEMENT_NAME); @@ -160,7 +166,7 @@ public OpenSaml4AuthenticationProvider() { * {@link #createDefaultResponseValidator()}, like so: * *

-     * OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();
+     * OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();
      * provider.setResponseValidator(responseToken -> {
      * 		Saml2ResponseValidatorResult result = createDefaultResponseValidator()
      * 			.convert(responseToken)
@@ -235,7 +241,7 @@ public static Converter createDefau
                 result = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, message));
             }
             String assertingPartyEntityId = token.getRelyingPartyRegistration()
-                    .getAssertingPartyDetails()
+                    .getAssertingPartyMetadata()
                     .getEntityId();
             if (!StringUtils.hasText(issuer) || !issuer.equals(assertingPartyEntityId)) {
                 String message = "Invalid issuer [%s] for SAML response [%s]".formatted(issuer, response.getID());
@@ -574,7 +580,7 @@ private static Converter createAss
             }
             String message = "Invalid assertion [%s] for SAML response [%s]: %s".formatted(assertion.getID(),
                     assertion.getParent() != null ? ((Response) assertion.getParent()).getID() : assertion.getID(),
-                    context.getValidationFailureMessage());
+                    String.join("; ", context.getValidationFailureMessages()));
             return Saml2ResponseValidatorResult.failure(new Saml2Error(errorCode, message));
         };
     }
@@ -591,7 +597,7 @@ private static ValidationContext createValidationContext(AssertionToken assertio
         } else {
             recipient = relyingPartyRegistration.getAssertionConsumerServiceLocation();
         }
-        String assertingPartyEntityId = relyingPartyRegistration.getAssertingPartyDetails().getEntityId();
+        String assertingPartyEntityId = relyingPartyRegistration.getAssertingPartyMetadata().getEntityId();
         Map params = new HashMap<>();
         Assertion assertion = assertionToken.getAssertion();
         if (!saml2Bearer && assertionContainsInResponseTo(assertion)) {
@@ -601,6 +607,8 @@ private static ValidationContext createValidationContext(AssertionToken assertio
         params.put(SAML2AssertionValidationParameters.COND_VALID_AUDIENCES, Collections.singleton(audience));
         params.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, Collections.singleton(recipient));
         params.put(SAML2AssertionValidationParameters.VALID_ISSUERS, Collections.singleton(assertingPartyEntityId));
+        // Disable address checking - we don't track valid client addresses
+        params.put(SAML2AssertionValidationParameters.SC_CHECK_ADDRESS, false);
         paramsConsumer.accept(params);
         return new ValidationContext(params);
     }
@@ -681,18 +689,11 @@ public ValidationResult validate(Condition condition, Assertion assertion, Valid
                 }
             });
             conditions.add(new ProxyRestrictionConditionValidator());
-            subjects.add(new BearerSubjectConfirmationValidator() {
-                @Override
-                protected ValidationResult validateAddress(SubjectConfirmation confirmation, Assertion assertion,
-                        ValidationContext context, boolean required) {
-                    // applications should validate their own addresses - gh-7514
-                    return ValidationResult.VALID;
-                }
-            });
+            subjects.add(new BearerSubjectConfirmationValidator());
         }
 
         private static final SAML20AssertionValidator attributeValidator = new SAML20AssertionValidator(conditions,
-                subjects, statements, null, null) {
+                subjects, statements, null, null, null) {
             @Nonnull
             @Override
             protected ValidationResult validateSignature(Assertion token, ValidationContext context) {
@@ -701,7 +702,7 @@ protected ValidationResult validateSignature(Assertion token, ValidationContext
         };
 
         static SAML20AssertionValidator createSignatureValidator(SignatureTrustEngine engine) {
-            return new SAML20AssertionValidator(new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), engine,
+            return new SAML20AssertionValidator(new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), null, engine,
                     validator) {
                 @Nonnull
                 @Override
diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSamlDecryptionUtils.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSamlDecryptionUtils.java
index b4350efdc8e..8cf48ba4003 100644
--- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSamlDecryptionUtils.java
+++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSamlDecryptionUtils.java
@@ -42,8 +42,8 @@
 import java.util.Collection;
 
 /**
- * This class was copied from Spring Security 5.6.0 to get the OpenSaml4AuthenticationProvider to work.
- * It should be removed once we are able to more to the spring-security version of OpenSaml4AuthenticationProvider.
+ * This class was copied from Spring Security 5.6.0 to get the OpenSaml5AuthenticationProvider to work.
+ * It should be removed once we are able to move to the spring-security version of OpenSaml5AuthenticationProvider.
  * 

* Utility methods for decrypting SAML components with OpenSAML * diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSamlVerificationUtils.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSamlVerificationUtils.java index 2284afb6045..0a28068b90f 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSamlVerificationUtils.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSamlVerificationUtils.java @@ -16,7 +16,7 @@ package org.cloudfoundry.identity.uaa.provider.saml; -import net.shibboleth.utilities.java.support.resolver.CriteriaSet; +import net.shibboleth.shared.resolver.CriteriaSet; import org.opensaml.core.criterion.EntityIdCriterion; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.criterion.ProtocolCriterion; @@ -48,8 +48,8 @@ import java.util.Set; /** - * This class was copied from Spring Security 5.6.0 to get the OpenSaml4AuthenticationProvider to work. - * It should be removed once we are able to more to the spring-security version of OpenSaml4AuthenticationProvider. + * This class was copied from Spring Security 5.6.0 to get the OpenSaml5AuthenticationProvider to work. + * It should be removed once we are able to move to the spring-security version of OpenSaml5AuthenticationProvider. *

* Utility methods for verifying SAML component signatures with OpenSAML *

@@ -65,11 +65,11 @@ static VerifierPartial verifySignature(StatusResponseType object, RelyingPartyRe static SignatureTrustEngine trustEngine(RelyingPartyRegistration registration) { Set credentials = new HashSet<>(); - Collection keys = registration.getAssertingPartyDetails().getVerificationX509Credentials(); + Collection keys = registration.getAssertingPartyMetadata().getVerificationX509Credentials(); for (Saml2X509Credential key : keys) { BasicX509Credential cred = new BasicX509Credential(key.getCertificate()); cred.setUsageType(UsageType.SIGNING); - cred.setEntityId(registration.getAssertingPartyDetails().getEntityId()); + cred.setEntityId(registration.getAssertingPartyMetadata().getEntityId()); credentials.add(cred); } CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/RelyingPartyRegistrationBuilder.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/RelyingPartyRegistrationBuilder.java index be03059b82e..efabfb2742c 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/RelyingPartyRegistrationBuilder.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/RelyingPartyRegistrationBuilder.java @@ -87,7 +87,7 @@ public static RelyingPartyRegistration buildRelyingPartyRegistration(Params buil }) // alter the default value of the APs wantAuthnRequestsSigned, // to reflect the UAA configured desire to always sign/or-not the AuthnRequest - .assertingPartyDetails(details -> { + .assertingPartyMetadata(details -> { details.wantAuthnRequestsSigned(params.requestSigned); details.signingAlgorithms(alg -> { alg.clear(); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2BearerGrantAuthenticationConverter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2BearerGrantAuthenticationConverter.java index 37381cf0111..965cdbb84b4 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2BearerGrantAuthenticationConverter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2BearerGrantAuthenticationConverter.java @@ -17,7 +17,7 @@ package org.cloudfoundry.identity.uaa.provider.saml; import lombok.extern.slf4j.Slf4j; -import net.shibboleth.utilities.java.support.xml.ParserPool; +import net.shibboleth.shared.xml.ParserPool; import org.cloudfoundry.identity.uaa.authentication.BackwardsCompatibleTokenEndpointAuthenticationFilter; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; @@ -61,7 +61,7 @@ import java.util.Optional; import java.util.function.Consumer; -import static org.cloudfoundry.identity.uaa.provider.saml.OpenSaml4AuthenticationProvider.createDefaultAssertionValidatorWithParameters; +import static org.cloudfoundry.identity.uaa.provider.saml.OpenSaml5AuthenticationProvider.createDefaultAssertionValidatorWithParameters; /** * This {@link AuthenticationConverter} is used in the SAML2 Bearer Grant exchange in {@link BackwardsCompatibleTokenEndpointAuthenticationFilter} @@ -89,13 +89,13 @@ public final class Saml2BearerGrantAuthenticationConverter implements Authentica parserPool = registry.getParserPool(); } - private final Converter assertionSignatureValidator = OpenSaml4AuthenticationProvider.createDefaultAssertionSignatureValidator(); + private final Converter assertionSignatureValidator = OpenSaml5AuthenticationProvider.createDefaultAssertionSignatureValidator(); - private final Consumer assertionElementsDecrypter = OpenSaml4AuthenticationProvider.createDefaultAssertionElementsDecrypter(); + private final Consumer assertionElementsDecrypter = OpenSaml5AuthenticationProvider.createDefaultAssertionElementsDecrypter(); - private final Converter assertionValidator = createDefaultAssertionValidator(); + private final Converter assertionValidator = createDefaultAssertionValidator(); - private final Converter assertionTokenAuthenticationConverter = createDefaultAssertionAuthenticationConverter(); + private final Converter assertionTokenAuthenticationConverter = createDefaultAssertionAuthenticationConverter(); private final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver; private final IdentityZoneManager identityZoneManager; @@ -120,7 +120,7 @@ public Saml2BearerGrantAuthenticationConverter(RelyingPartyRegistrationResolver * * @return the default assertion validator strategy */ - public static Converter createDefaultAssertionValidator() { + public static Converter createDefaultAssertionValidator() { return createDefaultAssertionValidatorWithParameters( params -> params.put(SAML2AssertionValidationParameters.CLOCK_SKEW, Duration.ofMinutes(5)), true); @@ -132,13 +132,13 @@ public static Converter createDefaultAssertionAuthenticationConverter() { + static Converter createDefaultAssertionAuthenticationConverter() { return assertionToken -> { Assertion assertion = assertionToken.getAssertion(); Saml2AuthenticationToken token = assertionToken.getToken(); String username = assertion.getSubject().getNameID().getValue(); - Map> attributes = OpenSaml4AuthenticationProvider.getAssertionAttributes(assertion); - List sessionIndexes = OpenSaml4AuthenticationProvider.getSessionIndexes(assertion); + Map> attributes = OpenSaml5AuthenticationProvider.getAssertionAttributes(assertion); + List sessionIndexes = OpenSaml5AuthenticationProvider.getSessionIndexes(assertion); DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes, sessionIndexes); String registrationId = token.getRelyingPartyRegistration().getRegistrationId(); @@ -192,7 +192,7 @@ public Authentication authenticate(Authentication authentication) throws Authent Assertion assertion = parseAssertion(serializedAssertion); process(token, assertion); AbstractAuthenticationToken authenticationResponse = this.assertionTokenAuthenticationConverter - .convert(new OpenSaml4AuthenticationProvider.AssertionToken(assertion, token)); + .convert(new OpenSaml5AuthenticationProvider.AssertionToken(assertion, token)); if (authenticationResponse != null) { authenticationResponse.setDetails(authentication.getDetails()); } @@ -200,7 +200,7 @@ public Authentication authenticate(Authentication authentication) throws Authent } catch (Saml2AuthenticationException ex) { throw ex; } catch (Exception ex) { - throw OpenSaml4AuthenticationProvider.createAuthenticationException(Saml2ErrorCodes.INTERNAL_VALIDATION_ERROR, ex.getMessage(), ex); + throw OpenSaml5AuthenticationProvider.createAuthenticationException(Saml2ErrorCodes.INTERNAL_VALIDATION_ERROR, ex.getMessage(), ex); } } @@ -211,7 +211,7 @@ private static Assertion parseAssertion(String assertion) throws Saml2Exception, Element element = document.getDocumentElement(); return (Assertion) assertionUnmarshaller.unmarshall(element); } catch (Exception ex) { - throw OpenSaml4AuthenticationProvider.createAuthenticationException(Saml2ErrorCodes.INVALID_ASSERTION, "Unable to parse bearer assertion", ex); + throw OpenSaml5AuthenticationProvider.createAuthenticationException(Saml2ErrorCodes.INVALID_ASSERTION, "Unable to parse bearer assertion", ex); } } @@ -222,7 +222,7 @@ protected static Response parseSamlResponse(String samlResponse) throws Saml2Exc Element element = document.getDocumentElement(); return (Response) responseUnMarshaller.unmarshall(element); } catch (Exception ex) { - throw OpenSaml4AuthenticationProvider.createAuthenticationException(Saml2ErrorCodes.INVALID_RESPONSE, "Unable to parse saml response", ex); + throw OpenSaml5AuthenticationProvider.createAuthenticationException(Saml2ErrorCodes.INVALID_RESPONSE, "Unable to parse saml response", ex); } } @@ -252,14 +252,14 @@ private void process(Saml2AuthenticationToken token, Assertion assertion) { String issuer = getIssuer(assertion); log.debug("Processing SAML response from {}", issuer); - OpenSaml4AuthenticationProvider.AssertionToken assertionToken = new OpenSaml4AuthenticationProvider.AssertionToken(assertion, token); + OpenSaml5AuthenticationProvider.AssertionToken assertionToken = new OpenSaml5AuthenticationProvider.AssertionToken(assertion, token); Saml2ResponseValidatorResult result = this.assertionSignatureValidator.convert(assertionToken); if (assertion.isSigned()) { - this.assertionElementsDecrypter.accept(new OpenSaml4AuthenticationProvider.AssertionToken(assertion, token)); + this.assertionElementsDecrypter.accept(new OpenSaml5AuthenticationProvider.AssertionToken(assertion, token)); } else if (hasEncryptedElements(assertion)) { - this.assertionElementsDecrypter.accept(new OpenSaml4AuthenticationProvider.AssertionToken(assertion, token)); + this.assertionElementsDecrypter.accept(new OpenSaml5AuthenticationProvider.AssertionToken(assertion, token)); } else { - throw OpenSaml4AuthenticationProvider.createAuthenticationException( + throw OpenSaml5AuthenticationProvider.createAuthenticationException( Saml2ErrorCodes.INVALID_SIGNATURE, "Assertion is missing a signature.", null @@ -267,7 +267,7 @@ private void process(Saml2AuthenticationToken token, Assertion assertion) { } result = result.concat(this.assertionValidator.convert(assertionToken)); - if (!OpenSaml4AuthenticationProvider.hasName(assertion)) { + if (!OpenSaml5AuthenticationProvider.hasName(assertion)) { Saml2Error error = new Saml2Error(Saml2ErrorCodes.SUBJECT_NOT_FOUND, "Assertion [" + assertion.getID() + "] is missing a subject"); result = result.concat(error); @@ -281,7 +281,7 @@ private void process(Saml2AuthenticationToken token, Assertion assertion) { log.debug("Found {} validation errors in SAML assertion [{}}]", errors.size(), assertion.getID()); } Saml2Error first = errors.iterator().next(); - throw OpenSaml4AuthenticationProvider.createAuthenticationException(first.getErrorCode(), first.getDescription(), null); + throw OpenSaml5AuthenticationProvider.createAuthenticationException(first.getErrorCode(), first.getDescription(), null); } else { log.debug("Successfully processed SAML Assertion [{}]", assertion.getID()); } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2Utils.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2Utils.java index 076fd35fb73..8fc3b2d0780 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2Utils.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2Utils.java @@ -59,7 +59,7 @@ public static byte[] samlBearerDecode(String s) { try { return Base64.getUrlDecoder().decode(s); } catch (IllegalArgumentException ex) { - throw OpenSaml4AuthenticationProvider.createAuthenticationException(Saml2ErrorCodes.INVALID_ASSERTION, "Unable to urlBase64Decode bearer assertion", ex); + throw OpenSaml5AuthenticationProvider.createAuthenticationException(Saml2ErrorCodes.INVALID_ASSERTION, "Unable to urlBase64Decode bearer assertion", ex); } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java index bdd1aa99886..37863ff6085 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlAuthenticationFilterConfig.java @@ -22,10 +22,10 @@ import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter; import org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter; -import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver; +import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml5AuthenticationRequestResolver; import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter; -import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutRequestResolver; -import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutResponseResolver; +import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutRequestResolver; +import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutResponseResolver; import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter; import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver; import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter; @@ -36,7 +36,7 @@ import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.security.web.csrf.CsrfLogoutHandler; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import jakarta.servlet.Filter; @@ -53,9 +53,9 @@ public class SamlAuthenticationFilterConfig { */ @Bean FilterRegistrationBean saml2WebSsoAuthenticationRequestFilter(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) { - OpenSaml4AuthenticationRequestResolver openSaml4AuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationResolver); + OpenSaml5AuthenticationRequestResolver openSaml5AuthenticationRequestResolver = new OpenSaml5AuthenticationRequestResolver(relyingPartyRegistrationResolver); - Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(openSaml4AuthenticationRequestResolver); + Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(openSaml5AuthenticationRequestResolver); FilterRegistrationBean bean = new FilterRegistrationBean<>(filter); bean.setEnabled(false); return bean; @@ -93,12 +93,12 @@ AuthenticationProvider samlAuthenticationProvider(IdentityZoneManager identityZo new SamlUaaResponseAuthenticationConverter(identityZoneManager, samlUaaAuthenticationUserManager); samlResponseAuthenticationConverter.setApplicationEventPublisher(applicationEventPublisher); - OpenSaml4AuthenticationProvider samlResponseAuthenticationProvider = new OpenSaml4AuthenticationProvider(); + OpenSaml5AuthenticationProvider samlResponseAuthenticationProvider = new OpenSaml5AuthenticationProvider(); samlResponseAuthenticationProvider.setResponseAuthenticationConverter(samlResponseAuthenticationConverter); // This validator ignores wraps the default validator and ignores InResponseTo errors, if configured UaaInResponseToHandlingResponseValidator uaaInResponseToHandlingResponseValidator = - new UaaInResponseToHandlingResponseValidator(OpenSaml4AuthenticationProvider.createDefaultResponseValidator(), samlConfigProps.getDisableInResponseToCheck()); + new UaaInResponseToHandlingResponseValidator(OpenSaml5AuthenticationProvider.createDefaultResponseValidator(), samlConfigProps.getDisableInResponseToCheck()); samlResponseAuthenticationProvider.setResponseValidator(uaaInResponseToHandlingResponseValidator); return samlResponseAuthenticationProvider; @@ -149,7 +149,7 @@ public UaaSavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() @Bean Saml2LogoutRequestResolver saml2LogoutRequestResolver(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) { - OpenSaml4LogoutRequestResolver logoutRequestResolver = new OpenSaml4LogoutRequestResolver(relyingPartyRegistrationResolver); + OpenSaml5LogoutRequestResolver logoutRequestResolver = new OpenSaml5LogoutRequestResolver(relyingPartyRegistrationResolver); logoutRequestResolver.setParametersConsumer((parameters) -> { LogoutRequest logoutRequest = parameters.getLogoutRequest(); NameID nameId = logoutRequest.getNameID(); @@ -178,7 +178,7 @@ FilterRegistrationBean saml2LogoutResponseFilter(Rely Saml2LogoutResponseValidator openSamlLogoutResponseValidator = new SamlLogoutResponseValidator(); Saml2LogoutResponseFilter filter = new Saml2LogoutResponseFilter(relyingPartyRegistrationResolver, openSamlLogoutResponseValidator, successHandler); - filter.setLogoutRequestMatcher(new AntPathRequestMatcher("/saml/SingleLogout/alias/{registrationId}")); + filter.setLogoutRequestMatcher(PathPatternRequestMatcher.withDefaults().matcher("/saml/SingleLogout/alias/{registrationId}")); FilterRegistrationBean bean = new FilterRegistrationBean<>(filter); bean.setEnabled(false); @@ -195,7 +195,7 @@ FilterRegistrationBean saml2LogoutRequestFilter(UaaRel // This validator ignores missing signatures in the SAML2 Logout Response Saml2LogoutRequestValidator logoutRequestValidator = new SamlLogoutRequestValidator(); - Saml2LogoutResponseResolver logoutResponseResolver = new OpenSaml4LogoutResponseResolver(relyingPartyRegistrationResolver); + Saml2LogoutResponseResolver logoutResponseResolver = new OpenSaml5LogoutResponseResolver(relyingPartyRegistrationResolver); SecurityContextLogoutHandler securityContextLogoutHandlerWithHandler = new SecurityContextLogoutHandler(); CsrfLogoutHandler csrfLogoutHandler = new CsrfLogoutHandler(loginCookieCsrfRepository); @@ -205,7 +205,7 @@ FilterRegistrationBean saml2LogoutRequestFilter(UaaRel logoutRequestValidator, logoutResponseResolver, authenticationFailureHandler, securityContextLogoutHandlerWithHandler, csrfLogoutHandler, cookieClearingLogoutHandlerWithHandler); - filter.setLogoutRequestMatcher(new AntPathRequestMatcher("/saml/SingleLogout/alias/*")); + filter.setLogoutRequestMatcher(PathPatternRequestMatcher.withDefaults().matcher("/saml/SingleLogout/alias/*")); FilterRegistrationBean bean = new FilterRegistrationBean<>(filter); bean.setEnabled(false); return bean; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigProps.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigProps.java index 0c8220250b8..a1f62386031 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigProps.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfigProps.java @@ -6,12 +6,12 @@ import lombok.extern.slf4j.Slf4j; import org.cloudfoundry.identity.uaa.impl.config.NestedMapPropertySource; import org.cloudfoundry.identity.uaa.saml.SamlKey; +import org.jspecify.annotations.Nullable; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; -import org.springframework.lang.Nullable; import java.util.HashMap; import java.util.LinkedHashMap; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfiguration.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfiguration.java index c4bf6903fb9..e7a278e6827 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfiguration.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlConfiguration.java @@ -11,6 +11,7 @@ import org.opensaml.core.config.ConfigurationService; import org.opensaml.core.config.InitializationException; import org.opensaml.core.config.Initializer; +import org.opensaml.core.config.provider.PropertiesAdapter; import org.opensaml.security.config.GlobalNamedCurveRegistryInitializer; import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap; import org.springframework.beans.factory.annotation.Qualifier; @@ -74,10 +75,13 @@ public class SamlConfiguration { @Bean public static Boolean setupOpenSaml() { if (samlInitialized.compareAndSet(false, true)) { - Properties props = ConfigurationService.getConfigurationProperties(); - props.put(CONFIG_PROPERTY_ECDH_DEFAULT_KDF, DefaultSecurityConfigurationBootstrap.PBKDF2); + Properties props = new Properties(); + props.setProperty(CONFIG_PROPERTY_ECDH_DEFAULT_KDF, DefaultSecurityConfigurationBootstrap.PBKDF2); + ConfigurationService.setDefaultConfigurationPropertiesSource(() -> new PropertiesAdapter(props)); Class toSkip = GlobalNamedCurveRegistryInitializer.class; - ServiceLoader.load(Initializer.class).stream().filter((provider) -> provider.type() != toSkip).forEach((provider) -> init(provider)); + ServiceLoader.load(Initializer.class).stream() + .filter(provider -> provider.type() != toSkip) + .forEach(SamlConfiguration::init); try { OpenSamlInitializationService.initialize(); } catch (NoClassDefFoundError | NoSuchMethodError e) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderConfigurator.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderConfigurator.java index d6db5c569d4..dcc8f07c25d 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderConfigurator.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderConfigurator.java @@ -87,7 +87,7 @@ public synchronized String validateSamlIdentityProviderDefinition(SamlIdentityPr } SamlIdentityProviderDefinition clone = providerDefinition.clone(); added = getExtendedMetadataDelegate(clone); - String entityIDToBeAdded = added.getAssertingPartyDetails().getEntityId(); + String entityIDToBeAdded = added.getAssertingPartyMetadata().getEntityId(); if (!hasText(entityIDToBeAdded)) { throw new IllegalStateException("Emtpy entityID for SAML provider with zoneId:" + providerDefinition.getZoneId() + " and origin:" + providerDefinition.getIdpEntityAlias()); } @@ -100,7 +100,7 @@ public synchronized String validateSamlIdentityProviderDefinition(SamlIdentityPr continue; } RelyingPartyRegistration existingProvider = getExtendedMetadataDelegate(existing); - if (entityIDToBeAdded.equals(existingProvider.getAssertingPartyDetails().getEntityId()) && !existing.getUniqueAlias().equals(clone.getUniqueAlias())) { + if (entityIDToBeAdded.equals(existingProvider.getAssertingPartyMetadata().getEntityId()) && !existing.getUniqueAlias().equals(clone.getUniqueAlias())) { entityIDexists = true; break; } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEndpoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEndpoint.java index b37588c1acd..f4495f75c6c 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEndpoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEndpoint.java @@ -6,7 +6,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; -import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver; +import org.springframework.security.saml2.provider.service.metadata.OpenSaml5MetadataResolver; import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResolver; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; @@ -34,7 +34,7 @@ public SamlMetadataEndpoint(RelyingPartyRegistrationResolver registrationResolve @Qualifier("signSamlMetaData") boolean signMetaData) { Assert.notNull(registrationResolver, "registrationResolver cannot be null"); relyingPartyRegistrationResolver = registrationResolver; - OpenSamlMetadataResolver metadataResolver = new OpenSamlMetadataResolver(); + OpenSaml5MetadataResolver metadataResolver = new OpenSaml5MetadataResolver(); saml2MetadataResolver = metadataResolver; metadataResolver.setEntityDescriptorCustomizer( new SamlMetadataEntityDescriptorCustomizer(identityZoneManager, signatureAlgorithms, signMetaData)); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEntityDescriptorCustomizer.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEntityDescriptorCustomizer.java index 8e9e564f663..da19eb8122d 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEntityDescriptorCustomizer.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEntityDescriptorCustomizer.java @@ -2,7 +2,7 @@ import lombok.Value; import lombok.extern.slf4j.Slf4j; -import net.shibboleth.utilities.java.support.resolver.CriteriaSet; +import net.shibboleth.shared.resolver.CriteriaSet; import org.cloudfoundry.identity.uaa.zone.SamlConfig; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; import org.opensaml.core.xml.XMLObjectBuilder; @@ -31,7 +31,7 @@ import org.opensaml.xmlsec.signature.support.SignatureSupport; import org.springframework.security.saml2.Saml2Exception; import org.springframework.security.saml2.core.Saml2X509Credential; -import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver; +import org.springframework.security.saml2.provider.service.metadata.OpenSaml5MetadataResolver; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.util.Assert; @@ -54,11 +54,11 @@ /** * This class is used to customize the EntityDescriptor used in the Metadata call, - * it is called as part of the {@link OpenSamlMetadataResolver} after basic creation is completed. + * it is called as part of the {@link OpenSaml5MetadataResolver} after basic creation is completed. */ @Slf4j @Value -public class SamlMetadataEntityDescriptorCustomizer implements Consumer { +public class SamlMetadataEntityDescriptorCustomizer implements Consumer { private static final Set NAME_ID_FORMATS = new HashSet<>(); private static final String URI_BINDING = "urn:oasis:names:tc:SAML:2.0:bindings:URI"; private static final UnaryOperator assertionConsumerServiceLocationMutationFunction = o -> o.replace("/saml/SSO/alias/", "/oauth/token/alias/"); @@ -76,7 +76,7 @@ public class SamlMetadataEntityDescriptorCustomizer implements Consumer existingNameIDFormats = spSsoDescriptor.getNameIDFormats().stream().map(NameIDFormat::getURI).collect(Collectors.toSet()); spSsoDescriptor.getNameIDFormats().addAll(NAME_ID_FORMATS.stream().filter(Predicate.not(existingNameIDFormats::contains)).map(this::buildNameIDFormat).collect(Collectors.toSet())); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlUaaResponseAuthenticationConverter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlUaaResponseAuthenticationConverter.java index 18c02a62e29..44ab2caba26 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlUaaResponseAuthenticationConverter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/SamlUaaResponseAuthenticationConverter.java @@ -26,7 +26,7 @@ @Slf4j @Getter public class SamlUaaResponseAuthenticationConverter - implements Converter, + implements Converter, ApplicationEventPublisherAware { private final IdentityZoneManager identityZoneManager; @@ -42,7 +42,7 @@ public SamlUaaResponseAuthenticationConverter(IdentityZoneManager identityZoneMa } @Override - public UaaAuthentication convert(OpenSaml4AuthenticationProvider.ResponseToken responseToken) { + public UaaAuthentication convert(OpenSaml5AuthenticationProvider.ResponseToken responseToken) { Saml2AuthenticationToken authenticationToken = responseToken.getToken(); Response response = responseToken.getResponse(); List assertions = response.getAssertions(); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaDelegatingLogoutSuccessHandler.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaDelegatingLogoutSuccessHandler.java index 77dbf522017..f5379c27b8a 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaDelegatingLogoutSuccessHandler.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaDelegatingLogoutSuccessHandler.java @@ -94,7 +94,9 @@ private boolean shouldPerformSamlRelyingPartyLogout(HttpServletRequest request, return false; } - String singleLogoutServiceLocation = Optional.ofNullable(registration.getAssertingPartyDetails()).map(RelyingPartyRegistration.AssertingPartyDetails::getSingleLogoutServiceLocation).orElse(null); + String singleLogoutServiceLocation = Optional.ofNullable(registration.getAssertingPartyMetadata()) + .map(metadata -> metadata.getSingleLogoutServiceLocation()) + .orElse(null); return singleLogoutServiceLocation != null; } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaInResponseToHandlingResponseValidator.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaInResponseToHandlingResponseValidator.java index e1374883518..ae9bec0d98d 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaInResponseToHandlingResponseValidator.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaInResponseToHandlingResponseValidator.java @@ -21,8 +21,8 @@ import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.SamlConfig; +import org.jspecify.annotations.NonNull; import org.springframework.core.convert.converter.Converter; -import org.springframework.lang.NonNull; import org.springframework.security.saml2.core.Saml2Error; import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; @@ -32,26 +32,26 @@ /** * Strategy for validating the SAML 2.0 Response used with - * {@link org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider} + * {@link org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider} * Handles the property `login.saml.disableInResponseToCheck` when set to true * we will ignore errors on the InResponseTo check of the SAML Response. *

* The InResponseTo attribute is optional, but if it is present, the default validator checks against the ID of the request. */ @Slf4j -public final class UaaInResponseToHandlingResponseValidator implements Converter { +public final class UaaInResponseToHandlingResponseValidator implements Converter { private final boolean uaaWideDisableInResponseToCheck; - private final Converter delegate; + private final Converter delegate; - public UaaInResponseToHandlingResponseValidator(Converter delegate, + public UaaInResponseToHandlingResponseValidator(Converter delegate, boolean uaaWideDisableInResponseToCheck) { this.delegate = delegate; this.uaaWideDisableInResponseToCheck = uaaWideDisableInResponseToCheck; } @Override - public Saml2ResponseValidatorResult convert(@NonNull OpenSaml4AuthenticationProvider.ResponseToken source) { + public Saml2ResponseValidatorResult convert(OpenSaml5AuthenticationProvider.@NonNull ResponseToken source) { Saml2ResponseValidatorResult result = delegate.convert(source); // if the result is successful, return it if (result == null || !result.hasErrors()) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaRelyingPartyRegistrationResolver.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaRelyingPartyRegistrationResolver.java index 0a0a950e1a4..851ebb1f344 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaRelyingPartyRegistrationResolver.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaRelyingPartyRegistrationResolver.java @@ -20,8 +20,8 @@ import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; +import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.security.web.util.UrlUtils; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -46,7 +46,7 @@ public final class UaaRelyingPartyRegistrationResolver implements Converter metadata + .entityId(relyingPartyRegistration.getAssertingPartyMetadata().getEntityId()) + .wantAuthnRequestsSigned(relyingPartyRegistration.getAssertingPartyMetadata().getWantAuthnRequestsSigned()) + .signingAlgorithms(algs -> algs.addAll(relyingPartyRegistration.getAssertingPartyMetadata().getSigningAlgorithms())) + .verificationX509Credentials(creds -> creds.addAll(relyingPartyRegistration.getAssertingPartyMetadata().getVerificationX509Credentials())) + .encryptionX509Credentials(creds -> creds.addAll(relyingPartyRegistration.getAssertingPartyMetadata().getEncryptionX509Credentials())) + .singleSignOnServiceLocation(relyingPartyRegistration.getAssertingPartyMetadata().getSingleSignOnServiceLocation()) + .singleSignOnServiceBinding(relyingPartyRegistration.getAssertingPartyMetadata().getSingleSignOnServiceBinding()) + ) + .signingX509Credentials(creds -> creds.addAll(relyingPartyRegistration.getSigningX509Credentials())) + .decryptionX509Credentials(creds -> creds.addAll(relyingPartyRegistration.getDecryptionX509Credentials())) + .build(); } } } @@ -130,7 +146,7 @@ private static String resolveUrlTemplate(String template, String baseUrl, Relyin } private static Map constructUriVariables(String baseUrl, RelyingPartyRegistration relyingParty) { - String entityId = relyingParty.getAssertingPartyDetails().getEntityId(); + String entityId = relyingParty.getAssertingPartyMetadata().getEntityId(); String registrationId = relyingParty.getRegistrationId(); Map uriVariables = new HashMap<>(); UriComponents uriComponents = UriComponentsBuilder.fromUriString(baseUrl).replaceQuery(null).fragment(null).build(); @@ -156,4 +172,4 @@ private static String getApplicationUri(HttpServletRequest request) { UriComponents uriComponents = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request)).replacePath(request.getContextPath()).replaceQuery(null).fragment(null).build(); return uriComponents.toUriString(); } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/common/InternalLimiter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/common/InternalLimiter.java index 5bede42f5f3..967f699aa9e 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/common/InternalLimiter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/common/InternalLimiter.java @@ -4,8 +4,8 @@ import java.util.Iterator; import java.util.concurrent.atomic.AtomicInteger; -import jakarta.annotation.Nonnull; import org.cloudfoundry.identity.uaa.ratelimiting.core.CompoundKey; +import org.jspecify.annotations.NonNull; public class InternalLimiter { // Uses an "inner" Lock Object to discourage other developers from messing with the lock order @@ -39,7 +39,7 @@ public boolean isExpired(Instant now) { * @param orderedInternalLimiters an appropriately ordered (non-null) iterator of InternalLimiter(s) (no entries null) to be called recursively * @return true - if should limit, ; otherwise - false, don't limit */ - public boolean shouldLimit(@Nonnull Iterator orderedInternalLimiters, @Nonnull LimiterImpl limiter) { + public boolean shouldLimit(@NonNull Iterator orderedInternalLimiters, @NonNull LimiterImpl limiter) { // Note: synchronization here NOT for Memory barriers, but for temporary exclusive access! synchronized (lockObject) { // build up synchronization locks in list order! if (limiter.recordLimiting(getRequestsRemaining() < 1)) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/common/InternalLimiterFactory.java b/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/common/InternalLimiterFactory.java index 67176e60de7..f5aa363d946 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/common/InternalLimiterFactory.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/common/InternalLimiterFactory.java @@ -2,8 +2,8 @@ import java.time.Instant; -import jakarta.annotation.Nonnull; import org.cloudfoundry.identity.uaa.ratelimiting.core.CompoundKey; +import org.jspecify.annotations.NonNull; public interface InternalLimiterFactory { /** @@ -13,6 +13,6 @@ public interface InternalLimiterFactory { * @param now non-Null Instant for the current time. * @return internal limiter (not Null) */ - @Nonnull - InternalLimiter newLimiter(CompoundKey compoundKey, @Nonnull Instant now); + @NonNull + InternalLimiter newLimiter(CompoundKey compoundKey, @NonNull Instant now); } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/limitertracking/InternalLimiterFactoryImpl.java b/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/limitertracking/InternalLimiterFactoryImpl.java index 6b6349b4dc1..25aa29f7dd2 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/limitertracking/InternalLimiterFactoryImpl.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/ratelimiting/internal/limitertracking/InternalLimiterFactoryImpl.java @@ -2,13 +2,13 @@ import java.time.Instant; -import jakarta.annotation.Nonnull; import lombok.Builder; import lombok.Getter; import org.cloudfoundry.identity.uaa.ratelimiting.core.CompoundKey; import org.cloudfoundry.identity.uaa.ratelimiting.core.config.RequestsPerWindowSecs; import org.cloudfoundry.identity.uaa.ratelimiting.internal.common.InternalLimiter; import org.cloudfoundry.identity.uaa.ratelimiting.internal.common.InternalLimiterFactory; +import org.jspecify.annotations.NonNull; @Getter public class InternalLimiterFactoryImpl implements InternalLimiterFactory { @@ -29,8 +29,8 @@ public InternalLimiterFactoryImpl(RequestsPerWindowSecs requestsPerWindow, } @Override - @Nonnull - public InternalLimiter newLimiter(CompoundKey compoundKey, @Nonnull Instant now) { + @NonNull + public InternalLimiter newLimiter(CompoundKey compoundKey, @NonNull Instant now) { return new InternalLimiter( compoundKey, getInitialRequestsRemaining(), getWindowEndExclusive(now, getWindowSecs()) ); } @@ -50,7 +50,7 @@ public String toString() { + " @ " + getRequestsPerWindow(); } - private Instant getWindowEndExclusive(@Nonnull Instant now, int windowSecs) { + private Instant getWindowEndExclusive(@NonNull Instant now, int windowSecs) { return now.plusSeconds(windowSecs); } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/resources/jdbc/SimpleSearchQueryConverter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/resources/jdbc/SimpleSearchQueryConverter.java index dd9e36c63e0..34c778fa956 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/resources/jdbc/SimpleSearchQueryConverter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/resources/jdbc/SimpleSearchQueryConverter.java @@ -9,9 +9,9 @@ import org.cloudfoundry.identity.uaa.resources.SimpleAttributeNameMapper; import org.cloudfoundry.identity.uaa.util.AlphanumericRandomValueStringGenerator; import org.cloudfoundry.identity.uaa.util.UaaStringUtils; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.lang.Nullable; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/beans/ScimBeanConfiguration.java b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/beans/ScimBeanConfiguration.java index b83d71e6b4b..8192a414dcd 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/beans/ScimBeanConfiguration.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/beans/ScimBeanConfiguration.java @@ -122,7 +122,7 @@ public Set nonDefaultUserGroups() { @Bean(name = "exceptionToStatusMap") public Map, HttpStatus> exceptionToStatusMap() { Map, HttpStatus> map = new LinkedHashMap<>(); - map.put(org.springframework.dao.DataAccessException.class, HttpStatus.UNPROCESSABLE_ENTITY); + map.put(org.springframework.dao.DataAccessException.class, HttpStatus.UNPROCESSABLE_CONTENT); map.put(org.springframework.dao.DataIntegrityViolationException.class, HttpStatus.BAD_REQUEST); map.put(org.springframework.http.converter.HttpMessageConversionException.class, HttpStatus.BAD_REQUEST); map.put(org.springframework.web.HttpMediaTypeException.class, HttpStatus.BAD_REQUEST); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ChangeEmailEndpoints.java b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ChangeEmailEndpoints.java index d742b24742e..5964eebf332 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ChangeEmailEndpoints.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ChangeEmailEndpoints.java @@ -13,10 +13,10 @@ import org.cloudfoundry.identity.uaa.scim.event.UserModifiedEvent; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.NonNull; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.http.ResponseEntity; -import org.springframework.lang.NonNull; import org.cloudfoundry.identity.uaa.oauth.provider.ClientDetails; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; @@ -109,7 +109,7 @@ public ResponseEntity changeEmail(@RequestBody String code) emailChangeResponse.setRedirectUrl(redirectLocation); return new ResponseEntity<>(emailChangeResponse, OK); } else { - return new ResponseEntity<>(UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(UNPROCESSABLE_CONTENT); } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimGroupEndpoints.java b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimGroupEndpoints.java index b1e61d14bee..956f6239153 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimGroupEndpoints.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimGroupEndpoints.java @@ -23,6 +23,7 @@ import org.cloudfoundry.identity.uaa.web.ExceptionReport; import org.cloudfoundry.identity.uaa.web.ExceptionReportHttpMessageConverter; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -141,12 +142,12 @@ private List filterForCurrentUser(List input, int startInd @GetMapping({"/Groups", "/Groups/"}) @ResponseBody public SearchResults listGroups( - @RequestParam(value = "attributes", required = false) String attributesCommaSeparated, - @RequestParam(required = false, defaultValue = "id pr") String filter, - @RequestParam(required = false, defaultValue = "created") String sortBy, - @RequestParam(required = false, defaultValue = "ascending") String sortOrder, - @RequestParam(required = false, defaultValue = "1") int startIndex, - @RequestParam(required = false, defaultValue = "100") int count) { + @Nullable @RequestParam("attributes") String attributesCommaSeparated, + @Nullable @RequestParam(defaultValue = "id pr") String filter, + @Nullable @RequestParam(defaultValue = "created") String sortBy, + @Nullable @RequestParam(defaultValue = "ascending") String sortOrder, + @Nullable @RequestParam(defaultValue = "1") int startIndex, + @Nullable @RequestParam(defaultValue = "100") int count) { if (count > groupMaxCount) { count = groupMaxCount; @@ -187,20 +188,20 @@ public SearchResults listGroups( @ResponseBody @Deprecated public SearchResults listExternalGroups( - @RequestParam(required = false, defaultValue = "1") int startIndex, - @RequestParam(required = false, defaultValue = "100") int count, - @RequestParam(required = false, defaultValue = "") String filter) { + @Nullable @RequestParam(defaultValue = "1") int startIndex, + @Nullable @RequestParam(defaultValue = "100") int count, + @Nullable @RequestParam(defaultValue = "") String filter) { return getExternalGroups(startIndex, count, filter, "", ""); } @GetMapping({"/Groups/External", "/Groups/External/"}) @ResponseBody public SearchResults getExternalGroups( - @RequestParam(required = false, defaultValue = "1") int startIndex, - @RequestParam(required = false, defaultValue = "100") int count, - @RequestParam(required = false, defaultValue = "") String filter, - @RequestParam(required = false, defaultValue = "") String origin, - @RequestParam(required = false, defaultValue = "") String externalGroup) { + @Nullable @RequestParam(defaultValue = "1") int startIndex, + @Nullable @RequestParam(defaultValue = "100") int count, + @Nullable @RequestParam(defaultValue = "") String filter, + @Nullable @RequestParam(defaultValue = "") String origin, + @Nullable @RequestParam(defaultValue = "") String externalGroup) { if (hasText(filter)) { if (hasText(origin) || hasText(externalGroup)) { @@ -553,8 +554,8 @@ public ResponseEntity getGroupMembership(@PathVariable String g @GetMapping({"/Groups/{groupId}/members", "/Groups/{groupId}/members/"}) public ResponseEntity> listGroupMemberships(@PathVariable String groupId, - @RequestParam(required = false, defaultValue = "false") boolean returnEntities, - @RequestParam(required = false, defaultValue = "", name = "filter") String deprecatedFilter) { + @Nullable @RequestParam(defaultValue = "false") boolean returnEntities, + @Nullable @RequestParam(defaultValue = "", name = "filter") String deprecatedFilter) { dao.retrieve(groupId, identityZoneManager.getCurrentIdentityZoneId()); List members = membershipManager.getMembers(groupId, returnEntities, diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpoints.java b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpoints.java index 7e652b0b452..94e8dfb7f27 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpoints.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpoints.java @@ -47,6 +47,8 @@ import org.cloudfoundry.identity.uaa.web.ExceptionReport; import org.cloudfoundry.identity.uaa.web.ExceptionReportHttpMessageConverter; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; @@ -62,8 +64,6 @@ import org.springframework.jmx.export.annotation.ManagedMetric; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.jmx.support.MetricType; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; @@ -434,7 +434,7 @@ public ScimUser deleteUser(@PathVariable String userId, @GetMapping("/Users/{userId}/verify-link") @ResponseBody public ResponseEntity getUserVerificationLink(@PathVariable String userId, - @RequestParam(value = "client_id", required = false) String clientId, + @Nullable @RequestParam("client_id") String clientId, @RequestParam(value = "redirect_uri") String redirectUri) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); @@ -492,12 +492,12 @@ private int getVersion(String userId, String etag) { @GetMapping({"/Users", "/Users/"}) @ResponseBody public SearchResults findUsers( - @RequestParam(value = "attributes", required = false) String attributesCommaSeparated, - @RequestParam(required = false, defaultValue = "id pr") String filter, - @RequestParam(required = false, defaultValue = "created") String sortBy, - @RequestParam(required = false, defaultValue = "ascending") String sortOrder, - @RequestParam(required = false, defaultValue = "1") int startIndex, - @RequestParam(required = false, defaultValue = "100") int count) { + @Nullable @RequestParam("attributes") String attributesCommaSeparated, + @Nullable @RequestParam(defaultValue = "id pr") String filter, + @Nullable @RequestParam(defaultValue = "created") String sortBy, + @Nullable @RequestParam(defaultValue = "ascending") String sortOrder, + @Nullable @RequestParam(defaultValue = "1") int startIndex, + @Nullable @RequestParam(defaultValue = "100") int count) { if (startIndex < 1) { startIndex = 1; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/UserIdConversionEndpoints.java b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/UserIdConversionEndpoints.java index 89106a182a6..405de1ae657 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/UserIdConversionEndpoints.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/UserIdConversionEndpoints.java @@ -15,6 +15,7 @@ import org.cloudfoundry.identity.uaa.util.UaaPagingUtils; import org.cloudfoundry.identity.uaa.util.UaaStringUtils; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; @@ -67,10 +68,10 @@ public UserIdConversionEndpoints( @ResponseBody public ResponseEntity findUsers( @RequestParam(defaultValue = "") String filter, - @RequestParam(required = false, defaultValue = "ascending") final String sortOrder, - @RequestParam(required = false, defaultValue = "1") int startIndex, - @RequestParam(required = false, defaultValue = "100") int count, - @RequestParam(required = false, defaultValue = "false") final boolean includeInactive + @Nullable @RequestParam(defaultValue = "ascending") final String sortOrder, + @Nullable @RequestParam(defaultValue = "1") int startIndex, + @Nullable @RequestParam(defaultValue = "100") int count, + @Nullable @RequestParam(defaultValue = "false") final boolean includeInactive ) { if (!enabled) { logger.info("Request from user {} received at disabled Id translation endpoint with filter:{}", diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/util/UaaHttpRequestUtils.java b/server/src/main/java/org/cloudfoundry/identity/uaa/util/UaaHttpRequestUtils.java index 00747ac3461..a3adbcb67c9 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/util/UaaHttpRequestUtils.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/util/UaaHttpRequestUtils.java @@ -33,6 +33,7 @@ import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; +import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.core5.http.message.BasicHeaderElementIterator; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.ssl.SSLContextBuilder; @@ -67,33 +68,34 @@ public abstract class UaaHttpRequestUtils { private static final Logger logger = LoggerFactory.getLogger(UaaHttpRequestUtils.class); public static ClientHttpRequestFactory createRequestFactory(boolean skipSslValidation, int timeout) { - return createRequestFactory(getClientBuilder(skipSslValidation, 10, 5, 0, 2000, 0), timeout, timeout); + return createRequestFactory(getClientBuilder(skipSslValidation, 10, 5, 0, 2000, 0, timeout), timeout, timeout); } public static ClientHttpRequestFactory createRequestFactory(boolean skipSslValidation, int timeout, int readTimeout, int poolSize, int defaultMaxPerRoute, int maxKeepAlive, int validateAfterInactivity, int retryCount) { - return createRequestFactory(getClientBuilder(skipSslValidation, poolSize, defaultMaxPerRoute, maxKeepAlive, validateAfterInactivity, retryCount), timeout, readTimeout); + return createRequestFactory(getClientBuilder(skipSslValidation, poolSize, defaultMaxPerRoute, maxKeepAlive, validateAfterInactivity, retryCount, timeout), timeout, readTimeout); } public static ClientHttpRequestFactory createRequestFactory(boolean skipSslValidation, int timeout, int readTimeout, RestTemplateConfig restTemplateConfig) { - return createRequestFactory(getClientBuilder(skipSslValidation, restTemplateConfig.maxTotal, restTemplateConfig.maxPerRoute, restTemplateConfig.maxKeepAlive, restTemplateConfig.validateAfterInactivity, restTemplateConfig.retryCount), timeout, readTimeout); + return createRequestFactory(getClientBuilder(skipSslValidation, restTemplateConfig.maxTotal, restTemplateConfig.maxPerRoute, restTemplateConfig.maxKeepAlive, restTemplateConfig.validateAfterInactivity, restTemplateConfig.retryCount, timeout), timeout, readTimeout); } protected static ClientHttpRequestFactory createRequestFactory(HttpClientBuilder builder, int timeoutInMs, int readTimeoutInMs) { HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(builder.build()); - // Manual migration to `SocketConfig.Builder.setSoTimeout(Timeout)` necessary; see: https://docs.spring.io/spring-framework/docs/6.0.0/javadoc-api/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.html#setReadTimeout(int) httpComponentsClientHttpRequestFactory.setReadTimeout(readTimeoutInMs); httpComponentsClientHttpRequestFactory.setConnectionRequestTimeout(timeoutInMs); - httpComponentsClientHttpRequestFactory.setConnectTimeout(timeoutInMs); return httpComponentsClientHttpRequestFactory; } - protected static HttpClientBuilder getClientBuilder(boolean skipSslValidation, int poolSize, int defaultMaxPerRoute, int maxKeepAlive, int validateAfterInactivity, int retryCount) { + protected static HttpClientBuilder getClientBuilder(boolean skipSslValidation, int poolSize, int defaultMaxPerRoute, int maxKeepAlive, int validateAfterInactivity, int retryCount, int connectTimeoutInMs) { HttpClientBuilder builder = HttpClients.custom() .useSystemProperties() .setUserTokenHandler(NoopUserTokenHandler.INSTANCE) .setRedirectStrategy(new DefaultRedirectStrategy()); PoolingHttpClientConnectionManager cm; + ConnectionConfig connectionConfig = ConnectionConfig.custom() + .setConnectTimeout(connectTimeoutInMs, TimeUnit.MILLISECONDS) + .build(); if (skipSslValidation) { SSLContext sslContext = getNonValidatingSslContext(); final String[] supportedProtocols = split(System.getProperty("https.protocols")); @@ -111,6 +113,7 @@ protected static HttpClientBuilder getClientBuilder(boolean skipSslValidation, i cm.setMaxTotal(poolSize); cm.setDefaultMaxPerRoute(defaultMaxPerRoute); cm.setValidateAfterInactivity(TimeValue.of(validateAfterInactivity, TimeUnit.MILLISECONDS)); + cm.setDefaultConnectionConfig(connectionConfig); builder.setConnectionManager(cm); if (maxKeepAlive <= 0) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/web/AuthorizationManagersUtils.java b/server/src/main/java/org/cloudfoundry/identity/uaa/web/AuthorizationManagersUtils.java index b7d2f6d71a8..58c70b9c121 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/web/AuthorizationManagersUtils.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/web/AuthorizationManagersUtils.java @@ -10,10 +10,12 @@ import org.cloudfoundry.identity.uaa.oauth.common.exceptions.InsufficientScopeException; import org.cloudfoundry.identity.uaa.oauth.provider.expression.OAuth2ExpressionUtils; import org.cloudfoundry.identity.uaa.security.ContextSensitiveOAuth2SecurityExpressionMethods; +import org.jspecify.annotations.Nullable; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authorization.AuthenticatedAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; import org.springframework.security.web.access.intercept.RequestAuthorizationContext; @@ -44,13 +46,13 @@ public static class AnyOfAuthorizationManager implements AuthorizationManager missingScopes = new LinkedHashSet<>(); @Override - public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext object) { + public AuthorizationResult authorize(Supplier authentication, RequestAuthorizationContext object) { for (var authorizationManager : this.delegateAuthorizationManagers) { - AuthorizationDecision decision = authorizationManager.check(authentication, object); - if (decision != null) { - if (decision.isGranted()) { - return decision; - } else if (decision instanceof ScopeTrackingAuthorizationDecision scopeDecision) { + AuthorizationResult result = authorizationManager.authorize(authentication, object); + if (result != null) { + if (result.isGranted()) { + return result; + } else if (result instanceof ScopeTrackingAuthorizationDecision scopeDecision) { missingScopes.addAll(scopeDecision.getScopes()); } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/web/ConvertingExceptionView.java b/server/src/main/java/org/cloudfoundry/identity/uaa/web/ConvertingExceptionView.java index 1dc1aab1718..c78e610c9b8 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/web/ConvertingExceptionView.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/web/ConvertingExceptionView.java @@ -32,6 +32,7 @@ import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; +import org.springframework.util.MimeTypeUtils; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.servlet.View; @@ -119,7 +120,7 @@ private void writeWithMessageConverters(Object returnValue, HttpInputMessage inp } else { acceptedMediaTypes.add(MediaType.APPLICATION_JSON); } - MediaType.sortByQualityValue(acceptedMediaTypes); + MimeTypeUtils.sortBySpecificity(acceptedMediaTypes); Class returnValueType = returnValue.getClass(); List allSupportedMediaTypes = new ArrayList<>(); if (messageConverters != null) { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/web/LimitedModeUaaFilter.java b/server/src/main/java/org/cloudfoundry/identity/uaa/web/LimitedModeUaaFilter.java index de2bf3e8340..c25d19bccc7 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/web/LimitedModeUaaFilter.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/web/LimitedModeUaaFilter.java @@ -6,10 +6,10 @@ import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.util.TimeService; import org.cloudfoundry.identity.uaa.util.TimeServiceImpl; +import org.jspecify.annotations.NonNull; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.lang.NonNull; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.web.filter.OncePerRequestFilter; import jakarta.servlet.FilterChain; @@ -37,7 +37,7 @@ public class LimitedModeUaaFilter extends OncePerRequestFilter { public static final long STATUS_INTERVAL_MS = 5000; private Set permittedMethods = emptySet(); - private List endpoints = emptyList(); + private List endpoints = emptyList(); private volatile boolean enabled; @Getter private File statusFile; @@ -96,7 +96,7 @@ public void setPermittedEndpoints(Set permittedEndpoints) { this.endpoints = ofNullable(permittedEndpoints) .orElse(emptySet()) .stream() - .map(AntPathRequestMatcher::new) + .map(pattern -> PathPatternRequestMatcher.withDefaults().matcher(pattern)) .toList(); } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/web/SelfCheckAuthorizationManager.java b/server/src/main/java/org/cloudfoundry/identity/uaa/web/SelfCheckAuthorizationManager.java index 75fd7fcdcc4..b65a21c5b1c 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/web/SelfCheckAuthorizationManager.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/web/SelfCheckAuthorizationManager.java @@ -4,8 +4,10 @@ import jakarta.servlet.http.HttpServletRequest; import org.cloudfoundry.identity.uaa.security.IsSelfCheck; +import org.jspecify.annotations.Nullable; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; import org.springframework.security.web.access.intercept.RequestAuthorizationContext; @@ -63,12 +65,12 @@ private SelfCheckAuthorizationManager(CheckType type, IsSelfCheck selfCheck, int } @Override - public void verify(Supplier authentication, RequestAuthorizationContext context) { + public void verify(Supplier authentication, RequestAuthorizationContext context) { AuthorizationManager.super.verify(authentication, context); } @Override - public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext context) { + public AuthorizationResult authorize(Supplier authentication, RequestAuthorizationContext context) { HttpServletRequest request = context.getRequest(); switch (type) { case USER -> { diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCache.java b/server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCache.java index 0aab5230551..be2f9b48396 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCache.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCache.java @@ -21,7 +21,6 @@ import org.cloudfoundry.identity.uaa.util.UaaUrlUtils; import org.springframework.http.HttpMethod; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.PortResolver; import org.springframework.security.web.savedrequest.DefaultSavedRequest; import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.savedrequest.SavedRequest; @@ -130,7 +129,7 @@ public static class ClientRedirectSavedRequest extends DefaultSavedRequest { private final Map parameters; public ClientRedirectSavedRequest(HttpServletRequest request, String redirectUrl) { - super(request, ServletRequest::getServerPort); + super(request); this.redirectUrl = redirectUrl; parameters = Collections.unmodifiableMap(UaaUrlUtils.getParameterMap(redirectUrl)); } @@ -180,8 +179,7 @@ public Collection getParameterNames() { return parameters.keySet(); } - @Override - public boolean doesRequestMatch(HttpServletRequest request, PortResolver portResolver) { + public boolean doesRequestMatch(HttpServletRequest request) { boolean result = UrlUtils.buildFullRequestUrl(request).equals(redirectUrl); String formRedirect = request.getParameter(FORM_REDIRECT_PARAMETER); if (!result && diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/web/WebConfig.java b/server/src/main/java/org/cloudfoundry/identity/uaa/web/WebConfig.java index 79160310c1f..b5ce190dd65 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/web/WebConfig.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/web/WebConfig.java @@ -1,20 +1,16 @@ package org.cloudfoundry.identity.uaa.web; -import jakarta.annotation.PostConstruct; import org.cloudfoundry.identity.uaa.authentication.manager.AutologinRequestConverter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import java.util.List; /** - * Web stack configuration. It relies on Spring Boot's {@link WebMvcAutoConfiguration}, + * Web stack configuration. It relies on Spring Boot's WebMvcAutoConfiguration, * with a few adjustments in a properties file to match the legacy behavior from UAA. */ @Configuration @@ -26,12 +22,4 @@ class WebConfig implements WebMvcConfigurer { public void extendMessageConverters(List> converters) { converters.add(new AutologinRequestConverter()); } - - @Autowired - private RequestMappingHandlerAdapter requestMappingHandlerAdapter; - - @PostConstruct - public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); - } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaJdbcSessionConfig.java b/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaJdbcSessionConfig.java index 5e7e62b4d2f..e8a1427412f 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaJdbcSessionConfig.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaJdbcSessionConfig.java @@ -1,6 +1,7 @@ package org.cloudfoundry.identity.uaa.web.beans; import org.cloudfoundry.identity.uaa.UaaProperties; +import org.jspecify.annotations.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -10,7 +11,6 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.type.AnnotatedTypeMetadata; -import org.springframework.lang.NonNull; import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession; import org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessionConfiguration; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaMemorySessionConfig.java b/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaMemorySessionConfig.java index 2d837772f6b..0d909a0eea0 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaMemorySessionConfig.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaMemorySessionConfig.java @@ -1,6 +1,7 @@ package org.cloudfoundry.identity.uaa.web.beans; import org.cloudfoundry.identity.uaa.UaaProperties; +import org.jspecify.annotations.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -10,7 +11,6 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.type.AnnotatedTypeMetadata; -import org.springframework.lang.NonNull; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.session.MapSessionRepository; import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession; @@ -48,4 +48,4 @@ public MapSessionRepository sessionRepository( void log() { logger.info("Using memory session configuration"); } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaSessionConfig.java b/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaSessionConfig.java index ec3de8f68fc..2bea0f4d1db 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaSessionConfig.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/web/beans/UaaSessionConfig.java @@ -2,10 +2,12 @@ import org.cloudfoundry.identity.uaa.UaaProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.session.web.http.CookieSerializer; import org.springframework.session.web.http.DefaultCookieSerializer; +@Configuration public class UaaSessionConfig { private static final String SERVLET_SESSION_STORE = "servlet.session-store"; static final String DATABASE_SESSION_STORE_TYPE = "database"; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneEndpoints.java b/server/src/main/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneEndpoints.java index f0a70ae3d7d..0825efc79ab 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneEndpoints.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneEndpoints.java @@ -53,11 +53,7 @@ import java.util.UUID; import static java.util.Optional.ofNullable; -import static org.springframework.http.HttpStatus.CONFLICT; -import static org.springframework.http.HttpStatus.CREATED; -import static org.springframework.http.HttpStatus.NOT_FOUND; -import static org.springframework.http.HttpStatus.OK; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static org.springframework.http.HttpStatus.*; @RestController("identityZoneEndpoints") @RequestMapping({"/identity-zones", "/identity-zones/"}) @@ -340,7 +336,7 @@ public ResponseEntity deleteIdentityZone(@PathVariable String id) * here, since they can only exist if their origin IdP has an alias as well */ final boolean idpWithAliasExists = idpDao.idpWithAliasExistsInZone(zone.getId()); if (idpWithAliasExists) { - return new ResponseEntity<>(UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(UNPROCESSABLE_CONTENT); } if (publisher != null) { @@ -348,7 +344,7 @@ public ResponseEntity deleteIdentityZone(@PathVariable String id) logger.debug("Zone - deleted id[" + zone.getId() + "]"); return new ResponseEntity<>(removeKeys(zone), OK); } else { - return new ResponseEntity<>(UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(UNPROCESSABLE_CONTENT); } } finally { IdentityZoneHolder.set(previous); @@ -443,7 +439,7 @@ public ResponseEntity handleAccessDeniedException(AccessDeniedException e) @ExceptionHandler(UnprocessableEntityException.class) public ResponseEntity handleUnprocessableEntityException(UnprocessableEntityException e) { - return new ResponseEntity<>(e, HttpStatus.UNPROCESSABLE_ENTITY); + return new ResponseEntity<>(e, HttpStatus.UNPROCESSABLE_CONTENT); } @ExceptionHandler(Exception.class) diff --git a/server/src/main/resources/spring-mvc.properties b/server/src/main/resources/spring-mvc.properties index 4e089ef848c..43f05d6becd 100644 --- a/server/src/main/resources/spring-mvc.properties +++ b/server/src/main/resources/spring-mvc.properties @@ -19,4 +19,4 @@ # In Spring, defaults from Jackson are used. Dates (e.g. "2025-02-06T11:04:12") # are serialized as timestamps, integers that look like 1339478482000000. In # Spring Boot, by default they are issued as raw strings. -spring.jackson.serialization.write-dates-as-timestamps=true \ No newline at end of file +spring.jackson.datatype.datetime.write-dates-as-timestamps=true \ No newline at end of file diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/ServerRunningExtension.java b/server/src/test/java/org/cloudfoundry/identity/uaa/ServerRunningExtension.java index 80eb9391ce2..2056d2dc506 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/ServerRunningExtension.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/ServerRunningExtension.java @@ -271,7 +271,7 @@ public ResponseEntity postForRedirect(String path, HttpHeaders headers, Mu } headers.remove("Cookie"); - if (exchange.getHeaders().containsKey("Set-Cookie")) { + if (exchange.getHeaders().containsHeader("Set-Cookie")) { for (String cookie : exchange.getHeaders().get("Set-Cookie")) { headers.add("Cookie", cookie); } @@ -309,7 +309,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pass through } }); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/account/AccountsControllerViewZonePathTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/account/AccountsControllerViewZonePathTests.java index dbb768f888f..0034b76e268 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/account/AccountsControllerViewZonePathTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/account/AccountsControllerViewZonePathTests.java @@ -20,6 +20,7 @@ import org.junit.jupiter.params.provider.EnumSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.http.HttpStatus; @@ -130,11 +131,12 @@ void createAccountPageContainsZoneAwareFormActionAndLoginLink(ZoneRequestPathMod void linkPromptPageContainsZoneAwareCreateAccountAndLoginLinks(ZoneRequestPathMode mode) throws Exception { setZoneWithSelfServiceEnabled(mode); mockMvc.perform(request(mode, "/verify_user").param("code", "expired-code")) - .andExpect(status().is(HttpStatus.UNPROCESSABLE_ENTITY.value())) + .andExpect(status().is(HttpStatus.UNPROCESSABLE_CONTENT.value())) .andExpect(content().string(containsString(expectedHref(mode, "/create_account")))) .andExpect(content().string(containsString(expectedHref(mode, "/login")))); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -144,7 +146,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/account/ChangeEmailControllerViewZonePathTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/account/ChangeEmailControllerViewZonePathTests.java index 73c1ed3f6d0..40f3a296af3 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/account/ChangeEmailControllerViewZonePathTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/account/ChangeEmailControllerViewZonePathTests.java @@ -22,6 +22,7 @@ import org.junit.jupiter.params.provider.EnumSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.security.core.context.SecurityContextHolder; @@ -114,6 +115,7 @@ void changeEmailPageContainsZoneAwareFormAction(ZoneRequestPathMode mode) throws .andExpect(content().string(containsString(expectedFormAction(mode)))); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -123,7 +125,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/account/ChangePasswordControllerViewZonePathTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/account/ChangePasswordControllerViewZonePathTests.java index 50845a2428e..8d070935b3f 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/account/ChangePasswordControllerViewZonePathTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/account/ChangePasswordControllerViewZonePathTests.java @@ -16,6 +16,7 @@ import org.junit.jupiter.params.provider.EnumSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.security.core.context.SecurityContextHolder; @@ -95,6 +96,7 @@ void changePasswordPageContainsZoneAwareFormAction(ZoneRequestPathMode mode) thr .andExpect(content().string(containsString(expectedFormAction(mode)))); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -104,7 +106,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationEntryPointTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationEntryPointTests.java index 03617c0976d..035adfd85e2 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationEntryPointTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/account/ResetPasswordAuthenticationEntryPointTests.java @@ -80,7 +80,7 @@ void invalid_password_match() throws Exception { verify(request, times(1)).setAttribute(eq("message_code"), eq(messageCode)); verify(requestDispatcher, timeout(1)).forward(any(HttpServletRequest.class), same(response)); - verify(response, times(1)).setStatus(eq(HttpStatus.UNPROCESSABLE_ENTITY.value())); + verify(response, times(1)).setStatus(eq(HttpStatus.UNPROCESSABLE_CONTENT.value())); } @Test @@ -93,7 +93,7 @@ void when_uaa_exception() throws Exception { verify(request, times(1)).getRequestDispatcher(eq("/forgot_password")); verify(request, times(1)).setAttribute(eq("message_code"), eq("bad_code")); verify(requestDispatcher, timeout(1)).forward(any(HttpServletRequest.class), same(response)); - verify(response, times(1)).setStatus(eq(HttpStatus.UNPROCESSABLE_ENTITY.value())); + verify(response, times(1)).setStatus(eq(HttpStatus.UNPROCESSABLE_CONTENT.value())); } @Test @@ -107,7 +107,7 @@ void when_invalid_password_exception() throws Exception { verify(request, times(1)).setAttribute(eq("message"), eq(pe.getMessagesAsOneString())); verify(requestDispatcher, timeout(1)).forward(any(HttpServletRequest.class), same(response)); - verify(response, times(1)).setStatus(eq(HttpStatus.UNPROCESSABLE_ENTITY.value())); + verify(response, times(1)).setStatus(eq(HttpStatus.UNPROCESSABLE_CONTENT.value())); } } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandlerEnsureConsistencyTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandlerEnsureConsistencyTest.java index 6ecf7f04025..cb9feb1f453 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandlerEnsureConsistencyTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandlerEnsureConsistencyTest.java @@ -2,10 +2,10 @@ import org.cloudfoundry.identity.uaa.EntityWithAlias; import org.cloudfoundry.identity.uaa.scim.ScimUser; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatcher; -import org.springframework.lang.Nullable; import java.util.Objects; import java.util.UUID; diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandlerValidationTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandlerValidationTest.java index b503c2130b0..15b4170e422 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandlerValidationTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/alias/EntityAliasHandlerValidationTest.java @@ -2,12 +2,12 @@ import org.cloudfoundry.identity.uaa.EntityWithAlias; import org.cloudfoundry.identity.uaa.zone.IdentityZone; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import java.util.UUID; @@ -297,4 +297,4 @@ final void shouldReturnFalse_OnlyAliasZidSetInReqBody() { assertThat(aliasHandler.aliasPropertiesAreValid(requestBody, existingEntity)).isFalse(); } } -} \ No newline at end of file +} diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/annotations/WithDatabaseContext.java b/server/src/test/java/org/cloudfoundry/identity/uaa/annotations/WithDatabaseContext.java index f0cc7c1901a..b76cfc228aa 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/annotations/WithDatabaseContext.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/annotations/WithDatabaseContext.java @@ -7,9 +7,9 @@ import org.cloudfoundry.identity.uaa.util.beans.PasswordEncoderConfig; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; -import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration; +import org.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.test.context.web.WebAppConfiguration; diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/PasswordChangeUiRequiredFilterTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/PasswordChangeUiRequiredFilterTest.java index baf4c01f89d..65e5ae6e240 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/PasswordChangeUiRequiredFilterTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/PasswordChangeUiRequiredFilterTest.java @@ -93,6 +93,7 @@ void authenticatedPasswordExpired() throws Exception { @Test void loadingChangePasswordPage() throws Exception { + mockHttpServletRequest.setRequestURI("/force_password_change"); mockHttpServletRequest.setPathInfo("/force_password_change"); mockHttpServletRequest.setMethod(HttpMethod.GET.name()); SecurityContextHolder.getContext().setAuthentication(mockUaaAuthentication); @@ -105,6 +106,7 @@ void loadingChangePasswordPage() throws Exception { @Test void submitChangePassword() throws Exception { + mockHttpServletRequest.setRequestURI("/force_password_change"); mockHttpServletRequest.setPathInfo("/force_password_change"); mockHttpServletRequest.setMethod(HttpMethod.POST.name()); SecurityContextHolder.getContext().setAuthentication(mockUaaAuthentication); @@ -117,6 +119,7 @@ void submitChangePassword() throws Exception { @Test void followCompletedRedirect() throws Exception { + mockHttpServletRequest.setRequestURI("/force_password_change_completed"); mockHttpServletRequest.setPathInfo("/force_password_change_completed"); mockHttpServletRequest.setMethod(HttpMethod.POST.name()); SecurityContextHolder.getContext().setAuthentication(mockUaaAuthentication); @@ -132,6 +135,7 @@ void followCompletedRedirectWithSavedRequest() throws Exception { String location = "/oauth/authorize"; SavedRequest savedRequest = getSavedRequest(location); when(mockRequestCache.getRequest(any(), any())).thenReturn(savedRequest); + mockHttpServletRequest.setRequestURI("/force_password_change_completed"); mockHttpServletRequest.setPathInfo("/force_password_change_completed"); mockHttpServletRequest.setMethod(HttpMethod.POST.name()); SecurityContextHolder.getContext().setAuthentication(mockUaaAuthentication); @@ -144,6 +148,7 @@ void followCompletedRedirectWithSavedRequest() throws Exception { @Test void tryingAccessForcePasswordPage() throws Exception { + mockHttpServletRequest.setRequestURI("/force_password_change"); mockHttpServletRequest.setPathInfo("/force_password_change"); SecurityContextHolder.getContext().setAuthentication(mockUaaAuthentication); when(mockUaaAuthentication.isAuthenticated()).thenReturn(true); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/manager/LdapLoginAuthenticationManagerTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/manager/LdapLoginAuthenticationManagerTests.java index 8cf69a9ef9a..2f4661ca828 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/manager/LdapLoginAuthenticationManagerTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/authentication/manager/LdapLoginAuthenticationManagerTests.java @@ -320,7 +320,7 @@ void test_authentication_attributes(boolean storeUserInfo) { assertThat(authentication.getUserAttributes()).as("Expected two user attributes") .hasSize(2) - .as("Expected cost center attribute").containsKey(COST_CENTERS); + .as("Expected cost center attribute").containsKeys(COST_CENTERS); assertThat(authentication.getUserAttributes().getFirst(COST_CENTERS)).isEqualTo(DENVER_CO); assertThat(authentication.getUserAttributes()).as("Expected manager attribute").containsKey(MANAGERS); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/db/beans/DatabasePropertiesTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/db/beans/DatabasePropertiesTest.java index a61c0901e0e..dc7dccf6c19 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/db/beans/DatabasePropertiesTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/db/beans/DatabasePropertiesTest.java @@ -8,9 +8,11 @@ import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit.jupiter.SpringExtensionConfig; import static org.assertj.core.api.Assertions.assertThat; +@SpringExtensionConfig(useTestClassScopedExtensionContext = true) class DatabasePropertiesTest { @Nested diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerTest.java index 4ad120ff9fe..b8c8ed18995 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerTest.java @@ -38,6 +38,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.security.authentication.AnonymousAuthenticationToken; @@ -770,6 +771,7 @@ void acceptInviteWorksWithConsentProvided() throws Exception { defaultZone.getConfig().setBranding(null); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -779,7 +781,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerZonePathTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerZonePathTest.java index e7e9a78567e..384e188256a 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerZonePathTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerZonePathTest.java @@ -41,6 +41,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.security.authentication.AnonymousAuthenticationToken; @@ -852,6 +853,7 @@ void acceptInviteWorksWithConsentProvided(ZoneRequestPathMode mode) throws Excep defaultZone.getConfig().setBranding(null); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -861,7 +863,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/AccountsControllerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/AccountsControllerTest.java index d1f415fb8c8..98577bf679a 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/AccountsControllerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/AccountsControllerTest.java @@ -21,6 +21,7 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.mock.web.MockHttpSession; @@ -233,6 +234,7 @@ void verifyUserWithPriorHeadRequest() throws Exception { Mockito.verify(accountCreationService, times(1)).completeActivation("the_secret_code"); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -242,7 +244,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ChangeEmailControllerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ChangeEmailControllerTest.java index ac033a34142..ad32db66158 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ChangeEmailControllerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ChangeEmailControllerTest.java @@ -20,6 +20,7 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.security.authentication.AnonymousAuthenticationToken; @@ -373,6 +374,7 @@ private void setupSecurityContext() { SecurityContextHolder.getContext().setAuthentication(authentication); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -382,7 +384,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/HomeControllerViewTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/HomeControllerViewTests.java index 70297c299f4..89fb4ace363 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/HomeControllerViewTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/HomeControllerViewTests.java @@ -24,6 +24,7 @@ import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.core.context.SecurityContextHolder; @@ -248,6 +249,7 @@ void configuredGlobalHomePage() throws Exception { .andExpect(header().string("Location", customHomePage)); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -257,7 +259,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/HomeControllerViewZonePathTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/HomeControllerViewZonePathTests.java index 74b8ed72ded..10352e42e7c 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/HomeControllerViewZonePathTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/HomeControllerViewZonePathTests.java @@ -27,6 +27,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.security.authentication.InternalAuthenticationServiceException; @@ -365,6 +366,7 @@ void configuredGlobalHomePage(ZoneRequestPathMode mode) throws Exception { .andExpect(header().string("Location", customHomePage)); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -374,7 +376,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointTests.java index 233a3870d2a..28ad60766ae 100755 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointTests.java @@ -575,8 +575,8 @@ void promptLogic() throws Exception { Map mapPrompts = (Map) extendedModelMap.get("prompts"); assertThat(mapPrompts).as("there should be two prompts for html") .hasSize(2) - .containsKey("username") - .containsKey("password") + .containsKeys("username") + .containsKeys("password") .doesNotContainKey("passcode"); extendedModelMap.clear(); @@ -585,8 +585,8 @@ void promptLogic() throws Exception { assertThat(extendedModelMap.get("prompts")).as("prompts should be a Map for JSON content").isInstanceOf(Map.class); mapPrompts = (Map) extendedModelMap.get("prompts"); assertThat(mapPrompts).as("there should be three prompts for html").hasSize(2) - .containsKey("username") - .containsKey("password") + .containsKeys("username") + .containsKeys("password") .doesNotContainKey("passcode"); //add a SAML IDP @@ -597,9 +597,9 @@ void promptLogic() throws Exception { assertThat(extendedModelMap.get("prompts")).as("prompts should be a Map for JSON content").isInstanceOf(Map.class); mapPrompts = (Map) extendedModelMap.get("prompts"); assertThat(mapPrompts).as("there should be three prompts for html").hasSize(3) - .containsKey("username") - .containsKey("password") - .containsKey("passcode"); + .containsKeys("username") + .containsKeys("password") + .containsKeys("passcode"); when(mockSamlIdentityProviderConfigurator.getIdentityProviderDefinitions(isNull(), eq(IdentityZone.getUaa()))).thenReturn(idps); @@ -617,7 +617,7 @@ void promptLogic() throws Exception { mapPrompts = (Map) extendedModelMap.get("prompts"); assertThat(mapPrompts).doesNotContainKey("username") .doesNotContainKey("password") - .containsKey("passcode"); + .containsKeys("passcode"); } @Test @@ -1276,8 +1276,8 @@ void getPromptsFromOIDCProvider() { assertThat(extendedModelMap.get("prompts")).isInstanceOf(Map.class); Map returnedPrompts = (Map) extendedModelMap.get("prompts"); assertThat(returnedPrompts).hasSize(2) - .containsKey("username") - .containsKey("password") + .containsKeys("username") + .containsKeys("password") .doesNotContainKey("passcode"); assertThat(returnedPrompts.get("username")[1]).isEqualTo("MyEmail"); assertThat(returnedPrompts.get("password")[1]).isEqualTo("MyPassword"); @@ -1784,8 +1784,8 @@ private static void assertUsernamePasswordButNoPasscodePromptsAreReturned( final Map returnedPrompts ) { assertThat(returnedPrompts).hasSize(2) - .containsKey("username") - .containsKey("password") + .containsKeys("username") + .containsKeys("password") .doesNotContainKey("passcode"); assertThat(returnedPrompts.get("username")[1]).isEqualTo("Email"); assertThat(returnedPrompts.get("password")[1]).isEqualTo("Password"); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ProfileControllerMockMvcTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ProfileControllerMockMvcTests.java index 4d7530058d4..18365df8f2b 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ProfileControllerMockMvcTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ProfileControllerMockMvcTests.java @@ -25,6 +25,7 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; @@ -72,6 +73,7 @@ @SpringJUnitConfig(classes = ProfileControllerMockMvcTests.ContextConfiguration.class) class ProfileControllerMockMvcTests { + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -81,7 +83,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ProfileControllerMockMvcZonePathTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ProfileControllerMockMvcZonePathTests.java index f974971d3a8..d25663e64e0 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ProfileControllerMockMvcZonePathTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ProfileControllerMockMvcZonePathTests.java @@ -31,6 +31,7 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; @@ -82,6 +83,7 @@ @EnabledIfZonePathsEnabled class ProfileControllerMockMvcZonePathTests { + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -91,7 +93,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerTest.java index 4d4913a5dcb..5205f0167c9 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerTest.java @@ -33,6 +33,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.security.core.context.SecurityContextHolder; @@ -337,6 +338,7 @@ void resetPasswordPageWhenExpiringCodeNull() throws Exception { .andExpect(model().attribute("message_code", "bad_code")); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -346,7 +348,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerViewZonePathTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerViewZonePathTests.java index 1d666f9e115..1e63ce93e09 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerViewZonePathTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerViewZonePathTests.java @@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.security.core.context.SecurityContextHolder; @@ -109,6 +110,7 @@ void emailSentPageContainsZoneAwareBackToSignInLink(ZoneRequestPathMode mode) th .andExpect(content().string(containsString(expectedLoginHref(mode)))); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { @@ -118,7 +120,6 @@ static class ContextConfiguration implements WebMvcConfigurer { @PostConstruct public void init() { - requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(false); } @Override diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/SessionControllerViewZonePathTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/SessionControllerViewZonePathTests.java index ad90ff64965..d611458e82c 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/SessionControllerViewZonePathTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/SessionControllerViewZonePathTests.java @@ -11,6 +11,7 @@ import org.junit.jupiter.params.provider.EnumSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.annotation.DirtiesContext; @@ -109,6 +110,7 @@ void sessionManagementPageReturnsOkAndContainsExpectedPaths(ZoneRequestPathMode .andExpect(content().string(containsString(MESSAGE_ORIGIN))); } + @Configuration @EnableWebMvc @Import(ThymeleafConfig.class) static class ContextConfiguration implements WebMvcConfigurer { diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ThymeleafAdditional.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ThymeleafAdditional.java index 85ae80e8824..59445b47ac8 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/ThymeleafAdditional.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/ThymeleafAdditional.java @@ -16,8 +16,10 @@ package org.cloudfoundry.identity.uaa.login; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.web.accept.ContentNegotiationManager; +@Configuration public class ThymeleafAdditional { @Bean diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/UaaResetPasswordServiceTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/UaaResetPasswordServiceTests.java index 85589336ac3..4c02c21caf8 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/UaaResetPasswordServiceTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/UaaResetPasswordServiceTests.java @@ -46,7 +46,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static org.springframework.http.HttpStatus.UNPROCESSABLE_CONTENT; class UaaResetPasswordServiceTests { @@ -209,7 +209,7 @@ void resetPassword_InvalidPasswordException_NewPasswordSameAsOld() { when(codeStore.retrieveCode("good_code", currentZoneId)).thenReturn(expiringCode); when(scimUserProvisioning.retrieve("user-id", currentZoneId)).thenReturn(user); when(scimUserProvisioning.checkPasswordMatches("user-id", "Passwo3dAsOld", currentZoneId)) - .thenThrow(new InvalidPasswordException("Your new password cannot be the same as the old password.", UNPROCESSABLE_ENTITY)); + .thenThrow(new InvalidPasswordException("Your new password cannot be the same as the old password.", UNPROCESSABLE_CONTENT)); SecurityContext securityContext = mock(SecurityContext.class); when(securityContext.getAuthentication()).thenReturn(new MockAuthentication()); SecurityContextHolder.setContext(securityContext); @@ -218,7 +218,7 @@ void resetPassword_InvalidPasswordException_NewPasswordSameAsOld() { fail(""); } catch (InvalidPasswordException e) { assertThat(e.getMessage()).isEqualTo("Your new password cannot be the same as the old password."); - assertThat(e.getStatus()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(e.getStatus()).isEqualTo(UNPROCESSABLE_CONTENT); } } @@ -296,7 +296,7 @@ void resetPassword_ForcedChange_NewPasswordSameAsOld() { user.setPrimaryEmail("foo@example.com"); when(scimUserProvisioning.retrieve(userId, currentZoneId)).thenReturn(user); when(scimUserProvisioning.checkPasswordMatches("user-id", "password", currentZoneId)) - .thenThrow(new InvalidPasswordException("Your new password cannot be the same as the old password.", UNPROCESSABLE_ENTITY)); + .thenThrow(new InvalidPasswordException("Your new password cannot be the same as the old password.", UNPROCESSABLE_CONTENT)); assertThatExceptionOfType(InvalidPasswordException.class).isThrownBy(() -> uaaResetPasswordService.resetUserPassword(userId, "password")); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/metrics/UaaMetricsFilterTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/metrics/UaaMetricsFilterTests.java index 91842fd8250..210ce69232a 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/metrics/UaaMetricsFilterTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/metrics/UaaMetricsFilterTests.java @@ -9,7 +9,7 @@ import org.springframework.jmx.export.notification.NotificationPublisher; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -198,9 +198,7 @@ void deserialize_summary() throws Exception { @Test void url_groups() { request.setServerName("localhost:8080"); - setRequestData("/uaa/authenticate"); - request.setPathInfo("/authenticate"); - request.setContextPath("/uaa"); + setRequestData("/authenticate"); assertThat(filter.getUriGroup(request).getGroup()).isEqualTo("/api"); } @@ -251,7 +249,7 @@ void uri_groups_when_fails_to_load() { void validate_matcher() { //validates that patterns that end with /** still match at parent level setRequestData("/some/path"); - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/some/path/**"); + PathPatternRequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher("/some/path/**"); assertThat(matcher.matches(request)).isTrue(); } } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/DeprecatedUaaTokenServicesTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/DeprecatedUaaTokenServicesTests.java index 7f087ef22c0..9588c921258 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/DeprecatedUaaTokenServicesTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/DeprecatedUaaTokenServicesTests.java @@ -2206,7 +2206,7 @@ private void validateExternalAttributes(OAuth2AccessToken accessToken) { }); assertThat(claims).containsKey("ext_attr") - .containsKey("ex_prop"); + .containsKeys("ex_prop"); assertThat(((Map) claims.get("ext_attr"))).containsEntry("purpose", "test"); assertThat(((Map) claims.get("ex_prop"))).containsEntry("country", "nz"); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/RemoteTokenServicesTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/RemoteTokenServicesTests.java index 55715e30711..f5d693600ab 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/RemoteTokenServicesTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/RemoteTokenServicesTests.java @@ -87,7 +87,7 @@ void tokenRetrievalWithClaims() { assertThat(result.getUserAuthentication().getName()).isEqualTo("olds"); assertThat(((RemoteUserAuthentication) result.getUserAuthentication()).getId()).isEqualTo("HDGFJSHGDF"); assertThat(result.getOAuth2Request().getRequestParameters()).isNotNull() - .containsKey(ClaimConstants.ISS); + .containsKeys(ClaimConstants.ISS); } @Test diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthenticationTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthenticationTest.java index d6bcb5e788e..7337ad3dc0a 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthenticationTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtClientAuthenticationTest.java @@ -101,7 +101,7 @@ void getClientAssertionUsingTrueBooleanConfig() throws ParseException { params = jwtClientAuthentication.getClientAuthenticationParameters(params, config); // Then assertThat(params).containsKey("client_assertion") - .containsKey("client_assertion_type"); + .containsKeys("client_assertion_type"); String clientAssertion = params.get("client_assertion").getFirst(); validateClientAssertionOidcCompliant(clientAssertion); JWSHeader header = getJwtHeader(clientAssertion); @@ -152,7 +152,7 @@ void getClientAuthenticationParameters() throws ParseException { params = jwtClientAuthentication.getClientAuthenticationParameters(params, config); // Then assertThat(params).containsKey("client_assertion") - .containsKey("client_assertion_type") + .containsKeys("client_assertion_type") .containsEntry("client_assertion_type", Collections.singletonList(JwtClientAuthentication.GRANT_TYPE)); assertThat(params.get("client_assertion").getFirst()).isNotNull(); validateClientAssertionOidcCompliant(params.get("client_assertion").getFirst()); @@ -199,7 +199,7 @@ void getClientAssertionUsingCustomSingingKeyFromEnvironment() throws ParseExcept params = jwtClientAuthentication.getClientAuthenticationParameters(params, config); // Then assertThat(params).containsKey("client_assertion") - .containsKey("client_assertion_type"); + .containsKeys("client_assertion_type"); String clientAssertion = params.get("client_assertion").getFirst(); validateClientAssertionOidcCompliant(clientAssertion); JWSHeader header = getJwtHeader(clientAssertion); @@ -220,7 +220,7 @@ void getClientAssertionUsingCustomSingingKeyFromEnvironmentNoDefault() throws Pa MultiValueMap params = jwtClientAuthentication.getClientAuthenticationParameters(new LinkedMultiValueMap<>(), config); // Then assertThat(params).containsKey("client_assertion") - .containsKey("client_assertion_type"); + .containsKeys("client_assertion_type"); String clientAssertion = params.get("client_assertion").getFirst(); validateClientAssertionOidcCompliant(clientAssertion); JWSHeader header = getJwtHeader(clientAssertion); @@ -278,7 +278,7 @@ void getClientAssertionCustomSingingKeyButNoCertificate() throws ParseException, params = jwtClientAuthentication.getClientAuthenticationParameters(params, config); // Then assertThat(params).containsKey("client_assertion") - .containsKey("client_assertion_type"); + .containsKeys("client_assertion_type"); String clientAssertion = params.get("client_assertion").getFirst(); validateClientAssertionOidcCompliant(clientAssertion); JWSHeader header = getJwtHeader(clientAssertion); @@ -305,7 +305,7 @@ void getClientAssertionUsingCustomSingingPrivateKeyFromEnvironment() throws Pars params = jwtClientAuthentication.getClientAuthenticationParameters(params, config); // Then assertThat(params).containsKey("client_assertion") - .containsKey("client_assertion_type"); + .containsKeys("client_assertion_type"); String clientAssertion = params.get("client_assertion").getFirst(); validateClientAssertionOidcCompliant(clientAssertion); JWSHeader header = getJwtHeader(clientAssertion); @@ -367,7 +367,7 @@ void getClientAssertionUsingCustomSingingPrivateKeyFromEnvironmentEnabledForCust // Then assertThat(params).containsKey("client_assertion") - .containsKey("client_assertion_type"); + .containsKeys("client_assertion_type"); final String clientAssertion = params.get("client_assertion").getFirst(); validateClientAssertionOidcCompliant(clientAssertion); final JWSHeader header = getJwtHeader(clientAssertion); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtHelperX5tTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtHelperX5tTest.java index e2b0eee1908..573d932bd7f 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtHelperX5tTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/jwt/JwtHelperX5tTest.java @@ -79,7 +79,7 @@ void jwtKeysShouldIgnoreNullCertificatesAndNotContainX5t() { private static void validateThatNoX509InformationInMap(Map tokenKey) { assertThat(tokenKey).doesNotContainKey("x5t") .doesNotContainKey("x5c") - .containsKey("value") + .containsKeys("value") .containsEntry("kid", "testKid") .containsEntry("alg", "RS256"); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/AbstractDefaultTokenServicesTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/AbstractDefaultTokenServicesTests.java index e799cc0a280..2f39560560e 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/AbstractDefaultTokenServicesTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/AbstractDefaultTokenServicesTests.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; import java.io.Serial; import java.util.Arrays; @@ -244,7 +245,7 @@ protected static class TestAuthentication extends AbstractAuthenticationToken { private final String principal; public TestAuthentication(String name, boolean authenticated) { - super(null); + super((Collection) null); setAuthenticated(authenticated); this.principal = name; } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/ClientDetailsServiceBuilder.java b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/ClientDetailsServiceBuilder.java index 97199d22e5b..8d6cd60efa5 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/ClientDetailsServiceBuilder.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/ClientDetailsServiceBuilder.java @@ -33,7 +33,7 @@ public InMemoryClientDetailsServiceBuilder inMemory() throws Exception { public ClientDetailsServiceBuilder clients(final ClientDetailsService clientDetailsService) throws Exception { return new ClientDetailsServiceBuilder() { @Override - public ClientDetailsService build() throws Exception { + public ClientDetailsService build() { return clientDetailsService; } }; @@ -46,9 +46,13 @@ public ClientBuilder withClient(String clientId) { } @Override - public ClientDetailsService build() throws Exception { + public ClientDetailsService build() { for (ClientBuilder clientDetailsBldr : clientBuilders) { - addClient(clientDetailsBldr.clientId, clientDetailsBldr.build()); + try { + addClient(clientDetailsBldr.clientId, clientDetailsBldr.build()); + } catch (Exception e) { + throw new RuntimeException(e); + } } return performBuild(); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/TokenStoreBaseTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/TokenStoreBaseTests.java index d09b54713f8..0481697a568 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/TokenStoreBaseTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/token/TokenStoreBaseTests.java @@ -10,6 +10,7 @@ import org.cloudfoundry.identity.uaa.oauth.provider.RequestTokenFactory; import org.junit.jupiter.api.Test; import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; import java.io.Serial; import java.util.Collection; @@ -203,7 +204,7 @@ protected static class TestAuthentication extends AbstractAuthenticationToken { private final String principal; public TestAuthentication(String name, boolean authenticated) { - super(null); + super((Collection) null); setAuthenticated(authenticated); this.principal = name; } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderAliasHandlerValidationTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderAliasHandlerValidationTest.java index d76f7bb339b..d91bd72adef 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderAliasHandlerValidationTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderAliasHandlerValidationTest.java @@ -5,6 +5,8 @@ import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneProvisioning; import org.cloudfoundry.identity.uaa.zone.ZoneDoesNotExistsException; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -12,8 +14,6 @@ import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import java.util.Arrays; import java.util.Set; diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpointsTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpointsTest.java index f3328d5081c..d4713c7722b 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpointsTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpointsTest.java @@ -62,7 +62,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static org.springframework.http.HttpStatus.UNPROCESSABLE_CONTENT; @ExtendWith(PollutionPreventionExtension.class) @ExtendWith(MockitoExtension.class) @@ -395,7 +395,7 @@ void update_saml_provider_validator_failed() { when(mockIdentityProviderProvisioning.retrieve(any(), eq(zoneId))).thenReturn(provider); ResponseEntity response = identityProviderEndpoints.updateIdentityProvider(provider.getId(), provider, true); assertThat(response).isNotNull(); - assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); verify(mockPlatformTransactionManager, never()).getTransaction(any()); verify(mockIdpAliasHandler, never()).ensureConsistencyOfAliasEntity(any(), any()); } @@ -413,7 +413,7 @@ void update_saml_provider_alias_failed() { when(mockIdentityProviderProvisioning.retrieve(any(), eq(zoneId))).thenReturn(provider); ResponseEntity response = identityProviderEndpoints.updateIdentityProvider(provider.getId(), provider, true); assertThat(response).isNotNull(); - assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); verify(mockPlatformTransactionManager).getTransaction(any()); verify(mockIdpAliasHandler, times(1)).ensureConsistencyOfAliasEntity(any(), any()); } @@ -431,7 +431,7 @@ void create_saml_provider_validator_failed() { doThrow(new IllegalArgumentException("error")).when(mockIdentityProviderConfigValidationDelegator).validate(any()); ResponseEntity response = identityProviderEndpoints.createIdentityProvider(provider, true); assertThat(response).isNotNull(); - assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); verify(mockIdpAliasHandler, never()).aliasPropertiesAreValid(provider, null); } @@ -447,7 +447,7 @@ void create_saml_provider_alias_failed() { provider.setConfig(samlConfig); ResponseEntity response = identityProviderEndpoints.createIdentityProvider(provider, true); assertThat(response).isNotNull(); - assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); verify(mockPlatformTransactionManager).getTransaction(any()); verify(mockIdpAliasHandler, times(1)).ensureConsistencyOfAliasEntity(any(), any()); } @@ -544,7 +544,7 @@ void shouldRespondWith422_WhenAliasPropertiesAreNotValid() { true ); - assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); } @ParameterizedTest @@ -660,7 +660,7 @@ void shouldRespondWith422_WhenAliasPropertiesAreNotValid() { true ); - assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); } @ParameterizedTest @@ -793,7 +793,7 @@ void deleteIdpWithAliasAliasFeatureDisabled() { ); // deletion should be rejected - assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(response.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); } private Pair, IdentityProvider> arrangeIdpWithAliasExists(final String zone1Id, final String zone2Id) { @@ -898,7 +898,7 @@ void create_oauth_provider_set_auth_method_none() { void patchIdentityProviderStatusInvalidPayload() { IdentityProviderStatus identityProviderStatus = new IdentityProviderStatus(); ResponseEntity responseEntity = identityProviderEndpoints.updateIdentityProviderStatus("123", identityProviderStatus); - assertThat(responseEntity.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(responseEntity.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); } @Test @@ -911,7 +911,7 @@ void patchIdentityProviderStatusInvalidIDP() { notUAAIDP.setConfig(new SamlIdentityProviderDefinition()); when(mockIdentityProviderProvisioning.retrieve(anyString(), eq(zoneId))).thenReturn(notUAAIDP); ResponseEntity responseEntity = identityProviderEndpoints.updateIdentityProviderStatus("123", identityProviderStatus); - assertThat(responseEntity.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(responseEntity.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); } @Test @@ -924,7 +924,7 @@ void patchIdentityProviderStatusWithNoIDPDefinition() { invalidIDP.setType(OriginKeys.UAA); when(mockIdentityProviderProvisioning.retrieve(anyString(), eq(zoneId))).thenReturn(invalidIDP); ResponseEntity responseEntity = identityProviderEndpoints.updateIdentityProviderStatus("123", identityProviderStatus); - assertThat(responseEntity.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(responseEntity.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); } @Test @@ -937,7 +937,7 @@ void patchIdentityProviderStatusWithNoPasswordPolicy() { invalidIDP.setConfig(new UaaIdentityProviderDefinition(null, null)); when(mockIdentityProviderProvisioning.retrieve(anyString(), eq(zoneId))).thenReturn(invalidIDP); ResponseEntity responseEntity = identityProviderEndpoints.updateIdentityProviderStatus("123", identityProviderStatus); - assertThat(responseEntity.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(responseEntity.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); } @Test @@ -984,7 +984,7 @@ void deleteIdentityProviderNotExisting() { ResponseEntity deleteResponse = identityProviderEndpoints.deleteIdentityProvider( identityProviderIdentifier, false); - assertThat(deleteResponse.getStatusCode()).isEqualTo(UNPROCESSABLE_ENTITY); + assertThat(deleteResponse.getStatusCode()).isEqualTo(UNPROCESSABLE_CONTENT); } @Test diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/ldap/ProcessLdapPropertiesTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/ldap/ProcessLdapPropertiesTest.java index a7a02014748..08e261f2d34 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/ldap/ProcessLdapPropertiesTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/ldap/ProcessLdapPropertiesTest.java @@ -14,11 +14,16 @@ package org.cloudfoundry.identity.uaa.provider.ldap; +import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; import org.cloudfoundry.identity.uaa.provider.ldap.extension.DefaultTlsDirContextAuthenticationStrategy; import org.cloudfoundry.identity.uaa.provider.ldap.extension.ExternalTlsDirContextAuthenticationStrategy; import org.junit.jupiter.api.Test; +import org.springframework.ldap.core.support.AbstractTlsDirContextAuthenticationStrategy; import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy; +import org.springframework.test.util.ReflectionTestUtils; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; import java.util.HashMap; import java.util.Map; @@ -65,20 +70,74 @@ void process_whenSslValidationIsEnabled() throws Exception { } @Test - void authentication_strategy() throws Exception { + void authenticationStrategy() throws Exception { ProcessLdapProperties process = new ProcessLdapProperties("ldap://localhost:389", false, null); - assertThat(process.getAuthenticationStrategy().getClass()).isEqualTo(SimpleDirContextAuthenticationStrategy.class); - process = new ProcessLdapProperties("ldap://localhost:389", false, LDAP_TLS_NONE); - assertThat(process.getAuthenticationStrategy().getClass()).isEqualTo(SimpleDirContextAuthenticationStrategy.class); - process = new ProcessLdapProperties("ldap://localhost:389", false, LDAP_TLS_SIMPLE); - assertThat(process.getAuthenticationStrategy().getClass()).isEqualTo(DefaultTlsDirContextAuthenticationStrategy.class); - process = new ProcessLdapProperties("ldap://localhost:389", false, LDAP_TLS_EXTERNAL); - assertThat(process.getAuthenticationStrategy().getClass()).isEqualTo(ExternalTlsDirContextAuthenticationStrategy.class); + assertThat(process.getAuthenticationStrategy()).isExactlyInstanceOf(SimpleDirContextAuthenticationStrategy.class); } @Test - void invalid_authentication_strategy() { + void invalid_authenticationStrategy() { ProcessLdapProperties process = new ProcessLdapProperties("ldap://localhost:389", false, "asdadasda"); assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(process::getAuthenticationStrategy); } + + @Test + void authenticationStrategy_forTlsNone() throws Exception { + // Case 1: LDAP_TLS_NONE - No TLS, returns SimpleDirContextAuthenticationStrategy (no hostname verification) + ProcessLdapProperties tlsNone = new ProcessLdapProperties("ldap://localhost:389", false, LDAP_TLS_NONE); + assertThat(tlsNone.getAuthenticationStrategy()).isInstanceOf(SimpleDirContextAuthenticationStrategy.class); + + ProcessLdapProperties tlsNoneSkipSsl = new ProcessLdapProperties("ldap://localhost:389", true, LDAP_TLS_NONE); + assertThat(tlsNoneSkipSsl.getAuthenticationStrategy()).isInstanceOf(SimpleDirContextAuthenticationStrategy.class); + } + + @Test + void authenticationStrategy_forTlsSimple() throws Exception { + // Case 2: LDAP_TLS_SIMPLE with SSL verification enabled (default) + // Expected: Uses JDK default hostname verification (secure) + ProcessLdapProperties tlsSimpleSecure = new ProcessLdapProperties("ldap://localhost:389", false, LDAP_TLS_SIMPLE); + var simpleSecureStrategy = (AbstractTlsDirContextAuthenticationStrategy) tlsSimpleSecure.getAuthenticationStrategy(); + assertThat(simpleSecureStrategy).isInstanceOf(DefaultTlsDirContextAuthenticationStrategy.class); + assertThat(tlsSimpleSecure.isDisableSslVerification()).isFalse(); + HostnameVerifier actualVerifier = getHostnameVerifierFromStrategy(simpleSecureStrategy); + assertThat(actualVerifier).isNotInstanceOf(NoopHostnameVerifier.class) + .isSameAs(HttpsURLConnection.getDefaultHostnameVerifier()); + + // Case 3: LDAP_TLS_SIMPLE with SSL verification disabled + // Expected: Uses NoopHostnameVerifier + ProcessLdapProperties tlsSimpleInsecure = new ProcessLdapProperties("ldap://localhost:389", true, LDAP_TLS_SIMPLE); + var simpleInsecureStrategy = (AbstractTlsDirContextAuthenticationStrategy) tlsSimpleInsecure.getAuthenticationStrategy(); + assertThat(simpleInsecureStrategy).isInstanceOf(DefaultTlsDirContextAuthenticationStrategy.class); + assertThat(tlsSimpleInsecure.isDisableSslVerification()).isTrue(); + assertThat(getHostnameVerifierFromStrategy(simpleInsecureStrategy)).isInstanceOf(NoopHostnameVerifier.class); + } + + @Test + void authenticationStrategy_forTlsExternal() throws Exception { + // Case 4: LDAP_TLS_EXTERNAL with SSL verification enabled (default) + // Expected: Uses JDK default hostname verification (secure) + ProcessLdapProperties tlsExternalSecure = new ProcessLdapProperties("ldap://localhost:389", false, LDAP_TLS_EXTERNAL); + var externalSecureStrategy = (AbstractTlsDirContextAuthenticationStrategy) tlsExternalSecure.getAuthenticationStrategy(); + assertThat(externalSecureStrategy).isInstanceOf(ExternalTlsDirContextAuthenticationStrategy.class); + assertThat(tlsExternalSecure.isDisableSslVerification()).isFalse(); + HostnameVerifier actualVerifier = getHostnameVerifierFromStrategy(externalSecureStrategy); + assertThat(actualVerifier).isNotInstanceOf(NoopHostnameVerifier.class) + .isSameAs(HttpsURLConnection.getDefaultHostnameVerifier()); + + // Case 5: LDAP_TLS_EXTERNAL with SSL verification disabled + // Expected: Uses NoopHostnameVerifier + ProcessLdapProperties tlsExternalInsecure = new ProcessLdapProperties("ldap://localhost:389", true, LDAP_TLS_EXTERNAL); + var externalInsecureStrategy = (AbstractTlsDirContextAuthenticationStrategy) tlsExternalInsecure.getAuthenticationStrategy(); + assertThat(externalInsecureStrategy).isInstanceOf(ExternalTlsDirContextAuthenticationStrategy.class); + assertThat(tlsExternalInsecure.isDisableSslVerification()).isTrue(); + assertThat(getHostnameVerifierFromStrategy(externalInsecureStrategy)).isInstanceOf(NoopHostnameVerifier.class); + } + + /** + * Helper method to extract the hostname verifier from an AbstractTlsDirContextAuthenticationStrategy + * using ReflectionTestUtils since there's no public getter method. + */ + private HostnameVerifier getHostnameVerifierFromStrategy(AbstractTlsDirContextAuthenticationStrategy strategy) { + return (HostnameVerifier) ReflectionTestUtils.getField(strategy, "hostnameVerifier"); + } } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java index f31894bd580..e0049cd7835 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java @@ -952,10 +952,10 @@ void oidcJwtBearerProviderProxyThrowException() throws JOSEException, MalformedU })); HttpEntity httpEntity = httpEntityArgumentCaptor.getValue(); HttpHeaders httpHeaders = httpEntity.getHeaders(); - assertThat(httpHeaders).containsKey("Authorization") - .containsEntry("Authorization", Collections.singletonList("Basic aWRlbnRpdHk6c2VjcmV0")) - .containsKey("Accept") - .containsEntry("Content-Type", Collections.singletonList("application/x-www-form-urlencoded")); + assertThat(httpHeaders.get("Authorization")).isNotNull(); + assertThat(httpHeaders.get("Authorization")).isEqualTo(Collections.singletonList("Basic aWRlbnRpdHk6c2VjcmV0")); + assertThat(httpHeaders.get("Accept")).isNotNull(); + assertThat(httpHeaders.get("Content-Type")).isEqualTo(Collections.singletonList("application/x-www-form-urlencoded")); } @Test @@ -1012,7 +1012,7 @@ void oidcPasswordGrantWithForwardHeader() throws JOSEException, MalformedURLExce assertThat(headers.getAccept()).isEqualTo(Collections.singletonList(MediaType.APPLICATION_JSON)); assertThat(headers.getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED); assertAuthorizationHeaderIsSetAndStartsWithBasic(headers); - assertThat(headers).containsKey("X-Forwarded-For"); + assertThat(headers.get("X-Forwarded-For")).isNotNull(); final List xForwardedForHeaders = headers.get("X-Forwarded-For"); assertThat(xForwardedForHeaders).hasSize(1); assertThat(xForwardedForHeaders.getFirst()).isEqualTo("203.0.113.1"); @@ -1117,7 +1117,7 @@ void oidcPasswordGrantWithPrompts() throws MalformedURLException, JOSEException assertThat(headers.getAccept()).isEqualTo(Collections.singletonList(MediaType.APPLICATION_JSON)); assertThat(headers.getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED); assertAuthorizationHeaderIsSetAndStartsWithBasic(headers); - assertThat(headers).doesNotContainKey("X-Forwarded-For"); + assertThat(headers.get("X-Forwarded-For")).isNull(); } @Test @@ -1165,7 +1165,7 @@ public RestTemplate getRestTemplate(AbstractExternalOAuthIdentityProviderDefinit } private static void assertAuthorizationHeaderIsSetAndStartsWithBasic(final HttpHeaders headers) { - assertThat(headers).containsKey("Authorization"); + assertThat(headers.get("Authorization")).isNotNull(); final List authorizationHeaders = headers.get("Authorization"); assertThat(authorizationHeaders).hasSize(1); assertThat(authorizationHeaders.getFirst()).startsWith("Basic "); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthProviderConfiguratorTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthProviderConfiguratorTests.java index b1f76990223..c6221c2fb2b 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthProviderConfiguratorTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthProviderConfiguratorTests.java @@ -440,7 +440,7 @@ void getIdpAuthenticationUrl_includesPkceOnPublicOIDC() { Map queryParams = UriComponentsBuilder.fromUriString(authzUri).build().getQueryParams().toSingleValueMap(); - assertThat(queryParams).containsKeys("code_challenge", "code_challenge_method"); + assertThat(queryParams).containsKey("code_challenge").containsKey("code_challenge_method"); } @Test @@ -453,8 +453,8 @@ void getIdpAuthenticationUrl_includesPkce() { Map queryParams = UriComponentsBuilder.fromUriString(authzUri).build().getQueryParams().toSingleValueMap(); assertThat(queryParams) - .containsKey("code_challenge") - .containsKey("code_challenge_method"); + .containsKeys("code_challenge") + .containsKeys("code_challenge_method"); } @Test @@ -492,7 +492,7 @@ void getIdpAuthenticationUrl_deactivatesPkce() { Map queryParams = UriComponentsBuilder.fromUriString(authzUri).build().getQueryParams().toSingleValueMap(); - assertThat(queryParams).doesNotContainKeys("code_challenge", "code_challenge_method"); + assertThat(queryParams).doesNotContainKey("code_challenge").doesNotContainKey("code_challenge_method"); } @Test @@ -529,7 +529,7 @@ void getIdpAuthenticationUrl_hasAllRequiredQueryParametersForOidc() { .containsEntry("response_type", "id_token+code") .containsEntry("scope", "openid+password.write") .containsEntry("state", "01234567890123456789012345678901234567890123456789") - .containsKey("nonce"); + .containsKeys("nonce"); assertThat(queryParams.get("redirect_uri")).contains("login%2Fcallback%2Falias"); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepositoryTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepositoryTest.java index 1852ab86c75..6953788a4fd 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepositoryTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/ConfiguratorRelyingPartyRegistrationRepositoryTest.java @@ -16,6 +16,7 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata; import org.springframework.util.FileCopyUtils; import java.io.IOException; @@ -119,8 +120,8 @@ void findByRegistrationIdWithMultipleInDb() { .returns("{baseUrl}/saml/SSO/alias/entityIdAlias", RelyingPartyRegistration::getAssertionConsumerServiceLocation) .returns("{baseUrl}/saml/SingleLogout/alias/entityIdAlias", RelyingPartyRegistration::getSingleLogoutServiceResponseLocation) // from xml - .extracting(RelyingPartyRegistration::getAssertingPartyDetails) - .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", RelyingPartyRegistration.AssertingPartyDetails::getEntityId); + .extracting(RelyingPartyRegistration::getAssertingPartyMetadata) + .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", AssertingPartyMetadata::getEntityId); } @Test @@ -167,8 +168,8 @@ void buildsCorrectRegistrationWhenMetadataXmlIsStored() { .returns("{baseUrl}/saml/SSO/alias/entityIdAlias", RelyingPartyRegistration::getAssertionConsumerServiceLocation) .returns("{baseUrl}/saml/SingleLogout/alias/entityIdAlias", RelyingPartyRegistration::getSingleLogoutServiceResponseLocation) // from xml - .extracting(RelyingPartyRegistration::getAssertingPartyDetails) - .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", RelyingPartyRegistration.AssertingPartyDetails::getEntityId); + .extracting(RelyingPartyRegistration::getAssertingPartyMetadata) + .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", AssertingPartyMetadata::getEntityId); } @Test @@ -222,8 +223,8 @@ void buildsCorrectRegistrationWhenMetadataLocationIsStored() { .returns("{baseUrl}/saml/SSO/alias/entityIdAlias", RelyingPartyRegistration::getAssertionConsumerServiceLocation) .returns("{baseUrl}/saml/SingleLogout/alias/entityIdAlias", RelyingPartyRegistration::getSingleLogoutServiceResponseLocation) // from xml - .extracting(RelyingPartyRegistration::getAssertingPartyDetails) - .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", RelyingPartyRegistration.AssertingPartyDetails::getEntityId); + .extracting(RelyingPartyRegistration::getAssertingPartyMetadata) + .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", AssertingPartyMetadata::getEntityId); } @Test @@ -271,10 +272,10 @@ void buildsCorrectRegistrationWhenZoneIdIsStored() { .returns("{baseUrl}/saml/SSO/alias/zoneDomain.entityIdAlias", RelyingPartyRegistration::getAssertionConsumerServiceLocation) .returns("{baseUrl}/saml/SingleLogout/alias/zoneDomain.entityIdAlias", RelyingPartyRegistration::getSingleLogoutServiceResponseLocation) // from xml - .extracting(RelyingPartyRegistration::getAssertingPartyDetails) - .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", RelyingPartyRegistration.AssertingPartyDetails::getEntityId) + .extracting(RelyingPartyRegistration::getAssertingPartyMetadata) + .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", AssertingPartyMetadata::getEntityId) // signature algorithm defaults to SHA256 - .extracting(RelyingPartyRegistration.AssertingPartyDetails::getSigningAlgorithms) + .extracting(AssertingPartyMetadata::getSigningAlgorithms) .isEqualTo(List.of(ALGO_ID_SIGNATURE_RSA_SHA256)); } @@ -343,7 +344,7 @@ void withSha512SignatureAlgorithm() { when(configurator.getIdentityProviderDefinitionsForZone(identityZone)).thenReturn(List.of(definition)); RelyingPartyRegistration registration = repository.findByRegistrationId(REGISTRATION_ID); - assertThat(registration.getAssertingPartyDetails().getSigningAlgorithms()) + assertThat(registration.getAssertingPartyMetadata().getSigningAlgorithms()) .hasSize(1) .first() .isEqualTo(ALGO_ID_SIGNATURE_RSA_SHA512); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/DefaultRelyingPartyRegistrationRepositoryTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/DefaultRelyingPartyRegistrationRepositoryTest.java index 86d370329bc..fdd55e4d439 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/DefaultRelyingPartyRegistrationRepositoryTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/DefaultRelyingPartyRegistrationRepositoryTest.java @@ -13,6 +13,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata; import java.security.Security; import java.util.List; @@ -81,8 +82,8 @@ void findByRegistrationId() { .returns("{baseUrl}/saml/SSO/alias/entityIdAlias", RelyingPartyRegistration::getAssertionConsumerServiceLocation) .returns("{baseUrl}/saml/SingleLogout/alias/entityIdAlias", RelyingPartyRegistration::getSingleLogoutServiceResponseLocation) // from xml - .extracting(RelyingPartyRegistration::getAssertingPartyDetails) - .returns("entityId", RelyingPartyRegistration.AssertingPartyDetails::getEntityId); + .extracting(RelyingPartyRegistration::getAssertingPartyMetadata) + .returns("entityId", AssertingPartyMetadata::getEntityId); } @Test @@ -104,10 +105,10 @@ void findByRegistrationIdForZone() { .returns("{baseUrl}/saml/SSO/alias/testzone.entityIdAlias", RelyingPartyRegistration::getAssertionConsumerServiceLocation) .returns("{baseUrl}/saml/SingleLogout/alias/testzone.entityIdAlias", RelyingPartyRegistration::getSingleLogoutServiceResponseLocation) // from xml - .extracting(RelyingPartyRegistration::getAssertingPartyDetails) - .returns("testzone.entityId", RelyingPartyRegistration.AssertingPartyDetails::getEntityId) + .extracting(RelyingPartyRegistration::getAssertingPartyMetadata) + .returns("testzone.entityId", AssertingPartyMetadata::getEntityId) // signature algorithm defaults to SHA256 - .extracting(RelyingPartyRegistration.AssertingPartyDetails::getSigningAlgorithms) + .extracting(AssertingPartyMetadata::getSigningAlgorithms) .isEqualTo(List.of(ALGO_ID_SIGNATURE_RSA_SHA256)); } @@ -204,7 +205,7 @@ void withSha512SignatureAlgorithm() { when(identityZoneConfig.getSamlConfig()).thenReturn(samlConfig); RelyingPartyRegistration registration = repository.findByRegistrationId(REGISTRATION_ID); - assertThat(registration.getAssertingPartyDetails().getSigningAlgorithms()) + assertThat(registration.getAssertingPartyMetadata().getSigningAlgorithms()) .hasSize(1) .first() .isEqualTo(ALGO_ID_SIGNATURE_RSA_SHA512); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProviderUaaTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5AuthenticationProviderUaaTests.java similarity index 99% rename from server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProviderUaaTests.java rename to server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5AuthenticationProviderUaaTests.java index 1f654be1ee2..7e0812a010e 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProviderUaaTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5AuthenticationProviderUaaTests.java @@ -105,7 +105,7 @@ import static org.mockito.Mockito.when; @WithDatabaseContext -class OpenSaml4AuthenticationProviderUaaTests { +class OpenSaml5AuthenticationProviderUaaTests { private static final String SAML_USER = "saml.user"; private static final String SAML_ADMIN = "saml.admin"; @@ -122,7 +122,7 @@ class OpenSaml4AuthenticationProviderUaaTests { private static final String JOHN_THE_SLOTH = "John the Sloth"; private static final String KARI_THE_ANT_EATER = "Kari the Ant Eater"; private static final String IDP_META_DATA = getResourceAsString( - OpenSaml4AuthenticationProviderUaaTests.class, "IDP_META_DATA.xml"); + OpenSaml5AuthenticationProviderUaaTests.class, "IDP_META_DATA.xml"); private static final String TEST_EMAIL = "john.doe@example.com"; private static final String TEST_USERNAME = "test@saml.user"; diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProviderUnitTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5AuthenticationProviderUnitTests.java similarity index 97% rename from server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProviderUnitTests.java rename to server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5AuthenticationProviderUnitTests.java index 41b861f109f..ff331780643 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml4AuthenticationProviderUnitTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5AuthenticationProviderUnitTests.java @@ -1,8 +1,8 @@ package org.cloudfoundry.identity.uaa.provider.saml; import com.fasterxml.jackson.databind.ObjectMapper; -import net.shibboleth.utilities.java.support.xml.SerializeSupport; -import org.cloudfoundry.identity.uaa.provider.saml.OpenSaml4AuthenticationProvider.ResponseToken; +import net.shibboleth.shared.xml.SerializeSupport; +import org.cloudfoundry.identity.uaa.provider.saml.OpenSaml5AuthenticationProvider.ResponseToken; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.junit.jupiter.api.AfterEach; @@ -70,21 +70,21 @@ import static org.mockito.Mockito.verify; /** - * This was copied from Spring Security, Test Classes and modified to work with the modified OpenSaml4AuthenticationProvider. + * This was copied from Spring Security, Test Classes and modified to work with the modified OpenSaml5AuthenticationProvider. *

- * Once we can move to the spring-security version of OpenSaml4AuthenticationProvider, + * Once we can move to the spring-security version of OpenSaml5AuthenticationProvider, * this class should be removed, along with OpenSamlDecryptionUtils and OpenSamlVerificationUtils. *

* Modified Tests: * authenticateWhenAssertionContainsAttributesThenItSucceeds * deserializeWhenAssertionContainsAttributesThenWorks *

- * Tests for {@link OpenSaml4AuthenticationProvider} + * Tests for {@link OpenSaml5AuthenticationProvider} * * @author Filip Hanik * @author Josh Cummings */ -class OpenSaml4AuthenticationProviderUnitTests { +class OpenSaml5AuthenticationProviderUnitTests { private static final String DESTINATION = "http://localhost:8080/uaa/saml/SSO/alias/integration-saml-entity-id"; @@ -92,7 +92,8 @@ class OpenSaml4AuthenticationProviderUnitTests { private static final String ASSERTING_PARTY_ENTITY_ID = "https://some.idp.test/saml2/idp"; - private final OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); + private final OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider(); + @BeforeEach void setUp() { @@ -106,14 +107,14 @@ void setUp() { @Test void supportsWhenSaml2AuthenticationTokenThenReturnTrue() { assertThat(this.provider.supports(Saml2AuthenticationToken.class)) - .withFailMessage(OpenSaml4AuthenticationProvider.class + "should support " + Saml2AuthenticationToken.class) + .withFailMessage(OpenSaml5AuthenticationProvider.class + "should support " + Saml2AuthenticationToken.class) .isTrue(); } @Test void supportsWhenNotSaml2AuthenticationTokenThenReturnFalse() { assertThat(this.provider.supports(Authentication.class)) - .withFailMessage(OpenSaml4AuthenticationProvider.class + "should not support " + Authentication.class) + .withFailMessage(OpenSaml5AuthenticationProvider.class + "should not support " + Authentication.class) .isFalse(); } @@ -670,10 +671,10 @@ void writeObjectWhenTypeIsSaml2AuthenticationThenNoException() throws IOExceptio void createDefaultAssertionValidatorWhenAssertionThenValidates() { Response response = TestOpenSamlObjects.signedResponseWithOneAssertion(); Assertion assertion = response.getAssertions().getFirst(); - OpenSaml4AuthenticationProvider.AssertionToken assertionToken = new OpenSaml4AuthenticationProvider.AssertionToken( + OpenSaml5AuthenticationProvider.AssertionToken assertionToken = new OpenSaml5AuthenticationProvider.AssertionToken( assertion, token()); assertThat( - OpenSaml4AuthenticationProvider.createDefaultAssertionValidator().convert(assertionToken).hasErrors()) + OpenSaml5AuthenticationProvider.createDefaultAssertionValidator().convert(assertionToken).hasErrors()) .isFalse(); } @@ -693,7 +694,7 @@ void createDefaultResponseAuthenticationConverterWhenResponseThenConverts() { Response response = TestOpenSamlObjects.signedResponseWithOneAssertion(); Saml2AuthenticationToken token = token(response, verifying(registration())); ResponseToken responseToken = new ResponseToken(response, token); - Saml2Authentication authentication = OpenSaml4AuthenticationProvider + Saml2Authentication authentication = OpenSaml5AuthenticationProvider .createDefaultResponseAuthenticationConverter() .convert(responseToken); assertThat(authentication.getName()).isEqualTo("test@saml.user"); @@ -745,7 +746,7 @@ void setResponseValidatorWhenNullThenIllegalArgument() { void authenticateWhenCustomResponseValidatorThenUses() { Converter validator = mock(Converter.class); // @formatter:off - provider.setResponseValidator(responseToken -> OpenSaml4AuthenticationProvider.createDefaultResponseValidator() + provider.setResponseValidator(responseToken -> OpenSaml5AuthenticationProvider.createDefaultResponseValidator() .convert(responseToken) .concat(validator.convert(responseToken)) ); @@ -774,7 +775,7 @@ void authenticateWhenAssertionIssuerNotValidThenFailsWithInvalidIssuer() { // gh-14931 @Test void authenticateWhenAssertionHasProxyRestrictionThenParses() { - OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); + OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider(); Response response = response(); Assertion assertion = assertion(); ProxyRestriction condition = new ProxyRestrictionBuilder().buildObject(); @@ -905,15 +906,13 @@ private AbstractSaml2AuthenticationRequest mockedStoredAuthenticationRequest(Str } private RelyingPartyRegistration.Builder registration() { - return TestRelyingPartyRegistrations.noCredentials() - .entityId(RELYING_PARTY_ENTITY_ID) - .assertionConsumerServiceLocation(DESTINATION) - .assertingPartyDetails(party -> party.entityId(ASSERTING_PARTY_ENTITY_ID)); + return TestRelyingPartyRegistrations.noCredentials().entityId(RELYING_PARTY_ENTITY_ID).assertionConsumerServiceLocation(DESTINATION) + .assertingPartyMetadata(party -> party.entityId(ASSERTING_PARTY_ENTITY_ID)); } private RelyingPartyRegistration.Builder verifying(RelyingPartyRegistration.Builder builder) { - return builder.assertingPartyDetails(party -> party - .verificationX509Credentials(c -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))); + return builder.assertingPartyMetadata( + party -> party.verificationX509Credentials(c -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))); } private RelyingPartyRegistration.Builder decrypting(RelyingPartyRegistration.Builder builder) { diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/RelyingPartyRegistrationBuilderTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/RelyingPartyRegistrationBuilderTest.java index 28a4f962970..163323162dd 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/RelyingPartyRegistrationBuilderTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/RelyingPartyRegistrationBuilderTest.java @@ -7,6 +7,7 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.security.saml2.Saml2Exception; import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.util.FileCopyUtils; @@ -54,9 +55,9 @@ void buildsRelyingPartyRegistrationFromLocation() { .returns("{baseUrl}/saml/SSO/alias/entityIdAlias", RelyingPartyRegistration::getAssertionConsumerServiceLocation) .returns("{baseUrl}/saml/SingleLogout/alias/entityIdAlias", RelyingPartyRegistration::getSingleLogoutServiceResponseLocation) // from xml - .extracting(RelyingPartyRegistration::getAssertingPartyDetails) - .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", RelyingPartyRegistration.AssertingPartyDetails::getEntityId) - .returns(true, RelyingPartyRegistration.AssertingPartyDetails::getWantAuthnRequestsSigned); + .extracting(RelyingPartyRegistration::getAssertingPartyMetadata) + .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", AssertingPartyMetadata::getEntityId) + .returns(true, AssertingPartyMetadata::getWantAuthnRequestsSigned); } @Test @@ -83,9 +84,9 @@ void buildsRelyingPartyRegistrationFromXML() { .returns("{baseUrl}/saml/SSO/alias/entityIdAlias", RelyingPartyRegistration::getAssertionConsumerServiceLocation) .returns("{baseUrl}/saml/SingleLogout/alias/entityIdAlias", RelyingPartyRegistration::getSingleLogoutServiceResponseLocation) // from xml - .extracting(RelyingPartyRegistration::getAssertingPartyDetails) - .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", RelyingPartyRegistration.AssertingPartyDetails::getEntityId) - .returns(false, RelyingPartyRegistration.AssertingPartyDetails::getWantAuthnRequestsSigned); + .extracting(RelyingPartyRegistration::getAssertingPartyMetadata) + .returns("https://idp-saml.ua3.int/simplesaml/saml2/idp/metadata.php", AssertingPartyMetadata::getEntityId) + .returns(false, AssertingPartyMetadata::getWantAuthnRequestsSigned); } @Test @@ -114,7 +115,7 @@ void withCredentials() { .extracting(Saml2X509Credential::getCertificate) .containsOnly(x509Certificate1()); - assertThat(registration.getAssertingPartyDetails().getSigningAlgorithms()) + assertThat(registration.getAssertingPartyMetadata().getSigningAlgorithms()) .hasSize(2) .containsOnly(SignatureAlgorithm.SHA512.getSignatureAlgorithmURI(), SignatureAlgorithm.SHA256.getSignatureAlgorithmURI()); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2BearerGrantAuthenticationConverterTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2BearerGrantAuthenticationConverterTest.java index 7da9cb13ae8..5e534fe5ff9 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2BearerGrantAuthenticationConverterTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2BearerGrantAuthenticationConverterTest.java @@ -1,7 +1,7 @@ package org.cloudfoundry.identity.uaa.provider.saml; import com.fasterxml.jackson.databind.ObjectMapper; -import net.shibboleth.utilities.java.support.xml.SerializeSupport; +import net.shibboleth.shared.xml.SerializeSupport; import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider; import org.cloudfoundry.identity.uaa.impl.config.RestTemplateConfig; import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; @@ -65,7 +65,7 @@ import static org.mockito.Mockito.mock; /** - * This is based on OpenSaml4AuthenticationProviderTest from Spring Security + * This is based on OpenSaml5AuthenticationProviderTest from Spring Security */ class Saml2BearerGrantAuthenticationConverterTest { @@ -396,7 +396,7 @@ void writeObjectWhenTypeIsSaml2AuthenticationThenNoException() throws IOExceptio @Test void createDefaultAssertionValidatorWhenAssertionThenValidates() { Assertion assertion = signed(assertion()); - OpenSaml4AuthenticationProvider.AssertionToken assertionToken = new OpenSaml4AuthenticationProvider.AssertionToken( + OpenSaml5AuthenticationProvider.AssertionToken assertionToken = new OpenSaml5AuthenticationProvider.AssertionToken( assertion, token()); assertThat( Saml2BearerGrantAuthenticationConverter.createDefaultAssertionValidator().convert(assertionToken).hasErrors()) @@ -417,7 +417,7 @@ void authenticateWithSHA1SignatureThenItSucceeds() throws Exception { void createDefaultResponseAuthenticationConverterWhenResponseThenConverts() { Assertion assertion = assertion(); Saml2AuthenticationToken token = token(assertion, verifying(registration())); - OpenSaml4AuthenticationProvider.AssertionToken assertionToken = new OpenSaml4AuthenticationProvider.AssertionToken(assertion, token); + OpenSaml5AuthenticationProvider.AssertionToken assertionToken = new OpenSaml5AuthenticationProvider.AssertionToken(assertion, token); AbstractAuthenticationToken authentication = Saml2BearerGrantAuthenticationConverter .createDefaultAssertionAuthenticationConverter() .convert(assertionToken); @@ -557,11 +557,11 @@ private RelyingPartyRegistration.Builder registration() { return TestRelyingPartyRegistrations.noCredentials() .entityId(RELYING_PARTY_ENTITY_ID) .assertionConsumerServiceLocation(DESTINATION) - .assertingPartyDetails(party -> party.entityId(ASSERTING_PARTY_ENTITY_ID)); + .assertingPartyMetadata(party -> party.entityId(ASSERTING_PARTY_ENTITY_ID)); } private RelyingPartyRegistration.Builder verifying(RelyingPartyRegistration.Builder builder) { - return builder.assertingPartyDetails(party -> party + return builder.assertingPartyMetadata(party -> party .verificationX509Credentials(c -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2TestUtils.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2TestUtils.java index 6a6cf8cf467..6ea0d9860e7 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2TestUtils.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2TestUtils.java @@ -16,7 +16,7 @@ package org.cloudfoundry.identity.uaa.provider.saml; -import net.shibboleth.utilities.java.support.xml.SerializeSupport; +import net.shibboleth.shared.xml.SerializeSupport; import org.cloudfoundry.identity.uaa.saml.SamlKey; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; @@ -58,7 +58,7 @@ * @see TestOpenSamlObjects *

* The Functions in here were copied from Spring-Security Test Classes and made static: - * - spring-security/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests + * - spring-security/saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProviderTests */ public final class Saml2TestUtils { @@ -244,11 +244,11 @@ public static RelyingPartyRegistration.Builder registration() { return TestRelyingPartyRegistrations.noCredentials() .entityId(RELYING_PARTY_ENTITY_ID) .assertionConsumerServiceLocation(DESTINATION) - .assertingPartyDetails(party -> party.entityId(ASSERTING_PARTY_ENTITY_ID)); + .assertingPartyMetadata(party -> party.entityId(ASSERTING_PARTY_ENTITY_ID)); } public static RelyingPartyRegistration.Builder verifying(RelyingPartyRegistration.Builder builder) { - return builder.assertingPartyDetails(party -> party + return builder.assertingPartyMetadata(party -> party .verificationX509Credentials(c -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderConfiguratorTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderConfiguratorTests.java index 0e1344b17b4..ed887219734 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderConfiguratorTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlIdentityProviderConfiguratorTests.java @@ -147,7 +147,7 @@ void getEntityID() { } case "simplesamlphp-url": { RelyingPartyRegistration extendedMetadataDelegate = configurator.getExtendedMetadataDelegate(def); - assertThat(extendedMetadataDelegate.getAssertingPartyDetails().getEntityId()).isEqualTo("http://simplesamlphp.somewhere.com/saml2/idp/metadata.php"); + assertThat(extendedMetadataDelegate.getAssertingPartyMetadata().getEntityId()).isEqualTo("http://simplesamlphp.somewhere.com/saml2/idp/metadata.php"); break; } default: diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEndpointKeyRotationTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEndpointKeyRotationTests.java index 46cdf9c73f9..92ab4c38d78 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEndpointKeyRotationTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlMetadataEndpointKeyRotationTests.java @@ -1,6 +1,5 @@ package org.cloudfoundry.identity.uaa.provider.saml; -import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; @@ -18,7 +17,6 @@ import org.xmlunit.assertj.MultipleNodeAssert; import org.xmlunit.assertj.XmlAssert; -import java.security.Security; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -51,7 +49,7 @@ public class SamlMetadataEndpointKeyRotationTests { @BeforeAll static void beforeAll() { - Security.addProvider(new BouncyCastleFipsProvider()); + SamlConfiguration.setupOpenSaml(); SamlConfigProps samlConfigProps = new SamlConfigProps(); samlConfigProps.setKeys(Map.of(legacyKeyName(), legacySamlKey())); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlRelyingPartyRegistrationRepositoryConfigTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlRelyingPartyRegistrationRepositoryConfigTest.java index b733e64b3eb..86437e36eb2 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlRelyingPartyRegistrationRepositoryConfigTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/SamlRelyingPartyRegistrationRepositoryConfigTest.java @@ -9,6 +9,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; @@ -85,7 +86,7 @@ void buildsRegistrationForExample() { .returns("{baseUrl}/saml/SSO/alias/entityId", RelyingPartyRegistration::getAssertionConsumerServiceLocation) .returns("{baseUrl}/saml/SingleLogout/alias/entityId", RelyingPartyRegistration::getSingleLogoutServiceResponseLocation) // from xml - .extracting(RelyingPartyRegistration::getAssertingPartyDetails) - .returns("entityId", RelyingPartyRegistration.AssertingPartyDetails::getEntityId); + .extracting(RelyingPartyRegistration::getAssertingPartyMetadata) + .returns("entityId", AssertingPartyMetadata::getEntityId); } } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestCustomOpenSamlObjects.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestCustomOpenSamlObjects.java index 07574b620b0..0d0e986b373 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestCustomOpenSamlObjects.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestCustomOpenSamlObjects.java @@ -23,7 +23,7 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import net.shibboleth.utilities.java.support.xml.ElementSupport; +import net.shibboleth.shared.xml.ElementSupport; import org.opensaml.core.xml.AbstractXMLObject; import org.opensaml.core.xml.AbstractXMLObjectBuilder; import org.opensaml.core.xml.ElementExtensibleXMLObject; @@ -43,7 +43,7 @@ /** * This was copied from Spring Security Test Classes *

- * Once we can move to the spring-security version of OpenSaml4AuthenticationProvider, + * Once we can move to the spring-security version of OpenSaml5AuthenticationProvider, * this class should be removed. */ public final class TestCustomOpenSamlObjects { @@ -202,7 +202,7 @@ public CustomSamlObjectUnmarshaller() { protected void processChildElement(@Nonnull XMLObject parentXMLObject, @Nonnull XMLObject childXMLObject) throws UnmarshallingException { final CustomOpenSamlObject customSamlObject = (CustomOpenSamlObject) parentXMLObject; - super.processChildElement(customSamlObject, childXMLObject); + // OpenSAML 5: super no longer handles unknown elements; add them directly customSamlObject.getUnknownXMLObjects().add(childXMLObject); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestOpenSamlObjects.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestOpenSamlObjects.java index 36720470fab..380a9d41d82 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestOpenSamlObjects.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestOpenSamlObjects.java @@ -503,7 +503,7 @@ public static LogoutRequest assertingPartyLogoutRequest(RelyingPartyRegistration logoutRequest.setNameID(nameId); IssuerBuilder issuerBuilder = new IssuerBuilder(); Issuer issuer = issuerBuilder.buildObject(); - issuer.setValue(registration.getAssertingPartyDetails().getEntityId()); + issuer.setValue(registration.getAssertingPartyMetadata().getEntityId()); logoutRequest.setIssuer(issuer); logoutRequest.setDestination(registration.getSingleLogoutServiceLocation()); return logoutRequest; @@ -517,7 +517,7 @@ public static LogoutRequest assertingPartyLogoutRequestNameIdInEncryptedId(Relyi NameID nameId = nameIdBuilder.buildObject(); nameId.setValue("user"); logoutRequest.setNameID(null); - Saml2X509Credential credential = registration.getAssertingPartyDetails() + Saml2X509Credential credential = registration.getAssertingPartyMetadata() .getEncryptionX509Credentials() .iterator() .next(); @@ -525,7 +525,7 @@ public static LogoutRequest assertingPartyLogoutRequestNameIdInEncryptedId(Relyi logoutRequest.setEncryptedID(encrypted); IssuerBuilder issuerBuilder = new IssuerBuilder(); Issuer issuer = issuerBuilder.buildObject(); - issuer.setValue(registration.getAssertingPartyDetails().getEntityId()); + issuer.setValue(registration.getAssertingPartyMetadata().getEntityId()); logoutRequest.setIssuer(issuer); logoutRequest.setDestination(registration.getSingleLogoutServiceLocation()); return logoutRequest; @@ -544,7 +544,7 @@ public static LogoutResponse assertingPartyLogoutResponse(RelyingPartyRegistrati logoutResponse.setStatus(status); IssuerBuilder issuerBuilder = new IssuerBuilder(); Issuer issuer = issuerBuilder.buildObject(); - issuer.setValue(registration.getAssertingPartyDetails().getEntityId()); + issuer.setValue(registration.getAssertingPartyMetadata().getEntityId()); logoutResponse.setIssuer(issuer); logoutResponse.setDestination(registration.getSingleLogoutServiceResponseLocation()); return logoutResponse; @@ -561,9 +561,9 @@ public static LogoutRequest relyingPartyLogoutRequest(RelyingPartyRegistration r logoutRequest.setNameID(nameId); IssuerBuilder issuerBuilder = new IssuerBuilder(); Issuer issuer = issuerBuilder.buildObject(); - issuer.setValue(registration.getAssertingPartyDetails().getEntityId()); + issuer.setValue(registration.getAssertingPartyMetadata().getEntityId()); logoutRequest.setIssuer(issuer); - logoutRequest.setDestination(registration.getAssertingPartyDetails().getSingleLogoutServiceLocation()); + logoutRequest.setDestination(registration.getAssertingPartyMetadata().getSingleLogoutServiceLocation()); return logoutRequest; } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestRelyingPartyRegistrations.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestRelyingPartyRegistrations.java index 2bfe428f869..7719263ecf1 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestRelyingPartyRegistrations.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestRelyingPartyRegistrations.java @@ -48,7 +48,7 @@ public static RelyingPartyRegistration.Builder relyingPartyRegistration() { .nameIdFormat("format") .assertionConsumerServiceLocation(assertionConsumerServiceLocation) .singleLogoutServiceLocation(singleLogoutServiceLocation) - .assertingPartyDetails(c -> c.entityId(apEntityId).singleSignOnServiceLocation(singleSignOnServiceLocation)) + .assertingPartyMetadata(c -> c.entityId(apEntityId).singleSignOnServiceLocation(singleSignOnServiceLocation)) .signingX509Credentials(c -> c.add(signingCredential)) .decryptionX509Credentials(c -> c.add(verificationCertificate)); } @@ -59,7 +59,7 @@ public static RelyingPartyRegistration.Builder noCredentials() { .singleLogoutServiceLocation("https://rp.example.org/logout/saml2/request") .singleLogoutServiceResponseLocation("https://rp.example.org/logout/saml2/response") .assertionConsumerServiceLocation("https://rp.example.org/acs") - .assertingPartyDetails(party -> party.entityId("ap-entity-id") + .assertingPartyMetadata(party -> party.entityId("ap-entity-id") .singleSignOnServiceLocation("https://ap.example.org/sso") .singleLogoutServiceLocation("https://ap.example.org/logout/saml2/request") .singleLogoutServiceResponseLocation("https://ap.example.org/logout/saml2/response")); @@ -69,7 +69,7 @@ public static RelyingPartyRegistration.Builder full() { return noCredentials() .signingX509Credentials(c -> c.add(TestSaml2X509Credentials.relyingPartySigningCredential())) .decryptionX509Credentials(c -> c.add(TestSaml2X509Credentials.relyingPartyDecryptingCredential())) - .assertingPartyDetails(party -> party.verificationX509Credentials( + .assertingPartyMetadata(party -> party.verificationX509Credentials( c -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))); } } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaDelegatingLogoutSuccessHandlerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaDelegatingLogoutSuccessHandlerTest.java index 92518cd3ad9..e1ae5e8d356 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaDelegatingLogoutSuccessHandlerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaDelegatingLogoutSuccessHandlerTest.java @@ -83,7 +83,7 @@ void shouldPerformSamlRelyingPartyLogout() throws ServletException, IOException var mockRegistration = mock(RelyingPartyRegistration.class); when(relyingPartyRegistrationResolver.resolve(any(), eq(REG_ID))).thenReturn(mockRegistration); var mockAssertingPartyDetails = mock(RelyingPartyRegistration.AssertingPartyDetails.class); - when(mockRegistration.getAssertingPartyDetails()).thenReturn(mockAssertingPartyDetails); + when(mockRegistration.getAssertingPartyMetadata()).thenReturn(mockAssertingPartyDetails); when(mockAssertingPartyDetails.getSingleLogoutServiceLocation()).thenReturn(URL); logoutSuccessHandler.onLogoutSuccess(request, response, authentication); @@ -141,7 +141,7 @@ void nullSingleLogoutServiceLocationFallsThruToZoneAwareWhitelistLogoutHandler() var mockRegistration = mock(RelyingPartyRegistration.class); when(relyingPartyRegistrationResolver.resolve(any(), eq(REG_ID))).thenReturn(mockRegistration); var mockAssertingPartyDetails = mock(RelyingPartyRegistration.AssertingPartyDetails.class); - when(mockRegistration.getAssertingPartyDetails()).thenReturn(mockAssertingPartyDetails); + when(mockRegistration.getAssertingPartyMetadata()).thenReturn(mockAssertingPartyDetails); logoutSuccessHandler.onLogoutSuccess(request, response, authentication); verifyCorrectOnLogoutSuccessCalled(false, false, true); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaInResponseToHandlingResponseValidatorTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaInResponseToHandlingResponseValidatorTest.java index 81805f6f3da..ed27b82f619 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaInResponseToHandlingResponseValidatorTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaInResponseToHandlingResponseValidatorTest.java @@ -21,10 +21,10 @@ class UaaInResponseToHandlingResponseValidatorTest { @Mock - Converter delegate; + Converter delegate; @Mock - OpenSaml4AuthenticationProvider.ResponseToken responseToken; + OpenSaml5AuthenticationProvider.ResponseToken responseToken; @BeforeEach void beforeEach() { diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaRelyingPartyRegistrationResolverTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaRelyingPartyRegistrationResolverTests.java index 589e3a354cd..98628e4cc04 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaRelyingPartyRegistrationResolverTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/UaaRelyingPartyRegistrationResolverTests.java @@ -85,6 +85,7 @@ void resolveWhenRequestIsMissingRegistrationIdThenNull() { @Test void resolveWhenRequestIsWithInvalidSamlResponse() { MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/some/path/cloudfoundry-saml-login"); request.setPathInfo("/some/path/cloudfoundry-saml-login"); request.setMethod("POST"); request.setParameter("SAMLResponse", "PGJhc2U2ND4="); @@ -94,6 +95,7 @@ void resolveWhenRequestIsWithInvalidSamlResponse() { @Test void resolveWhenRequestIsWithValiddSamlResponseFromSimplySamlButNoTrust() { MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/some/path/cloudfoundry-saml-login"); request.setPathInfo("/some/path/cloudfoundry-saml-login"); request.setMethod("POST"); request.setParameter("SAMLResponse", SIMPLE_SAML_RESPONSE); @@ -107,7 +109,7 @@ void resolveWhenRequestIsWithValiddSamlResponseFromSimplySaml() { RelyingPartyRegistration.AssertingPartyDetails details = mock(RelyingPartyRegistration.AssertingPartyDetails.class); doReturn("simpleID").when(newMock).getRegistrationId(); doReturn("simpleEndityID").when(newMock).getEntityId(); - doReturn(details).when(newMock).getAssertingPartyDetails(); + doReturn(details).when(newMock).getAssertingPartyMetadata(); doReturn("simpleEndityID").when(details).getEntityId(); doReturn("sso").when(details).getSingleSignOnServiceLocation(); doReturn("acs").when(newMock).getAssertionConsumerServiceLocation(); @@ -115,6 +117,7 @@ void resolveWhenRequestIsWithValiddSamlResponseFromSimplySaml() { doReturn(mock(Saml2MessageBinding.class)).when(details).getSingleSignOnServiceBinding(); doReturn(mock(Saml2MessageBinding.class)).when(newMock).getAssertionConsumerServiceBinding(); doReturn(newMock).when(repository).findByRegistrationId("http://uaa-acceptance.cf-app.com/saml-idp"); + request.setRequestURI("/some/path/cloudfoundry-saml-login"); request.setPathInfo("/some/path/cloudfoundry-saml-login"); request.setMethod("POST"); request.setParameter("SAMLResponse", SIMPLE_SAML_RESPONSE); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/resources/jdbc/SimpleSearchQueryConverterTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/resources/jdbc/SimpleSearchQueryConverterTests.java index b27688678ae..8a74dffb725 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/resources/jdbc/SimpleSearchQueryConverterTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/resources/jdbc/SimpleSearchQueryConverterTests.java @@ -44,7 +44,7 @@ void simpleValueExtract(final String query) { MultiValueMap result = converter.getFilterValues(query, Arrays.asList("origin", "externalGroup".toLowerCase())); assertThat(result) .hasSize(2) - .containsKey("origin"); + .containsKeys("origin"); assertThat(result.get("origin")) .hasSize(1) .contains("origin-value"); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/scim/services/ScimUserServiceTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/scim/services/ScimUserServiceTest.java index cc9f9c72920..50d4a0b58ae 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/scim/services/ScimUserServiceTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/scim/services/ScimUserServiceTest.java @@ -1,13 +1,13 @@ package org.cloudfoundry.identity.uaa.scim.services; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import org.cloudfoundry.identity.uaa.alias.AliasPropertiesInvalidException; import org.cloudfoundry.identity.uaa.scim.ScimUser; import org.cloudfoundry.identity.uaa.scim.ScimUserAliasHandler; import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning; import org.cloudfoundry.identity.uaa.util.AlphanumericRandomValueStringGenerator; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -164,8 +164,8 @@ void updateShouldUpdateOnlyOriginalUserWhenAliasEnabledAndPropertiesAreValid() { private static ScimUser buildExemplaryUser( @Nullable final String id, - @Nonnull final String idzId, - @Nonnull final String origin + @NonNull final String idzId, + @NonNull final String origin ) { final ScimUser user = new ScimUser(); user.setId(id); @@ -219,4 +219,4 @@ private static ScimUser cloneScimUser(final ScimUser user) { clone.setPreviousLogonTime(user.getPreviousLogonTime()); return clone; } -} \ No newline at end of file +} diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/security/CsrfAwareEntryPointAndDeniedHandlerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/security/CsrfAwareEntryPointAndDeniedHandlerTest.java index 2b7f127fc94..beea633b157 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/security/CsrfAwareEntryPointAndDeniedHandlerTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/security/CsrfAwareEntryPointAndDeniedHandlerTest.java @@ -53,7 +53,7 @@ void handleWhenNotLoggedInAndNoCsrf() throws Exception { assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_FOUND); assertThat(ex).isSameAs(request.getAttribute(WebAttributes.ACCESS_DENIED_403)); assertThat(response.isCommitted()).isTrue(); - assertThat(response.getHeader("Location")).isEqualTo("http://localhost/login"); + assertThat(response.getHeader("Location")).isEqualTo("/login"); assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_MOVED_TEMPORARILY); } @@ -74,7 +74,7 @@ void handleWhenNotLoggedIn() throws Exception { assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_FOUND); assertThat(ex).isSameAs(request.getAttribute(WebAttributes.ACCESS_DENIED_403)); assertThat(response.isCommitted()).isTrue(); - assertThat(response.getHeader("Location")).isEqualTo("http://localhost/login"); + assertThat(response.getHeader("Location")).isEqualTo("/login"); assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_MOVED_TEMPORARILY); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/test/TestAccountExtension.java b/server/src/test/java/org/cloudfoundry/identity/uaa/test/TestAccountExtension.java index 433d95ac06e..0764d92fe7f 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/test/TestAccountExtension.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/test/TestAccountExtension.java @@ -285,7 +285,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // do nothing } }); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/test/UaaTestAccounts.java b/server/src/test/java/org/cloudfoundry/identity/uaa/test/UaaTestAccounts.java index d84730f453e..67d537bda27 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/test/UaaTestAccounts.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/test/UaaTestAccounts.java @@ -30,13 +30,13 @@ import org.springframework.core.env.Environment; import org.springframework.core.env.Profiles; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.security.crypto.codec.Base64; import org.cloudfoundry.identity.uaa.oauth.provider.ClientDetails; import org.springframework.util.StringUtils; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Base64; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -140,7 +140,7 @@ public String getAuthorizationHeader(String prefix, String defaultUsername, Stri public static String getAuthorizationHeader(String username, String password) { String credentials = "%s:%s".formatted(URLEncoder.encode(username, StandardCharsets.UTF_8), URLEncoder.encode(password, StandardCharsets.UTF_8)); - return "Basic %s".formatted(new String(Base64.encode(credentials.getBytes()))); + return "Basic %s".formatted(Base64.getEncoder().encodeToString(credentials.getBytes())); } public String getJsonCredentials(String prefix, String defaultUsername, String defaultPassword) { diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/test/network/NetworkTestUtils.java b/server/src/test/java/org/cloudfoundry/identity/uaa/test/network/NetworkTestUtils.java index 396d196a190..4c9671fa14f 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/test/network/NetworkTestUtils.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/test/network/NetworkTestUtils.java @@ -167,7 +167,7 @@ public boolean wasInvoked() { public void handle(HttpExchange httpExchange) throws IOException { wasInvoked = true; HttpsExchange exchange = (HttpsExchange) httpExchange; - for (Map.Entry> entry : headers.entrySet()) { + for (Map.Entry> entry : headers.headerSet()) { for (String value : entry.getValue()) { exchange.getResponseHeaders().add(entry.getKey(), value); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/util/KeyWithCertTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/util/KeyWithCertTest.java index 3a183ec4e8f..878f59eec9b 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/util/KeyWithCertTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/util/KeyWithCertTest.java @@ -115,7 +115,7 @@ static void addProvider() { PwMhO0+dASJ83e2Bu63pKO808BrVjD51sSEMb0qwFc5IV6RzK/mkJgO0fphhoqOm ZLzGcSYwCmj0Vc0GO5NgnFVZg4N9CyYCpDMeQynumlrNhRgnZRzlqXtQgL2bQDiu coxNL/KY05iVlE1bmq/fzNEmEi2zf3dQV8CNSYs= - -----END CERTIFICATE---- + -----END CERTIFICATE----- """; // openssl req -out cert.pem -nodes -keyout private.key -newkey rsa:2048 -new -x509 diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/util/UaaHttpRequestUtilsTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/util/UaaHttpRequestUtilsTest.java index e48a614b59c..5ba80947a6a 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/util/UaaHttpRequestUtilsTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/util/UaaHttpRequestUtilsTest.java @@ -128,7 +128,7 @@ void httpsIpProxy() { } public void testHttpProxy(String url, int expectedPort, String expectedHost, boolean wantHandlerInvoked) { - HttpClientBuilder builder = UaaHttpRequestUtils.getClientBuilder(true, 20, 2, 5, 2000, 2); + HttpClientBuilder builder = UaaHttpRequestUtils.getClientBuilder(true, 20, 2, 5, 2000, 2, 1000); HttpRoutePlanner planner = (HttpRoutePlanner) ReflectionTestUtils.getField(builder.build(), "routePlanner"); SystemProxyRoutePlanner routePlanner = new SystemProxyRoutePlanner(planner); builder.setRoutePlanner(routePlanner); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/util/beans/PasswordEncoderConfig.java b/server/src/test/java/org/cloudfoundry/identity/uaa/util/beans/PasswordEncoderConfig.java index 433f2b0ff17..d18f441cc8c 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/util/beans/PasswordEncoderConfig.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/util/beans/PasswordEncoderConfig.java @@ -3,6 +3,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.DelegatingPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -10,6 +11,7 @@ import java.util.HashMap; import java.util.Map; +@Configuration public class PasswordEncoderConfig { private static final Logger logger = LoggerFactory.getLogger(PasswordEncoderConfig.class); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/web/AuthorizationManagersUtilsTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/web/AuthorizationManagersUtilsTests.java index 0a8c1785335..1f1d836fd99 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/web/AuthorizationManagersUtilsTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/web/AuthorizationManagersUtilsTests.java @@ -6,6 +6,7 @@ import org.cloudfoundry.identity.uaa.test.ModelTestUtils; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import org.springframework.security.authentication.AnonymousAuthenticationToken; @@ -13,6 +14,7 @@ import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.web.access.intercept.RequestAuthorizationContext; @@ -43,7 +45,7 @@ static void afterAll() { void noAuthenticationManager() { AuthorizationManager authManager = AuthorizationManagersUtils.anyOf(); - var authorizationDecision = authManager.check(() -> FULLY_AUTHENTICATED, null); + var authorizationDecision = authManager.authorize(() -> FULLY_AUTHENTICATED, null); assertThat(authorizationDecision.isGranted()).isFalse(); } @@ -53,11 +55,11 @@ void oneAuthenticationManager() { var granted = granted(); var notGranted = notGranted(); var unknown = unknown(); - assertThat(AuthorizationManagersUtils.anyOf().or(granted).check(() -> FULLY_AUTHENTICATED, null).isGranted()).isTrue(); + assertThat(AuthorizationManagersUtils.anyOf().or(granted).authorize(() -> FULLY_AUTHENTICATED, null).isGranted()).isTrue(); assertThat(granted.called).isTrue(); - assertThat(AuthorizationManagersUtils.anyOf().or(notGranted).check(() -> FULLY_AUTHENTICATED, null).isGranted()).isFalse(); + assertThat(AuthorizationManagersUtils.anyOf().or(notGranted).authorize(() -> FULLY_AUTHENTICATED, null).isGranted()).isFalse(); assertThat(notGranted.called).isTrue(); - assertThat(AuthorizationManagersUtils.anyOf().or(unknown).check(() -> FULLY_AUTHENTICATED, null).isGranted()).isFalse(); + assertThat(AuthorizationManagersUtils.anyOf().or(unknown).authorize(() -> FULLY_AUTHENTICATED, null).isGranted()).isFalse(); assertThat(unknown.called).isTrue(); } @@ -71,7 +73,7 @@ void manyAuthenticationManagers() { .or(granted) .or(unknown); - assertThat(authorizationManager.check(() -> FULLY_AUTHENTICATED, null).isGranted()).isTrue(); + assertThat(authorizationManager.authorize(() -> FULLY_AUTHENTICATED, null).isGranted()).isTrue(); assertThat(notGranted.called).isTrue(); assertThat(granted.called).isTrue(); assertThat(unknown.called).isFalse(); @@ -82,10 +84,10 @@ void anonymous() { AuthorizationManager authManager = AuthorizationManagersUtils.anyOf() .anonymous(); - assertThat(authManager.check(() -> ANONYMOUS, null).isGranted()).isTrue(); - assertThat(authManager.check(() -> NOT_AUTHENTICATED, null).isGranted()).isFalse(); - assertThat(authManager.check(() -> FULLY_AUTHENTICATED, null).isGranted()).isFalse(); - assertThat(authManager.check(() -> REMEMBER_ME, null).isGranted()).isFalse(); + assertThat(authManager.authorize(() -> ANONYMOUS, null).isGranted()).isTrue(); + assertThat(authManager.authorize(() -> NOT_AUTHENTICATED, null).isGranted()).isFalse(); + assertThat(authManager.authorize(() -> FULLY_AUTHENTICATED, null).isGranted()).isFalse(); + assertThat(authManager.authorize(() -> REMEMBER_ME, null).isGranted()).isFalse(); } @Test @@ -93,26 +95,26 @@ void fullyAuthenticated() { AuthorizationManager authManager = AuthorizationManagersUtils.anyOf() .fullyAuthenticated(); - assertThat(authManager.check(() -> ANONYMOUS, null).isGranted()).isFalse(); - assertThat(authManager.check(() -> NOT_AUTHENTICATED, null).isGranted()).isFalse(); - assertThat(authManager.check(() -> FULLY_AUTHENTICATED, null).isGranted()).isTrue(); - assertThat(authManager.check(() -> REMEMBER_ME, null).isGranted()).isFalse(); + assertThat(authManager.authorize(() -> ANONYMOUS, null).isGranted()).isFalse(); + assertThat(authManager.authorize(() -> NOT_AUTHENTICATED, null).isGranted()).isFalse(); + assertThat(authManager.authorize(() -> FULLY_AUTHENTICATED, null).isGranted()).isTrue(); + assertThat(authManager.authorize(() -> REMEMBER_ME, null).isGranted()).isFalse(); } @Test void uaaAdmin() { AuthorizationManager authManager = AuthorizationManagersUtils.anyOf().isUaaAdmin(); - assertThat(authManager.check(() -> withScopes("foo.bar"), null).isGranted()).isFalse(); - assertThat(authManager.check(() -> withScopes("uaa.admin"), null).isGranted()).isTrue(); + assertThat(authManager.authorize(() -> withScopes("foo.bar"), null).isGranted()).isFalse(); + assertThat(authManager.authorize(() -> withScopes("uaa.admin"), null).isGranted()).isTrue(); } @Test void hasScope() { AuthorizationManager authManager = AuthorizationManagersUtils.anyOf().hasScope("foo.bar"); - assertThat(authManager.check(() -> withScopes("uaa.admin"), null).isGranted()).isFalse(); - assertThat(authManager.check(() -> withScopes("uaa.admin", "foo.bar"), null).isGranted()).isTrue(); + assertThat(authManager.authorize(() -> withScopes("uaa.admin"), null).isGranted()).isFalse(); + assertThat(authManager.authorize(() -> withScopes("uaa.admin", "foo.bar"), null).isGranted()).isTrue(); } @Test @@ -120,7 +122,7 @@ void throwOnError() { AuthorizationManager authManager = AuthorizationManagersUtils.anyOf(true).hasScope("foo.bar"); assertThatThrownBy( - () -> {authManager.check(() -> withScopes("uaa.admin"), null);} + () -> {authManager.authorize(() -> withScopes("uaa.admin"), null);} ).getCause().isInstanceOf(InsufficientScopeException.class); } @@ -131,8 +133,8 @@ void zoneAdmin() { IdentityZoneHolder.set(ModelTestUtils.identityZone("someZoneId", "some-domain")); - assertThat(authManager.check(() -> withScopes("foo.bar"), null).isGranted()).isFalse(); - assertThat(authManager.check(() -> withScopes("zones.someZoneId.admin"), null).isGranted()).isTrue(); + assertThat(authManager.authorize(() -> withScopes("foo.bar"), null).isGranted()).isFalse(); + assertThat(authManager.authorize(() -> withScopes("zones.someZoneId.admin"), null).isGranted()).isTrue(); } static class TestAuthManager implements AuthorizationManager { @@ -161,7 +163,7 @@ public static TestAuthManager unknown() { } @Override - public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext object) { + public AuthorizationResult authorize(Supplier authentication, RequestAuthorizationContext object) { called = true; return authorizationDecision; } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/web/CookieBasedCsrfTokenRepositoryTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/web/CookieBasedCsrfTokenRepositoryTests.java index c14f69fca29..75487928681 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/web/CookieBasedCsrfTokenRepositoryTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/web/CookieBasedCsrfTokenRepositoryTests.java @@ -142,9 +142,15 @@ void saveToken_MakeAnExpiredTokenInResponse_whenNoTokenInRequest() { MockHttpServletResponse response = new MockHttpServletResponse(); repo.saveToken(null, request, response); - Cookie cookie = response.getCookie("X-Uaa-Csrf"); - assertThat(cookie.getMaxAge()).isZero(); - assertThat(cookie.getValue()).isNotEmpty(); + // Spring Framework 7: MockHttpServletResponse.getCookie() no longer parses + // Set-Cookie headers added via addHeader(). Check the header directly instead. + String setCookieHeader = response.getHeader("Set-Cookie"); + assertThat(setCookieHeader).contains("X-Uaa-Csrf="); + // Max-Age=0 or Expires at Unix epoch indicates expired/deleted cookie + assertThat(setCookieHeader).satisfiesAnyOf( + header -> assertThat(header).contains("Max-Age=0"), + header -> assertThat(header).contains("Expires=Thu, 1 Jan 1970") + ); } private MockHttpServletResponse saveTokenAndReturnResponse(boolean isSecure, String protocol) { diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCacheTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCacheTests.java index 51f8e18175b..a515e3403c9 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCacheTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCacheTests.java @@ -208,15 +208,15 @@ void saved_request_matcher() { request.setQueryString("name=value"); request.setServerPort(443); ClientRedirectSavedRequest saved = new ClientRedirectSavedRequest(request, redirectUrl); - assertThat(saved.doesRequestMatch(request, null)).isTrue(); + assertThat(saved.doesRequestMatch(request)).isTrue(); request.setQueryString("name=value&name2=value2"); - assertThat(saved.doesRequestMatch(request, null)).isFalse(); + assertThat(saved.doesRequestMatch(request)).isFalse(); request.setQueryString("name=value"); request = new MockHttpServletRequest(POST.name(), "/login.do"); request.setParameter(FORM_REDIRECT_PARAMETER, redirectUrl); - assertThat(saved.doesRequestMatch(request, null)).isTrue(); + assertThat(saved.doesRequestMatch(request)).isTrue(); } @Test diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/web/beans/PurgeableSessionMapTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/web/beans/PurgeableSessionMapTest.java index 077e9550d3f..9e8f31d8334 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/web/beans/PurgeableSessionMapTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/web/beans/PurgeableSessionMapTest.java @@ -24,7 +24,7 @@ void doesNotDeleteActiveSessions() { sessions.purge(); assertThat(sessions) .hasSize(1) - .containsKey(SESSION_ID); + .containsKeys(SESSION_ID); } @Test diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneEndpointsTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneEndpointsTests.java index c580bcd6f3a..863679af112 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneEndpointsTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneEndpointsTests.java @@ -189,7 +189,7 @@ void deleteIdentityZone_ShouldReject_IfIdpWithAliasExists() { final ResponseEntity response = endpoints.deleteIdentityZone(idzId); assertThat(response).isNotNull(); - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNPROCESSABLE_ENTITY); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNPROCESSABLE_CONTENT); } @Test diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneHolderTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneHolderTest.java index 7c13286e8e8..4c6ffa91ee6 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneHolderTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/zone/IdentityZoneHolderTest.java @@ -83,6 +83,7 @@ void isUaa() { } @Nested + @ExtendWith(MockitoExtension.class) class InitializerSetUp { @Mock IdentityZoneProvisioning mockProvisioning2; diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/zone/MultitenantJdbcClientDetailsServiceTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/zone/MultitenantJdbcClientDetailsServiceTests.java index 75ee2f914b7..7171acd8251 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/zone/MultitenantJdbcClientDetailsServiceTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/zone/MultitenantJdbcClientDetailsServiceTests.java @@ -493,7 +493,7 @@ void updateClientSecret() { "newClientIdWithNoDetails"); assertThat(map).containsEntry("client_id", "newClientIdWithNoDetails") - .containsKey("client_secret"); + .containsKeys("client_secret"); assertThat(passwordEncoder.matches(newClientSecret, (String) map.get("client_secret"))).isTrue(); } @@ -530,7 +530,7 @@ void updateClientJwt() { "newClientIdWithNoDetails"); assertThat(map).containsEntry("client_id", "newClientIdWithNoDetails") - .containsKey("client_jwt_config"); + .containsKeys("client_jwt_config"); assertThat((String) map.get("client_jwt_config")).isEqualTo("{\"jwks_uri\":\"http://localhost:8080/uaa/token_keys\"}"); } @@ -545,7 +545,7 @@ void updateFederatedClientJwt() { "newClientIdWithNoDetails"); assertThat(map).containsEntry("client_id", "newClientIdWithNoDetails") - .containsKey("client_jwt_config") + .containsKeys("client_jwt_config") .containsEntry("client_jwt_config", "{\"jwt_creds\":[{\"sub\":\"subject\",\"iss\":\"issuer\"}]}"); } @@ -592,7 +592,7 @@ void updateClientJwtConfig() { "newClientIdWithClientJwtConfig"); assertThat(map).containsEntry("client_id", "newClientIdWithClientJwtConfig") - .containsKey("client_jwt_config"); + .containsKeys("client_jwt_config"); assertThat((String) map.get("client_jwt_config")).isEqualTo("any json web key config"); } @@ -615,7 +615,7 @@ void updateClientRedirectURI() { "newClientIdWithNoDetails"); assertThat(map).containsEntry("client_id", "newClientIdWithNoDetails") - .containsKey("web_server_redirect_uri") + .containsKeys("web_server_redirect_uri") .containsEntry("web_server_redirect_uri", "http://localhost:8080,http://localhost:9090"); } diff --git a/statsd-lib/build.gradle b/statsd-lib/build.gradle index ff814a735a6..b8e07bec47b 100644 --- a/statsd-lib/build.gradle +++ b/statsd-lib/build.gradle @@ -13,13 +13,21 @@ dependencies { testImplementation(libraries.mockitoJunit5) testImplementation(libraries.bytebuddy) testImplementation(libraries.bytebuddyagent) + testImplementation(libraries.springBootStarterRestclient) { + exclude(group: "org.springframework.boot", module: "spring-boot-starter-jackson") + } + testImplementation(libraries.springBootStarterRestclientTest) { + exclude(group: "org.springframework.boot", module: "spring-boot-starter-jackson") + } implementation(libraries.jacksonDataformatYaml) implementation(libraries.jacksonDatabind) + implementation(libraries.springBootJackson2) } test { exclude("org/cloudfoundry/identity/statsd/integration/*.class") exclude("**/*IT.class") + useJUnitPlatform() } integrationTest { diff --git a/statsd-lib/src/test/java/org/cloudfoundry/identity/statsd/integration/IntegrationTestUtils.java b/statsd-lib/src/test/java/org/cloudfoundry/identity/statsd/integration/IntegrationTestUtils.java index 8e7a1a6b1d9..0bacdb7fc10 100644 --- a/statsd-lib/src/test/java/org/cloudfoundry/identity/statsd/integration/IntegrationTestUtils.java +++ b/statsd-lib/src/test/java/org/cloudfoundry/identity/statsd/integration/IntegrationTestUtils.java @@ -15,7 +15,7 @@ public class IntegrationTestUtils { static final String TEST_PASSWORD = "koala"; static void copyCookies(ResponseEntity response, HttpHeaders headers) { - if (response.getHeaders().containsKey("Set-Cookie")) { + if (response.getHeaders().containsHeader("Set-Cookie")) { for (String cookie : response.getHeaders().get("Set-Cookie")) { headers.add("Cookie", cookie); } diff --git a/statsd/build.gradle b/statsd/build.gradle index fa380622721..7a77996307e 100644 --- a/statsd/build.gradle +++ b/statsd/build.gradle @@ -15,11 +15,13 @@ dependencies { implementation(libraries.springBootStarterWeb) providedCompile(libraries.tomcatEmbed) providedRuntime(libraries.springBootStarterTomcat) + testImplementation(libraries.springBootStarterTest) } test { exclude("org/cloudfoundry/identity/statsd/integration/*.class") exclude("**/*IT.class") + useJUnitPlatform() } integrationTest {}.onlyIf { //disable since we don't have any diff --git a/uaa/build.gradle b/uaa/build.gradle index b41d7cbaf84..dd9709ebe5d 100644 --- a/uaa/build.gradle +++ b/uaa/build.gradle @@ -39,6 +39,7 @@ dependencies { implementation(libraries.springSecurityWeb) implementation(libraries.springBootStarter) implementation(libraries.springBootStarterWeb) + implementation(libraries.springBootJackson2) implementation(libraries.thymeLeaf) { exclude(module: "ognl") } diff --git a/uaa/src/main/java/org/cloudfoundry/experimental/boot/UaaBootConfiguration.java b/uaa/src/main/java/org/cloudfoundry/experimental/boot/UaaBootConfiguration.java index 6e1f53088af..cdace122df6 100644 --- a/uaa/src/main/java/org/cloudfoundry/experimental/boot/UaaBootConfiguration.java +++ b/uaa/src/main/java/org/cloudfoundry/experimental/boot/UaaBootConfiguration.java @@ -11,7 +11,7 @@ import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletContextInitializer; -import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; diff --git a/uaa/src/main/java/org/cloudfoundry/experimental/boot/UaaBootServerCustomizer.java b/uaa/src/main/java/org/cloudfoundry/experimental/boot/UaaBootServerCustomizer.java index b78de70f4bc..318502d2549 100644 --- a/uaa/src/main/java/org/cloudfoundry/experimental/boot/UaaBootServerCustomizer.java +++ b/uaa/src/main/java/org/cloudfoundry/experimental/boot/UaaBootServerCustomizer.java @@ -4,11 +4,8 @@ import org.apache.catalina.valves.ErrorReportValve; import org.apache.coyote.http11.Http11NioProtocol; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.server.ErrorPage; import org.springframework.boot.web.server.WebServerFactoryCustomizer; -import org.springframework.http.HttpStatus; -import org.springframework.security.web.firewall.RequestRejectedException; +import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory; import org.springframework.stereotype.Component; @Component @@ -23,25 +20,31 @@ public class UaaBootServerCustomizer implements public void customize(TomcatServletWebServerFactory factory) { //customize tomcat - // from web.xml - factory.addErrorPages( - new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error500"), - new ErrorPage(HttpStatus.NOT_FOUND, "/error404"), - new ErrorPage(HttpStatus.TOO_MANY_REQUESTS, "/error429"), - new ErrorPage("/error"), - new ErrorPage(RequestRejectedException.class, "/rejected") - ); + // Note: ErrorPage class removed in Spring Boot 3.0 + // Error pages should now be configured via application.properties: + // server.error.path=/error + // server.error.whitelabel.enabled=false + // Or via ErrorPageRegistrar bean + // Removing error page configuration as it needs to be done differently in Spring Boot 4 //add error report valve //https://github.com/cloudfoundry/uaa-release/blob/0be1fa547aa975019b957cd9aafe9ca09edf62d9/jobs/uaa/templates/config/tomcat/tomcat.server.xml.erb#L111-L114 factory.addEngineValves(getErrorReportValve()); if (this.serverHttp.port() > 0) { - factory.addAdditionalTomcatConnectors( - getHttpPort( - this.serverHttp - ) - ); + // Note: addAdditionalTomcatConnectors() renamed to addConnectorCustomizers() in Spring Boot 3+ + factory.addConnectorCustomizers(connector -> { + // Apply HTTP connector configuration if not already configured + if (connector.getPort() == 0) { + Connector httpConnector = getHttpPort(this.serverHttp); + connector.setPort(httpConnector.getPort()); + connector.setProperty("connectionTimeout", "20000"); + connector.setProperty("keepAliveTimeout", String.valueOf(this.serverHttp.keepAliveTimeout())); + connector.setProperty("address", this.serverHttp.address()); + connector.setProperty("bindOnInit", String.valueOf(this.serverHttp.bindOnInit())); + connector.setProperty("maxHttpHeaderSize", String.valueOf(this.serverHttp.maxHttpHeaderSize())); + } + }); } } @@ -63,4 +66,4 @@ ErrorReportValve getErrorReportValve() { valve.setShowServerInfo(false); return valve; } -} \ No newline at end of file +} diff --git a/uaa/src/test/java/org/cloudfoundry/experimental/boot/UaaBootServerCustomizerTest.java b/uaa/src/test/java/org/cloudfoundry/experimental/boot/UaaBootServerCustomizerTest.java index 63face84a86..cd9eee5091e 100644 --- a/uaa/src/test/java/org/cloudfoundry/experimental/boot/UaaBootServerCustomizerTest.java +++ b/uaa/src/test/java/org/cloudfoundry/experimental/boot/UaaBootServerCustomizerTest.java @@ -9,10 +9,10 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; -import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import java.net.Inet4Address; import java.net.UnknownHostException; @@ -27,7 +27,7 @@ @TestPropertySource(properties = {"server.http.port = 8081"}) class UaaBootServerCustomizerTest { - @SpyBean + @MockitoSpyBean UaaBootServerCustomizer customizer; @Test @@ -47,16 +47,16 @@ void customizerAddedReportValve() { void localhostConnectorAdded() throws UnknownHostException { ArgumentCaptor captor = ArgumentCaptor.forClass(TomcatServletWebServerFactory.class); Mockito.verify(customizer, Mockito.atMostOnce()).customize(captor.capture()); - List connectors = captor.getValue().getAdditionalTomcatConnectors(); - assertThat(connectors).isNotEmpty(); - assertThat(connectors).hasSize(1); - Connector httpConnector = connectors.getFirst(); - assertThat(httpConnector.getProperty("class")).isEqualTo(Http11NioProtocol.class); + + // Verify the connector configuration via the getHttpPort method + Connector httpConnector = customizer.getHttpPort(customizer.serverHttp); + assertThat(httpConnector).isNotNull(); + assertThat(httpConnector.getProperty("class")).isEqualTo(Http11NioProtocol.class.getName()); assertThat(httpConnector.getPort()).isEqualTo(8081); - assertThat(httpConnector.getProperty("connectionTimeout")).isEqualTo(20000); - assertThat(httpConnector.getProperty("keepAliveTimeout")).isEqualTo(12000); - assertThat(httpConnector.getProperty("address")).isEqualTo(Inet4Address.getByAddress(new byte[] {127,0,0,1})); + assertThat(httpConnector.getProperty("connectionTimeout")).isEqualTo("20000"); + assertThat(httpConnector.getProperty("keepAliveTimeout")).isEqualTo("12000"); + assertThat(httpConnector.getProperty("address")).isEqualTo(Inet4Address.getByAddress(new byte[] {127,0,0,1}).getHostAddress()); assertThat(httpConnector.getProperty("bindOnInit")).isEqualTo("true"); - assertThat(httpConnector.getProperty("maxHttpHeaderSize")).isEqualTo(14336); + assertThat(httpConnector.getProperty("maxHttpHeaderSize")).isEqualTo("14336"); } -} \ No newline at end of file +} diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/DefaultTestContext.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/DefaultTestContext.java index d65064c3175..f4da0aa1d40 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/DefaultTestContext.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/DefaultTestContext.java @@ -9,16 +9,14 @@ import org.cloudfoundry.identity.uaa.test.TestClient; import org.cloudfoundry.identity.uaa.zone.ZoneContextPathSessionFilter; import org.cloudfoundry.identity.uaa.zone.ZonePathContextRewritingFilter; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; -import org.springframework.boot.autoconfigure.session.SessionAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.annotation.Bean; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; +import org.springframework.context.annotation.Configuration; import org.springframework.mock.web.MockRequestDispatcher; import org.springframework.mock.web.MockServletContext; import org.springframework.security.web.FilterChainProxy; @@ -58,12 +56,6 @@ webEnvironment = SpringBootTest.WebEnvironment.MOCK ) @SpringJUnitConfig(initializers = {TestPropertyInitializer.class, YamlServletProfileInitializer.class}) -@EnableAutoConfiguration(exclude = { - // Conflicts with UaaJdbcSessionConfig - SessionAutoConfiguration.class, - // Conflicts with LdapSearchAndCompareConfig/LdapSearchAndBindConfig/LdapSimpleBindConfig - LdapAutoConfiguration.class -}) public @interface DefaultTestContext { } @@ -76,6 +68,7 @@ public void initialize(ConfigurableWebApplicationContext applicationContext) { } } +@Configuration class TestClientAndMockMvcTestConfig { @Bean public MockMvc mockMvc( @@ -123,6 +116,7 @@ public void addListener(@Nullable T t) { } +@Configuration class DatabasePropertiesOverrideConfiguration { /** diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/YamlConfigurationValidationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/YamlConfigurationValidationTests.java index 2805b0f44d5..658db932cd2 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/YamlConfigurationValidationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/YamlConfigurationValidationTests.java @@ -2,13 +2,13 @@ import org.cloudfoundry.identity.uaa.impl.config.YamlConfigurationValidator; import org.cloudfoundry.identity.uaa.impl.config.YamlServletProfileInitializer; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.cloudfoundry.identity.uaa.provider.saml.SamlConfiguration; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import org.springframework.mock.web.MockServletConfig; import org.springframework.mock.web.MockServletContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/CfUserIdTranslationEndpointIntegrationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/CfUserIdTranslationEndpointIntegrationTests.java index 70d6cfc77ae..d9f777b6191 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/CfUserIdTranslationEndpointIntegrationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/CfUserIdTranslationEndpointIntegrationTests.java @@ -112,7 +112,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pass through } }); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/CheckTokenEndpointIntegrationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/CheckTokenEndpointIntegrationTests.java index d063c4420a5..95a62447413 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/CheckTokenEndpointIntegrationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/CheckTokenEndpointIntegrationTests.java @@ -29,7 +29,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.security.crypto.codec.Base64; +import java.util.Base64; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -164,7 +164,7 @@ void forbidden() { MultiValueMap formData = new LinkedMultiValueMap<>(); formData.add("token", "FOO"); HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", "Basic " + new String(Base64.encode("cf:".getBytes(UTF_8)))); + headers.set("Authorization", "Basic " + new String(Base64.getEncoder().encode("cf:".getBytes(UTF_8)))); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); @SuppressWarnings("rawtypes") diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/IdentityZoneEndpointsIntegrationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/IdentityZoneEndpointsIntegrationTests.java index 7d2cfd87322..219fc7e0487 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/IdentityZoneEndpointsIntegrationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/IdentityZoneEndpointsIntegrationTests.java @@ -69,7 +69,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pass through } }); @@ -179,7 +179,7 @@ void updateZoneAllowedGroups() { new ParameterizedTypeReference<>() { }, id); - assertThat(response.getStatusCode()).as(response.getBody()).isEqualTo(HttpStatus.UNPROCESSABLE_ENTITY); + assertThat(response.getStatusCode()).as(response.getBody()).isEqualTo(HttpStatus.UNPROCESSABLE_CONTENT); } @Test diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/LoginServerSecurityIntegrationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/LoginServerSecurityIntegrationTests.java index f53610f1f15..eda27ecdbe1 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/LoginServerSecurityIntegrationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/LoginServerSecurityIntegrationTests.java @@ -105,7 +105,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pass through } }); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/NativeApplicationIntegrationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/NativeApplicationIntegrationTests.java index 3e0c4a15ab5..aabd7d3ba37 100755 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/NativeApplicationIntegrationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/NativeApplicationIntegrationTests.java @@ -24,7 +24,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.security.crypto.codec.Base64; +import java.util.Base64; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -85,7 +85,7 @@ void secretRequired() { formData.add("password", resource.getPassword()); formData.add("scope", "cloud_controller.read"); HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", "Basic " + new String(Base64.encode("no-such-client:".getBytes(StandardCharsets.UTF_8)))); + headers.set("Authorization", "Basic " + new String(Base64.getEncoder().encode("no-such-client:".getBytes(StandardCharsets.UTF_8)))); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); ResponseEntity response = serverRunning.postForString("/oauth/token", formData, headers); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/OpenIdTokenAuthorizationWithApprovalIntegrationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/OpenIdTokenAuthorizationWithApprovalIntegrationTests.java index 4814ec77ed7..65c2620b429 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/OpenIdTokenAuthorizationWithApprovalIntegrationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/OpenIdTokenAuthorizationWithApprovalIntegrationTests.java @@ -100,7 +100,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pass through } }); @@ -114,7 +114,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pas through } }); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/PasswordChangeEndpointIntegrationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/PasswordChangeEndpointIntegrationTests.java index 402aa22b1c4..73657325c91 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/PasswordChangeEndpointIntegrationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/PasswordChangeEndpointIntegrationTests.java @@ -89,7 +89,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pass through } }); @@ -133,7 +133,7 @@ void changePasswordSameAsOldFails() { .exchange(serverRunning.getUrl(userEndpoint) + "/{id}/password", HttpMethod.PUT, new HttpEntity<>(change, headers), Void.class, joe.getId()); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.UNPROCESSABLE_ENTITY); + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.UNPROCESSABLE_CONTENT); } @Test diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/PasswordGrantIntegrationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/PasswordGrantIntegrationTests.java index 0e4caa210a4..fdf39ce06f7 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/PasswordGrantIntegrationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/PasswordGrantIntegrationTests.java @@ -110,7 +110,7 @@ protected UaaClientDetails addUserGroupsRequiredClient() { HttpEntity request = new HttpEntity<>(JsonUtils.writeValueAsString(client), headers); ResponseEntity response = new RestTemplate().postForEntity(serverRunning.getUrl("/oauth/clients"), request, String.class); - assertThat(response.getStatusCodeValue()).isEqualTo(201); + assertThat(response.getStatusCode().value()).isEqualTo(201); return JsonUtils.readValue(response.getBody(), UaaClientDetails.class); } @@ -140,7 +140,7 @@ public boolean hasError(ClientHttpResponse response) throws IOException { } @Override - public void handleError(ClientHttpResponse response) throws IOException { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) throws IOException { //do nothing } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/ScimGroupEndpointsIntegrationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/ScimGroupEndpointsIntegrationTests.java index 8c7330e230b..91046f4b4b2 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/ScimGroupEndpointsIntegrationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/ScimGroupEndpointsIntegrationTests.java @@ -43,7 +43,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpResponse; -import org.springframework.security.crypto.codec.Base64; +import java.util.Base64; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpClientErrorException; @@ -120,7 +120,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pass through } }); @@ -258,7 +258,7 @@ void createNotAllowedGroupFailsCorrectly() throws URISyntaxException { fail("must fail"); } catch (HttpClientErrorException e) { assertThat(e.getStatusCode().is4xxClientError()).isTrue(); - assertThat(e.getRawStatusCode()).isEqualTo(400); + assertThat(e.getStatusCode().value()).isEqualTo(400); assertThat(e.getMessage()).contains("The group with displayName: " + g1.getDisplayName() + " is not allowed in Identity Zone " + testZoneId); } finally { IntegrationTestUtils.deleteZone(serverRunning.getBaseUrl(), testZoneId, adminToken); @@ -563,7 +563,7 @@ private OAuth2AccessToken getClientCredentialsAccessToken(String scope) { HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); headers.set("Authorization", - "Basic " + new String(Base64.encode("%s:%s".formatted(clientId, clientSecret).getBytes()))); + "Basic " + new String(Base64.getEncoder().encode("%s:%s".formatted(clientId, clientSecret).getBytes()))); @SuppressWarnings("rawtypes") ResponseEntity response = serverRunning.postForMap("/oauth/token", formData, headers); @@ -618,7 +618,7 @@ private OAuth2AccessToken getAccessToken(String clientId, String clientSecret, S .contains("username") .contains("password"); - if (response.getHeaders().containsKey("Set-Cookie")) { + if (response.getHeaders().containsHeader("Set-Cookie")) { String cookie = response.getHeaders().getFirst("Set-Cookie"); int nameLength = cookie.indexOf('='); cookies.addCookie(new BasicClientCookie(cookie.substring(0, nameLength), cookie.substring(nameLength + 1))); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/ScimUserEndpointsIntegrationTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/ScimUserEndpointsIntegrationTests.java index 97532830190..8f6c7e3449a 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/ScimUserEndpointsIntegrationTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/ScimUserEndpointsIntegrationTests.java @@ -84,7 +84,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pass through } }); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/DefaultIntegrationTestConfig.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/DefaultIntegrationTestConfig.java index 4b05e9e74ae..35d0aa87867 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/DefaultIntegrationTestConfig.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/DefaultIntegrationTestConfig.java @@ -21,6 +21,7 @@ import org.openqa.selenium.chrome.ChromeOptions; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.http.client.SimpleClientHttpRequestFactory; @@ -31,6 +32,7 @@ import java.net.HttpURLConnection; import java.time.Duration; +@Configuration @PropertySource("classpath:integration.test.properties") @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD, hierarchyMode = DirtiesContext.HierarchyMode.EXHAUSTIVE) public class DefaultIntegrationTestConfig { diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/IdentityZoneNotAvailableIT.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/IdentityZoneNotAvailableIT.java index 7cd37dea00d..86963689501 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/IdentityZoneNotAvailableIT.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/IdentityZoneNotAvailableIT.java @@ -47,7 +47,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pass through } }); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/LoginIT.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/LoginIT.java index 9fa069e863e..5711d0602b3 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/LoginIT.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/LoginIT.java @@ -285,7 +285,7 @@ public boolean hasError(ClientHttpResponse response) throws IOException { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // pass through } }); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/PasswordIT.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/PasswordIT.java index 510ced39e9a..5d18a5d514d 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/PasswordIT.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/PasswordIT.java @@ -63,7 +63,7 @@ void getClientCredentials() { "/oauth/token?client_id=client_with_bcrypt_prefix&client_secret=password&grant_type=client_credentials"); ResponseEntity responseEntity = restTemplate.exchange(requestEntity, Void.class); - assertThat(responseEntity.getStatusCodeValue()).as("Status 200 expected").isEqualTo(200); + assertThat(responseEntity.getStatusCode().value()).as("Status 200 expected").isEqualTo(200); } @Test diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java index 1bbc40ca0c7..3b8af027273 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/util/IntegrationTestUtils.java @@ -273,7 +273,7 @@ public boolean hasError(ClientHttpResponse response) { } @Override - public void handleError(ClientHttpResponse response) { + public void handleError(java.net.URI url, org.springframework.http.HttpMethod method, ClientHttpResponse response) { // ignore } }); @@ -1547,7 +1547,7 @@ public static Map getAuthorizationCodeTokenMap(ServerRunningExte } public static void extractCookies(ResponseEntity response, BasicCookieStore cookies) { - if (response.getHeaders().containsKey("Set-Cookie")) { + if (response.getHeaders().containsHeader("Set-Cookie")) { for (String cookie : response.getHeaders().get("Set-Cookie")) { int nameLength = cookie.indexOf('='); cookies.addCookie(new BasicClientCookie(cookie.substring(0, nameLength), cookie.substring(nameLength + 1))); @@ -1556,7 +1556,7 @@ public static void extractCookies(ResponseEntity response, BasicCookieStore c } public static void copyCookies(ResponseEntity response, HttpHeaders headers) { - if (response.getHeaders().containsKey("Set-Cookie")) { + if (response.getHeaders().containsHeader("Set-Cookie")) { for (String cookie : response.getHeaders().get("Set-Cookie")) { headers.add("Cookie", cookie); } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/BootstrapTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/BootstrapTests.java index f73c98a0605..44546240b90 100755 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/BootstrapTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/BootstrapTests.java @@ -17,6 +17,8 @@ import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.IdentityZoneProvisioning; import org.cloudfoundry.identity.uaa.zone.SamlConfig; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -30,8 +32,6 @@ import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import org.springframework.mock.web.MockRequestDispatcher; import org.springframework.mock.web.MockServletConfig; import org.springframework.mock.web.MockServletContext; diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointDocs.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointDocs.java index 0345976b721..110fb3455aa 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointDocs.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointDocs.java @@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpSession; import org.springframework.restdocs.snippet.Snippet; -import org.springframework.security.crypto.codec.Base64; +import java.util.Base64; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -183,7 +183,7 @@ Map generate_auto_login_code(boolean x) throws Exception { request.setPassword("koala"); String body = mockMvc.perform( post("/autologin") - .header("Authorization", "Basic " + new String(Base64.encode("admin:adminsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("admin:adminsecret".getBytes()))) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(request))) diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcTests.java index 47b00c9ef97..923f475412c 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcTests.java @@ -51,7 +51,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -61,12 +60,12 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.security.web.PortResolverImpl; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.savedrequest.DefaultSavedRequest; import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -202,7 +201,7 @@ public class LoginMockMvcTests { private IdentityZone identityZone; private File originalLimitedModeStatusFile; - @MockBean + @MockitoBean OidcMetadataFetcher oidcMetadataFetcher; @BeforeEach @@ -1671,7 +1670,7 @@ void noCreateAccountLinksWhenUAAisNotAllowedProvider() throws Exception { MockMvcUtils.updateClient(webApplicationContext, zoneAdminClient, identityZone); MockHttpSession session = new MockHttpSession(); - SavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest(), new PortResolverImpl()) { + SavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest()) { @Override public String getRedirectUrl() { return "http://test/redirect/oauth/authorize"; @@ -2859,7 +2858,7 @@ private static IdentityZone setupZone( } private static SavedRequest getSavedRequest(UaaClientDetails client) { - return new DefaultSavedRequest(new MockHttpServletRequest(), new PortResolverImpl()) { + return new DefaultSavedRequest(new MockHttpServletRequest()) { @Override public String getRedirectUrl() { return "http://test/redirect/oauth/authorize"; diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcZonePathTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcZonePathTests.java index 6582fc33c40..a864ed016d9 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcZonePathTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcZonePathTests.java @@ -57,7 +57,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -67,12 +66,12 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.security.web.PortResolverImpl; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.savedrequest.DefaultSavedRequest; import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -204,7 +203,7 @@ public class LoginMockMvcZonePathTests { private IdentityZone identityZone; private File originalLimitedModeStatusFile; - @MockBean + @MockitoBean OidcMetadataFetcher oidcMetadataFetcher; @BeforeEach @@ -2038,7 +2037,7 @@ void noCreateAccountLinksWhenUAAisNotAllowedProvider(ZoneResolutionMode mode) th MockMvcUtils.updateClient(webApplicationContext, zoneAdminClient, identityZone); MockHttpSession session = new MockHttpSession(); - SavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest(), new PortResolverImpl()) { + SavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest()) { @Override public String getRedirectUrl() { return "http://test/redirect/oauth/authorize"; @@ -3462,7 +3461,7 @@ private static IdentityZone setupZone( } private static SavedRequest getSavedRequest(UaaClientDetails client) { - return new DefaultSavedRequest(new MockHttpServletRequest(), new PortResolverImpl()) { + return new DefaultSavedRequest(new MockHttpServletRequest()) { @Override public String getRedirectUrl() { return "http://test/redirect/oauth/authorize"; diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcTests.java index c03841cd055..6737009bf18 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcTests.java @@ -29,7 +29,6 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpSession; -import org.springframework.security.web.PortResolverImpl; import org.springframework.security.web.savedrequest.DefaultSavedRequest; import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.test.web.servlet.MockMvc; @@ -245,7 +244,7 @@ void redirectToSavedRequest_ifPresent() throws Exception { user = MockMvcUtils.createUser(mockMvc, token, user); MockHttpSession session = new MockHttpSession(); - SavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest(), new PortResolverImpl()) { + SavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest()) { @Override public String getRedirectUrl() { return "http://test/redirect/oauth/authorize"; diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcZonePathTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcZonePathTests.java index 452e5d93261..d1dacba9dc5 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcZonePathTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcZonePathTests.java @@ -36,7 +36,6 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpSession; -import org.springframework.security.web.PortResolverImpl; import org.springframework.security.web.savedrequest.DefaultSavedRequest; import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.test.web.servlet.MockMvc; @@ -260,7 +259,7 @@ void redirectToSavedRequest_ifPresent() throws Exception { user = MockMvcUtils.createUser(mockMvc, token, user); MockHttpSession session = new MockHttpSession(); - SavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest(), new PortResolverImpl()) { + SavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest()) { @Override public String getRedirectUrl() { return "http://test/redirect/oauth/authorize"; diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/limited/LimitedModeTokenMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/limited/LimitedModeTokenMockMvcTests.java index ddf115ce165..a5752e14044 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/limited/LimitedModeTokenMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/limited/LimitedModeTokenMockMvcTests.java @@ -25,7 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.security.crypto.codec.Base64; +import java.util.Base64; import java.io.File; @@ -73,7 +73,7 @@ void check_token_while_limited() throws Exception { post("/check_token") .param("token", token) .header(AUTHORIZATION, - "Basic " + new String(Base64.encode((client.getClientId() + ":" + SECRET).getBytes()))) + "Basic " + new String(Base64.getEncoder().encode((client.getClientId() + ":" + SECRET).getBytes()))) ) .andExpect(status().isOk()) .andExpect(jsonPath("$.scope").value(containsInAnyOrder("clients.read", "uaa.resource"))) diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointsAliasMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointsAliasMockMvcTests.java index d87986d79a1..ce3ff604896 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointsAliasMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointsAliasMockMvcTests.java @@ -197,7 +197,7 @@ private void shouldReject_AliasIdIsSet( ) throws Exception { final String aliasId = UUID.randomUUID().toString(); final IdentityProvider idp = buildOidcIdpWithAliasProperties(zone1.getId(), aliasId, zone2.getId()); - shouldRejectCreation(zone1, idp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectCreation(zone1, idp, HttpStatus.UNPROCESSABLE_CONTENT); } } @@ -256,7 +256,7 @@ void shouldReject_IdzAndAliasZidAreEqual_CustomZone() throws Exception { private void shouldReject_IdzAndAliasZidAreEqual(final IdentityZone zone) throws Exception { final IdentityProvider idp = buildOidcIdpWithAliasProperties(zone.getId(), null, zone.getId()); - shouldRejectCreation(zone, idp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectCreation(zone, idp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -271,14 +271,14 @@ void shouldReject_AliasNotSupportedForIdpType_CustomToUaaZone() throws Exception private void shouldReject_AliasNotSupportedForIdpType(final IdentityZone zone1, final IdentityZone zone2) throws Exception { final IdentityProvider uaaIdp = buildUaaIdpWithAliasProperties(zone1.getId(), null, zone2.getId()); - shouldRejectCreation(zone1, uaaIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectCreation(zone1, uaaIdp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test void shouldReject_NeitherIdzNorAliasZidIsUaa() throws Exception { final IdentityZone otherCustomZone = MockMvcUtils.createZoneUsingWebRequest(mockMvc, identityToken); final IdentityProvider idp = buildOidcIdpWithAliasProperties(customZone.getId(), null, otherCustomZone.getId()); - shouldRejectCreation(customZone, idp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectCreation(customZone, idp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -288,7 +288,7 @@ void shouldReject_IdzReferencedInAliasZidDoesNotExist() throws Exception { null, UUID.randomUUID().toString() // does not exist ); - shouldRejectCreation(IdentityZone.getUaa(), provider, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectCreation(IdentityZone.getUaa(), provider, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -343,7 +343,7 @@ private void shouldReject_OnlyAliasZidSet( null, zone2.getId() ); - shouldRejectCreation(zone1, idp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectCreation(zone1, idp, HttpStatus.UNPROCESSABLE_CONTENT); } } @@ -391,7 +391,7 @@ private void shouldReject_NoExistingAlias_AliasIdSet(final IdentityZone zone) th ); assertThat(existingIdp.getAliasZid()).isBlank(); existingIdp.setAliasId(UUID.randomUUID().toString()); - shouldRejectUpdate(zone, existingIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone, existingIdp, HttpStatus.UNPROCESSABLE_CONTENT); } } @@ -451,7 +451,7 @@ void shouldReject_ReferencedZoneDoesNotExist() throws Exception { existingIdp.setAliasZid(UUID.randomUUID().toString()); // non-existing zone - shouldRejectUpdate(zone, existingIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone, existingIdp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -471,7 +471,7 @@ private void shouldReject_AliasNotSupportedForIdpType(final IdentityZone zone1, // try to create an alias for the IdP -> should fail because of the IdP's type createdProvider.setAliasZid(zone2.getId()); - shouldRejectUpdate(zone1, createdProvider, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, createdProvider, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -520,7 +520,7 @@ private void shouldReject_AliasZidSetToSameZone(final IdentityZone zone) throws buildOidcIdpWithAliasProperties(zone.getId(), null, null) ); idp.setAliasZid(zone.getId()); - shouldRejectUpdate(zone, idp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone, idp, HttpStatus.UNPROCESSABLE_CONTENT); } } @@ -582,7 +582,7 @@ private void shouldReject_AliasIdNotSetInPayload( existingIdp.setAliasId(null); existingIdp.setName("some-new-name"); - shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -651,7 +651,7 @@ private void shouldReject_ChangingAliasPropertiesOfIdpWithAlias( ); originalIdp.setAliasId(newAliasId); originalIdp.setAliasZid(newAliasZid); - shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_CONTENT); } private static Stream shouldReject_ChangingAliasPropertiesOfIdpWithAlias() { @@ -681,7 +681,7 @@ void shouldReject_CannotFixDanglingRefAsAliasZoneIsNotExisting_UaaToCustomZone() updateIdpViaDb(zone1.getId(), existingIdp); existingIdp.setName("some-new-name"); - shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_CONTENT); } } @@ -717,7 +717,7 @@ private void shouldReject_DanglingRefCannotBeFixedAsOriginAlreadyExistsInAliasZo existingIdp.setAliasId(null); existingIdp.setAliasZid(null); existingIdp.setName("some-new-name"); - shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -729,7 +729,7 @@ void shouldReject_IdpInCustomZone_AliasToOtherCustomZone() throws Exception { // try to create an alias in another custom zone -> should fail idpInCustomZone.setAliasZid("not-uaa"); - shouldRejectUpdate(customZone, idpInCustomZone, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(customZone, idpInCustomZone, HttpStatus.UNPROCESSABLE_CONTENT); } } @@ -762,7 +762,7 @@ private void shouldReject_AliasZidSet( // setting the alias zid should fail existingIdp.setAliasZid(zone2.getId()); - shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_CONTENT); } } @@ -792,7 +792,7 @@ private void shouldReject_OtherPropertiesChangedWhileAliasPropertiesUnchanged( // change non-alias property without setting alias properties to null originalIdp.setName("some-new-name"); - shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -822,7 +822,7 @@ private void shouldReject_SetOnlyAliasPropertiesToNull( // change non-alias property without setting alias properties to null originalIdp.setAliasId(null); originalIdp.setAliasZid(null); - shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -855,7 +855,7 @@ private void shouldReject_SetAliasPropertiesToNullAndChangeOtherProperties( originalIdp.setAliasId(null); originalIdp.setAliasZid(null); originalIdp.setName("some-new-name"); - shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -890,7 +890,7 @@ private void shouldReject_AliasIdOfExistingIdpMissing( existingIdp.setAliasId(null); existingIdp.setAliasZid(null); existingIdp.setName("some-new-name"); - shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -917,7 +917,7 @@ private void shouldReject_EvenIfAliasReferenceIsBroken( // try to update IdP -> should still fail, even if the alias reference is broken existingIdp.setName("some-new-name"); - shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, existingIdp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -943,7 +943,7 @@ private void shouldReject_OnlyAliasIdSetToNull( assertThat(originalIdp.getAliasZid()).isNotBlank(); originalIdp.setAliasId(null); - shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_CONTENT); } @Test @@ -969,7 +969,7 @@ private void shouldReject_OnlyAliasZidSetToNull( assertThat(originalIdp.getAliasZid()).isNotBlank(); originalIdp.setAliasZid(null); - shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(zone1, originalIdp, HttpStatus.UNPROCESSABLE_CONTENT); } } } @@ -1178,7 +1178,7 @@ private void shouldRejectDeletion_WhenAliasIdpExists( // delete IdP in zone 1 -> should be rejected since alias feature is disabled final MvcResult deleteResult = deleteIdpAndReturnResult(zone1, id); - assertThat(deleteResult.getResponse().getStatus()).isEqualTo(HttpStatus.UNPROCESSABLE_ENTITY.value()); + assertThat(deleteResult.getResponse().getStatus()).isEqualTo(HttpStatus.UNPROCESSABLE_CONTENT.value()); } } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeDefaultConfigMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeDefaultConfigMockMvcTests.java index be2ccac0c41..d5908abd899 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeDefaultConfigMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeDefaultConfigMockMvcTests.java @@ -200,7 +200,7 @@ void token_exchange_impersonate_client() throws Exception { ); //update the worker server client to have `token_exchange.impersonate. authority - String requiredImpersonationAuthority = String.format(TOKEN_EXCHANGE_IMPERSONATE_CLIENT_PERMISSION, audience.getClientId()); + String requiredImpersonationAuthority = TOKEN_EXCHANGE_IMPERSONATE_CLIENT_PERMISSION.formatted(audience.getClientId()); UaaClientDetails workerClient = (UaaClientDetails) workerServer.client(); workerClient.setAuthorities(List.of(new SimpleGrantedAuthority(requiredImpersonationAuthority))); webApplicationContext.getBean(MultitenantJdbcClientDetailsService.class).updateClientDetails( diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeGrantEndpointDocs.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeGrantEndpointDocs.java index a706b16025e..2131dee3b23 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeGrantEndpointDocs.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeGrantEndpointDocs.java @@ -68,7 +68,7 @@ class TokenExchangeGrantEndpointDocs extends TokenExchangeMockMvcBase { ParameterDescriptor p_client_secret = parameterWithName("client_secret").type(STRING).optional().description("The [secret passphrase configured](#change-secret) for the OAuth client. Optional if it is passed as part of the Basic Authorization header or if client_assertion is sent as part of private_key_jwt authentication."); ParameterDescriptor p_client_assertion = parameterWithName("client_assertion").type(STRING).optional(null).description("NOT YET SUPPORTED! UAA 76.23.0 Client authentication using method [private_key_jwt](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication). Optional as replacement of methods client_secret_basic or client_secret_post using secrets. The client needs to have a valid [JWT confiuration](#change-client-jwt) for trust to JWT in client_assertion."); ParameterDescriptor p_client_assertion_type = parameterWithName("client_assertion_type").type(STRING).optional(null).description("NOT YET SUPPORTED!UAA 76.23.0 [RFC 7523](https://tools.ietf.org/html/rfc7523) describes the type. Must be set to `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` if `client_assertion` parameter is present."); - ParameterDescriptor p_audience = parameterWithName("audience").type(STRING).optional(null).description("Valid client_id used to impersonate/swap the identity of the calling client in the resulting token. [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html#name-request). Note: The calling client must have `"+String.format(TOKEN_EXCHANGE_IMPERSONATE_CLIENT_PERMISSION, "")+"` as an an authority (client.authorities). For example `"+String.format(TOKEN_EXCHANGE_IMPERSONATE_CLIENT_PERMISSION, "cf")+"` to be allowed to impersonate the `cf` client."); + ParameterDescriptor p_audience = parameterWithName("audience").type(STRING).optional(null).description("Valid client_id used to impersonate/swap the identity of the calling client in the resulting token. [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html#name-request). Note: The calling client must have `"+TOKEN_EXCHANGE_IMPERSONATE_CLIENT_PERMISSION.formatted("")+"` as an an authority (client.authorities). For example `"+TOKEN_EXCHANGE_IMPERSONATE_CLIENT_PERMISSION.formatted("cf")+"` to be allowed to impersonate the `cf` client."); ParameterDescriptor p_subject_token = parameterWithName("subject_token").type(STRING).required().description("Must be a valid JWT token representing the identity to be assumed in the resulting token. [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html#name-request)"); ParameterDescriptor p_subject_token_type = parameterWithName("subject_token_type").type(STRING).required().description("Must be " + TOKEN_TYPE_ACCESS + " or " + TOKEN_TYPE_ID + ". [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html#name-request)"); ParameterDescriptor p_requested_token_type = parameterWithName("requested_token_type").type(STRING).optional().description("Must be " + TOKEN_TYPE_ACCESS + ". [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html#name-request)"); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeMockMvcBase.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeMockMvcBase.java index 1971590f27b..10e288848be 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeMockMvcBase.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeMockMvcBase.java @@ -123,7 +123,7 @@ public AuthorizationServer workerServer() { public Jwt getTokenClaims(String token, String tokenKey, String serverKey) { assertThat(token) - .withFailMessage(String.format("Server: %s does not have a token under key: %s", serverKey, tokenKey)) + .withFailMessage("Server: %s does not have a token under key: %s".formatted(serverKey, tokenKey)) .isNotNull() .isNotEmpty(); @@ -131,7 +131,7 @@ public Jwt getTokenClaims(String token, String tokenKey, String serverKey) { return JwtHelper.decode(token); } catch (RuntimeException e) { Assertions.fail( - String.format("Unable to decode token: %s for server: %s and key: %s", token, serverKey, tokenKey) + "Unable to decode token: %s for server: %s and key: %s".formatted(token, serverKey, tokenKey) ); } return null; diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeOverrideAuthManagerMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeOverrideAuthManagerMockMvcTests.java index ea124da2a27..5849a76e291 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeOverrideAuthManagerMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenExchangeOverrideAuthManagerMockMvcTests.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.security.core.Authentication; import org.springframework.test.annotation.DirtiesContext; @@ -39,6 +40,7 @@ @DefaultTestContext public class TokenExchangeOverrideAuthManagerMockMvcTests extends TokenExchangeMockMvcBase { + @Configuration static class TokenExchangeConfiguration { @Bean ExternalOAuthAuthenticationManager tokenExchangeAuthenticationManager( diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java index 9303b2314a2..a30a6c42901 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java @@ -73,7 +73,7 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.codec.Base64; +import java.util.Base64; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.test.context.TestPropertySource; @@ -2531,7 +2531,7 @@ void loginAuthenticationFilter() throws Exception { String userScopes = "space.1.developer,space.2.developer,org.1.reader,org.2.reader,org.12345.admin,scope.one,scope.two,scope.three"; ScimUser developer = setUpUser(jdbcScimUserProvisioning, jdbcScimGroupMembershipManager, jdbcScimGroupProvisioning, userId, userScopes, OriginKeys.LOGIN_SERVER, IdentityZoneHolder.get().getId()); String loginToken = testClient.getClientCredentialsOAuthAccessToken("login", "loginsecret", ""); - String basicAuthForLoginClient = new String(Base64.encode("%s:%s".formatted("login", "loginsecret").getBytes())); + String basicAuthForLoginClient = new String(Base64.getEncoder().encode("%s:%s".formatted("login", "loginsecret").getBytes())); //the login server is matched by providing //1. Bearer token (will be authenticated for oauth.login scope) @@ -2904,7 +2904,7 @@ void otherClientAuthenticationMethods() throws Exception { String userId = "testuser" + generator.generate(); String userScopes = "space.1.developer,space.2.developer,org.1.reader,org.2.reader,org.12345.admin,scope.one,scope.two,scope.three,uaa.user"; ScimUser developer = setUpUser(jdbcScimUserProvisioning, jdbcScimGroupMembershipManager, jdbcScimGroupProvisioning, userId, userScopes, OriginKeys.UAA, IdentityZoneHolder.get().getId()); - String basicAuthForOauthClient = new String(Base64.encode("%s:%s".formatted(oauthClientId, SECRET).getBytes())); + String basicAuthForOauthClient = new String(Base64.getEncoder().encode("%s:%s".formatted(oauthClientId, SECRET).getBytes())); //success - regular password grant but client is authenticated using POST parameters mockMvc.perform(post("/oauth/token") @@ -3036,7 +3036,7 @@ void validateOldTokenAfterAddClientSecret() throws Exception { clientDetailsService.addClientSecret(clientId, "newSecret", IdentityZoneHolder.get().getId()); mockMvc.perform(post("/check_token") - .header("Authorization", "Basic " + new String(Base64.encode("app:appclientsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("app:appclientsecret".getBytes()))) .param("token", accessToken)) .andDo(print()) .andExpect(status().isOk()); @@ -3066,7 +3066,7 @@ void validateNewTokenAfterAddClientSecret() throws Exception { assertThat(accessToken).isNotNull(); mockMvc.perform(post("/check_token") - .header("Authorization", "Basic " + new String(Base64.encode("app:appclientsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("app:appclientsecret".getBytes()))) .param("token", accessToken)) .andExpect(status().isOk()); } @@ -3096,7 +3096,7 @@ void validateOldTokenAfterDeleteClientSecret() throws Exception { clientDetailsService.deleteClientSecret(clientId, IdentityZoneHolder.get().getId()); MockHttpServletResponse response = mockMvc.perform(post("/check_token") - .header("Authorization", "Basic " + new String(Base64.encode("app:appclientsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("app:appclientsecret".getBytes()))) .param("token", accessToken)) .andExpect(status().isBadRequest()) .andReturn().getResponse(); @@ -3129,7 +3129,7 @@ void validateNewTokenBeforeDeleteClientSecret() throws Exception { clientDetailsService.deleteClientSecret(clientId, IdentityZoneHolder.get().getId()); mockMvc.perform(post("/check_token") - .header("Authorization", "Basic " + new String(Base64.encode("app:appclientsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("app:appclientsecret".getBytes()))) .param("token", accessToken)) .andExpect(status().isOk()); } @@ -3144,7 +3144,7 @@ void validateNewTokenAfterDeleteClientSecret() throws Exception { String body = mockMvc.perform(post("/oauth/token") .accept(MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":newSecret").getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode((clientId + ":newSecret").getBytes()))) .param("grant_type", "client_credentials") .param("client_id", clientId) .param("client_secret", SECRET)) @@ -3157,7 +3157,7 @@ void validateNewTokenAfterDeleteClientSecret() throws Exception { assertThat(accessToken).isNotNull(); mockMvc.perform(post("/check_token") - .header("Authorization", "Basic " + new String(Base64.encode("app:appclientsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("app:appclientsecret".getBytes()))) .param("token", accessToken)) .andExpect(status().isOk()); } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockZonePathTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockZonePathTests.java index 4a77e45f90d..937eff76cd4 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockZonePathTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockZonePathTests.java @@ -75,7 +75,7 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.codec.Base64; +import java.util.Base64; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.test.context.TestPropertySource; @@ -2586,7 +2586,7 @@ void loginAuthenticationFilter() throws Exception { String userScopes = "space.1.developer,space.2.developer,org.1.reader,org.2.reader,org.12345.admin,scope.one,scope.two,scope.three"; ScimUser developer = setUpUser(jdbcScimUserProvisioning, jdbcScimGroupMembershipManager, jdbcScimGroupProvisioning, userId, userScopes, OriginKeys.LOGIN_SERVER, IdentityZoneHolder.get().getId()); String loginToken = testClient.getClientCredentialsOAuthAccessToken("login", "loginsecret", ""); - String basicAuthForLoginClient = new String(Base64.encode("%s:%s".formatted("login", "loginsecret").getBytes())); + String basicAuthForLoginClient = new String(Base64.getEncoder().encode("%s:%s".formatted("login", "loginsecret").getBytes())); //the login server is matched by providing //1. Bearer token (will be authenticated for oauth.login scope) @@ -2959,7 +2959,7 @@ void otherClientAuthenticationMethods() throws Exception { String userId = "testuser" + generator.generate(); String userScopes = "space.1.developer,space.2.developer,org.1.reader,org.2.reader,org.12345.admin,scope.one,scope.two,scope.three,uaa.user"; ScimUser developer = setUpUser(jdbcScimUserProvisioning, jdbcScimGroupMembershipManager, jdbcScimGroupProvisioning, userId, userScopes, OriginKeys.UAA, IdentityZoneHolder.get().getId()); - String basicAuthForOauthClient = new String(Base64.encode("%s:%s".formatted(oauthClientId, SECRET).getBytes())); + String basicAuthForOauthClient = new String(Base64.getEncoder().encode("%s:%s".formatted(oauthClientId, SECRET).getBytes())); //success - regular password grant but client is authenticated using POST parameters mockMvc.perform(post("/oauth/token") @@ -3091,7 +3091,7 @@ void validateOldTokenAfterAddClientSecret() throws Exception { clientDetailsService.addClientSecret(clientId, "newSecret", IdentityZoneHolder.get().getId()); mockMvc.perform(post("/check_token") - .header("Authorization", "Basic " + new String(Base64.encode("app:appclientsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("app:appclientsecret".getBytes()))) .param("token", accessToken)) .andDo(print()) .andExpect(status().isOk()); @@ -3121,7 +3121,7 @@ void validateNewTokenAfterAddClientSecret() throws Exception { assertThat(accessToken).isNotNull(); mockMvc.perform(post("/check_token") - .header("Authorization", "Basic " + new String(Base64.encode("app:appclientsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("app:appclientsecret".getBytes()))) .param("token", accessToken)) .andExpect(status().isOk()); } @@ -3151,7 +3151,7 @@ void validateOldTokenAfterDeleteClientSecret() throws Exception { clientDetailsService.deleteClientSecret(clientId, IdentityZoneHolder.get().getId()); MockHttpServletResponse response = mockMvc.perform(post("/check_token") - .header("Authorization", "Basic " + new String(Base64.encode("app:appclientsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("app:appclientsecret".getBytes()))) .param("token", accessToken)) .andExpect(status().isBadRequest()) .andReturn().getResponse(); @@ -3184,7 +3184,7 @@ void validateNewTokenBeforeDeleteClientSecret() throws Exception { clientDetailsService.deleteClientSecret(clientId, IdentityZoneHolder.get().getId()); mockMvc.perform(post("/check_token") - .header("Authorization", "Basic " + new String(Base64.encode("app:appclientsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("app:appclientsecret".getBytes()))) .param("token", accessToken)) .andExpect(status().isOk()); } @@ -3199,7 +3199,7 @@ void validateNewTokenAfterDeleteClientSecret() throws Exception { String body = mockMvc.perform(post("/oauth/token") .accept(MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":newSecret").getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode((clientId + ":newSecret").getBytes()))) .param("grant_type", "client_credentials") .param("client_id", clientId) .param("client_secret", SECRET)) @@ -3212,7 +3212,7 @@ void validateNewTokenAfterDeleteClientSecret() throws Exception { assertThat(accessToken).isNotNull(); mockMvc.perform(post("/check_token") - .header("Authorization", "Basic " + new String(Base64.encode("app:appclientsecret".getBytes()))) + .header("Authorization", "Basic " + new String(Base64.getEncoder().encode("app:appclientsecret".getBytes()))) .param("token", accessToken)) .andExpect(status().isOk()); } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/util/MockMvcUtils.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/util/MockMvcUtils.java index 5a40a317694..360ba5bde73 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/util/MockMvcUtils.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/util/MockMvcUtils.java @@ -84,7 +84,6 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.security.web.PortResolverImpl; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; @@ -1200,7 +1199,7 @@ public static IdentityZone updateZone(MockMvc mockMvc, IdentityZone updatedZone) public static class MockSavedRequest extends DefaultSavedRequest { public MockSavedRequest() { - super(new MockHttpServletRequest(), new PortResolverImpl()); + super(new MockHttpServletRequest()); } @Override diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/IdentityZoneEndpointsMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/IdentityZoneEndpointsMockMvcTests.java index 77acca6d62f..4ce8a24fcaa 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/IdentityZoneEndpointsMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/IdentityZoneEndpointsMockMvcTests.java @@ -768,7 +768,7 @@ void updateWithInvalidSamlKeyCertPair() throws Exception { samlConfig.setPrivateKey(samlPrivateKey); samlConfig.setPrivateKeyPassword(samlKeyPassphrase); samlConfig.setCertificate(samlCertificate); - updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); + updateZone(created, HttpStatus.UNPROCESSABLE_CONTENT, identityClientToken); } @Test @@ -787,7 +787,7 @@ void updateWithPartialSamlKeyCertPair() throws Exception { samlConfig.setPrivateKey(null); samlConfig.setPrivateKeyPassword(SERVICE_PROVIDER_KEY_PASSWORD); samlConfig.setCertificate(SERVICE_PROVIDER_CERTIFICATE); - updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); + updateZone(created, HttpStatus.UNPROCESSABLE_CONTENT, identityClientToken); } @Test @@ -821,7 +821,7 @@ void updateWithNewSamlCertNoKeyIsUnprocessableEntity() throws Exception { samlConfig.setCertificate(KeyWithCertTest.INVALID_CERT); samlConfig.setPrivateKey(null); samlConfig.setPrivateKeyPassword(null); - updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); + updateZone(created, HttpStatus.UNPROCESSABLE_CONTENT, identityClientToken); } @Test @@ -835,7 +835,7 @@ void updateWithNewKeyNoCertIsUnprocessableEntity() throws Exception { samlConfig.setCertificate(null); samlConfig.setPrivateKey(SERVICE_PROVIDER_KEY); samlConfig.setPrivateKeyPassword(SERVICE_PROVIDER_KEY_PASSWORD); - updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); + updateZone(created, HttpStatus.UNPROCESSABLE_CONTENT, identityClientToken); } @Test @@ -2154,7 +2154,7 @@ void updateZoneWithDifferentIdInBodyAndPath_fails() throws Exception { IdentityZone identityZone2 = createZone(new AlphanumericRandomValueStringGenerator(5).generate(), HttpStatus.CREATED, adminToken, new IdentityZoneConfiguration()); identityZone.setId(identityZone2.getId()); - updateZone(id, identityZone, HttpStatus.UNPROCESSABLE_ENTITY, adminToken); + updateZone(id, identityZone, HttpStatus.UNPROCESSABLE_CONTENT, adminToken); } @Test @@ -2179,7 +2179,7 @@ void createZoneWithCustomIssuerAndNoTokenPolicyShouldFail() throws Exception { createZone( "should-not-exist" + new AlphanumericRandomValueStringGenerator(5).generate(), - HttpStatus.UNPROCESSABLE_ENTITY, + HttpStatus.UNPROCESSABLE_CONTENT, "You cannot set issuer value unless you have set your own signing key for this identity zone.", adminToken, identityZoneConfiguration @@ -2194,7 +2194,7 @@ void createZoneWithCustomIssuerAndNoActiveSigningKeyShouldFail() throws Exceptio createZone( "should-not-exist" + new AlphanumericRandomValueStringGenerator(5).generate(), - HttpStatus.UNPROCESSABLE_ENTITY, + HttpStatus.UNPROCESSABLE_CONTENT, "You cannot set issuer value unless you have set your own signing key for this identity zone.", adminToken, identityZoneConfiguration @@ -2222,7 +2222,7 @@ void updateZoneWithCustomIssuerSetAndNoTokenPolicyShouldFail() throws Exception IdentityZone identityZone = createZone(zoneId, HttpStatus.CREATED, adminToken, identityZoneConfiguration); identityZone.getConfig().setTokenPolicy(null); - updateZone(zoneId, identityZone, HttpStatus.UNPROCESSABLE_ENTITY, "You cannot set issuer value unless you have set your own signing key for this identity zone.", adminToken); + updateZone(zoneId, identityZone, HttpStatus.UNPROCESSABLE_CONTENT, "You cannot set issuer value unless you have set your own signing key for this identity zone.", adminToken); } @Test @@ -2244,7 +2244,7 @@ void updateZoneWithCustomIssuerSetAndNoActiveSigningKeyShouldFail() throws Excep updateZone( zoneId, identityZone, - HttpStatus.UNPROCESSABLE_ENTITY, + HttpStatus.UNPROCESSABLE_CONTENT, "You cannot set issuer value unless you have set your own signing key for this identity zone.", adminToken ); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/IdentityZoneEndpointsMockMvcZonePathTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/IdentityZoneEndpointsMockMvcZonePathTests.java index 66a4f4aec60..8ef2c06f8de 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/IdentityZoneEndpointsMockMvcZonePathTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/IdentityZoneEndpointsMockMvcZonePathTests.java @@ -776,7 +776,7 @@ void updateWithInvalidSamlKeyCertPair() throws Exception { samlConfig.setPrivateKey(samlPrivateKey); samlConfig.setPrivateKeyPassword(samlKeyPassphrase); samlConfig.setCertificate(samlCertificate); - updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); + updateZone(created, HttpStatus.UNPROCESSABLE_CONTENT, identityClientToken); } @Test @@ -795,7 +795,7 @@ void updateWithPartialSamlKeyCertPair() throws Exception { samlConfig.setPrivateKey(null); samlConfig.setPrivateKeyPassword(SERVICE_PROVIDER_KEY_PASSWORD); samlConfig.setCertificate(SERVICE_PROVIDER_CERTIFICATE); - updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); + updateZone(created, HttpStatus.UNPROCESSABLE_CONTENT, identityClientToken); } @Test @@ -829,7 +829,7 @@ void updateWithNewSamlCertNoKeyIsUnprocessableEntity() throws Exception { samlConfig.setCertificate(KeyWithCertTest.INVALID_CERT); samlConfig.setPrivateKey(null); samlConfig.setPrivateKeyPassword(null); - updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); + updateZone(created, HttpStatus.UNPROCESSABLE_CONTENT, identityClientToken); } @Test @@ -843,7 +843,7 @@ void updateWithNewKeyNoCertIsUnprocessableEntity() throws Exception { samlConfig.setCertificate(null); samlConfig.setPrivateKey(SERVICE_PROVIDER_KEY); samlConfig.setPrivateKeyPassword(SERVICE_PROVIDER_KEY_PASSWORD); - updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); + updateZone(created, HttpStatus.UNPROCESSABLE_CONTENT, identityClientToken); } @Test @@ -2159,7 +2159,7 @@ void updateZoneWithDifferentIdInBodyAndPath_fails() throws Exception { IdentityZone identityZone2 = createZone(new AlphanumericRandomValueStringGenerator(5).generate(), HttpStatus.CREATED, adminToken, new IdentityZoneConfiguration()); identityZone.setId(identityZone2.getId()); - updateZone(id, identityZone, HttpStatus.UNPROCESSABLE_ENTITY, adminToken); + updateZone(id, identityZone, HttpStatus.UNPROCESSABLE_CONTENT, adminToken); } @Test @@ -2184,7 +2184,7 @@ void createZoneWithCustomIssuerAndNoTokenPolicyShouldFail() throws Exception { createZone( "should-not-exist" + new AlphanumericRandomValueStringGenerator(5).generate(), - HttpStatus.UNPROCESSABLE_ENTITY, + HttpStatus.UNPROCESSABLE_CONTENT, "You cannot set issuer value unless you have set your own signing key for this identity zone.", adminToken, identityZoneConfiguration @@ -2199,7 +2199,7 @@ void createZoneWithCustomIssuerAndNoActiveSigningKeyShouldFail() throws Exceptio createZone( "should-not-exist" + new AlphanumericRandomValueStringGenerator(5).generate(), - HttpStatus.UNPROCESSABLE_ENTITY, + HttpStatus.UNPROCESSABLE_CONTENT, "You cannot set issuer value unless you have set your own signing key for this identity zone.", adminToken, identityZoneConfiguration @@ -2227,7 +2227,7 @@ void updateZoneWithCustomIssuerSetAndNoTokenPolicyShouldFail() throws Exception IdentityZone identityZone = createZone(zoneId, HttpStatus.CREATED, adminToken, identityZoneConfiguration); identityZone.getConfig().setTokenPolicy(null); - updateZone(zoneId, identityZone, HttpStatus.UNPROCESSABLE_ENTITY, "You cannot set issuer value unless you have set your own signing key for this identity zone.", adminToken); + updateZone(zoneId, identityZone, HttpStatus.UNPROCESSABLE_CONTENT, "You cannot set issuer value unless you have set your own signing key for this identity zone.", adminToken); } @Test @@ -2249,7 +2249,7 @@ void updateZoneWithCustomIssuerSetAndNoActiveSigningKeyShouldFail() throws Excep updateZone( zoneId, identityZone, - HttpStatus.UNPROCESSABLE_ENTITY, + HttpStatus.UNPROCESSABLE_CONTENT, "You cannot set issuer value unless you have set your own signing key for this identity zone.", adminToken ); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/performance/LoginPagePerformanceMockMvcTest.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/performance/LoginPagePerformanceMockMvcTest.java index 6754ac1aa33..f49f4332d9b 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/performance/LoginPagePerformanceMockMvcTest.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/performance/LoginPagePerformanceMockMvcTest.java @@ -22,11 +22,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.util.StopWatch; @@ -57,7 +57,7 @@ class LoginPagePerformanceMockMvcTest { private File originalLimitedModeStatusFile; - @MockBean + @MockitoBean OidcMetadataFetcher oidcMetadataFetcher; @BeforeEach diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/performance/LoginPagePerformanceMockMvcZonePathTest.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/performance/LoginPagePerformanceMockMvcZonePathTest.java index 01033b7d9e4..19a6104cc94 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/performance/LoginPagePerformanceMockMvcZonePathTest.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/performance/LoginPagePerformanceMockMvcZonePathTest.java @@ -24,11 +24,11 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.util.StopWatch; @@ -60,7 +60,7 @@ class LoginPagePerformanceMockMvcZonePathTest { private File originalLimitedModeStatusFile; - @MockBean + @MockitoBean OidcMetadataFetcher oidcMetadataFetcher; @BeforeEach diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsAliasMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsAliasMockMvcTests.java index 1df3b5d86b1..49ce4a07d37 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsAliasMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsAliasMockMvcTests.java @@ -746,7 +746,7 @@ private void shouldReject_DanglingReferenceAndZoneNotExisting( // updating the user should fail - the dangling reference cannot be fixed userWithDanglingRef.setUserName("some-new-username"); - shouldRejectUpdate(method, zone1, userWithDanglingRef, HttpStatus.UNPROCESSABLE_ENTITY); + shouldRejectUpdate(method, zone1, userWithDanglingRef, HttpStatus.UNPROCESSABLE_CONTENT); } } diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcTests.java index 8a1a40b27bf..b495a5aeb06 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcTests.java @@ -72,7 +72,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.is; import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8; +import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -628,7 +628,7 @@ void unlockAccount() throws Exception { alteredAccountStatus.setLocked(false); updateAccountStatus(userToLockout, alteredAccountStatus) .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON_UTF8)) + .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(content().string(JsonUtils.writeValueAsString(alteredAccountStatus))); attemptLogin(userToLockout) @@ -642,7 +642,7 @@ void accountStatusEmptyPatchDoesNotUnlock() throws Exception { updateAccountStatus(userToLockout, new UserAccountStatus()) .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON_UTF8)) + .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(content().string("{}")); attemptLogin(userToLockout) @@ -670,7 +670,7 @@ void unlockAccountWhenNotLocked() throws Exception { alteredAccountStatus.setLocked(false); updateAccountStatus(userToLockout, alteredAccountStatus) .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON_UTF8)) + .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(content().string(JsonUtils.writeValueAsString(alteredAccountStatus))); attemptLogin(userToLockout) @@ -714,7 +714,7 @@ void forcePasswordChange() throws Exception { updateAccountStatus(user, alteredAccountStatus) .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON_UTF8)) + .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(content().string(JsonUtils.writeValueAsString(alteredAccountStatus))); assertThat(usersRepository.checkPasswordChangeIndividuallyRequired(user.getId(), IdentityZoneHolder.get().getId())).isTrue(); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcZonePathTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcZonePathTests.java index 0f6a3d69ecf..55684c11ed5 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcZonePathTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcZonePathTests.java @@ -80,7 +80,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.is; import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8; +import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -658,7 +658,7 @@ void unlockAccount(ZoneResolutionMode mode) throws Exception { alteredAccountStatus.setLocked(false); updateAccountStatus(userToLockout, alteredAccountStatus) .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON_UTF8)) + .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(content().string(JsonUtils.writeValueAsString(alteredAccountStatus))); attemptLogin(userToLockout) @@ -673,7 +673,7 @@ void accountStatusEmptyPatchDoesNotUnlock(ZoneResolutionMode mode) throws Except updateAccountStatus(userToLockout, new UserAccountStatus()) .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON_UTF8)) + .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(content().string("{}")); attemptLogin(userToLockout) @@ -701,7 +701,7 @@ void unlockAccountWhenNotLocked() throws Exception { alteredAccountStatus.setLocked(false); updateAccountStatus(userToLockout, alteredAccountStatus) .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON_UTF8)) + .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(content().string(JsonUtils.writeValueAsString(alteredAccountStatus))); attemptLogin(userToLockout) @@ -745,7 +745,7 @@ void forcePasswordChange() throws Exception { updateAccountStatus(user, alteredAccountStatus) .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON_UTF8)) + .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(content().string(JsonUtils.writeValueAsString(alteredAccountStatus))); assertThat(usersRepository.checkPasswordChangeIndividuallyRequired(user.getId(), IdentityZoneHolder.get().getId())).isTrue(); diff --git a/uaa/src/test/resources/invalid_configuration.yml b/uaa/src/test/resources/invalid_configuration.yml index d7364c998ad..fd35d5caa2e 100644 --- a/uaa/src/test/resources/invalid_configuration.yml +++ b/uaa/src/test/resources/invalid_configuration.yml @@ -1,6 +1,4 @@ # a simple invalid configuration -database: {} - login: url: http://localhost:8080/uaa entityBaseURL: http://localhost:8080/uaa