From 9d8888c5f016a0e5498c25cc6fbdfd4b905a756d Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Fri, 19 Jul 2024 18:24:25 -0600 Subject: [PATCH] Use AssertingPartyMetadata Issue gh-15394 --- ...artyRegistrationsBeanDefinitionParser.java | 13 +- .../saml2/login/authentication-requests.adoc | 12 +- .../pages/servlet/saml2/login/overview.adoc | 10 +- .../ROOT/pages/servlet/saml2/logout.adoc | 4 +- .../ROOT/pages/servlet/saml2/metadata.adoc | 15 +- .../OpenSaml4AuthenticationProvider.java | 4 +- .../authentication/OpenSamlSigningUtils.java | 2 +- .../OpenSamlVerificationUtils.java | 5 +- .../Saml2PostAuthenticationRequest.java | 2 +- .../Saml2RedirectAuthenticationRequest.java | 2 +- .../OpenSamlLogoutRequestValidator.java | 2 +- .../OpenSamlLogoutResponseValidator.java | 2 +- .../logout/OpenSamlVerificationUtils.java | 4 +- .../logout/Saml2LogoutRequest.java | 4 +- .../logout/Saml2LogoutResponse.java | 4 +- .../metadata/OpenSamlSigningUtils.java | 4 +- ...oryRelyingPartyRegistrationRepository.java | 2 +- .../OpenSamlRelyingPartyRegistration.java | 7 +- .../RelyingPartyRegistration.java | 94 ++++++++-- ...PartyRegistrationPlaceholderResolvers.java | 2 +- ...OpenSamlAuthenticationRequestResolver.java | 8 +- .../authentication/OpenSamlSigningUtils.java | 2 +- .../OpenSamlVerificationUtils.java | 4 +- .../logout/OpenSamlLogoutRequestResolver.java | 6 +- .../OpenSamlLogoutResponseResolver.java | 7 +- .../logout/OpenSamlSigningUtils.java | 2 +- .../logout/Saml2LogoutRequestResolver.java | 2 +- .../logout/Saml2LogoutResponseResolver.java | 2 +- .../RelyingPartyRegistrationTests.java | 172 +++++++++++++++++- 29 files changed, 320 insertions(+), 79 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParser.java index 60fe23c43e..30274206b5 100644 --- a/config/src/main/java/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParser.java @@ -39,6 +39,7 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.security.converter.RsaKeyConverters; import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata; import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; @@ -153,7 +154,7 @@ public final class RelyingPartyRegistrationsBeanDefinitionParser implements Bean } private static void addVerificationCredentials(Map assertingParty, - RelyingPartyRegistration.AssertingPartyDetails.Builder builder) { + AssertingPartyMetadata.Builder builder) { List verificationCertificateLocations = (List) assertingParty.get(ELT_VERIFICATION_CREDENTIAL); List verificationCredentials = new ArrayList<>(); for (String certificateLocation : verificationCertificateLocations) { @@ -163,7 +164,7 @@ public final class RelyingPartyRegistrationsBeanDefinitionParser implements Bean } private static void addEncryptionCredentials(Map assertingParty, - RelyingPartyRegistration.AssertingPartyDetails.Builder builder) { + AssertingPartyMetadata.Builder builder) { List encryptionCertificateLocations = (List) assertingParty.get(ELT_ENCRYPTION_CREDENTIAL); List encryptionCredentials = new ArrayList<>(); for (String certificateLocation : encryptionCertificateLocations) { @@ -220,8 +221,8 @@ public final class RelyingPartyRegistrationsBeanDefinitionParser implements Bean } else { builder = RelyingPartyRegistration.withRegistrationId(registrationId) - .assertingPartyDetails((apBuilder) -> buildAssertingParty(relyingPartyRegistrationElt, assertingParties, - apBuilder, parserContext)); + .assertingPartyMetadata((apBuilder) -> buildAssertingParty(relyingPartyRegistrationElt, + assertingParties, apBuilder, parserContext)); } addRemainingProperties(relyingPartyRegistrationElt, builder); return builder; @@ -260,7 +261,7 @@ public final class RelyingPartyRegistrationsBeanDefinitionParser implements Bean } private static void buildAssertingParty(Element relyingPartyElt, Map> assertingParties, - RelyingPartyRegistration.AssertingPartyDetails.Builder builder, ParserContext parserContext) { + AssertingPartyMetadata.Builder builder, ParserContext parserContext) { String assertingPartyId = relyingPartyElt.getAttribute(ATT_ASSERTING_PARTY_ID); if (!assertingParties.containsKey(assertingPartyId)) { Object source = parserContext.extractSource(relyingPartyElt); @@ -293,7 +294,7 @@ public final class RelyingPartyRegistrationsBeanDefinitionParser implements Bean } private static void addSigningAlgorithms(Map assertingParty, - RelyingPartyRegistration.AssertingPartyDetails.Builder builder) { + AssertingPartyMetadata.Builder builder) { String signingAlgorithmsAttr = getAsString(assertingParty, ATT_SIGNING_ALGORITHMS); if (StringUtils.hasText(signingAlgorithmsAttr)) { List signingAlgorithms = Arrays.asList(signingAlgorithmsAttr.split(",")); diff --git a/docs/modules/ROOT/pages/servlet/saml2/login/authentication-requests.adoc b/docs/modules/ROOT/pages/servlet/saml2/login/authentication-requests.adoc index 6e4e49d891..34ca0d8bc7 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/login/authentication-requests.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/login/authentication-requests.adoc @@ -114,7 +114,7 @@ Java:: ---- RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta") // ... - .assertingPartyDetails(party -> party + .assertingPartyMetadata(party -> party // ... .wantAuthnRequestsSigned(false) ) @@ -128,7 +128,7 @@ Kotlin:: var relyingPartyRegistration: RelyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta") // ... - .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party + .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party // ... .wantAuthnRequestsSigned(false) } @@ -154,7 +154,7 @@ Java:: String metadataLocation = "classpath:asserting-party-metadata.xml"; RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation) // ... - .assertingPartyDetails((party) -> party + .assertingPartyMetadata((party) -> party // ... .signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512)) ) @@ -169,7 +169,7 @@ var metadataLocation = "classpath:asserting-party-metadata.xml" var relyingPartyRegistration: RelyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation) // ... - .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party + .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party // ... .signingAlgorithms { sign: MutableList -> sign.add( @@ -197,7 +197,7 @@ Java:: ---- RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta") // ... - .assertingPartyDetails(party -> party + .assertingPartyMetadata(party -> party // ... .singleSignOnServiceBinding(Saml2MessageBinding.POST) ) @@ -211,7 +211,7 @@ Kotlin:: var relyingPartyRegistration: RelyingPartyRegistration? = RelyingPartyRegistration.withRegistrationId("okta") // ... - .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party + .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party // ... .singleSignOnServiceBinding(Saml2MessageBinding.POST) } diff --git a/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc b/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc index 77c43da2dc..d86eeb2c56 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc @@ -484,7 +484,7 @@ public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exc Saml2X509Credential credential = Saml2X509Credential.verification(certificate); RelyingPartyRegistration registration = RelyingPartyRegistration .withRegistrationId("example") - .assertingPartyDetails(party -> party + .assertingPartyMetadata(party -> party .entityId("https://idp.example.com/issuer") .singleSignOnServiceLocation("https://idp.example.com/SSO.saml2") .wantAuthnRequestsSigned(false) @@ -508,7 +508,7 @@ open fun relyingPartyRegistrations(): RelyingPartyRegistrationRepository { val credential: Saml2X509Credential = Saml2X509Credential.verification(certificate) val registration = RelyingPartyRegistration .withRegistrationId("example") - .assertingPartyDetails { party: AssertingPartyDetails.Builder -> + .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party .entityId("https://idp.example.com/issuer") .singleSignOnServiceLocation("https://idp.example.com/SSO.saml2") @@ -699,7 +699,7 @@ RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.wit .entityId("{baseUrl}/{registrationId}") .decryptionX509Credentials(c -> c.add(relyingPartyDecryptingCredential())) .assertionConsumerServiceLocation("/my-login-endpoint/{registrationId}") - .assertingPartyDetails(party -> party + .assertingPartyMetadata(party -> party .entityId("https://ap.example.org") .verificationX509Credentials(c -> c.add(assertingPartyVerifyingCredential())) .singleSignOnServiceLocation("https://ap.example.org/SSO.saml2") @@ -718,7 +718,7 @@ val relyingPartyRegistration = c.add(relyingPartyDecryptingCredential()) } .assertionConsumerServiceLocation("/my-login-endpoint/{registrationId}") - .assertingPartyDetails { party -> party + .assertingPartyMetadata { party -> party .entityId("https://ap.example.org") .verificationX509Credentials { c -> c.add(assertingPartyVerifyingCredential()) } .singleSignOnServiceLocation("https://ap.example.org/SSO.saml2") @@ -730,7 +730,7 @@ val relyingPartyRegistration = [TIP] ==== The top-level metadata methods are details about the relying party. -The methods inside `assertingPartyDetails` are details about the asserting party. +The methods inside `AssertingPartyMetadata` are details about the asserting party. ==== [NOTE] diff --git a/docs/modules/ROOT/pages/servlet/saml2/logout.adoc b/docs/modules/ROOT/pages/servlet/saml2/logout.adoc index 97e155cdd3..91aeb651ae 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/logout.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/logout.adoc @@ -339,7 +339,7 @@ It's common to need to set other values in the `` than the By default, Spring Security will issue a `` and supply: -* The `Destination` attribute - from `RelyingPartyRegistration#getAssertingPartyDetails#getSingleLogoutServiceLocation` +* The `Destination` attribute - from `RelyingPartyRegistration#getAssertingPartyMetadata#getSingleLogoutServiceLocation` * The `ID` attribute - a GUID * The `` element - from `RelyingPartyRegistration#getEntityId` * The `` element - from `Authentication#getName` @@ -424,7 +424,7 @@ It's common to need to set other values in the `` than the By default, Spring Security will issue a `` and supply: -* The `Destination` attribute - from `RelyingPartyRegistration#getAssertingPartyDetails#getSingleLogoutServiceResponseLocation` +* The `Destination` attribute - from `RelyingPartyRegistration#getAssertingPartyMetadata#getSingleLogoutServiceResponseLocation` * The `ID` attribute - a GUID * The `` element - from `RelyingPartyRegistration#getEntityId` * The `` element - `SUCCESS` diff --git a/docs/modules/ROOT/pages/servlet/saml2/metadata.adoc b/docs/modules/ROOT/pages/servlet/saml2/metadata.adoc index a5e13c50e0..ac9b697013 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/metadata.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/metadata.adoc @@ -1,14 +1,14 @@ [[servlet-saml2login-metadata]] = Saml 2.0 Metadata -Spring Security can <> to produce an `AssertingPartyDetails` instance as well as <> from a `RelyingPartyRegistration` instance. +Spring Security can <> to produce an `AssertingPartyMetadata` instance as well as <> from a `RelyingPartyRegistration` instance. [[parsing-asserting-party-metadata]] == Parsing `` metadata You can parse an asserting party's metadata xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistrationrepository[using `RelyingPartyRegistrations`]. -When using the OpenSAML vendor support, the resulting `AssertingPartyDetails` will be of type `OpenSamlAssertingPartyDetails`. +When using the OpenSAML vendor support, the resulting `AssertingPartyMetadata` will be of type `OpenSamlAssertingPartyDetails`. This means you'll be able to do get the underlying OpenSAML XMLObject by doing the following: [tabs] @@ -18,7 +18,7 @@ Java:: [source,java,role="primary"] ---- OpenSamlAssertingPartyDetails details = (OpenSamlAssertingPartyDetails) - registration.getAssertingPartyDetails(); + registration.getAssertingPartyMetadata(); EntityDescriptor openSamlEntityDescriptor = details.getEntityDescriptor(); ---- @@ -27,7 +27,7 @@ Kotlin:: [source,kotlin,role="secondary"] ---- val details: OpenSamlAssertingPartyDetails = - registration.getAssertingPartyDetails() as OpenSamlAssertingPartyDetails + registration.getAssertingPartyMetadata() as OpenSamlAssertingPartyDetails val openSamlEntityDescriptor: EntityDescriptor = details.getEntityDescriptor() ---- ====== @@ -76,8 +76,7 @@ public class RefreshableRelyingPartyRegistrationRepository } private RelyingPartyRegistration applyRelyingParty(AssertingPartyMetadata metadata) { - AssertingPartyDetails details = (AssertingPartyDetails) metadata; - return RelyingPartyRegistration.withAssertingPartyDetails(details) + return RelyingPartyRegistration.withAssertingPartyMetadata(metadata) // apply any relying party configuration .build(); } @@ -110,8 +109,8 @@ class RefreshableRelyingPartyRegistrationRepository : IterableRelyingPartyRegist } private fun applyRelyingParty(metadata: AssertingPartyMetadata): RelyingPartyRegistration { - val details: AssertingPartyDetails = metadata as AssertingPartyDetails - return RelyingPartyRegistration.withAssertingPartyDetails(details) + val details: AssertingPartyMetadata = metadata as AssertingPartyMetadata + return RelyingPartyRegistration.withAssertingPartyMetadata(details) // apply any relying party configuration .build() } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java index dbb5bc464a..a2a390b1bb 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java @@ -400,7 +400,7 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv result = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, message)); } String assertingPartyEntityId = token.getRelyingPartyRegistration() - .getAssertingPartyDetails() + .getAssertingPartyMetadata() .getEntityId(); if (!StringUtils.hasText(issuer) || !issuer.equals(assertingPartyEntityId)) { String message = String.format("Invalid issuer [%s] for SAML response [%s]", issuer, response.getID()); @@ -775,7 +775,7 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv RelyingPartyRegistration relyingPartyRegistration = token.getRelyingPartyRegistration(); String audience = relyingPartyRegistration.getEntityId(); String recipient = relyingPartyRegistration.getAssertionConsumerServiceLocation(); - String assertingPartyEntityId = relyingPartyRegistration.getAssertingPartyDetails().getEntityId(); + String assertingPartyEntityId = relyingPartyRegistration.getAssertingPartyMetadata().getEntityId(); Map params = new HashMap<>(); Assertion assertion = assertionToken.getAssertion(); if (assertionContainsInResponseTo(assertion)) { diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlSigningUtils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlSigningUtils.java index 67910737c6..4861ebce7e 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlSigningUtils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlSigningUtils.java @@ -96,7 +96,7 @@ final class OpenSamlSigningUtils { private static SignatureSigningParameters resolveSigningParameters( RelyingPartyRegistration relyingPartyRegistration) { List credentials = resolveSigningCredentials(relyingPartyRegistration); - List algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms(); + List algorithms = relyingPartyRegistration.getAssertingPartyMetadata().getSigningAlgorithms(); List digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256); String canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS; SignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver(); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlVerificationUtils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlVerificationUtils.java index b581501a89..93928202a2 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlVerificationUtils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlVerificationUtils.java @@ -73,11 +73,12 @@ final class OpenSamlVerificationUtils { static SignatureTrustEngine trustEngine(RelyingPartyRegistration registration) { Set credentials = new HashSet<>(); - Collection keys = registration.getAssertingPartyDetails().getVerificationX509Credentials(); + Collection keys = registration.getAssertingPartyMetadata() + .getVerificationX509Credentials(); for (Saml2X509Credential key : keys) { BasicX509Credential cred = new BasicX509Credential(key.getCertificate()); cred.setUsageType(UsageType.SIGNING); - cred.setEntityId(registration.getAssertingPartyDetails().getEntityId()); + cred.setEntityId(registration.getAssertingPartyMetadata().getEntityId()); credentials.add(cred); } CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2PostAuthenticationRequest.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2PostAuthenticationRequest.java index dbf348ebe5..d0fb791970 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2PostAuthenticationRequest.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2PostAuthenticationRequest.java @@ -50,7 +50,7 @@ public class Saml2PostAuthenticationRequest extends AbstractSaml2AuthenticationR * @since 5.7 */ public static Builder withRelyingPartyRegistration(RelyingPartyRegistration registration) { - String location = registration.getAssertingPartyDetails().getSingleSignOnServiceLocation(); + String location = registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation(); return new Builder(registration).authenticationRequestUri(location); } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2RedirectAuthenticationRequest.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2RedirectAuthenticationRequest.java index 8dd6589962..4101801204 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2RedirectAuthenticationRequest.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2RedirectAuthenticationRequest.java @@ -73,7 +73,7 @@ public final class Saml2RedirectAuthenticationRequest extends AbstractSaml2Authe * @since 5.7 */ public static Builder withRelyingPartyRegistration(RelyingPartyRegistration registration) { - String location = registration.getAssertingPartyDetails().getSingleSignOnServiceLocation(); + String location = registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation(); return new Builder(registration).authenticationRequestUri(location); } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlLogoutRequestValidator.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlLogoutRequestValidator.java index 00875571a3..061c97d00a 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlLogoutRequestValidator.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlLogoutRequestValidator.java @@ -134,7 +134,7 @@ public final class OpenSamlLogoutRequestValidator implements Saml2LogoutRequestV return; } String issuer = request.getIssuer().getValue(); - if (!issuer.equals(registration.getAssertingPartyDetails().getEntityId())) { + if (!issuer.equals(registration.getAssertingPartyMetadata().getEntityId())) { errors .add(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, "Failed to match issuer to configured issuer")); } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlLogoutResponseValidator.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlLogoutResponseValidator.java index b718bd04e6..64041583b2 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlLogoutResponseValidator.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlLogoutResponseValidator.java @@ -132,7 +132,7 @@ public class OpenSamlLogoutResponseValidator implements Saml2LogoutResponseValid return; } String issuer = response.getIssuer().getValue(); - if (!issuer.equals(registration.getAssertingPartyDetails().getEntityId())) { + if (!issuer.equals(registration.getAssertingPartyMetadata().getEntityId())) { errors .add(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, "Failed to match issuer to configured issuer")); } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlVerificationUtils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlVerificationUtils.java index 0601b0bc6d..e0da985931 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlVerificationUtils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlVerificationUtils.java @@ -164,12 +164,12 @@ final class OpenSamlVerificationUtils { private SignatureTrustEngine trustEngine(RelyingPartyRegistration registration) { Set credentials = new HashSet<>(); - Collection keys = registration.getAssertingPartyDetails() + Collection keys = registration.getAssertingPartyMetadata() .getVerificationX509Credentials(); for (Saml2X509Credential key : keys) { BasicX509Credential cred = new BasicX509Credential(key.getCertificate()); cred.setUsageType(UsageType.SIGNING); - cred.setEntityId(registration.getAssertingPartyDetails().getEntityId()); + cred.setEntityId(registration.getAssertingPartyMetadata().getEntityId()); credentials.add(cred); } CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutRequest.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutRequest.java index b234935b11..ab51f9bbc5 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutRequest.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutRequest.java @@ -190,8 +190,8 @@ public final class Saml2LogoutRequest implements Serializable { private Builder(RelyingPartyRegistration registration) { this.registration = registration; - this.location = registration.getAssertingPartyDetails().getSingleLogoutServiceLocation(); - this.binding = registration.getAssertingPartyDetails().getSingleLogoutServiceBinding(); + this.location = registration.getAssertingPartyMetadata().getSingleLogoutServiceLocation(); + this.binding = registration.getAssertingPartyMetadata().getSingleLogoutServiceBinding(); } /** diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutResponse.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutResponse.java index a555b784a2..ebc3af8956 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutResponse.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutResponse.java @@ -156,8 +156,8 @@ public final class Saml2LogoutResponse { private Function, String> encoder = DEFAULT_ENCODER; private Builder(RelyingPartyRegistration registration) { - this.location = registration.getAssertingPartyDetails().getSingleLogoutServiceResponseLocation(); - this.binding = registration.getAssertingPartyDetails().getSingleLogoutServiceBinding(); + this.location = registration.getAssertingPartyMetadata().getSingleLogoutServiceResponseLocation(); + this.binding = registration.getAssertingPartyMetadata().getSingleLogoutServiceBinding(); } /** diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlSigningUtils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlSigningUtils.java index 1fadf48146..ae1fcb63fe 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlSigningUtils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlSigningUtils.java @@ -80,7 +80,7 @@ final class OpenSamlSigningUtils { } static O sign(O object, RelyingPartyRegistration relyingPartyRegistration) { - List algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms(); + List algorithms = relyingPartyRegistration.getAssertingPartyMetadata().getSigningAlgorithms(); List credentials = resolveSigningCredentials(relyingPartyRegistration); return sign(object, algorithms, credentials); } @@ -103,7 +103,7 @@ final class OpenSamlSigningUtils { private static SignatureSigningParameters resolveSigningParameters( RelyingPartyRegistration relyingPartyRegistration) { List credentials = resolveSigningCredentials(relyingPartyRegistration); - List algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms(); + List algorithms = relyingPartyRegistration.getAssertingPartyMetadata().getSigningAlgorithms(); return resolveSigningParameters(algorithms, credentials); } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/InMemoryRelyingPartyRegistrationRepository.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/InMemoryRelyingPartyRegistrationRepository.java index 01f4778027..7994843c25 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/InMemoryRelyingPartyRegistrationRepository.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/InMemoryRelyingPartyRegistrationRepository.java @@ -69,7 +69,7 @@ public class InMemoryRelyingPartyRegistrationRepository implements IterableRelyi Collection rps) { MultiValueMap result = new LinkedMultiValueMap<>(); for (RelyingPartyRegistration rp : rps) { - result.add(rp.getAssertingPartyDetails().getEntityId(), rp); + result.add(rp.getAssertingPartyMetadata().getEntityId(), rp); } return Collections.unmodifiableMap(result); } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistration.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistration.java index 9b3f26dc3a..8f9d585254 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistration.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistration.java @@ -36,7 +36,7 @@ import org.springframework.security.saml2.core.Saml2X509Credential; * EntityDescriptor descriptor = openSamlRegistration.getAssertingPartyDetails.getEntityDescriptor(); * } * do instead:
- * 	if (registration.getAssertingPartyDetails() instanceof openSamlAssertingPartyDetails) {
+ * 	if (registration.getAssertingPartyMetadata() instanceof openSamlAssertingPartyDetails) {
  * 	    EntityDescriptor descriptor = openSamlAssertingPartyDetails.getEntityDescriptor();
  * 	}
  * 
@@ -170,6 +170,11 @@ public final class OpenSamlRelyingPartyRegistration extends RelyingPartyRegistra return (Builder) super.assertingPartyDetails(assertingPartyDetails); } + @Override + public Builder assertingPartyMetadata(Consumer> assertingPartyMetadata) { + return (Builder) super.assertingPartyMetadata(assertingPartyMetadata); + } + /** * Build an {@link OpenSamlRelyingPartyRegistration} * {@link org.springframework.security.saml2.provider.service.registration.OpenSamlRelyingPartyRegistration} diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java index 56f1c9e7c1..e18c9499bc 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java @@ -88,7 +88,7 @@ public class RelyingPartyRegistration { private final boolean authnRequestsSigned; - private final AssertingPartyDetails assertingPartyDetails; + private final AssertingPartyMetadata assertingPartyMetadata; private final Collection decryptionX509Credentials; @@ -127,7 +127,45 @@ public class RelyingPartyRegistration { this.singleLogoutServiceBindings = Collections.unmodifiableList(new LinkedList<>(singleLogoutServiceBindings)); this.nameIdFormat = nameIdFormat; this.authnRequestsSigned = authnRequestsSigned; - this.assertingPartyDetails = assertingPartyDetails; + this.assertingPartyMetadata = assertingPartyDetails; + this.decryptionX509Credentials = Collections.unmodifiableList(new LinkedList<>(decryptionX509Credentials)); + this.signingX509Credentials = Collections.unmodifiableList(new LinkedList<>(signingX509Credentials)); + } + + private RelyingPartyRegistration(String registrationId, String entityId, String assertionConsumerServiceLocation, + Saml2MessageBinding assertionConsumerServiceBinding, String singleLogoutServiceLocation, + String singleLogoutServiceResponseLocation, Collection singleLogoutServiceBindings, + AssertingPartyMetadata assertingPartyMetadata, String nameIdFormat, boolean authnRequestsSigned, + Collection decryptionX509Credentials, + Collection signingX509Credentials) { + Assert.hasText(registrationId, "registrationId cannot be empty"); + Assert.hasText(entityId, "entityId cannot be empty"); + Assert.hasText(assertionConsumerServiceLocation, "assertionConsumerServiceLocation cannot be empty"); + Assert.notNull(assertionConsumerServiceBinding, "assertionConsumerServiceBinding cannot be null"); + Assert.isTrue(singleLogoutServiceLocation == null || !CollectionUtils.isEmpty(singleLogoutServiceBindings), + "singleLogoutServiceBindings cannot be null or empty when singleLogoutServiceLocation is set"); + Assert.notNull(assertingPartyMetadata, "assertingPartyDetails cannot be null"); + Assert.notNull(decryptionX509Credentials, "decryptionX509Credentials cannot be null"); + for (Saml2X509Credential c : decryptionX509Credentials) { + Assert.notNull(c, "decryptionX509Credentials cannot contain null elements"); + Assert.isTrue(c.isDecryptionCredential(), + "All decryptionX509Credentials must have a usage of DECRYPTION set"); + } + Assert.notNull(signingX509Credentials, "signingX509Credentials cannot be null"); + for (Saml2X509Credential c : signingX509Credentials) { + Assert.notNull(c, "signingX509Credentials cannot contain null elements"); + Assert.isTrue(c.isSigningCredential(), "All signingX509Credentials must have a usage of SIGNING set"); + } + this.registrationId = registrationId; + this.entityId = entityId; + this.assertionConsumerServiceLocation = assertionConsumerServiceLocation; + this.assertionConsumerServiceBinding = assertionConsumerServiceBinding; + this.singleLogoutServiceLocation = singleLogoutServiceLocation; + this.singleLogoutServiceResponseLocation = singleLogoutServiceResponseLocation; + this.singleLogoutServiceBindings = Collections.unmodifiableList(new LinkedList<>(singleLogoutServiceBindings)); + this.nameIdFormat = nameIdFormat; + this.authnRequestsSigned = authnRequestsSigned; + this.assertingPartyMetadata = assertingPartyMetadata; this.decryptionX509Credentials = Collections.unmodifiableList(new LinkedList<>(decryptionX509Credentials)); this.signingX509Credentials = Collections.unmodifiableList(new LinkedList<>(signingX509Credentials)); } @@ -139,7 +177,7 @@ public class RelyingPartyRegistration { * @since 6.1 */ public Builder mutate() { - return new Builder(this.registrationId, this.assertingPartyDetails.mutate()).entityId(this.entityId) + return new Builder(this.registrationId, this.assertingPartyMetadata.mutate()).entityId(this.entityId) .signingX509Credentials((c) -> c.addAll(this.signingX509Credentials)) .decryptionX509Credentials((c) -> c.addAll(this.decryptionX509Credentials)) .assertionConsumerServiceLocation(this.assertionConsumerServiceLocation) @@ -317,9 +355,22 @@ public class RelyingPartyRegistration { * Get the configuration details for the Asserting Party * @return the {@link AssertingPartyDetails} * @since 5.4 + * @deprecated Use {@link #getAssertingPartyMetadata()} instead */ + @Deprecated public AssertingPartyDetails getAssertingPartyDetails() { - return this.assertingPartyDetails; + Assert.isInstanceOf(AssertingPartyDetails.class, this.assertingPartyMetadata, + "This class was initialized with an AssertingPartyMetadata, please call #getAssertingPartyMetadata instead"); + return (AssertingPartyDetails) this.assertingPartyMetadata; + } + + /** + * Get the metadata for the Asserting Party + * @return the {@link AssertingPartyDetails} + * @since 6.4 + */ + public AssertingPartyMetadata getAssertingPartyMetadata() { + return this.assertingPartyMetadata; } /** @@ -333,6 +384,12 @@ public class RelyingPartyRegistration { return new Builder(registrationId, new AssertingPartyDetails.Builder()); } + /** + * @param assertingPartyDetails the asserting party metadata + * @return {@code Builder} to create a {@code RelyingPartyRegistration} object + * @deprecated Use {@link #withAssertingPartyMetadata} instead + */ + @Deprecated(forRemoval = true, since = "6.4") public static Builder withAssertingPartyDetails(AssertingPartyDetails assertingPartyDetails) { Assert.notNull(assertingPartyDetails, "assertingPartyDetails cannot be null"); return new Builder(assertingPartyDetails.getEntityId(), assertingPartyDetails.mutate()); @@ -353,8 +410,8 @@ public class RelyingPartyRegistration { * @since 6.4 */ public static Builder withAssertingPartyMetadata(AssertingPartyMetadata metadata) { - Assert.isInstanceOf(AssertingPartyDetails.class, metadata, "metadata must be of type AssertingPartyDetails"); - return withAssertingPartyDetails((AssertingPartyDetails) metadata); + Assert.notNull(metadata, "assertingPartyMetadata cannot be null"); + return new Builder(metadata.getEntityId(), metadata.mutate()); } /** @@ -819,11 +876,11 @@ public class RelyingPartyRegistration { private boolean authnRequestsSigned = false; - private AssertingPartyDetails.Builder assertingPartyDetailsBuilder; + private AssertingPartyMetadata.Builder assertingPartyMetadataBuilder; - protected Builder(String registrationId, AssertingPartyDetails.Builder assertingPartyDetailsBuilder) { + protected Builder(String registrationId, AssertingPartyMetadata.Builder assertingPartyMetadataBuilder) { this.registrationId = registrationId; - this.assertingPartyDetailsBuilder = assertingPartyDetailsBuilder; + this.assertingPartyMetadataBuilder = assertingPartyMetadataBuilder; } /** @@ -1028,9 +1085,24 @@ public class RelyingPartyRegistration { * @param assertingPartyDetails The {@link Consumer} to apply * @return the {@link Builder} for further configuration * @since 5.4 + * @deprecated Use {@link #assertingPartyMetadata} instead */ + @Deprecated(forRemoval = true, since = "6.4") public Builder assertingPartyDetails(Consumer assertingPartyDetails) { - assertingPartyDetails.accept(this.assertingPartyDetailsBuilder); + Assert.isInstanceOf(AssertingPartyDetails.Builder.class, this.assertingPartyMetadataBuilder, + "This class was constructed with an AssertingPartyMetadata instance, as such, please use #assertingPartyMetadata"); + assertingPartyDetails.accept((AssertingPartyDetails.Builder) this.assertingPartyMetadataBuilder); + return this; + } + + /** + * Apply this {@link Consumer} to further configure the Asserting Party metadata + * @param assertingPartyMetadata The {@link Consumer} to apply + * @return the {@link Builder} for further configuration + * @since 6.4 + */ + public Builder assertingPartyMetadata(Consumer> assertingPartyMetadata) { + assertingPartyMetadata.accept(this.assertingPartyMetadataBuilder); return this; } @@ -1048,7 +1120,7 @@ public class RelyingPartyRegistration { this.singleLogoutServiceBindings.add(Saml2MessageBinding.POST); } - AssertingPartyDetails party = this.assertingPartyDetailsBuilder.build(); + AssertingPartyMetadata party = this.assertingPartyMetadataBuilder.build(); return new RelyingPartyRegistration(this.registrationId, this.entityId, this.assertionConsumerServiceLocation, this.assertionConsumerServiceBinding, this.singleLogoutServiceLocation, this.singleLogoutServiceResponseLocation, diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/RelyingPartyRegistrationPlaceholderResolvers.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/RelyingPartyRegistrationPlaceholderResolvers.java index 1161a029b7..4a39f151cd 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/RelyingPartyRegistrationPlaceholderResolvers.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/RelyingPartyRegistrationPlaceholderResolvers.java @@ -68,7 +68,7 @@ public final class RelyingPartyRegistrationPlaceholderResolvers { */ public static UriResolver uriResolver(HttpServletRequest request, RelyingPartyRegistration registration) { String relyingPartyEntityId = registration.getEntityId(); - String assertingPartyEntityId = registration.getAssertingPartyDetails().getEntityId(); + String assertingPartyEntityId = registration.getAssertingPartyMetadata().getEntityId(); String registrationId = registration.getRegistrationId(); Map uriVariables = uriVariables(request); uriVariables.put("relyingPartyEntityId", StringUtils.hasText(relyingPartyEntityId) ? relyingPartyEntityId : ""); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlAuthenticationRequestResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlAuthenticationRequestResolver.java index 85ed7ae877..2ec777a70e 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlAuthenticationRequestResolver.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlAuthenticationRequestResolver.java @@ -146,7 +146,7 @@ class OpenSamlAuthenticationRequestResolver { Issuer iss = this.issuerBuilder.buildObject(); iss.setValue(entityId); authnRequest.setIssuer(iss); - authnRequest.setDestination(registration.getAssertingPartyDetails().getSingleSignOnServiceLocation()); + authnRequest.setDestination(registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation()); authnRequest.setAssertionConsumerServiceURL(assertionConsumerServiceLocation); if (registration.getNameIdFormat() != null) { NameIDPolicy nameIdPolicy = this.nameIdPolicyBuilder.buildObject(); @@ -158,9 +158,9 @@ class OpenSamlAuthenticationRequestResolver { authnRequest.setID("ARQ" + UUID.randomUUID().toString().substring(1)); } String relayState = this.relayStateResolver.convert(request); - Saml2MessageBinding binding = registration.getAssertingPartyDetails().getSingleSignOnServiceBinding(); + Saml2MessageBinding binding = registration.getAssertingPartyMetadata().getSingleSignOnServiceBinding(); if (binding == Saml2MessageBinding.POST) { - if (registration.getAssertingPartyDetails().getWantAuthnRequestsSigned() + if (registration.getAssertingPartyMetadata().getWantAuthnRequestsSigned() || registration.isAuthnRequestsSigned()) { OpenSamlSigningUtils.sign(authnRequest, registration); } @@ -180,7 +180,7 @@ class OpenSamlAuthenticationRequestResolver { .samlRequest(deflatedAndEncoded) .relayState(relayState) .id(authnRequest.getID()); - if (registration.getAssertingPartyDetails().getWantAuthnRequestsSigned() + if (registration.getAssertingPartyMetadata().getWantAuthnRequestsSigned() || registration.isAuthnRequestsSigned()) { OpenSamlSigningUtils.QueryParametersPartial parametersPartial = OpenSamlSigningUtils.sign(registration) .param(Saml2ParameterNames.SAML_REQUEST, deflatedAndEncoded); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlSigningUtils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlSigningUtils.java index df9d861065..c479b06cf5 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlSigningUtils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlSigningUtils.java @@ -95,7 +95,7 @@ final class OpenSamlSigningUtils { private static SignatureSigningParameters resolveSigningParameters( RelyingPartyRegistration relyingPartyRegistration) { List credentials = resolveSigningCredentials(relyingPartyRegistration); - List algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms(); + List algorithms = relyingPartyRegistration.getAssertingPartyMetadata().getSigningAlgorithms(); List digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256); String canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS; SignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver(); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlVerificationUtils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlVerificationUtils.java index 137aff26d8..7d52867e4d 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlVerificationUtils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlVerificationUtils.java @@ -155,12 +155,12 @@ final class OpenSamlVerificationUtils { private SignatureTrustEngine trustEngine(RelyingPartyRegistration registration) { Set credentials = new HashSet<>(); - Collection keys = registration.getAssertingPartyDetails() + Collection keys = registration.getAssertingPartyMetadata() .getVerificationX509Credentials(); for (Saml2X509Credential key : keys) { BasicX509Credential cred = new BasicX509Credential(key.getCertificate()); cred.setUsageType(UsageType.SIGNING); - cred.setEntityId(registration.getAssertingPartyDetails().getEntityId()); + cred.setEntityId(registration.getAssertingPartyMetadata().getEntityId()); credentials.add(cred); } CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutRequestResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutRequestResolver.java index 69d9ccc4a1..937233146e 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutRequestResolver.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutRequestResolver.java @@ -126,13 +126,13 @@ final class OpenSamlLogoutRequestResolver { if (registration == null) { return null; } - if (registration.getAssertingPartyDetails().getSingleLogoutServiceLocation() == null) { + if (registration.getAssertingPartyMetadata().getSingleLogoutServiceLocation() == null) { return null; } UriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration); String entityId = uriResolver.resolve(registration.getEntityId()); LogoutRequest logoutRequest = this.logoutRequestBuilder.buildObject(); - logoutRequest.setDestination(registration.getAssertingPartyDetails().getSingleLogoutServiceLocation()); + logoutRequest.setDestination(registration.getAssertingPartyMetadata().getSingleLogoutServiceLocation()); Issuer issuer = this.issuerBuilder.buildObject(); issuer.setValue(entityId); logoutRequest.setIssuer(issuer); @@ -154,7 +154,7 @@ final class OpenSamlLogoutRequestResolver { String relayState = this.relayStateResolver.convert(request); Saml2LogoutRequest.Builder result = Saml2LogoutRequest.withRelyingPartyRegistration(registration) .id(logoutRequest.getID()); - if (registration.getAssertingPartyDetails().getSingleLogoutServiceBinding() == Saml2MessageBinding.POST) { + if (registration.getAssertingPartyMetadata().getSingleLogoutServiceBinding() == Saml2MessageBinding.POST) { String xml = serialize(OpenSamlSigningUtils.sign(logoutRequest, registration)); String samlRequest = Saml2Utils.samlEncode(xml.getBytes(StandardCharsets.UTF_8)); return result.samlRequest(samlRequest).relayState(relayState).build(); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutResponseResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutResponseResolver.java index b726eb6d32..531bcad602 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutResponseResolver.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutResponseResolver.java @@ -143,13 +143,14 @@ final class OpenSamlLogoutResponseResolver { if (registration == null) { return null; } - if (registration.getAssertingPartyDetails().getSingleLogoutServiceResponseLocation() == null) { + if (registration.getAssertingPartyMetadata().getSingleLogoutServiceResponseLocation() == null) { return null; } UriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration); String entityId = uriResolver.resolve(registration.getEntityId()); LogoutResponse logoutResponse = this.logoutResponseBuilder.buildObject(); - logoutResponse.setDestination(registration.getAssertingPartyDetails().getSingleLogoutServiceResponseLocation()); + logoutResponse + .setDestination(registration.getAssertingPartyMetadata().getSingleLogoutServiceResponseLocation()); Issuer issuer = this.issuerBuilder.buildObject(); issuer.setValue(entityId); logoutResponse.setIssuer(issuer); @@ -164,7 +165,7 @@ final class OpenSamlLogoutResponseResolver { } logoutResponseConsumer.accept(registration, logoutResponse); Saml2LogoutResponse.Builder result = Saml2LogoutResponse.withRelyingPartyRegistration(registration); - if (registration.getAssertingPartyDetails().getSingleLogoutServiceBinding() == Saml2MessageBinding.POST) { + if (registration.getAssertingPartyMetadata().getSingleLogoutServiceBinding() == Saml2MessageBinding.POST) { String xml = serialize(OpenSamlSigningUtils.sign(logoutResponse, registration)); String samlResponse = Saml2Utils.samlEncode(xml.getBytes(StandardCharsets.UTF_8)); result.samlResponse(samlResponse); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlSigningUtils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlSigningUtils.java index f30c00cf0e..0b7ef324dd 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlSigningUtils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlSigningUtils.java @@ -96,7 +96,7 @@ final class OpenSamlSigningUtils { private static SignatureSigningParameters resolveSigningParameters( RelyingPartyRegistration relyingPartyRegistration) { List credentials = resolveSigningCredentials(relyingPartyRegistration); - List algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms(); + List algorithms = relyingPartyRegistration.getAssertingPartyMetadata().getSigningAlgorithms(); List digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256); String canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS; SignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver(); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestResolver.java index c0a8d850fa..49f7b3e7cd 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestResolver.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestResolver.java @@ -28,7 +28,7 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP * * The returned logout request is suitable for sending to the asserting party based on, * for example, the location and binding specified in - * {@link RelyingPartyRegistration#getAssertingPartyDetails()}. + * {@link RelyingPartyRegistration#getAssertingPartyMetadata()}. * * @author Josh Cummings * @since 5.6 diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.java index 84cf038af9..f722fd55b7 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.java @@ -28,7 +28,7 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP * * The returned logout response is suitable for sending to the asserting party based on, * for example, the location and binding specified in - * {@link RelyingPartyRegistration#getAssertingPartyDetails()}. + * {@link RelyingPartyRegistration#getAssertingPartyMetadata()}. * * @author Josh Cummings * @since 5.6 diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java index 04c6f79f1a..b5512e10c9 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java @@ -16,13 +16,19 @@ package org.springframework.security.saml2.provider.service.registration; +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; + import org.junit.jupiter.api.Test; import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.core.TestSaml2X509Credentials; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.AssertingPartyDetails; import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class RelyingPartyRegistrationTests { @@ -167,16 +173,16 @@ public class RelyingPartyRegistrationTests { } @Test - void withAssertingPartyMetadataWhenDetailsThenBuilderCopies() { + void withAssertingPartyMetadataWhenMetadataThenBuilderCopies() { RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration() .nameIdFormat("format") - .assertingPartyDetails((a) -> a.singleSignOnServiceBinding(Saml2MessageBinding.POST)) - .assertingPartyDetails((a) -> a.wantAuthnRequestsSigned(false)) - .assertingPartyDetails((a) -> a.signingAlgorithms((algs) -> algs.add("alg"))) + .assertingPartyMetadata((a) -> a.singleSignOnServiceBinding(Saml2MessageBinding.POST)) + .assertingPartyMetadata((a) -> a.wantAuthnRequestsSigned(false)) + .assertingPartyMetadata((a) -> a.signingAlgorithms((algs) -> algs.add("alg"))) .assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT) .build(); RelyingPartyRegistration copied = RelyingPartyRegistration - .withAssertingPartyMetadata(registration.getAssertingPartyDetails()) + .withAssertingPartyMetadata(registration.getAssertingPartyMetadata()) .registrationId(registration.getRegistrationId()) .entityId(registration.getEntityId()) .signingX509Credentials((c) -> c.addAll(registration.getSigningX509Credentials())) @@ -192,4 +198,160 @@ public class RelyingPartyRegistrationTests { compareRegistrations(registration, copied); } + @Test + void withAssertingPartyMetadataWhenMetadataThenDisallowsDetails() { + AssertingPartyMetadata metadata = new CustomAssertingPartyMetadata(); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> RelyingPartyRegistration.withAssertingPartyMetadata(metadata) + .assertingPartyDetails((a) -> a.entityId("entity-id")) + .build()); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( + () -> RelyingPartyRegistration.withAssertingPartyMetadata(metadata).build().getAssertingPartyDetails()); + } + + @Test + void withAssertingPartyMetadataWhenDetailsThenBuilderCopies() { + RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration() + .nameIdFormat("format") + .assertingPartyMetadata((a) -> a.singleSignOnServiceBinding(Saml2MessageBinding.POST)) + .assertingPartyMetadata((a) -> a.wantAuthnRequestsSigned(false)) + .assertingPartyMetadata((a) -> a.signingAlgorithms((algs) -> algs.add("alg"))) + .assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT) + .build(); + AssertingPartyDetails details = registration.getAssertingPartyDetails(); + RelyingPartyRegistration copied = RelyingPartyRegistration.withAssertingPartyDetails(details) + .assertingPartyDetails((a) -> a.entityId(details.getEntityId())) + .registrationId(registration.getRegistrationId()) + .entityId(registration.getEntityId()) + .signingX509Credentials((c) -> c.addAll(registration.getSigningX509Credentials())) + .decryptionX509Credentials((c) -> c.addAll(registration.getDecryptionX509Credentials())) + .assertionConsumerServiceLocation(registration.getAssertionConsumerServiceLocation()) + .assertionConsumerServiceBinding(registration.getAssertionConsumerServiceBinding()) + .singleLogoutServiceLocation(registration.getSingleLogoutServiceLocation()) + .singleLogoutServiceResponseLocation(registration.getSingleLogoutServiceResponseLocation()) + .singleLogoutServiceBindings((c) -> c.addAll(registration.getSingleLogoutServiceBindings())) + .nameIdFormat(registration.getNameIdFormat()) + .authnRequestsSigned(registration.isAuthnRequestsSigned()) + .build(); + compareRegistrations(registration, copied); + } + + private static class CustomAssertingPartyMetadata implements AssertingPartyMetadata { + + @Override + public String getEntityId() { + return ""; + } + + @Override + public boolean getWantAuthnRequestsSigned() { + return false; + } + + @Override + public List getSigningAlgorithms() { + return List.of(); + } + + @Override + public Collection getVerificationX509Credentials() { + return List.of(); + } + + @Override + public Collection getEncryptionX509Credentials() { + return List.of(); + } + + @Override + public String getSingleSignOnServiceLocation() { + return ""; + } + + @Override + public Saml2MessageBinding getSingleSignOnServiceBinding() { + return null; + } + + @Override + public String getSingleLogoutServiceLocation() { + return ""; + } + + @Override + public String getSingleLogoutServiceResponseLocation() { + return ""; + } + + @Override + public Saml2MessageBinding getSingleLogoutServiceBinding() { + return null; + } + + @Override + public Builder mutate() { + return new Builder(); + } + + private static class Builder implements AssertingPartyMetadata.Builder { + + @Override + public Builder entityId(String entityId) { + return this; + } + + @Override + public Builder wantAuthnRequestsSigned(boolean wantAuthnRequestsSigned) { + return this; + } + + @Override + public Builder signingAlgorithms(Consumer> signingMethodAlgorithmsConsumer) { + return this; + } + + @Override + public Builder verificationX509Credentials(Consumer> credentialsConsumer) { + return this; + } + + @Override + public Builder encryptionX509Credentials(Consumer> credentialsConsumer) { + return this; + } + + @Override + public Builder singleSignOnServiceLocation(String singleSignOnServiceLocation) { + return this; + } + + @Override + public Builder singleSignOnServiceBinding(Saml2MessageBinding singleSignOnServiceBinding) { + return this; + } + + @Override + public Builder singleLogoutServiceLocation(String singleLogoutServiceLocation) { + return this; + } + + @Override + public Builder singleLogoutServiceResponseLocation(String singleLogoutServiceResponseLocation) { + return this; + } + + @Override + public Builder singleLogoutServiceBinding(Saml2MessageBinding singleLogoutServiceBinding) { + return this; + } + + @Override + public AssertingPartyMetadata build() { + return new CustomAssertingPartyMetadata(); + } + + } + + } + }