65
65
import org .opensaml .saml .saml2 .core .OneTimeUse ;
66
66
import org .opensaml .saml .saml2 .core .Response ;
67
67
import org .opensaml .saml .saml2 .core .StatusCode ;
68
- import org .opensaml .saml .saml2 .core .Subject ;
69
68
import org .opensaml .saml .saml2 .core .SubjectConfirmation ;
70
69
import org .opensaml .saml .saml2 .core .SubjectConfirmationData ;
71
70
import org .opensaml .saml .saml2 .core .impl .AuthnRequestUnmarshaller ;
@@ -146,6 +145,13 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv
146
145
147
146
private final ResponseUnmarshaller responseUnmarshaller ;
148
147
148
+ private static final AuthnRequestUnmarshaller authnRequestUnmarshaller ;
149
+ static {
150
+ XMLObjectProviderRegistry registry = ConfigurationService .get (XMLObjectProviderRegistry .class );
151
+ authnRequestUnmarshaller = (AuthnRequestUnmarshaller ) registry .getUnmarshallerFactory ()
152
+ .getUnmarshaller (AuthnRequest .DEFAULT_ELEMENT_NAME );
153
+ }
154
+
149
155
private final ParserPool parserPool ;
150
156
151
157
private final Converter <ResponseToken , Saml2ResponseValidatorResult > responseSignatureValidator = createDefaultResponseSignatureValidator ();
@@ -355,37 +361,6 @@ public void setResponseAuthenticationConverter(
355
361
this .responseAuthenticationConverter = responseAuthenticationConverter ;
356
362
}
357
363
358
- private static Saml2ResponseValidatorResult validateInResponseTo (AbstractSaml2AuthenticationRequest storedRequest ,
359
- String inResponseTo ) {
360
- if (!StringUtils .hasText (inResponseTo )) {
361
- return Saml2ResponseValidatorResult .success ();
362
- }
363
- AuthnRequest request ;
364
- try {
365
- request = parseRequest (storedRequest );
366
- }
367
- catch (Exception ex ) {
368
- String message = "The stored AuthNRequest could not be properly deserialized [" + ex .getMessage () + "]" ;
369
- return Saml2ResponseValidatorResult
370
- .failure (new Saml2Error (Saml2ErrorCodes .MALFORMED_REQUEST_DATA , message ));
371
- }
372
- if (request == null ) {
373
- String message = "The response contained an InResponseTo attribute [" + inResponseTo + "]"
374
- + " but no saved AuthNRequest request was found" ;
375
- return Saml2ResponseValidatorResult
376
- .failure (new Saml2Error (Saml2ErrorCodes .INVALID_IN_RESPONSE_TO , message ));
377
- }
378
- else if (!request .getID ().equals (inResponseTo )) {
379
- String message = "The InResponseTo attribute [" + inResponseTo + "] does not match the ID of the "
380
- + "AuthNRequest [" + request .getID () + "]" ;
381
- return Saml2ResponseValidatorResult
382
- .failure (new Saml2Error (Saml2ErrorCodes .INVALID_IN_RESPONSE_TO , message ));
383
- }
384
- else {
385
- return Saml2ResponseValidatorResult .success ();
386
- }
387
- }
388
-
389
364
/**
390
365
* Construct a default strategy for validating the SAML 2.0 Response
391
366
* @return the default response validator strategy
@@ -428,6 +403,27 @@ public static Converter<ResponseToken, Saml2ResponseValidatorResult> createDefau
428
403
};
429
404
}
430
405
406
+ private static Saml2ResponseValidatorResult validateInResponseTo (AbstractSaml2AuthenticationRequest storedRequest ,
407
+ String inResponseTo ) {
408
+ if (!StringUtils .hasText (inResponseTo )) {
409
+ return Saml2ResponseValidatorResult .success ();
410
+ }
411
+ AuthnRequest request = parseRequest (storedRequest );
412
+ if (request == null ) {
413
+ String message = "The response contained an InResponseTo attribute [" + inResponseTo + "]"
414
+ + " but no saved authentication request was found" ;
415
+ return Saml2ResponseValidatorResult
416
+ .failure (new Saml2Error (Saml2ErrorCodes .INVALID_IN_RESPONSE_TO , message ));
417
+ }
418
+ if (!inResponseTo .equals (request .getID ())) {
419
+ String message = "The InResponseTo attribute [" + inResponseTo + "] does not match the ID of the "
420
+ + "authentication request [" + request .getID () + "]" ;
421
+ return Saml2ResponseValidatorResult
422
+ .failure (new Saml2Error (Saml2ErrorCodes .INVALID_IN_RESPONSE_TO , message ));
423
+ }
424
+ return Saml2ResponseValidatorResult .success ();
425
+ }
426
+
431
427
/**
432
428
* Construct a default strategy for validating each SAML 2.0 Assertion and associated
433
429
* {@link Authentication} token
@@ -522,28 +518,6 @@ private Response parseResponse(String response) throws Saml2Exception, Saml2Auth
522
518
}
523
519
}
524
520
525
- private static AuthnRequest parseRequest (AbstractSaml2AuthenticationRequest request ) throws Exception {
526
- if (request == null ) {
527
- return null ;
528
- }
529
- String samlRequest = request .getSamlRequest ();
530
- if (!StringUtils .hasText (samlRequest )) {
531
- return null ;
532
- }
533
- if (request .getBinding () == Saml2MessageBinding .REDIRECT ) {
534
- samlRequest = Saml2Utils .samlInflate (Saml2Utils .samlDecode (samlRequest ));
535
- }
536
- else {
537
- samlRequest = new String (Saml2Utils .samlDecode (samlRequest ), StandardCharsets .UTF_8 );
538
- }
539
- Document document = XMLObjectProviderRegistrySupport .getParserPool ()
540
- .parse (new ByteArrayInputStream (samlRequest .getBytes (StandardCharsets .UTF_8 )));
541
- Element element = document .getDocumentElement ();
542
- AuthnRequestUnmarshaller unmarshaller = (AuthnRequestUnmarshaller ) XMLObjectProviderRegistrySupport
543
- .getUnmarshallerFactory ().getUnmarshaller (AuthnRequest .DEFAULT_ELEMENT_NAME );
544
- return (AuthnRequest ) unmarshaller .unmarshall (element );
545
- }
546
-
547
521
private void process (Saml2AuthenticationToken token , Response response ) {
548
522
String issuer = response .getIssuer ().getValue ();
549
523
this .logger .debug (LogMessage .format ("Processing SAML response from %s" , issuer ));
@@ -748,40 +722,18 @@ private static Converter<AssertionToken, Saml2ResponseValidatorResult> createAss
748
722
};
749
723
}
750
724
751
- private static boolean assertionContainsInResponseTo (Assertion assertion ) {
752
- Subject subject = (assertion != null ) ? assertion .getSubject () : null ;
753
- List <SubjectConfirmation > confirmations = (subject != null ) ? subject .getSubjectConfirmations ()
754
- : new ArrayList <>();
755
- return confirmations .stream ().filter ((confirmation ) -> {
756
- SubjectConfirmationData confirmationData = confirmation .getSubjectConfirmationData ();
757
- return confirmationData != null && StringUtils .hasText (confirmationData .getInResponseTo ());
758
- }).findFirst ().orElse (null ) != null ;
759
- }
760
-
761
- private static void addRequestIdToValidationContext (AbstractSaml2AuthenticationRequest storedRequest ,
762
- Map <String , Object > context ) {
763
- String requestId = null ;
764
- try {
765
- AuthnRequest request = parseRequest (storedRequest );
766
- requestId = (request != null ) ? request .getID () : null ;
767
- }
768
- catch (Exception ex ) {
769
- }
770
- if (StringUtils .hasText (requestId )) {
771
- context .put (SAML2AssertionValidationParameters .SC_VALID_IN_RESPONSE_TO , requestId );
772
- }
773
- }
774
-
775
725
private static ValidationContext createValidationContext (AssertionToken assertionToken ,
776
726
Consumer <Map <String , Object >> paramsConsumer ) {
777
- RelyingPartyRegistration relyingPartyRegistration = assertionToken .token .getRelyingPartyRegistration ();
727
+ Saml2AuthenticationToken token = assertionToken .token ;
728
+ RelyingPartyRegistration relyingPartyRegistration = token .getRelyingPartyRegistration ();
778
729
String audience = relyingPartyRegistration .getEntityId ();
779
730
String recipient = relyingPartyRegistration .getAssertionConsumerServiceLocation ();
780
731
String assertingPartyEntityId = relyingPartyRegistration .getAssertingPartyDetails ().getEntityId ();
781
732
Map <String , Object > params = new HashMap <>();
782
733
Assertion assertion = assertionToken .getAssertion ();
783
734
if (assertionContainsInResponseTo (assertion )) {
784
- addRequestIdToValidationContext (assertionToken .token .getAuthenticationRequest (), params );
735
+ String requestId = getAuthnRequestId (token .getAuthenticationRequest ());
736
+ params .put (SAML2AssertionValidationParameters .SC_VALID_IN_RESPONSE_TO , requestId );
785
737
}
786
738
params .put (SAML2AssertionValidationParameters .COND_VALID_AUDIENCES , Collections .singleton (audience ));
787
739
params .put (SAML2AssertionValidationParameters .SC_VALID_RECIPIENTS , Collections .singleton (recipient ));
@@ -790,6 +742,56 @@ private static ValidationContext createValidationContext(AssertionToken assertio
790
742
return new ValidationContext (params );
791
743
}
792
744
745
+ private static boolean assertionContainsInResponseTo (Assertion assertion ) {
746
+ if (assertion .getSubject () == null ) {
747
+ return false ;
748
+ }
749
+ for (SubjectConfirmation confirmation : assertion .getSubject ().getSubjectConfirmations ()) {
750
+ SubjectConfirmationData confirmationData = confirmation .getSubjectConfirmationData ();
751
+ if (confirmationData == null ) {
752
+ continue ;
753
+ }
754
+ if (StringUtils .hasText (confirmationData .getInResponseTo ())) {
755
+ return true ;
756
+ }
757
+ }
758
+ return false ;
759
+ }
760
+
761
+ private static String getAuthnRequestId (AbstractSaml2AuthenticationRequest serialized ) {
762
+ AuthnRequest request = parseRequest (serialized );
763
+ if (request == null ) {
764
+ return null ;
765
+ }
766
+ return request .getID ();
767
+ }
768
+
769
+ private static AuthnRequest parseRequest (AbstractSaml2AuthenticationRequest request ) {
770
+ if (request == null ) {
771
+ return null ;
772
+ }
773
+ String samlRequest = request .getSamlRequest ();
774
+ if (!StringUtils .hasText (samlRequest )) {
775
+ return null ;
776
+ }
777
+ if (request .getBinding () == Saml2MessageBinding .REDIRECT ) {
778
+ samlRequest = Saml2Utils .samlInflate (Saml2Utils .samlDecode (samlRequest ));
779
+ }
780
+ else {
781
+ samlRequest = new String (Saml2Utils .samlDecode (samlRequest ), StandardCharsets .UTF_8 );
782
+ }
783
+ try {
784
+ Document document = XMLObjectProviderRegistrySupport .getParserPool ()
785
+ .parse (new ByteArrayInputStream (samlRequest .getBytes (StandardCharsets .UTF_8 )));
786
+ Element element = document .getDocumentElement ();
787
+ return (AuthnRequest ) authnRequestUnmarshaller .unmarshall (element );
788
+ }
789
+ catch (Exception ex ) {
790
+ String message = "Failed to deserialize associated authentication request [" + ex .getMessage () + "]" ;
791
+ throw createAuthenticationException (Saml2ErrorCodes .MALFORMED_REQUEST_DATA , message , ex );
792
+ }
793
+ }
794
+
793
795
private static class SAML20AssertionValidators {
794
796
795
797
private static final Collection <ConditionValidator > conditions = new ArrayList <>();
0 commit comments