Browse Source

Correctly set "Destination" in AuthNRequest message

Fixes gh-7494
https://github.com/spring-projects/spring-security/issues/7494
Filip Hanik 5 years ago
parent
commit
9731386de5

+ 1 - 6
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilter.java

@@ -107,12 +107,7 @@ public class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter
 		String localSpEntityId = Saml2Utils.getServiceProviderEntityId(relyingParty, request);
 		return new Saml2AuthenticationRequest(
 				localSpEntityId,
-				Saml2Utils.resolveUrlTemplate(
-						relyingParty.getAssertionConsumerServiceUrlTemplate(),
-						Saml2Utils.getApplicationUri(request),
-						relyingParty.getRemoteIdpEntityId(),
-						relyingParty.getRegistrationId()
-				),
+				relyingParty.getIdpWebSsoUrl(),
 				relyingParty.getSigningCredentials()
 		);
 	}

+ 57 - 15
samples/boot/saml2login/src/integration-test/java/org/springframework/security/samples/Saml2LoginIntegrationTests.java

@@ -15,21 +15,10 @@
  */
 package org.springframework.security.samples;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.SpringBootConfiguration;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.http.MediaType;
-import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
-import org.springframework.test.context.junit4.SpringRunner;
-import org.springframework.test.util.AssertionErrors;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.ResultActions;
-import org.springframework.test.web.servlet.ResultMatcher;
-
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.xml.BasicParserPool;
 import net.shibboleth.utilities.java.support.xml.SerializeSupport;
+import net.shibboleth.utilities.java.support.xml.XMLParserException;
 import org.hamcrest.Matcher;
 import org.joda.time.DateTime;
 import org.junit.Test;
@@ -38,8 +27,10 @@ import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.core.xml.io.MarshallerFactory;
 import org.opensaml.core.xml.io.MarshallingException;
+import org.opensaml.core.xml.io.UnmarshallingException;
 import org.opensaml.saml.common.SignableSAMLObject;
 import org.opensaml.saml.saml2.core.Assertion;
+import org.opensaml.saml.saml2.core.AuthnRequest;
 import org.opensaml.saml.saml2.core.EncryptedAssertion;
 import org.opensaml.saml.saml2.core.EncryptedID;
 import org.opensaml.saml.saml2.core.Response;
@@ -55,9 +46,26 @@ import org.opensaml.xmlsec.SignatureSigningParameters;
 import org.opensaml.xmlsec.signature.support.SignatureConstants;
 import org.opensaml.xmlsec.signature.support.SignatureException;
 import org.opensaml.xmlsec.signature.support.SignatureSupport;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.http.MediaType;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.util.AssertionErrors;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultActions;
+import org.springframework.test.web.servlet.ResultMatcher;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.util.UriComponentsBuilder;
+import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
 import java.io.ByteArrayInputStream;
+import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyException;
 import java.security.PrivateKey;
@@ -78,6 +86,7 @@ import static org.springframework.security.samples.OpenSamlActionTestingSupport.
 import static org.springframework.security.samples.OpenSamlActionTestingSupport.buildSubjectConfirmation;
 import static org.springframework.security.samples.OpenSamlActionTestingSupport.buildSubjectConfirmationData;
 import static org.springframework.security.samples.OpenSamlActionTestingSupport.encryptNameId;
+import static org.springframework.security.samples.OpenSamlActionTestingSupport.inflate;
 import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
 import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
 import static org.springframework.security.web.WebAttributes.AUTHENTICATION_EXCEPTION;
@@ -131,6 +140,29 @@ public class Saml2LoginIntegrationTests {
 				.andExpect(header().string("Location", containsString("RelayState=relay%20state%20value%20with%20spaces")));
 	}
 
+	@Test
+	public void authenticateRequestWhenWorkingThenDestinationAttributeIsSet() throws Exception {
+		final String redirectedUrl = mockMvc.perform(get("http://localhost:8080/saml2/authenticate/simplesamlphp"))
+				.andExpect(status().is3xxRedirection())
+				.andReturn()
+				.getResponse()
+				.getRedirectedUrl();
+		MultiValueMap<String, String> parameters =
+				UriComponentsBuilder.fromUriString(redirectedUrl).build(true).getQueryParams();
+		String request = parameters.getFirst("SAMLRequest");
+		AssertionErrors.assertNotNull("SAMLRequest parameter is missing", request);
+		request = URLDecoder.decode(request);
+		request = inflate(OpenSamlActionTestingSupport.decode(request));
+		AuthnRequest authnRequest = (AuthnRequest) fromXml(request);
+		String destination = authnRequest.getDestination();
+		assertEquals(
+				"Destination must match",
+				"https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php",
+				destination
+		);
+	}
+
+
 	@Test
 	public void authenticateWhenResponseIsSignedThenItSucceeds() throws Exception {
 		Assertion assertion = buildAssertion(USERNAME);
@@ -248,7 +280,7 @@ public class Saml2LoginIntegrationTests {
 								"invalid_issuer",
 								containsString(
 										"Response issuer 'invalid issuer' doesn't match "+
-										"'https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php'"
+												"'https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php'"
 								)
 						)
 				);
@@ -330,6 +362,16 @@ public class Saml2LoginIntegrationTests {
 		return SerializeSupport.nodeToString(element);
 	}
 
+	private XMLObject fromXml(String xml)
+			throws XMLParserException, UnmarshallingException, ComponentInitializationException {
+		BasicParserPool parserPool = new BasicParserPool();
+		parserPool.initialize();
+		Document document = parserPool.parse(new ByteArrayInputStream(xml.getBytes(UTF_8)));
+		Element element = document.getDocumentElement();
+		return XMLObjectProviderRegistrySupport.getUnmarshallerFactory().getUnmarshaller(element).unmarshall(element);
+
+	}
+
 	private X509Certificate decodeCertificate(String source) {
 		try {
 			final CertificateFactory factory = CertificateFactory.getInstance("X.509");