From 457c2a2d06241684ec6ccbf3ce1942a245bddef1 Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Tue, 4 May 2021 09:32:44 -0600 Subject: [PATCH] Add Response Status Check Closes gh-9718 --- .../OpenSamlAuthenticationProviderTests.java | 2 +- .../OpenSaml4AuthenticationProvider.java | 17 ++++++++++++++++ .../OpenSaml4AuthenticationProviderTests.java | 20 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/saml2/saml2-service-provider/opensaml3/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java b/saml2/saml2-service-provider/opensaml3/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java index 93e263a2e3..713c09f61c 100644 --- a/saml2/saml2-service-provider/opensaml3/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java +++ b/saml2/saml2-service-provider/opensaml3/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java @@ -573,7 +573,7 @@ public class OpenSamlAuthenticationProviderTests { Saml2AuthenticationToken token = token(response, verifying(registration())); assertThatExceptionOfType(Saml2AuthenticationException.class) .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.INVALID_RESPONSE)); + .satisfies(errorOf(Saml2ErrorCodes.INVALID_RESPONSE, "Invalid status")); } @Test diff --git a/saml2/saml2-service-provider/opensaml4/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java b/saml2/saml2-service-provider/opensaml4/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java index ef148ba6b7..08f53a82d1 100644 --- a/saml2/saml2-service-provider/opensaml4/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java +++ b/saml2/saml2-service-provider/opensaml4/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java @@ -61,6 +61,7 @@ import org.opensaml.saml.saml2.core.Condition; import org.opensaml.saml.saml2.core.EncryptedAssertion; import org.opensaml.saml.saml2.core.OneTimeUse; import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.core.StatusCode; import org.opensaml.saml.saml2.core.SubjectConfirmation; import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller; import org.opensaml.saml.saml2.encryption.Decrypter; @@ -491,6 +492,12 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv Response response = responseToken.getResponse(); Saml2AuthenticationToken token = responseToken.getToken(); Saml2ResponseValidatorResult result = Saml2ResponseValidatorResult.success(); + String statusCode = getStatusCode(response); + if (!StatusCode.SUCCESS.equals(statusCode)) { + String message = String.format("Invalid status [%s] for SAML response [%s]", statusCode, + response.getID()); + result = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, message)); + } String issuer = response.getIssuer().getValue(); String destination = response.getDestination(); String location = token.getRelyingPartyRegistration().getAssertionConsumerServiceLocation(); @@ -513,6 +520,16 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv }; } + private String getStatusCode(Response response) { + if (response.getStatus() == null) { + return StatusCode.SUCCESS; + } + if (response.getStatus().getStatusCode() == null) { + return StatusCode.SUCCESS; + } + return response.getStatus().getStatusCode().getValue(); + } + private Converter createDefaultAssertionSignatureValidator() { return createAssertionValidator(Saml2ErrorCodes.INVALID_SIGNATURE, (assertionToken) -> { RelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration(); diff --git a/saml2/saml2-service-provider/opensaml4/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java b/saml2/saml2-service-provider/opensaml4/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java index bc94bc3b2b..f57c645e4a 100644 --- a/saml2/saml2-service-provider/opensaml4/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java +++ b/saml2/saml2-service-provider/opensaml4/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java @@ -52,6 +52,7 @@ import org.opensaml.saml.saml2.core.EncryptedID; import org.opensaml.saml.saml2.core.NameID; import org.opensaml.saml.saml2.core.OneTimeUse; import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.core.StatusCode; import org.opensaml.saml.saml2.core.SubjectConfirmation; import org.opensaml.saml.saml2.core.SubjectConfirmationData; import org.opensaml.saml.saml2.core.impl.AttributeBuilder; @@ -565,6 +566,25 @@ public class OpenSaml4AuthenticationProviderTests { assertThat(authentication.getName()).isEqualTo("decrypted name"); } + @Test + public void authenticateWhenResponseStatusIsNotSuccessThenFails() { + Response response = TestOpenSamlObjects.signedResponseWithOneAssertion( + (r) -> r.setStatus(TestOpenSamlObjects.status(StatusCode.AUTHN_FAILED))); + Saml2AuthenticationToken token = token(response, verifying(registration())); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.INVALID_RESPONSE, "Invalid status")); + } + + @Test + public void authenticateWhenResponseStatusIsSuccessThenSucceeds() { + Response response = TestOpenSamlObjects + .signedResponseWithOneAssertion((r) -> r.setStatus(TestOpenSamlObjects.successStatus())); + Saml2AuthenticationToken token = token(response, verifying(registration())); + Authentication authentication = this.provider.authenticate(token); + assertThat(authentication.getName()).isEqualTo("test@saml.user"); + } + private T build(QName qName) { return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName); }