diff --git a/build.gradle b/build.gradle index 14b06caf377..b840210324b 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,11 @@ allprojects { mavenCentral() maven { url = uri("https://build.shibboleth.net/nexus/content/repositories/releases/") + content { + includeGroup("org.opensaml") + includeGroup("net.shibboleth") + includeGroupByRegex("net\\.shibboleth\\..*") + } } maven { url = uri("https://repository.mulesoft.org/releases/") } } @@ -79,7 +84,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 eebbbe2f9d9..65713743adc 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -12,7 +12,7 @@ versions.springBootVersion = "3.5.13" versions.guavaVersion = "33.5.0-jre" versions.seleniumVersion = "4.43.0" versions.braveVersion = "6.3.1" -versions.opensaml = "4.3.2" +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/provider/saml/OpenSaml4AuthenticationProvider.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5AuthenticationProvider.java similarity index 94% 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..f4ad246c365 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.zone.IdentityZone; @@ -34,6 +34,7 @@ import org.opensaml.core.xml.schema.XSInteger; import org.opensaml.core.xml.schema.XSString; import org.opensaml.core.xml.schema.XSURI; +import org.opensaml.saml.common.assertion.AssertionValidationException; import org.opensaml.saml.common.assertion.ValidationContext; import org.opensaml.saml.common.assertion.ValidationResult; import org.opensaml.saml.saml2.assertion.ConditionValidator; @@ -103,13 +104,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 +149,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 +165,7 @@ public OpenSaml4AuthenticationProvider() { * {@link #createDefaultResponseValidator()}, like so: * *

-     * OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();
+     * OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();
      * provider.setResponseValidator(responseToken -> {
      * 		Saml2ResponseValidatorResult result = createDefaultResponseValidator()
      * 			.convert(responseToken)
@@ -574,7 +579,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));
         };
     }
@@ -601,6 +606,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);
     }
@@ -675,54 +682,53 @@ public QName getServicedCondition() {
 
                 @Nonnull
                 @Override
-                public ValidationResult validate(Condition condition, Assertion assertion, ValidationContext context) {
+                public ValidationResult validate(Condition condition, Assertion assertion, ValidationContext context)
+                        throws AssertionValidationException {
                     // applications should validate their own OneTimeUse conditions
                     return ValidationResult.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) {
+            protected ValidationResult validateSignature(Assertion token, ValidationContext context)
+                    throws AssertionValidationException {
                 return ValidationResult.VALID;
             }
         };
 
         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
-                protected ValidationResult validateConditions(Assertion assertion, ValidationContext context) {
+                protected ValidationResult validateConditions(Assertion assertion, ValidationContext context)
+                        throws AssertionValidationException {
                     return ValidationResult.VALID;
                 }
 
                 @Nonnull
                 @Override
-                protected ValidationResult validateSubjectConfirmation(Assertion assertion, ValidationContext context) {
+                protected ValidationResult validateSubjectConfirmation(Assertion assertion, ValidationContext context)
+                        throws AssertionValidationException {
                     return ValidationResult.VALID;
                 }
 
                 @Nonnull
                 @Override
-                protected ValidationResult validateStatements(Assertion assertion, ValidationContext context) {
+                protected ValidationResult validateStatements(Assertion assertion, ValidationContext context)
+                        throws AssertionValidationException {
                     return ValidationResult.VALID;
                 }
 
                 @Override
-                protected ValidationResult validateIssuer(Assertion assertion, ValidationContext context) {
+                protected ValidationResult validateIssuer(Assertion assertion, ValidationContext context)
+                        throws AssertionValidationException {
                     return ValidationResult.VALID;
                 }
             };
diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5MetadataResolver.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5MetadataResolver.java
new file mode 100644
index 00000000000..fcf61adcf86
--- /dev/null
+++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/OpenSaml5MetadataResolver.java
@@ -0,0 +1,163 @@
+package org.cloudfoundry.identity.uaa.provider.saml;
+
+import net.shibboleth.shared.xml.SerializeSupport;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.io.Marshaller;
+import org.opensaml.core.xml.io.MarshallingException;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml.saml2.metadata.KeyDescriptor;
+import org.opensaml.saml.saml2.metadata.NameIDFormat;
+import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.saml.saml2.metadata.SingleLogoutService;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.xmlsec.signature.KeyInfo;
+import org.opensaml.xmlsec.signature.X509Certificate;
+import org.opensaml.xmlsec.signature.X509Data;
+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.Saml2MetadataResolver;
+import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
+import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
+import org.springframework.util.Assert;
+import org.w3c.dom.Element;
+
+import javax.xml.namespace.QName;
+import java.security.cert.CertificateEncodingException;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * OpenSAML 5 compatible replacement for Spring Security's {@link OpenSamlMetadataResolver}.
+ * 

+ * Spring Security's {@code OpenSamlMetadataResolver} uses the OpenSAML 4 internal class + * {@code net.shibboleth.utilities.java.support.xml.SerializeSupport} which is not present + * when using OpenSAML 5. This implementation uses the OpenSAML 5 equivalent + * {@code net.shibboleth.shared.xml.SerializeSupport} directly. + */ +public final class OpenSaml5MetadataResolver implements Saml2MetadataResolver { + + private Consumer entityDescriptorCustomizer = (parameters) -> { + }; + + public OpenSaml5MetadataResolver() { + } + + @Override + public String resolve(RelyingPartyRegistration relyingPartyRegistration) { + EntityDescriptor entityDescriptor = buildEntityDescriptor(relyingPartyRegistration); + return serialize(entityDescriptor); + } + + /** + * Set a {@link Consumer} for modifying the OpenSAML {@link EntityDescriptor}. + */ + public void setEntityDescriptorCustomizer(Consumer entityDescriptorCustomizer) { + Assert.notNull(entityDescriptorCustomizer, "entityDescriptorCustomizer cannot be null"); + this.entityDescriptorCustomizer = entityDescriptorCustomizer; + } + + private EntityDescriptor buildEntityDescriptor(RelyingPartyRegistration registration) { + EntityDescriptor entityDescriptor = build(EntityDescriptor.DEFAULT_ELEMENT_NAME); + entityDescriptor.setEntityID(registration.getEntityId()); + SPSSODescriptor spSsoDescriptor = buildSpSsoDescriptor(registration); + entityDescriptor.getRoleDescriptors(SPSSODescriptor.DEFAULT_ELEMENT_NAME).add(spSsoDescriptor); + this.entityDescriptorCustomizer.accept( + new OpenSamlMetadataResolver.EntityDescriptorParameters(entityDescriptor, registration)); + return entityDescriptor; + } + + private SPSSODescriptor buildSpSsoDescriptor(RelyingPartyRegistration registration) { + SPSSODescriptor spSsoDescriptor = build(SPSSODescriptor.DEFAULT_ELEMENT_NAME); + spSsoDescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS); + spSsoDescriptor.getKeyDescriptors() + .addAll(buildKeys(registration.getSigningX509Credentials(), UsageType.SIGNING)); + spSsoDescriptor.getKeyDescriptors() + .addAll(buildKeys(registration.getDecryptionX509Credentials(), UsageType.ENCRYPTION)); + spSsoDescriptor.getAssertionConsumerServices().add(buildAssertionConsumerService(registration)); + if (registration.getSingleLogoutServiceLocation() != null) { + for (Saml2MessageBinding binding : registration.getSingleLogoutServiceBindings()) { + spSsoDescriptor.getSingleLogoutServices().add(buildSingleLogoutService(registration, binding)); + } + } + if (registration.getNameIdFormat() != null) { + spSsoDescriptor.getNameIDFormats().add(buildNameIDFormat(registration)); + } + return spSsoDescriptor; + } + + private List buildKeys(Collection credentials, UsageType usageType) { + List list = new ArrayList<>(); + for (Saml2X509Credential credential : credentials) { + list.add(buildKeyDescriptor(usageType, credential.getCertificate())); + } + return list; + } + + private KeyDescriptor buildKeyDescriptor(UsageType usageType, java.security.cert.X509Certificate certificate) { + KeyDescriptor keyDescriptor = build(KeyDescriptor.DEFAULT_ELEMENT_NAME); + KeyInfo keyInfo = build(KeyInfo.DEFAULT_ELEMENT_NAME); + X509Certificate x509Certificate = build(X509Certificate.DEFAULT_ELEMENT_NAME); + X509Data x509Data = build(X509Data.DEFAULT_ELEMENT_NAME); + try { + x509Certificate.setValue(new String(Base64.getEncoder().encode(certificate.getEncoded()))); + } catch (CertificateEncodingException ex) { + throw new Saml2Exception("Cannot encode certificate " + certificate); + } + x509Data.getX509Certificates().add(x509Certificate); + keyInfo.getX509Datas().add(x509Data); + keyDescriptor.setUse(usageType); + keyDescriptor.setKeyInfo(keyInfo); + return keyDescriptor; + } + + private AssertionConsumerService buildAssertionConsumerService(RelyingPartyRegistration registration) { + AssertionConsumerService acs = build(AssertionConsumerService.DEFAULT_ELEMENT_NAME); + acs.setLocation(registration.getAssertionConsumerServiceLocation()); + acs.setBinding(registration.getAssertionConsumerServiceBinding().getUrn()); + acs.setIndex(1); + return acs; + } + + private SingleLogoutService buildSingleLogoutService(RelyingPartyRegistration registration, + Saml2MessageBinding binding) { + SingleLogoutService slo = build(SingleLogoutService.DEFAULT_ELEMENT_NAME); + slo.setLocation(registration.getSingleLogoutServiceLocation()); + slo.setResponseLocation(registration.getSingleLogoutServiceResponseLocation()); + slo.setBinding(binding.getUrn()); + return slo; + } + + private NameIDFormat buildNameIDFormat(RelyingPartyRegistration registration) { + NameIDFormat nameIdFormat = build(NameIDFormat.DEFAULT_ELEMENT_NAME); + nameIdFormat.setURI(registration.getNameIdFormat()); + return nameIdFormat; + } + + private String serialize(XMLObject xmlObject) { + try { + Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(xmlObject); + if (marshaller == null) { + throw new Saml2Exception("No marshaller found for " + xmlObject.getClass().getName()); + } + Element element = marshaller.marshall(xmlObject); + return SerializeSupport.prettyPrintXML(element); + } catch (MarshallingException ex) { + throw new Saml2Exception("Failed to serialize metadata", ex); + } + } + + @SuppressWarnings("unchecked") + private T build(QName elementName) { + return (T) XMLObjectProviderRegistrySupport.getBuilderFactory() + .getBuilder(elementName) + .buildObject(elementName); + } +} + 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..e851e2cddaf 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 more 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..642fd4ad049 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 more 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 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..a9ee8cb7de8 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; @@ -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(); @@ -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); 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..a143c422557 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,6 @@ 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.Saml2MetadataResolver; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; @@ -34,7 +33,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..7f96b156204 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; 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/UaaInResponseToHandlingResponseValidator.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/saml/UaaInResponseToHandlingResponseValidator.java index e1374883518..e2cba71ee16 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 @@ -39,19 +39,19 @@ * 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 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/Saml2BearerGrantAuthenticationConverterTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/Saml2BearerGrantAuthenticationConverterTest.java index 7da9cb13ae8..48faaf791f4 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; @@ -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..3491faf7f30 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; 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/TestCustomOpenSamlObjects.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/saml/TestCustomOpenSamlObjects.java index 07574b620b0..bc6bd2b6247 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); + // Do not call super - it throws UnmarshallingException in strict mode for unknown children 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() {