From b51c71c3b3b8797bd42ea9f92a64384fe758f2b8 Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Fri, 20 May 2022 17:45:38 -0600 Subject: [PATCH] Use original query string to verify signature Closes gh-11235 --- .../saml2/Saml2LogoutConfigurerTests.java | 53 +++++++++++++++- .../jackson2/Saml2LogoutRequestMixin.java | 5 ++ .../logout/OpenSamlVerificationUtils.java | 28 +++------ .../logout/Saml2LogoutRequest.java | 60 +++++++++++++++++-- .../logout/Saml2LogoutResponse.java | 58 ++++++++++++++++-- .../logout/Saml2LogoutRequestFilter.java | 22 ++----- .../logout/Saml2LogoutResponseFilter.java | 4 +- ...ingPartyInitiatedLogoutSuccessHandler.java | 21 +------ 8 files changed, 181 insertions(+), 70 deletions(-) diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java index dc85f6216f..33a2207386 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java @@ -22,6 +22,9 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.function.Consumer; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -267,6 +270,33 @@ public class Saml2LogoutConfigurerTests { verify(getBean(LogoutHandler.class)).logout(any(), any(), any()); } + // gh-11235 + @Test + public void saml2LogoutRequestWhenLowercaseEncodingThenLogsOutAndSendsLogoutResponse() throws Exception { + this.spring.register(Saml2LogoutDefaultsConfig.class).autowire(); + String apLogoutRequest = "nZFNa4QwEIb/iuQeP6K7dYO6FKQg2B622x56G3WwgiY2E8v239fqCksPPfSWIXmfNw+THC9D73yi\r\n" + + "oU6rlAWuzxxUtW461abs5fzAY3bMEoKhF6Msdasne8KPCck6c1KRXK9SNhklNVBHUsGAJG0tn+8f\r\n" + + "SylcX45GW13rnjn5HOwU2KXt3dqRpOeZ0cULDGOPrjat1y8t3gL2zFrGnCJPWXkKcR8KCHY8xmrP\r\n" + + "Iz868OpOVLwO4wohggagmd8STVgosqBsyoQvBPd3XITnIJaRL8PYjcThjTmvm/f8SXa1lEvY3Nr9\r\n" + + "LQdEaH6EWAYjR2U7+8W7JvFucRv8aY4X+b/g03zaoCsmu46/FpN9Aw=="; + String apLogoutRequestRelayState = "d118dbd5-3853-4268-b3e5-c40fc033fa2f"; + String apLogoutRequestSignature = "VZ7rWa5u3hIX60fAQs/gBQZWDP2BAIlCMMrNrTHafoKKj0uXWnuITYLuL8NdsWmyQN0+fqWW4X05+BqiLpL80jHLmQR5RVqqL1EtVv1SpPUna938lgz2sOliuYmfQNj4Bmd+Z5G1K6QhbVrtfb7TQHURjUafzfRm8+jGz3dPjVBrn/rD/umfGoSn6RuWngugcMNL4U0A+JcEh1NSfSYNVz7y+MqlW1UhX2kF86rm97ERCrxay7Gh/bI2f3fJPJ1r+EyLjzrDUkqw5cva3rVlFgEQouMVu35lUJn7SFompW8oTxkI23oc/t+AGZqaBupNITNdjyGCBpfukZ69EZrj8g=="; + DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("user", + Collections.emptyMap()); + principal.setRelyingPartyRegistrationId("get"); + Saml2Authentication user = new Saml2Authentication(principal, "response", + AuthorityUtils.createAuthorityList("ROLE_USER")); + MvcResult result = this.mvc + .perform(get("/logout/saml2/slo").param("SAMLRequest", apLogoutRequest) + .param("RelayState", apLogoutRequestRelayState).param("SigAlg", this.apLogoutRequestSigAlg) + .param("Signature", apLogoutRequestSignature) + .with(new SamlQueryStringRequestPostProcessor(true)).with(authentication(user))) + .andExpect(status().isFound()).andReturn(); + String location = result.getResponse().getHeader("Location"); + assertThat(location).startsWith("https://ap.example.org/logout/saml2/response"); + verify(getBean(LogoutHandler.class)).logout(any(), any(), any()); + } + @Test public void saml2LogoutRequestWhenNoRegistrationThen400() throws Exception { this.spring.register(Saml2LogoutDefaultsConfig.class).autowire(); @@ -612,6 +642,26 @@ public class Saml2LogoutConfigurerTests { static class SamlQueryStringRequestPostProcessor implements RequestPostProcessor { + private Function urlEncodingPostProcessor = Function.identity(); + + SamlQueryStringRequestPostProcessor() { + this(false); + } + + SamlQueryStringRequestPostProcessor(boolean lowercased) { + if (lowercased) { + Pattern encoding = Pattern.compile("%\\d[A-Fa-f]"); + this.urlEncodingPostProcessor = (encoded) -> { + Matcher m = encoding.matcher(encoded); + while (m.find()) { + String found = m.group(0); + encoded = encoded.replace(found, found.toLowerCase()); + } + return encoded; + }; + } + } + @Override public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); @@ -619,7 +669,8 @@ public class Saml2LogoutConfigurerTests { builder.queryParam(entries.getKey(), UriUtils.encode(entries.getValue()[0], StandardCharsets.ISO_8859_1)); } - request.setQueryString(builder.build(true).toUriString().substring(1)); + String queryString = this.urlEncodingPostProcessor.apply(builder.build(true).toUriString().substring(1)); + request.setQueryString(queryString); return request; } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2LogoutRequestMixin.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2LogoutRequestMixin.java index 17eb29f492..4eb0440eba 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2LogoutRequestMixin.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2LogoutRequestMixin.java @@ -17,9 +17,11 @@ package org.springframework.security.saml2.jackson2; import java.util.Map; +import java.util.function.Function; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -46,6 +48,9 @@ import org.springframework.security.saml2.provider.service.registration.Saml2Mes @JsonIgnoreProperties(ignoreUnknown = true) class Saml2LogoutRequestMixin { + @JsonIgnore + Function, String> encoder; + @JsonCreator Saml2LogoutRequestMixin(@JsonProperty("location") String location, @JsonProperty("relayState") Saml2MessageBinding relayState, 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 975f7a7525..24ea46312a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,7 +50,7 @@ import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2ParameterNames; import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.web.util.UriUtils; +import org.springframework.web.util.UriComponentsBuilder; /** * Utility methods for verifying SAML component signatures with OpenSAML @@ -191,8 +191,9 @@ final class OpenSamlVerificationUtils { else { this.signature = null; } - this.content = content(request.getSamlRequest(), Saml2ParameterNames.SAML_REQUEST, - request.getRelayState(), request.getParameter(Saml2ParameterNames.SIG_ALG)); + this.content = UriComponentsBuilder.newInstance().query(request.getParametersQuery()) + .replaceQueryParam(Saml2ParameterNames.SIGNATURE).build(true).toUriString().substring(1) + .getBytes(StandardCharsets.UTF_8); } RedirectSignature(Saml2LogoutResponse response) { @@ -203,22 +204,9 @@ final class OpenSamlVerificationUtils { else { this.signature = null; } - this.content = content(response.getSamlResponse(), Saml2ParameterNames.SAML_RESPONSE, - response.getRelayState(), response.getParameter(Saml2ParameterNames.SIG_ALG)); - } - - static byte[] content(String samlObject, String objectParameterName, String relayState, String algorithm) { - if (relayState != null) { - return String.format("%s=%s&%s=%s&%s=%s", objectParameterName, - UriUtils.encode(samlObject, StandardCharsets.ISO_8859_1), Saml2ParameterNames.RELAY_STATE, - UriUtils.encode(relayState, StandardCharsets.ISO_8859_1), Saml2ParameterNames.SIG_ALG, - UriUtils.encode(algorithm, StandardCharsets.ISO_8859_1)).getBytes(StandardCharsets.UTF_8); - } - else { - return String.format("%s=%s&%s=%s", objectParameterName, - UriUtils.encode(samlObject, StandardCharsets.ISO_8859_1), Saml2ParameterNames.SIG_ALG, - UriUtils.encode(algorithm, StandardCharsets.ISO_8859_1)).getBytes(StandardCharsets.UTF_8); - } + this.content = UriComponentsBuilder.newInstance().query(response.getParametersQuery()) + .replaceQueryParam(Saml2ParameterNames.SIGNATURE).build(true).toUriString().substring(1) + .getBytes(StandardCharsets.UTF_8); } byte[] getContent() { 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 3d35db3957..836de13a3b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,19 @@ package org.springframework.security.saml2.provider.service.authentication.logout; import java.io.Serializable; +import java.nio.charset.StandardCharsets; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Consumer; +import java.util.function.Function; import org.springframework.security.saml2.core.Saml2ParameterNames; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.UriUtils; /** * A class that represents a signed and serialized SAML 2.0 Logout Request @@ -35,6 +39,17 @@ import org.springframework.security.saml2.provider.service.web.authentication.lo */ public final class Saml2LogoutRequest implements Serializable { + private static final Function, String> DEFAULT_ENCODER = (params) -> { + if (params.isEmpty()) { + return null; + } + UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); + for (Map.Entry component : params.entrySet()) { + builder.queryParam(component.getKey(), UriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1)); + } + return builder.build(true).toString().substring(1); + }; + private final String location; private final Saml2MessageBinding binding; @@ -45,13 +60,21 @@ public final class Saml2LogoutRequest implements Serializable { private final String relyingPartyRegistrationId; + private Function, String> encoder; + private Saml2LogoutRequest(String location, Saml2MessageBinding binding, Map parameters, String id, String relyingPartyRegistrationId) { + this(location, binding, parameters, id, relyingPartyRegistrationId, DEFAULT_ENCODER); + } + + private Saml2LogoutRequest(String location, Saml2MessageBinding binding, Map parameters, String id, + String relyingPartyRegistrationId, Function, String> encoder) { this.location = location; this.binding = binding; - this.parameters = Collections.unmodifiableMap(new HashMap<>(parameters)); + this.parameters = Collections.unmodifiableMap(new LinkedHashMap<>(parameters)); this.id = id; this.relyingPartyRegistrationId = relyingPartyRegistrationId; + this.encoder = encoder; } /** @@ -119,6 +142,16 @@ public final class Saml2LogoutRequest implements Serializable { return this.parameters; } + /** + * Get an encoded query string of all parameters. Resulting query does not contain a + * leading question mark. + * @return an encoded string of all parameters + * @since 5.8 + */ + public String getParametersQuery() { + return this.encoder.apply(this.parameters); + } + /** * The identifier for the {@link RelyingPartyRegistration} associated with this Logout * Request @@ -149,7 +182,9 @@ public final class Saml2LogoutRequest implements Serializable { private Saml2MessageBinding binding; - private Map parameters = new HashMap<>(); + private Map parameters = new LinkedHashMap<>(); + + private Function, String> encoder = DEFAULT_ENCODER; private String id; @@ -235,13 +270,28 @@ public final class Saml2LogoutRequest implements Serializable { return this; } + /** + * Use this strategy for converting parameters into an encoded query string. The + * resulting query does not contain a leading question mark. + * + * In the event that you already have an encoded version that you want to use, you + * can call this by doing {@code parameterEncoder((params) -> encodedValue)}. + * @param encoder the strategy to use + * @return the {@link Builder} for further configurations + * @since 5.8 + */ + public Builder parametersQuery(Function, String> encoder) { + this.encoder = encoder; + return this; + } + /** * Build the {@link Saml2LogoutRequest} * @return a constructed {@link Saml2LogoutRequest} */ public Saml2LogoutRequest build() { return new Saml2LogoutRequest(this.location, this.binding, this.parameters, this.id, - this.registration.getRegistrationId()); + this.registration.getRegistrationId(), this.encoder); } } 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 43d64cf052..a555b784a2 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +16,19 @@ package org.springframework.security.saml2.provider.service.authentication.logout; +import java.nio.charset.StandardCharsets; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Consumer; +import java.util.function.Function; import org.springframework.security.saml2.core.Saml2ParameterNames; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.UriUtils; /** * A class that represents a signed and serialized SAML 2.0 Logout Response @@ -34,16 +38,31 @@ import org.springframework.security.saml2.provider.service.web.authentication.lo */ public final class Saml2LogoutResponse { + private static final Function, String> DEFAULT_ENCODER = (params) -> { + if (params.isEmpty()) { + return null; + } + UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); + for (Map.Entry component : params.entrySet()) { + builder.queryParam(component.getKey(), UriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1)); + } + return builder.build(true).toString().substring(1); + }; + private final String location; private final Saml2MessageBinding binding; private final Map parameters; - private Saml2LogoutResponse(String location, Saml2MessageBinding binding, Map parameters) { + private final Function, String> encoder; + + private Saml2LogoutResponse(String location, Saml2MessageBinding binding, Map parameters, + Function, String> encoder) { this.location = location; this.binding = binding; - this.parameters = Collections.unmodifiableMap(new HashMap<>(parameters)); + this.parameters = Collections.unmodifiableMap(new LinkedHashMap<>(parameters)); + this.encoder = encoder; } /** @@ -103,6 +122,16 @@ public final class Saml2LogoutResponse { return this.parameters; } + /** + * Get an encoded query string of all parameters. Resulting query does not contain a + * leading question mark. + * @return an encoded string of all parameters + * @since 5.8 + */ + public String getParametersQuery() { + return this.encoder.apply(this.parameters); + } + /** * Create a {@link Builder} instance from this {@link RelyingPartyRegistration} * @@ -122,7 +151,9 @@ public final class Saml2LogoutResponse { private Saml2MessageBinding binding; - private Map parameters = new HashMap<>(); + private Map parameters = new LinkedHashMap<>(); + + private Function, String> encoder = DEFAULT_ENCODER; private Builder(RelyingPartyRegistration registration) { this.location = registration.getAssertingPartyDetails().getSingleLogoutServiceResponseLocation(); @@ -195,12 +226,27 @@ public final class Saml2LogoutResponse { return this; } + /** + * Use this strategy for converting parameters into an encoded query string. The + * resulting query does not contain a leading question mark. + * + * In the event that you already have an encoded version that you want to use, you + * can call this by doing {@code parameterEncoder((params) -> encodedValue)}. + * @param encoder the strategy to use + * @return the {@link Saml2LogoutRequest.Builder} for further configurations + * @since 5.8 + */ + public Builder parametersQuery(Function, String> encoder) { + this.encoder = encoder; + return this; + } + /** * Build the {@link Saml2LogoutResponse} * @return a constructed {@link Saml2LogoutResponse} */ public Saml2LogoutResponse build() { - return new Saml2LogoutResponse(this.location, this.binding, this.parameters); + return new Saml2LogoutResponse(this.location, this.binding, this.parameters, this.encoder); } } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java index 1aa5e69b37..2ba5674c65 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ package org.springframework.security.saml2.provider.service.web.authentication.logout; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.function.Function; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -53,7 +51,6 @@ import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.HtmlUtils; import org.springframework.web.util.UriComponentsBuilder; -import org.springframework.web.util.UriUtils; /** * A filter for handling logout requests in the form of a <saml2:LogoutRequest> sent @@ -141,7 +138,7 @@ public final class Saml2LogoutRequestFilter extends OncePerRequestFilter { request.getParameter(Saml2ParameterNames.SIG_ALG))) .parameters((params) -> params.put(Saml2ParameterNames.SIGNATURE, request.getParameter(Saml2ParameterNames.SIGNATURE))) - .build(); + .parametersQuery((params) -> request.getQueryString()).build(); Saml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(logoutRequest, registration, authentication); Saml2LogoutValidatorResult result = this.logoutRequestValidator.validate(parameters); @@ -192,22 +189,11 @@ public final class Saml2LogoutRequestFilter extends OncePerRequestFilter { private void doRedirect(HttpServletRequest request, HttpServletResponse response, Saml2LogoutResponse logoutResponse) throws IOException { String location = logoutResponse.getResponseLocation(); - UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(location); - addParameter(Saml2ParameterNames.SAML_RESPONSE, logoutResponse::getParameter, uriBuilder); - addParameter(Saml2ParameterNames.RELAY_STATE, logoutResponse::getParameter, uriBuilder); - addParameter(Saml2ParameterNames.SIG_ALG, logoutResponse::getParameter, uriBuilder); - addParameter(Saml2ParameterNames.SIGNATURE, logoutResponse::getParameter, uriBuilder); + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(location) + .query(logoutResponse.getParametersQuery()); this.redirectStrategy.sendRedirect(request, response, uriBuilder.build(true).toUriString()); } - private void addParameter(String name, Function parameters, UriComponentsBuilder builder) { - Assert.hasText(name, "name cannot be empty or null"); - if (StringUtils.hasText(parameters.apply(name))) { - builder.queryParam(UriUtils.encode(name, StandardCharsets.ISO_8859_1), - UriUtils.encode(parameters.apply(name), StandardCharsets.ISO_8859_1)); - } - } - private void doPost(HttpServletResponse response, Saml2LogoutResponse logoutResponse) throws IOException { String location = logoutResponse.getResponseLocation(); String saml = logoutResponse.getSamlResponse(); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseFilter.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseFilter.java index 239249719a..7aa6ea539a 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseFilter.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -141,7 +141,7 @@ public final class Saml2LogoutResponseFilter extends OncePerRequestFilter { request.getParameter(Saml2ParameterNames.SIG_ALG))) .parameters((params) -> params.put(Saml2ParameterNames.SIGNATURE, request.getParameter(Saml2ParameterNames.SIGNATURE))) - .build(); + .parametersQuery((params) -> request.getQueryString()).build(); Saml2LogoutResponseValidatorParameters parameters = new Saml2LogoutResponseValidatorParameters(logoutResponse, logoutRequest, registration); Saml2LogoutValidatorResult result = this.logoutResponseValidator.validate(parameters); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2RelyingPartyInitiatedLogoutSuccessHandler.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2RelyingPartyInitiatedLogoutSuccessHandler.java index 8d8b1f204f..46f8d7a991 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2RelyingPartyInitiatedLogoutSuccessHandler.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2RelyingPartyInitiatedLogoutSuccessHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ package org.springframework.security.saml2.provider.service.web.authentication.logout; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.function.Function; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -28,7 +26,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.http.MediaType; import org.springframework.security.core.Authentication; -import org.springframework.security.saml2.core.Saml2ParameterNames; import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest; import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; import org.springframework.security.web.DefaultRedirectStrategy; @@ -38,7 +35,6 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.util.HtmlUtils; import org.springframework.web.util.UriComponentsBuilder; -import org.springframework.web.util.UriUtils; /** * A success handler for issuing a SAML 2.0 Logout Request to the the SAML 2.0 Asserting @@ -105,22 +101,11 @@ public final class Saml2RelyingPartyInitiatedLogoutSuccessHandler implements Log private void doRedirect(HttpServletRequest request, HttpServletResponse response, Saml2LogoutRequest logoutRequest) throws IOException { String location = logoutRequest.getLocation(); - UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(location); - addParameter(Saml2ParameterNames.SAML_REQUEST, logoutRequest::getParameter, uriBuilder); - addParameter(Saml2ParameterNames.RELAY_STATE, logoutRequest::getParameter, uriBuilder); - addParameter(Saml2ParameterNames.SIG_ALG, logoutRequest::getParameter, uriBuilder); - addParameter(Saml2ParameterNames.SIGNATURE, logoutRequest::getParameter, uriBuilder); + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(location) + .query(logoutRequest.getParametersQuery()); this.redirectStrategy.sendRedirect(request, response, uriBuilder.build(true).toUriString()); } - private void addParameter(String name, Function parameters, UriComponentsBuilder builder) { - Assert.hasText(name, "name cannot be empty or null"); - if (StringUtils.hasText(parameters.apply(name))) { - builder.queryParam(UriUtils.encode(name, StandardCharsets.ISO_8859_1), - UriUtils.encode(parameters.apply(name), StandardCharsets.ISO_8859_1)); - } - } - private void doPost(HttpServletResponse response, Saml2LogoutRequest logoutRequest) throws IOException { String location = logoutRequest.getLocation(); String saml = logoutRequest.getSamlRequest();