diff --git a/build.gradle b/build.gradle index 4f56b74f268..2114b3b544d 100644 --- a/build.gradle +++ b/build.gradle @@ -78,7 +78,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 1d8b79b63e1..925e593351a 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -11,7 +11,8 @@ versions.springBootVersion = "3.5.14" 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 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/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 72775c27dc7..008424940c0 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 @@ -18,7 +18,7 @@ import jakarta.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.util.UaaUrlUtils; @@ -106,13 +106,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(); @@ -147,9 +151,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); @@ -163,7 +167,7 @@ public OpenSaml4AuthenticationProvider() { * {@link #createDefaultResponseValidator()}, like so: * *

-     * OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();
+     * OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();
      * provider.setResponseValidator(responseToken -> {
      * 		Saml2ResponseValidatorResult result = createDefaultResponseValidator()
      * 			.convert(responseToken)
@@ -579,7 +583,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));
         };
     }
@@ -619,6 +623,8 @@ private static ValidationContext createValidationContext(AssertionToken assertio
         params.put(SAML2AssertionValidationParameters.COND_VALID_AUDIENCES, Collections.singleton(audience));
         params.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, recipientList);
         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);
     }
@@ -699,18 +705,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) {
@@ -719,7 +718,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..5043a81e7a1 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 *

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 9ffc4f5de01..5ebbfa7b6fc 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; @@ -60,7 +60,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} @@ -88,13 +88,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; @@ -119,7 +119,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); @@ -131,13 +131,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(); @@ -191,7 +191,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()); } @@ -199,7 +199,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); } } @@ -210,7 +210,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); } } @@ -221,7 +221,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); } } @@ -239,12 +239,12 @@ 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 { - throw OpenSaml4AuthenticationProvider.createAuthenticationException( + throw OpenSaml5AuthenticationProvider.createAuthenticationException( Saml2ErrorCodes.INVALID_SIGNATURE, "Assertion is missing a signature.", null @@ -252,7 +252,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); @@ -261,12 +261,12 @@ private void process(Saml2AuthenticationToken token, Assertion assertion) { if (result.hasErrors()) { Collection errors = result.getErrors(); if (log.isTraceEnabled()) { - log.trace("Found {} validation errors in SAML assertion [{}}]: {}", errors.size(), assertion.getID(), errors); + log.trace("Found {} validation errors in SAML assertion [{}]: {}", errors.size(), assertion.getID(), errors); } else if (log.isDebugEnabled()) { - log.debug("Found {} validation errors in SAML assertion [{}}]", errors.size(), assertion.getID()); + 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 6d31981453d..c713a3240ab 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; @@ -37,6 +37,7 @@ import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.security.web.csrf.CsrfLogoutHandler; import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; + import jakarta.servlet.Filter; /** @@ -52,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; @@ -92,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 + // This validator 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; @@ -148,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(); @@ -192,9 +193,9 @@ FilterRegistrationBean saml2LogoutRequestFilter(UaaRel UaaAuthenticationFailureHandler authenticationFailureHandler, CookieBasedCsrfTokenRepository loginCookieCsrfRepository) { - // This validator ignores missing signatures in the SAML2 Logout Response + // This validator ignores missing signatures in the SAML2 Logout Request Saml2LogoutRequestValidator logoutRequestValidator = new SamlLogoutRequestValidator(); - Saml2LogoutResponseResolver logoutResponseResolver = new OpenSaml4LogoutResponseResolver(relyingPartyRegistrationResolver); + Saml2LogoutResponseResolver logoutResponseResolver = new OpenSaml5LogoutResponseResolver(relyingPartyRegistrationResolver); SecurityContextLogoutHandler securityContextLogoutHandlerWithHandler = new SecurityContextLogoutHandler(); CsrfLogoutHandler csrfLogoutHandler = new CsrfLogoutHandler(loginCookieCsrfRepository); 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/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 f6af3e2cf42..e27ec35f44a 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, + 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/UaaInResponseToHandlingResponseValidator.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaInResponseToHandlingResponseValidator.java index e1374883518..1374a3e2e5d 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 @@ -16,7 +16,6 @@ package org.cloudfoundry.identity.uaa.provider.saml; -import groovy.util.logging.Slf4j; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; @@ -32,26 +31,25 @@ /** * Strategy for validating the SAML 2.0 Response used with - * {@link org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider} + * {@link 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(@NonNull OpenSaml5AuthenticationProvider.ResponseToken source) { Saml2ResponseValidatorResult result = delegate.convert(source); // if the result is successful, return it if (result == null || !result.hasErrors()) { 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 96% 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 db08c710373..d00bc48c214 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(); } @@ -672,10 +673,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(); } @@ -695,7 +696,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"); @@ -747,7 +748,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)) ); @@ -776,7 +777,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(); @@ -909,41 +910,39 @@ private AbstractSaml2AuthenticationRequest mockedStoredAuthenticationRequest(Str @Test void toOauthTokenRecipient_whenAliasPath_thenReturnsStandardTokenEndpoint() { String alias = "http://localhost:8080/uaa/oauth/token/alias/my-idp?query=value"; - assertThat(OpenSaml4AuthenticationProvider.toOauthTokenRecipient(alias)) + assertThat(OpenSaml5AuthenticationProvider.toOauthTokenRecipient(alias)) .isEqualTo("http://localhost:8080/uaa/oauth/token?query=value"); } @Test void toOauthTokenRecipient_whenAliasPathWithSubdomain_thenPreservesHost() { String alias = "http://myzone.localhost:8080/uaa/oauth/token/alias/myzone.my-idp"; - assertThat(OpenSaml4AuthenticationProvider.toOauthTokenRecipient(alias)) + assertThat(OpenSaml5AuthenticationProvider.toOauthTokenRecipient(alias)) .isEqualTo("http://myzone.localhost:8080/uaa/oauth/token"); } @Test void toOauthTokenRecipient_whenNoAliasPath_thenReturnsUnchanged() { String plain = "http://localhost:8080/uaa/oauth/token"; - assertThat(OpenSaml4AuthenticationProvider.toOauthTokenRecipient(plain)) + assertThat(OpenSaml5AuthenticationProvider.toOauthTokenRecipient(plain)) .isEqualTo(plain); } @Test void toOauthTokenRecipient_whenSamlSsoPath_thenReturnsUnchanged() { String sso = "http://localhost:8080/uaa/saml/SSO/alias/my-idp"; - assertThat(OpenSaml4AuthenticationProvider.toOauthTokenRecipient(sso)) + assertThat(OpenSaml5AuthenticationProvider.toOauthTokenRecipient(sso)) .isEqualTo(sso); } 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/Saml2BearerGrantAuthenticationConverterTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2BearerGrantAuthenticationConverterTest.java index fd74ff6c439..d8a57f3459c 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); 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..1cfe92c6b9a 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 { 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..43706428aa7 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 @@ -52,6 +52,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/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/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() {