Add Status Check
Closes gh-8955
This commit is contained in:
+8
-1
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2021 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -37,6 +37,13 @@ public interface Saml2ErrorCodes {
|
|||||||
*/
|
*/
|
||||||
String MALFORMED_RESPONSE_DATA = "malformed_response_data";
|
String MALFORMED_RESPONSE_DATA = "malformed_response_data";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response is invalid in a general way.
|
||||||
|
*
|
||||||
|
* @since 5.5
|
||||||
|
*/
|
||||||
|
String INVALID_RESPONSE = "invalid_response";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response destination does not match the request URL. A SAML 2 response object was
|
* Response destination does not match the request URL. A SAML 2 response object was
|
||||||
* received at a URL that did not match the URL stored in the {code Destination}
|
* received at a URL that did not match the URL stored in the {code Destination}
|
||||||
|
|||||||
+18
-1
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2021 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -73,6 +73,7 @@ import org.opensaml.saml.saml2.core.EncryptedAttribute;
|
|||||||
import org.opensaml.saml.saml2.core.NameID;
|
import org.opensaml.saml.saml2.core.NameID;
|
||||||
import org.opensaml.saml.saml2.core.OneTimeUse;
|
import org.opensaml.saml.saml2.core.OneTimeUse;
|
||||||
import org.opensaml.saml.saml2.core.Response;
|
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.SubjectConfirmation;
|
||||||
import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller;
|
import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller;
|
||||||
import org.opensaml.saml.saml2.encryption.Decrypter;
|
import org.opensaml.saml.saml2.encryption.Decrypter;
|
||||||
@@ -615,6 +616,12 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
|
|||||||
Response response = responseToken.getResponse();
|
Response response = responseToken.getResponse();
|
||||||
Saml2AuthenticationToken token = responseToken.getToken();
|
Saml2AuthenticationToken token = responseToken.getToken();
|
||||||
Saml2ResponseValidatorResult result = Saml2ResponseValidatorResult.success();
|
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 issuer = response.getIssuer().getValue();
|
||||||
String destination = response.getDestination();
|
String destination = response.getDestination();
|
||||||
String location = token.getRelyingPartyRegistration().getAssertionConsumerServiceLocation();
|
String location = token.getRelyingPartyRegistration().getAssertionConsumerServiceLocation();
|
||||||
@@ -637,6 +644,16 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionSignatureValidator() {
|
private Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionSignatureValidator() {
|
||||||
return createAssertionValidator(Saml2ErrorCodes.INVALID_SIGNATURE, (assertionToken) -> {
|
return createAssertionValidator(Saml2ErrorCodes.INVALID_SIGNATURE, (assertionToken) -> {
|
||||||
SignatureTrustEngine engine = this.signatureTrustEngineConverter.convert(assertionToken.token);
|
SignatureTrustEngine engine = this.signatureTrustEngineConverter.convert(assertionToken.token);
|
||||||
|
|||||||
+21
-1
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2021 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -48,6 +48,7 @@ import org.opensaml.saml.saml2.core.EncryptedID;
|
|||||||
import org.opensaml.saml.saml2.core.NameID;
|
import org.opensaml.saml.saml2.core.NameID;
|
||||||
import org.opensaml.saml.saml2.core.OneTimeUse;
|
import org.opensaml.saml.saml2.core.OneTimeUse;
|
||||||
import org.opensaml.saml.saml2.core.Response;
|
import org.opensaml.saml.saml2.core.Response;
|
||||||
|
import org.opensaml.saml.saml2.core.StatusCode;
|
||||||
import org.opensaml.saml.saml2.core.impl.EncryptedAssertionBuilder;
|
import org.opensaml.saml.saml2.core.impl.EncryptedAssertionBuilder;
|
||||||
import org.opensaml.saml.saml2.core.impl.EncryptedIDBuilder;
|
import org.opensaml.saml.saml2.core.impl.EncryptedIDBuilder;
|
||||||
import org.opensaml.saml.saml2.core.impl.NameIDBuilder;
|
import org.opensaml.saml.saml2.core.impl.NameIDBuilder;
|
||||||
@@ -557,6 +558,25 @@ public class OpenSamlAuthenticationProviderTests {
|
|||||||
assertThat(authentication.getName()).isEqualTo("decrypted name");
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 extends XMLObject> T build(QName qName) {
|
private <T extends XMLObject> T build(QName qName) {
|
||||||
return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);
|
return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-1
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2021 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -21,6 +21,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
@@ -59,11 +60,15 @@ import org.opensaml.saml.saml2.core.EncryptedID;
|
|||||||
import org.opensaml.saml.saml2.core.Issuer;
|
import org.opensaml.saml.saml2.core.Issuer;
|
||||||
import org.opensaml.saml.saml2.core.NameID;
|
import org.opensaml.saml.saml2.core.NameID;
|
||||||
import org.opensaml.saml.saml2.core.Response;
|
import org.opensaml.saml.saml2.core.Response;
|
||||||
|
import org.opensaml.saml.saml2.core.Status;
|
||||||
|
import org.opensaml.saml.saml2.core.StatusCode;
|
||||||
import org.opensaml.saml.saml2.core.Subject;
|
import org.opensaml.saml.saml2.core.Subject;
|
||||||
import org.opensaml.saml.saml2.core.SubjectConfirmation;
|
import org.opensaml.saml.saml2.core.SubjectConfirmation;
|
||||||
import org.opensaml.saml.saml2.core.SubjectConfirmationData;
|
import org.opensaml.saml.saml2.core.SubjectConfirmationData;
|
||||||
import org.opensaml.saml.saml2.core.impl.AttributeBuilder;
|
import org.opensaml.saml.saml2.core.impl.AttributeBuilder;
|
||||||
import org.opensaml.saml.saml2.core.impl.AttributeStatementBuilder;
|
import org.opensaml.saml.saml2.core.impl.AttributeStatementBuilder;
|
||||||
|
import org.opensaml.saml.saml2.core.impl.StatusBuilder;
|
||||||
|
import org.opensaml.saml.saml2.core.impl.StatusCodeBuilder;
|
||||||
import org.opensaml.saml.saml2.encryption.Encrypter;
|
import org.opensaml.saml.saml2.encryption.Encrypter;
|
||||||
import org.opensaml.security.SecurityException;
|
import org.opensaml.security.SecurityException;
|
||||||
import org.opensaml.security.credential.BasicCredential;
|
import org.opensaml.security.credential.BasicCredential;
|
||||||
@@ -118,8 +123,14 @@ public final class TestOpenSamlObjects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Response signedResponseWithOneAssertion() {
|
static Response signedResponseWithOneAssertion() {
|
||||||
|
return signedResponseWithOneAssertion((response) -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Response signedResponseWithOneAssertion(Consumer<Response> responseConsumer) {
|
||||||
Response response = response();
|
Response response = response();
|
||||||
response.getAssertions().add(assertion());
|
response.getAssertions().add(assertion());
|
||||||
|
responseConsumer.accept(response);
|
||||||
return signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
|
return signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,6 +349,18 @@ public final class TestOpenSamlObjects {
|
|||||||
return attributeStatements;
|
return attributeStatements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Status successStatus() {
|
||||||
|
return status(StatusCode.SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Status status(String code) {
|
||||||
|
Status status = new StatusBuilder().buildObject();
|
||||||
|
StatusCode statusCode = new StatusCodeBuilder().buildObject();
|
||||||
|
statusCode.setValue(code);
|
||||||
|
status.setStatusCode(statusCode);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
static <T extends XMLObject> T build(QName qName) {
|
static <T extends XMLObject> T build(QName qName) {
|
||||||
return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);
|
return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user