@ -21,13 +21,13 @@ import java.time.Duration;
@@ -21,13 +21,13 @@ import java.time.Duration;
import java.time.Instant ;
import java.util.ArrayList ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.LinkedHashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
import java.util.function.Consumer ;
import java.util.function.Function ;
import javax.annotation.Nonnull ;
@ -193,10 +193,12 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@@ -193,10 +193,12 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
private Converter < Saml2AuthenticationToken , SignatureTrustEngine > signatureTrustEngineConverter =
new SignatureTrustEngineConverter ( ) ;
private Converter < Saml2AuthenticationToken , SAML20AssertionValidator > assertionValidatorConverter =
private Converter < Tuple , SAML20AssertionValidator > assertionValidatorConverter =
new SAML20AssertionValidatorConverter ( ) ;
private Converter < Saml2AuthenticationToken , ValidationContext > validationContextConverter =
new ValidationContextConverter ( params - > { } ) ;
private Collection < ConditionValidator > conditionValidators =
Collections . singleton ( new AudienceRestrictionConditionValidator ( ) ) ;
private Converter < Tuple , ValidationContext > validationContextConverter =
new ValidationContextConverter ( ) ;
private Converter < Saml2AuthenticationToken , Decrypter > decrypterConverter = new DecrypterConverter ( ) ;
/ * *
@ -209,6 +211,33 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@@ -209,6 +211,33 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
this . parserPool = this . registry . getParserPool ( ) ;
}
/ * *
* Set the the collection of { @link ConditionValidator } s used when validating an assertion .
*
* @param conditionValidators the collection of validators to use
* @since 5 . 4
* /
public void setConditionValidators (
Collection < ConditionValidator > conditionValidators ) {
Assert . notEmpty ( conditionValidators , "conditionValidators cannot be empty" ) ;
this . conditionValidators = conditionValidators ;
}
/ * *
* Set the strategy for retrieving the { @link ValidationContext } used when
* validating an assertion .
*
* @param validationContextConverter the strategy to use
* @since 5 . 4
* /
public void setValidationContextConverter (
Converter < Tuple , ValidationContext > validationContextConverter ) {
Assert . notNull ( validationContextConverter , "validationContextConverter cannot be empty" ) ;
this . validationContextConverter = validationContextConverter ;
}
/ * *
* Sets the { @link Converter } used for extracting assertion attributes that
* can be mapped to authorities .
@ -238,8 +267,6 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@@ -238,8 +267,6 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
* /
public void setResponseTimeValidationSkew ( Duration responseTimeValidationSkew ) {
this . responseTimeValidationSkew = responseTimeValidationSkew ;
this . validationContextConverter = new ValidationContextConverter (
params - > params . put ( CLOCK_SKEW , responseTimeValidationSkew . toMillis ( ) ) ) ;
}
/ * *
@ -303,7 +330,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@@ -303,7 +330,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
throw authException ( INVALID_SIGNATURE , "Either the response or one of the assertions is unsigned. " +
"Please either sign the response or all of the assertions." ) ;
}
validationExceptions . putAll ( validateAssertions ( token , assertions ) ) ;
validationExceptions . putAll ( validateAssertions ( token , response ) ) ;
Assertion firstAssertion = CollectionUtils . firstElement ( response . getAssertions ( ) ) ;
NameID nameId = decryptPrincipal ( decrypter , firstAssertion ) ;
@ -392,7 +419,8 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@@ -392,7 +419,8 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
}
private Map < String , Saml2AuthenticationException > validateAssertions
( Saml2AuthenticationToken token , List < Assertion > assertions ) {
( Saml2AuthenticationToken token , Response response ) {
List < Assertion > assertions = response . getAssertions ( ) ;
if ( assertions . isEmpty ( ) ) {
throw authException ( MALFORMED_RESPONSE_DATA , "No assertions found in response." ) ;
}
@ -401,14 +429,16 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@@ -401,14 +429,16 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Validating " + assertions . size ( ) + " assertions" ) ;
}
Tuple tuple = new Tuple ( token , response ) ;
SAML20AssertionValidator validator = this . assertionValidatorConverter . convert ( tuple ) ;
ValidationContext context = this . validationContextConverter . convert ( tuple ) ;
for ( Assertion assertion : assertions ) {
if ( logger . isTraceEnabled ( ) ) {
logger . trace ( "Validating assertion " + assertion . getID ( ) ) ;
}
try {
ValidationContext context = this . validationContextConverter . convert ( token ) ;
ValidationResult result = this . assertionValidatorConverter . convert ( token ) . validate ( assertion , context ) ;
if ( result ! = ValidationResult . VALID ) {
if ( validator . validate ( assertion , context ) ! = ValidationResult . VALID ) {
String message = String . format ( "Invalid assertion [%s] for SAML response [%s]: %s" ,
assertion . getID ( ) , ( ( Response ) assertion . getParent ( ) ) . getID ( ) ,
context . getValidationFailureMessage ( ) ) ;
@ -512,6 +542,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@@ -512,6 +542,7 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
}
private static class SignatureTrustEngineConverter implements Converter < Saml2AuthenticationToken , SignatureTrustEngine > {
@Override
public SignatureTrustEngine convert ( Saml2AuthenticationToken token ) {
Set < Credential > credentials = new HashSet < > ( ) ;
@ -530,35 +561,27 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@@ -530,35 +561,27 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
}
}
private static class ValidationContextConverter implements Converter < Saml2AuthenticationToken , ValidationContext > {
Consumer < Map < String , Object > > validationContextParametersConverter ;
ValidationContextConverter ( Consumer < Map < String , Object > > validationContextParametersConverter ) {
this . validationContextParametersConverter = validationContextParametersConverter ;
}
private class ValidationContextConverter implements Converter < Tuple , ValidationContext > {
@Override
public ValidationContext convert ( Saml2AuthenticationToken token ) {
String audience = toke n . getRelyingPartyRegistration ( ) . getEntityId ( ) ;
String recipient = toke n . getRelyingPartyRegistration ( ) . getAssertionConsumerServiceLocation ( ) ;
public ValidationContext convert ( Tuple tuple ) {
String audience = tuple . authentication . getRelyingPartyRegistration ( ) . getEntityId ( ) ;
String recipient = tuple . authentication . getRelyingPartyRegistration ( ) . getAssertionConsumerServiceLocation ( ) ;
Map < String , Object > params = new HashMap < > ( ) ;
params . put ( CLOCK_SKEW , Duration . ofMinutes ( 5 ) . toMillis ( ) ) ;
params . put ( CLOCK_SKEW , OpenSamlAuthenticationProvider . this . responseTimeValidationSkew . toMillis ( ) ) ;
params . put ( COND_VALID_AUDIENCES , singleton ( audience ) ) ;
params . put ( SC_VALID_RECIPIENTS , singleton ( recipient ) ) ;
params . put ( SIGNATURE_REQUIRED , false ) ; // this verification is performed earlier
this . validationContextParametersConverter . accept ( params ) ;
return new ValidationContext ( params ) ;
}
}
private class SAML20AssertionValidatorConverter implements Converter < Saml2AuthenticationToken , SAML20AssertionValidator > {
private final Collection < ConditionValidator > conditions = new ArrayList < > ( ) ;
private class SAML20AssertionValidatorConverter implements Converter < Tuple , SAML20AssertionValidator > {
private final Collection < SubjectConfirmationValidator > subjects = new ArrayList < > ( ) ;
private final Collection < StatementValidator > statements = new ArrayList < > ( ) ;
private final SignaturePrevalidator validator = new SAMLSignatureProfileValidator ( ) ;
SAML20AssertionValidatorConverter ( ) {
this . conditions . add ( new AudienceRestrictionConditionValidator ( ) ) ;
this . subjects . add ( new BearerSubjectConfirmationValidator ( ) {
@Nonnull
@Override
@ -571,9 +594,11 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@@ -571,9 +594,11 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
}
@Override
public SAML20AssertionValidator convert ( Saml2AuthenticationToken token ) {
return new SAML20AssertionValidator ( this . conditions , this . subjects , this . statements ,
OpenSamlAuthenticationProvider . this . signatureTrustEngineConverter . convert ( token ) ,
public SAML20AssertionValidator convert ( Tuple tuple ) {
Collection < ConditionValidator > conditions =
OpenSamlAuthenticationProvider . this . conditionValidators ;
return new SAML20AssertionValidator ( conditions , this . subjects , this . statements ,
OpenSamlAuthenticationProvider . this . signatureTrustEngineConverter . convert ( tuple . authentication ) ,
this . validator ) ;
}
}
@ -616,4 +641,27 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
@@ -616,4 +641,27 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
return new Saml2AuthenticationException ( validationError ( code , description ) , cause ) ;
}
/ * *
* A tuple containing the authentication token and the associated OpenSAML { @link Response } .
*
* @since 5 . 4
* /
public static class Tuple {
private final Saml2AuthenticationToken authentication ;
private final Response response ;
private Tuple ( Saml2AuthenticationToken authentication , Response response ) {
this . authentication = authentication ;
this . response = response ;
}
public Saml2AuthenticationToken getAuthentication ( ) {
return this . authentication ;
}
public Response getResponse ( ) {
return this . response ;
}
}
}