Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow defining custom SAML 2.0 Assertion Signature Validator #10317

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv

private Converter<ResponseToken, Saml2ResponseValidatorResult> responseValidator = createDefaultResponseValidator();

private final Converter<AssertionToken, Saml2ResponseValidatorResult> assertionSignatureValidator = createDefaultAssertionSignatureValidator();
private Converter<AssertionToken, Saml2ResponseValidatorResult> assertionSignatureValidator = createDefaultAssertionSignatureValidator();

private Consumer<AssertionToken> assertionElementsDecrypter = createDefaultAssertionElementsDecrypter();

Expand Down Expand Up @@ -235,6 +235,29 @@ public void setResponseValidator(Converter<ResponseToken, Saml2ResponseValidator
this.responseValidator = responseValidator;
}

/**
* Set the {@link Converter} to use for validating the SAML 2.0 Assertions Signature.
*
* You can still invoke the default validator by delegating to
* {@link #createDefaultAssertionSignatureValidator()}, like so:
*
* <pre>
* OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();
* provider.setAssertionSignatureValidator(assertionToken -&gt; {
* Saml2ResponseValidatorResult result = createDefaultAssertionSignatureValidator()
* .convert(assertionToken)
* return result.concat(myCustomValidator.convert(assertionToken));
* });
* </pre>
* @param assertionSignatureValidator the {@link Converter} to use
* @since 5.6
*/
public void setAssertionSignatureValidator(
Converter<AssertionToken, Saml2ResponseValidatorResult> assertionSignatureValidator) {
Assert.notNull(assertionSignatureValidator, "assertionSignatureValidator cannot be null");
this.assertionSignatureValidator = assertionSignatureValidator;
}

/**
* Set the {@link Converter} to use for validating each {@link Assertion} in the SAML
* 2.0 Response.
Expand Down Expand Up @@ -386,6 +409,32 @@ public static Converter<ResponseToken, Saml2ResponseValidatorResult> createDefau
};
}

/**
* Construct a default strategy for validating each SAML 2.0 Assertion Signature
* @return the default assertion signature validator strategy
* @since 5.6
*/
public static Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionSignatureValidator() {
return createDefaultAssertionSignatureValidator((assertionToken) -> new ValidationContext(
Collections.singletonMap(SAML2AssertionValidationParameters.SIGNATURE_REQUIRED, false)));
}

/**
* Construct a default strategy for validating each SAML 2.0 Assertion Signature
* @param contextConverter the conversion strategy to use to generate a
* {@link ValidationContext} for each assertion being validated
* @return the default assertion signature validator strategy
* @since 5.6
*/
public static Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionSignatureValidator(
Converter<AssertionToken, ValidationContext> contextConverter) {
return createAssertionValidator(Saml2ErrorCodes.INVALID_SIGNATURE, (assertionToken) -> {
RelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration();
SignatureTrustEngine engine = OpenSamlVerificationUtils.trustEngine(registration);
return SAML20AssertionValidators.createSignatureValidator(engine);
}, contextConverter);
}

/**
* Construct a default strategy for validating each SAML 2.0 Assertion and associated
* {@link Authentication} token
Expand Down Expand Up @@ -560,15 +609,6 @@ private static String getStatusCode(Response response) {
return response.getStatus().getStatusCode().getValue();
}

private Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionSignatureValidator() {
return createAssertionValidator(Saml2ErrorCodes.INVALID_SIGNATURE, (assertionToken) -> {
RelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration();
SignatureTrustEngine engine = OpenSamlVerificationUtils.trustEngine(registration);
return SAML20AssertionValidators.createSignatureValidator(engine);
}, (assertionToken) -> new ValidationContext(
Collections.singletonMap(SAML2AssertionValidationParameters.SIGNATURE_REQUIRED, false)));
}

private Consumer<AssertionToken> createDefaultAssertionElementsDecrypter() {
return (assertionToken) -> {
Assertion assertion = assertionToken.getAssertion();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,34 @@ public void authenticateWhenCustomResponseValidatorThenUses() {
verify(validator).convert(any(OpenSaml4AuthenticationProvider.ResponseToken.class));
}

@Test
public void setAssertionSignatureValidatorWhenNullThenIllegalArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> this.provider.setAssertionSignatureValidator(null));
}

@Test
public void authenticateWhenCustomAssertionSignatureValidatorThenUses() {
Converter<OpenSaml4AuthenticationProvider.AssertionToken, Saml2ResponseValidatorResult> validator = mock(
Converter.class);
OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();
// @formatter:off
provider.setAssertionSignatureValidator((responseToken) -> OpenSaml4AuthenticationProvider.createDefaultAssertionSignatureValidator()
.convert(responseToken)
.concat(validator.convert(responseToken))
);
// @formatter:on
Response response = response();
Assertion assertion = assertion();
response.getAssertions().add(assertion);
TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(),
ASSERTING_PARTY_ENTITY_ID);
Saml2AuthenticationToken token = token(response, verifying(registration()));
given(validator.convert(any(OpenSaml4AuthenticationProvider.AssertionToken.class)))
.willReturn(Saml2ResponseValidatorResult.success());
provider.authenticate(token);
verify(validator).convert(any(OpenSaml4AuthenticationProvider.AssertionToken.class));
}

private <T extends XMLObject> T build(QName qName) {
return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);
}
Expand Down