21
21
import java .time .Instant ;
22
22
import java .util .ArrayList ;
23
23
import java .util .Collection ;
24
+ import java .util .Collections ;
24
25
import java .util .HashMap ;
25
26
import java .util .HashSet ;
26
27
import java .util .LinkedHashMap ;
27
28
import java .util .List ;
28
29
import java .util .Map ;
29
30
import java .util .Set ;
30
- import java .util .function .Consumer ;
31
31
import java .util .function .Function ;
32
32
import javax .annotation .Nonnull ;
33
33
@@ -193,10 +193,12 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
193
193
194
194
private Converter <Saml2AuthenticationToken , SignatureTrustEngine > signatureTrustEngineConverter =
195
195
new SignatureTrustEngineConverter ();
196
- private Converter <Saml2AuthenticationToken , SAML20AssertionValidator > assertionValidatorConverter =
196
+ private Converter <Tuple , SAML20AssertionValidator > assertionValidatorConverter =
197
197
new SAML20AssertionValidatorConverter ();
198
- private Converter <Saml2AuthenticationToken , ValidationContext > validationContextConverter =
199
- new ValidationContextConverter (params -> {});
198
+ private Collection <ConditionValidator > conditionValidators =
199
+ Collections .singleton (new AudienceRestrictionConditionValidator ());
200
+ private Converter <Tuple , ValidationContext > validationContextConverter =
201
+ new ValidationContextConverter ();
200
202
private Converter <Saml2AuthenticationToken , Decrypter > decrypterConverter = new DecrypterConverter ();
201
203
202
204
/**
@@ -209,6 +211,33 @@ public OpenSamlAuthenticationProvider() {
209
211
this .parserPool = this .registry .getParserPool ();
210
212
}
211
213
214
+ /**
215
+ * Set the the collection of {@link ConditionValidator}s used when validating an assertion.
216
+ *
217
+ * @param conditionValidators the collection of validators to use
218
+ * @since 5.4
219
+ */
220
+ public void setConditionValidators (
221
+ Collection <ConditionValidator > conditionValidators ) {
222
+
223
+ Assert .notEmpty (conditionValidators , "conditionValidators cannot be empty" );
224
+ this .conditionValidators = conditionValidators ;
225
+ }
226
+
227
+ /**
228
+ * Set the strategy for retrieving the {@link ValidationContext} used when
229
+ * validating an assertion.
230
+ *
231
+ * @param validationContextConverter the strategy to use
232
+ * @since 5.4
233
+ */
234
+ public void setValidationContextConverter (
235
+ Converter <Tuple , ValidationContext > validationContextConverter ) {
236
+
237
+ Assert .notNull (validationContextConverter , "validationContextConverter cannot be empty" );
238
+ this .validationContextConverter = validationContextConverter ;
239
+ }
240
+
212
241
/**
213
242
* Sets the {@link Converter} used for extracting assertion attributes that
214
243
* can be mapped to authorities.
@@ -238,8 +267,6 @@ public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
238
267
*/
239
268
public void setResponseTimeValidationSkew (Duration responseTimeValidationSkew ) {
240
269
this .responseTimeValidationSkew = responseTimeValidationSkew ;
241
- this .validationContextConverter = new ValidationContextConverter (
242
- params -> params .put (CLOCK_SKEW , responseTimeValidationSkew .toMillis ()));
243
270
}
244
271
245
272
/**
@@ -303,7 +330,7 @@ private void process(Saml2AuthenticationToken token, Response response) {
303
330
throw authException (INVALID_SIGNATURE , "Either the response or one of the assertions is unsigned. " +
304
331
"Please either sign the response or all of the assertions." );
305
332
}
306
- validationExceptions .putAll (validateAssertions (token , assertions ));
333
+ validationExceptions .putAll (validateAssertions (token , response ));
307
334
308
335
Assertion firstAssertion = CollectionUtils .firstElement (response .getAssertions ());
309
336
NameID nameId = decryptPrincipal (decrypter , firstAssertion );
@@ -392,7 +419,8 @@ private void process(Saml2AuthenticationToken token, Response response) {
392
419
}
393
420
394
421
private Map <String , Saml2AuthenticationException > validateAssertions
395
- (Saml2AuthenticationToken token , List <Assertion > assertions ) {
422
+ (Saml2AuthenticationToken token , Response response ) {
423
+ List <Assertion > assertions = response .getAssertions ();
396
424
if (assertions .isEmpty ()) {
397
425
throw authException (MALFORMED_RESPONSE_DATA , "No assertions found in response." );
398
426
}
@@ -401,14 +429,16 @@ private void process(Saml2AuthenticationToken token, Response response) {
401
429
if (logger .isDebugEnabled ()) {
402
430
logger .debug ("Validating " + assertions .size () + " assertions" );
403
431
}
432
+
433
+ Tuple tuple = new Tuple (token , response );
434
+ SAML20AssertionValidator validator = this .assertionValidatorConverter .convert (tuple );
435
+ ValidationContext context = this .validationContextConverter .convert (tuple );
404
436
for (Assertion assertion : assertions ) {
405
437
if (logger .isTraceEnabled ()) {
406
438
logger .trace ("Validating assertion " + assertion .getID ());
407
439
}
408
440
try {
409
- ValidationContext context = this .validationContextConverter .convert (token );
410
- ValidationResult result = this .assertionValidatorConverter .convert (token ).validate (assertion , context );
411
- if (result != ValidationResult .VALID ) {
441
+ if (validator .validate (assertion , context ) != ValidationResult .VALID ) {
412
442
String message = String .format ("Invalid assertion [%s] for SAML response [%s]: %s" ,
413
443
assertion .getID (), ((Response ) assertion .getParent ()).getID (),
414
444
context .getValidationFailureMessage ());
@@ -512,6 +542,7 @@ private Object getXSAnyObjectValue(XSAny xsAny) {
512
542
}
513
543
514
544
private static class SignatureTrustEngineConverter implements Converter <Saml2AuthenticationToken , SignatureTrustEngine > {
545
+
515
546
@ Override
516
547
public SignatureTrustEngine convert (Saml2AuthenticationToken token ) {
517
548
Set <Credential > credentials = new HashSet <>();
@@ -530,35 +561,27 @@ public SignatureTrustEngine convert(Saml2AuthenticationToken token) {
530
561
}
531
562
}
532
563
533
- private static class ValidationContextConverter implements Converter <Saml2AuthenticationToken , ValidationContext > {
534
- Consumer <Map <String , Object >> validationContextParametersConverter ;
535
-
536
- ValidationContextConverter (Consumer <Map <String , Object >> validationContextParametersConverter ) {
537
- this .validationContextParametersConverter = validationContextParametersConverter ;
538
- }
564
+ private class ValidationContextConverter implements Converter <Tuple , ValidationContext > {
539
565
540
566
@ Override
541
- public ValidationContext convert (Saml2AuthenticationToken token ) {
542
- String audience = token .getRelyingPartyRegistration ().getEntityId ();
543
- String recipient = token .getRelyingPartyRegistration ().getAssertionConsumerServiceLocation ();
567
+ public ValidationContext convert (Tuple tuple ) {
568
+ String audience = tuple . authentication .getRelyingPartyRegistration ().getEntityId ();
569
+ String recipient = tuple . authentication .getRelyingPartyRegistration ().getAssertionConsumerServiceLocation ();
544
570
Map <String , Object > params = new HashMap <>();
545
- params .put (CLOCK_SKEW , Duration . ofMinutes ( 5 ) .toMillis ());
571
+ params .put (CLOCK_SKEW , OpenSamlAuthenticationProvider . this . responseTimeValidationSkew .toMillis ());
546
572
params .put (COND_VALID_AUDIENCES , singleton (audience ));
547
573
params .put (SC_VALID_RECIPIENTS , singleton (recipient ));
548
574
params .put (SIGNATURE_REQUIRED , false ); // this verification is performed earlier
549
- this .validationContextParametersConverter .accept (params );
550
575
return new ValidationContext (params );
551
576
}
552
577
}
553
578
554
- private class SAML20AssertionValidatorConverter implements Converter <Saml2AuthenticationToken , SAML20AssertionValidator > {
555
- private final Collection <ConditionValidator > conditions = new ArrayList <>();
579
+ private class SAML20AssertionValidatorConverter implements Converter <Tuple , SAML20AssertionValidator > {
556
580
private final Collection <SubjectConfirmationValidator > subjects = new ArrayList <>();
557
581
private final Collection <StatementValidator > statements = new ArrayList <>();
558
582
private final SignaturePrevalidator validator = new SAMLSignatureProfileValidator ();
559
583
560
584
SAML20AssertionValidatorConverter () {
561
- this .conditions .add (new AudienceRestrictionConditionValidator ());
562
585
this .subjects .add (new BearerSubjectConfirmationValidator () {
563
586
@ Nonnull
564
587
@ Override
@@ -571,9 +594,11 @@ protected ValidationResult validateAddress(@Nonnull SubjectConfirmation confirma
571
594
}
572
595
573
596
@ Override
574
- public SAML20AssertionValidator convert (Saml2AuthenticationToken token ) {
575
- return new SAML20AssertionValidator (this .conditions , this .subjects , this .statements ,
576
- OpenSamlAuthenticationProvider .this .signatureTrustEngineConverter .convert (token ),
597
+ public SAML20AssertionValidator convert (Tuple tuple ) {
598
+ Collection <ConditionValidator > conditions =
599
+ OpenSamlAuthenticationProvider .this .conditionValidators ;
600
+ return new SAML20AssertionValidator (conditions , this .subjects , this .statements ,
601
+ OpenSamlAuthenticationProvider .this .signatureTrustEngineConverter .convert (tuple .authentication ),
577
602
this .validator );
578
603
}
579
604
}
@@ -616,4 +641,27 @@ private static Saml2AuthenticationException authException(String code, String de
616
641
617
642
return new Saml2AuthenticationException (validationError (code , description ), cause );
618
643
}
644
+
645
+ /**
646
+ * A tuple containing the authentication token and the associated OpenSAML {@link Response}.
647
+ *
648
+ * @since 5.4
649
+ */
650
+ public static class Tuple {
651
+ private final Saml2AuthenticationToken authentication ;
652
+ private final Response response ;
653
+
654
+ private Tuple (Saml2AuthenticationToken authentication , Response response ) {
655
+ this .authentication = authentication ;
656
+ this .response = response ;
657
+ }
658
+
659
+ public Saml2AuthenticationToken getAuthentication () {
660
+ return this .authentication ;
661
+ }
662
+
663
+ public Response getResponse () {
664
+ return this .response ;
665
+ }
666
+ }
619
667
}
0 commit comments