From aa3135169de5aeff182d11a5ee750b0f179f84d1 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Mon, 9 Jun 2025 16:48:43 -0600 Subject: [PATCH] Polish Documentation Closes gh-14635 --- .../Saml2LogoutBeanDefinitionParserTests.java | 1 + docs/modules/ROOT/nav.adoc | 1 + .../ROOT/pages/migration/servlet/saml2.adoc | 56 +++++++++++++++++++ .../logout/Saml2LogoutResponseResolver.java | 4 +- .../OpenSaml4LogoutResponseResolverTests.java | 2 +- .../logout/Saml2LogoutRequestFilterTests.java | 2 +- 6 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 docs/modules/ROOT/pages/migration/servlet/saml2.adoc diff --git a/config/src/test/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests.java index 5998c31314..4045c1b689 100644 --- a/config/src/test/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests.java @@ -287,6 +287,7 @@ public class Saml2LogoutBeanDefinitionParserTests { .andExpect(status().isBadRequest()); } + // gh-14635 @Test public void saml2LogoutRequestWhenInvalidSamlRequestThen302Redirect() throws Exception { this.spring.configLocations(this.xml("Default")).autowire(); diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 595e9a4531..24ca536e13 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -6,6 +6,7 @@ * xref:migration/index.adoc[Migrating to 7] ** xref:migration/servlet/index.adoc[Servlet] *** xref:migration/servlet/oauth2.adoc[OAuth 2.0] +*** xref:migration/servlet/saml2.adoc[SAML 2.0] ** xref:migration/reactive.adoc[Reactive] * xref:getting-spring-security.adoc[Getting Spring Security] * xref:attachment$api/java/index.html[Javadoc] diff --git a/docs/modules/ROOT/pages/migration/servlet/saml2.adoc b/docs/modules/ROOT/pages/migration/servlet/saml2.adoc new file mode 100644 index 0000000000..c87597aafb --- /dev/null +++ b/docs/modules/ROOT/pages/migration/servlet/saml2.adoc @@ -0,0 +1,56 @@ += SAML 2.0 Migrations + +== Expect `` When `` Validation Fails + +SAML identity providers expect service providers to return an error `` if it fails to process the ``. + +Past versions of Spring Security returned a 401 in some cases, breaking the chain of logout requests and responses from each relying party. + +In Spring Security 7, this behavior is repaired, and you need do nothing. + +However, if this gives you trouble, you can revert back to the old behavior by publishing a `Saml2LogoutRequestResolver` that returns `null` when an error `` is needed. +You can create a delegate like this one: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Bean +Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) { + OpenSaml5LogoutResponseResolver delegate = new OpenSaml5LogoutResponseResolver(registrations); + return new Saml2LogoutResponseResolver() { + @Override + public void resolve(HttpServletRequest request, Authentication authentication) { + delegate.resolve(request, authentication); + } + + @Override + public void resolve(HttpServletRequest request, Authentication authentication, Saml2AuthenticationException error) { + return null; + } + }; +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Bean +fun logoutResponseResolver(registrations: RelyingPartyRegistrationRepository?): Saml2LogoutResponseResolver { + val delegate = OpenSaml5LogoutResponseResolver(registrations) + return object : Saml2LogoutResponseResolver() { + override fun resolve(request: HttpServletRequest?, authentication: Authentication?) { + delegate.resolve(request, authentication) + } + + override fun resolve(request: HttpServletRequest?, authentication: Authentication?, error: Saml2AuthenticationException?) { + return null + } + } +} +---- +====== + 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 bebd44579a..4e6e30420c 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 @@ -51,7 +51,9 @@ public interface Saml2LogoutResponseResolver { * @param authentication the current user * @param authenticationException the thrown exception when the logout request was * processed - * @return a signed and serialized SAML 2.0 Logout Response + * @return a signed and serialized SAML 2.0 Logout Response, or {@code null} if it + * cannot generate a SAML 2.0 Error Logout Response + * @since 7.0 */ default Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication, Saml2AuthenticationException authenticationException) { diff --git a/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java b/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java index b363a06c6a..9d51f2622f 100644 --- a/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java +++ b/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java @@ -65,7 +65,7 @@ public class OpenSaml4LogoutResponseResolverTests { logoutResponseResolver.setParametersConsumer(parametersConsumer); MockHttpServletRequest request = new MockHttpServletRequest(); RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration() - .assertingPartyMetadata( + .assertingPartyDetails( (party) -> party.singleLogoutServiceResponseLocation("https://ap.example.com/logout")) .build(); Authentication authentication = new TestingAuthenticationToken("user", "password"); diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilterTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilterTests.java index f03c143ca0..cd4d88f62a 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilterTests.java +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilterTests.java @@ -99,7 +99,7 @@ public class Saml2LogoutRequestFilterTests { @Test public void doFilterWhenSamlRequestThenPosts() throws Exception { RelyingPartyRegistration registration = TestRelyingPartyRegistrations.full() - .assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST)) + .assertingPartyDetails((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST)) .build(); Authentication authentication = new TestingAuthenticationToken("user", "password"); given(this.securityContextHolderStrategy.getContext()).willReturn(new SecurityContextImpl(authentication));