All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.swedenconnect.opensaml.saml2.assertion.validation.AssertionValidator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016-2024 Sweden Connect
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package se.swedenconnect.opensaml.saml2.assertion.validation;

import org.opensaml.saml.common.SAMLVersion;
import org.opensaml.saml.common.assertion.AssertionValidationException;
import org.opensaml.saml.common.assertion.ValidationContext;
import org.opensaml.saml.common.assertion.ValidationResult;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.saml2.assertion.ConditionValidator;
import org.opensaml.saml.saml2.assertion.SAML20AssertionValidator;
import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters;
import org.opensaml.saml.saml2.assertion.StatementValidator;
import org.opensaml.saml.saml2.assertion.SubjectConfirmationValidator;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.Condition;
import org.opensaml.saml.saml2.core.Conditions;
import org.opensaml.saml.saml2.core.Statement;
import org.opensaml.saml.saml2.core.Subject;
import org.opensaml.saml.saml2.core.SubjectConfirmation;
import org.opensaml.saml.saml2.metadata.EntityDescriptor;
import org.opensaml.xmlsec.signature.support.SignaturePrevalidator;
import org.opensaml.xmlsec.signature.support.SignatureTrustEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.swedenconnect.opensaml.common.validation.AbstractObjectValidator;
import se.swedenconnect.opensaml.common.validation.AbstractSignableObjectValidator;
import se.swedenconnect.opensaml.common.validation.CoreValidatorParameters;
import se.swedenconnect.opensaml.common.validation.ValidationSupport;
import se.swedenconnect.opensaml.common.validation.ValidationSupport.ValidationResultException;
import se.swedenconnect.opensaml.saml2.metadata.HolderOfKeyMetadataSupport;

import javax.xml.namespace.QName;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * A validator for {@code Assertion} objects.
 *
 * 

* Supports the following {@link ValidationContext} static parameters: *

*
    *
  • The static parameters defined for {@link AbstractSignableObjectValidator}.
  • *
  • {@link CoreValidatorParameters#SP_METADATA}: Required. The SP metadata.
  • *
  • {@link CoreValidatorParameters#IDP_METADATA}: Required. The IdP metadata.
  • *
  • {@link CoreValidatorParameters#STRICT_VALIDATION}: Optional. If not supplied, defaults to 'false'. Tells whether * strict validation should be performed.
  • *
  • {@link SAML2AssertionValidationParameters#CLOCK_SKEW}: Optional. Gives the number of milliseconds that is the * maximum allowed clock skew. If not given {@link SAML20AssertionValidator#DEFAULT_CLOCK_SKEW} is used.
  • *
  • {@link CoreValidatorParameters#MAX_AGE_MESSAGE}: Optional. Gives the maximum age (difference between issuance * time and the validation time). If not given, the {@link AbstractObjectValidator#DEFAULT_MAX_AGE_RECEIVED_MESSAGE} is * used.
  • *
  • {@link CoreValidatorParameters#RECEIVE_INSTANT}: Optional. Gives the timestamp (Instant) for when the response * message was received. If not given the current time is used.
  • *
  • {@link CoreValidatorParameters#AUTHN_REQUEST}: Required. Will be used in a number of validations when information * from the corresponding {@code AuthnRequest} is needed.
  • *
  • {@link CoreValidatorParameters#AUTHN_REQUEST_ID}: Required if the {@link CoreValidatorParameters#AUTHN_REQUEST} * is not assigned. Is used when validating the {@code InResponseTo} attribute of the response.
  • *
  • {@link CoreValidatorParameters#RECEIVE_URL}: Required. A String holding the URL on which we received the response * message. Is used when the {@code Destination} attribute is validated.
  • *
  • {@link CoreValidatorParameters#EXPECTED_ISSUER}: Optional. If set, is used when the issuer of the response is * validated. If not set, the issuer from the {@link CoreValidatorParameters#AUTHN_REQUEST} is used (if available).
  • *
  • {@link #RESPONSE_ISSUE_INSTANT}: Optional. If set, the IssueInstant of the Assertion being validated is compared * with the corresponding response issue instant.
  • *
* *

* Supports the following {@link ValidationContext} dynamic parameters: *

*
    *
  • {@link SAML2AssertionValidationParameters#CONFIRMED_SUBJECT_CONFIRMATION}: Optional. Will be present after * validation if subject confirmation was successfully performed.
  • *
  • {@link #HOK_PROFILE_ACTIVE}: Is set to indicate whether the holder-of-key WebSSO profile is active.
  • *
* *

* Note: Also check the validation context parameters defined by the {@code SubjectConfirmationValidator} and * {@code ConditionValidator} instances that are installed. *

* * @author Martin Lindström ([email protected]) */ public class AssertionValidator extends AbstractSignableObjectValidator { /** * Carries a {@link Instant} holding the issue instant of the Response that contained the assertion being validated. */ public static final String RESPONSE_ISSUE_INSTANT = CoreValidatorParameters.STD_PREFIX + ".ResponseIssueInstant"; /** * Tells whether the AuthnRequest corresponding to this assertion was sent to the IdP's holder of key-endpoints, i.e., * whether the Holder-of-key profile is in use. Carries a {@link Boolean}. */ public static final String HOK_PROFILE_ACTIVE = CoreValidatorParameters.STD_PREFIX + ".HokProfileActive"; /** Class logger. */ private static final Logger log = LoggerFactory.getLogger(AssertionValidator.class); /** Registered {@link SubjectConfirmation} validators. */ protected Map subjectConfirmationValidators; /** Registered {@link Condition} validators. */ protected Map conditionValidators; /** Registered {@link Statement} validators. */ private final Map statementValidators; /** * Constructor. * * @param trustEngine the trust used to validate the object's signature * @param signaturePrevalidator the signature pre-validator used to pre-validate the object's signature * @param confirmationValidators validators used to validate {@link SubjectConfirmation} methods within the * assertion * @param conditionValidators validators used to validate the {@link Condition} elements within the assertion * @param statementValidators validators used to validate {@link Statement}s within the assertion */ public AssertionValidator(final SignatureTrustEngine trustEngine, final SignaturePrevalidator signaturePrevalidator, final Collection confirmationValidators, final Collection conditionValidators, final Collection statementValidators) { super(trustEngine, signaturePrevalidator); this.subjectConfirmationValidators = new HashMap<>(); if (confirmationValidators != null) { for (final SubjectConfirmationValidator validator : confirmationValidators) { if (validator != null) { this.subjectConfirmationValidators.put(validator.getServicedMethod(), validator); } } } this.conditionValidators = new HashMap<>(); if (conditionValidators != null) { for (final ConditionValidator validator : conditionValidators) { if (validator != null) { this.conditionValidators.put(validator.getServicedCondition(), validator); } } } this.statementValidators = new HashMap<>(); if (statementValidators != null) { for (final StatementValidator validator : statementValidators) { if (validator != null) { this.statementValidators.put(validator.getServicedStatement(), validator); } } } } /** * Validates the assertion. */ @Override public ValidationResult validate(final Assertion assertion, final ValidationContext context) { try { ValidationSupport.check(this.validateID(assertion, context)); ValidationSupport.check(this.validateVersion(assertion, context)); ValidationSupport.check(this.validateIssueInstant(assertion, context)); ValidationSupport.check(this.validateIssuer(assertion, context)); ValidationSupport.check(this.validateHolderOfKeyRequirement(assertion, context)); ValidationSupport.check(this.validateSignature(assertion, context)); ValidationSupport.check(this.validateSubject(assertion, context)); ValidationSupport.check(this.validateConditions(assertion, context)); ValidationSupport.check(this.validateStatements(assertion, context)); } catch (final ValidationResultException e) { return e.getResult(); } return ValidationResult.VALID; } /** * Validates that the {@code Assertion} object has an ID attribute. * * @param assertion the assertion * @param context the validation context * @return a validation result */ protected ValidationResult validateID(final Assertion assertion, final ValidationContext context) { if (assertion.getID() == null || assertion.getID().isBlank()) { context.getValidationFailureMessages().add("Missing ID attribute in Assertion"); return ValidationResult.INVALID; } return ValidationResult.VALID; } /** * Validates that the {@code Response} object has a valid Version attribute. * * @param assertion the assertion * @param context the validation context * @return a validation result */ protected ValidationResult validateVersion(final Assertion assertion, final ValidationContext context) { if (assertion.getVersion() == null || !assertion.getVersion().toString().equals(SAMLVersion.VERSION_20.toString())) { context.getValidationFailureMessages().add("Invalid SAML version in Assertion"); return ValidationResult.INVALID; } return ValidationResult.VALID; } /** * Validates that the {@code Assertion} object has a IssueInstant attribute and checks that its value is OK. If the * response that contained the assertion was previously validated the static context parameter * {@link #RESPONSE_ISSUE_INSTANT} should be passed. If so, the method checks that the assertion issue instant is not * after the response issue instant. Otherwise, the method checks that the IssueInstant is not too old given the * {@link CoreValidatorParameters#MAX_AGE_MESSAGE} and {@link CoreValidatorParameters#RECEIVE_INSTANT} context * parameters. * * @param assertion the response * @param context the validation context * @return a validation result */ protected ValidationResult validateIssueInstant(final Assertion assertion, final ValidationContext context) { if (assertion.getIssueInstant() == null) { context.getValidationFailureMessages().add("Missing IssueInstant attribute in Assertion"); return ValidationResult.INVALID; } // Is the response issue instance specified? If so, we only check that the assertion issue instant // is before the response issue instant. In these cases we assume that the response issue instant // has been verified. // final Instant responseIssueInstant = this.getResponseIssueInstant(context); if (responseIssueInstant != null) { if (assertion.getIssueInstant().isAfter(responseIssueInstant)) { final String msg = String.format("Invalid Assertion - Its issue-instant (%s) is after the response message issue-instant (%s)", assertion.getIssueInstant(), responseIssueInstant); context.getValidationFailureMessages().add(msg); return ValidationResult.INVALID; } } else { // Otherwise, we have to make more checks. final Instant receiveInstant = getReceiveInstant(context); final long receiveInstantMillis = receiveInstant.toEpochMilli(); final Instant issueInstant = assertion.getIssueInstant(); final long issueInstantMillis = issueInstant.toEpochMilli(); final Duration maxAgeResponse = getMaxAgeReceivedMessage(context); final Duration allowedClockSkew = getAllowedClockSkew(context); // Too old? // if ((receiveInstantMillis - issueInstantMillis) > (maxAgeResponse.toMillis() + allowedClockSkew.toMillis())) { final String msg = String.format("Received Assertion is too old - issue-instant: %s - receive-time: %s", assertion.getIssueInstant(), receiveInstant); context.getValidationFailureMessages().add(msg); return ValidationResult.INVALID; } // Not yet valid? -> Clock skew is unacceptable. // if ((issueInstantMillis - receiveInstantMillis) > allowedClockSkew.toMillis()) { final String msg = String.format("Issue-instant of Assertion (%s) is newer than receive time (%s) - Non accepted clock skew", assertion.getIssueInstant(), receiveInstant); context.getValidationFailureMessages().add(msg); return ValidationResult.INVALID; } } return ValidationResult.VALID; } /** * Gets the {@link #RESPONSE_ISSUE_INSTANT} setting. * * @param context the context * @return the response issue instant, or null if it is not set */ protected Instant getResponseIssueInstant(final ValidationContext context) { final Object object = context.getStaticParameters().get(RESPONSE_ISSUE_INSTANT); if (object != null) { if (object instanceof Instant) { return (Instant) object; } else if (object instanceof Long) { return Instant.ofEpochMilli((Long) object); } } return null; } /** * Ensures that the {@code Issuer} element is present and matches the expected issuer (if set in the context under the * {@link CoreValidatorParameters#EXPECTED_ISSUER} key). * * @param assertion the assertion * @param context the validation context * @return a validation result */ protected ValidationResult validateIssuer(final Assertion assertion, final ValidationContext context) { if (assertion.getIssuer() == null || assertion.getIssuer().getValue() == null) { context.getValidationFailureMessages().add("Missing Issuer element in Assertion"); return ValidationResult.INVALID; } final String expectedIssuer = (String) context.getStaticParameters().get(CoreValidatorParameters.EXPECTED_ISSUER); if (expectedIssuer != null) { if (!assertion.getIssuer().getValue().equals(expectedIssuer)) { final String msg = String.format("Issuer of Assertion (%s) did not match expected issuer (%s)", assertion.getIssuer().getValue(), expectedIssuer); context.getValidationFailureMessages().add(msg); return ValidationResult.INVALID; } } else { log.warn("EXPECTED_ISSUER key not set - will not check issuer of Assertion"); } return ValidationResult.VALID; } /** * Performs initial validation concerning the Holder-of-key WebSSO Profile. The method checks that if the request was * sent to an IdP HoK-endpoint, we verify that the SP received the response on an endpoint dedicated for HoK. *

* The method also sets the dynamic validation parameter {@link #HOK_PROFILE_ACTIVE}. *

* * @param assertion the assertion * @param context the validation context * @return a validation result */ protected ValidationResult validateHolderOfKeyRequirement(final Assertion assertion, final ValidationContext context) { // First see if the caller has informed us about the HoK status. // In that case we are done. // if (Optional.ofNullable(context.getStaticParameters().get(HOK_PROFILE_ACTIVE)).map(Boolean.class::cast) .orElse(null) != null) { context.getDynamicParameters().put(HOK_PROFILE_ACTIVE, context.getStaticParameters().get(HOK_PROFILE_ACTIVE)); return ValidationResult.VALID; } Boolean hokActive = Boolean.FALSE; try { final String receiveUrl = (String) context.getStaticParameters().get(CoreValidatorParameters.RECEIVE_URL); if (receiveUrl == null) { final String msg = String.format("Could not determine if Holder-of-key profile is active. '%s' parameter is missing", CoreValidatorParameters.RECEIVE_URL); log.debug(msg); context.getValidationFailureMessages().add(msg); return ValidationResult.INDETERMINATE; } final AuthnRequest authnRequest = (AuthnRequest) context.getStaticParameters().get(CoreValidatorParameters.AUTHN_REQUEST); if (authnRequest == null || authnRequest.getDestination() == null) { final String msg = String.format("Could not determine if Holder-of-key profile is active." + "'%s' parameter is missing or no Destination was set in AuthnRequest", CoreValidatorParameters.AUTHN_REQUEST); log.debug(msg); context.getValidationFailureMessages().add(msg); return ValidationResult.INDETERMINATE; } final String destination = authnRequest.getDestination(); final EntityDescriptor idpMetadata = (EntityDescriptor) context.getStaticParameters().get(CoreValidatorParameters.IDP_METADATA); if (idpMetadata == null) { final String msg = String.format("Could not determine if Holder-of-key profile is active. '%s' parameter is missing", CoreValidatorParameters.IDP_METADATA); log.debug(msg); context.getValidationFailureMessages().add(msg); return ValidationResult.INDETERMINATE; } hokActive = idpMetadata.getIDPSSODescriptor(SAMLConstants.SAML20P_NS).getSingleSignOnServices().stream() .filter(s -> s.getLocation() != null && s.getLocation().equals(destination)) .anyMatch(HolderOfKeyMetadataSupport::isHoKSingleSignOnService); if (!hokActive) { // HoK is not active. return ValidationResult.VALID; } final EntityDescriptor spMetadata = (EntityDescriptor) context.getStaticParameters().get(CoreValidatorParameters.SP_METADATA); if (spMetadata == null) { final String msg = String.format("Could not determine if Holder-of-key profile is active. '%s' parameter is missing", CoreValidatorParameters.SP_METADATA); log.debug(msg); context.getValidationFailureMessages().add(msg); return ValidationResult.INDETERMINATE; } if (spMetadata.getSPSSODescriptor(SAMLConstants.SAML20P_NS).getAssertionConsumerServices().stream() .filter(s -> receiveUrl.equals(s.getLocation())) .filter(HolderOfKeyMetadataSupport::isHoKAssertionConsumerService) .findFirst().isEmpty()) { final String msg = "Expected response to be delivered to a Holder-of-key AssertionConsumerService endpoint"; log.debug(msg); context.getValidationFailureMessages().add(msg); return ValidationResult.INVALID; } return ValidationResult.VALID; } finally { context.getDynamicParameters().put(HOK_PROFILE_ACTIVE, hokActive); } } /** * Validates the {@code Subject} element of the assertion. The default implementation returns * {@link ValidationResult#VALID} if there is no {@code Subject} element since it is optional according to the SAML * 2.0 Core specifications. * * @param assertion the assertion * @param context the validation context * @return a validation result */ protected ValidationResult validateSubject(final Assertion assertion, final ValidationContext context) { if (assertion.getSubject() == null) { // Assertions containing AuthnStatements must contain a Subject. // if (assertion.getAuthnStatements() != null && !assertion.getAuthnStatements().isEmpty()) { context.getValidationFailureMessages().add("Assertion contains AuthnStatement but no Subject - invalid"); return ValidationResult.INVALID; } log.debug("Assertion does not contain a Subject element - allowed by default assertion validator"); return ValidationResult.VALID; } final Subject subject = assertion.getSubject(); final List confirmations = subject.getSubjectConfirmations(); if (confirmations == null || confirmations.isEmpty()) { final boolean hokProfileActive = Optional.ofNullable(context.getDynamicParameters().get(HOK_PROFILE_ACTIVE)) .map(Boolean.class::cast).orElse(Boolean.FALSE); if (hokProfileActive) { final String msg = String.format("No subject confirmation for method '%s' found for assertion with ID '%s'", SubjectConfirmation.METHOD_HOLDER_OF_KEY, assertion.getID()); log.debug(msg); context.getValidationFailureMessages().add(msg); return ValidationResult.INVALID; } else { log.debug( "Assertion contains no SubjectConfirmations, default assertion validator skips subject confirmation"); return ValidationResult.VALID; } } return this.validateSubjectConfirmations(assertion, confirmations, context); } /** * Validates the subject confirmations and for the one that is confirmed, it is saved in the validation context under * the {@link SAML2AssertionValidationParameters#CONFIRMED_SUBJECT_CONFIRMATION} key. * * @param assertion the assertion * @param subjectConfirmations the subject confirmations * @param context the validation context * @return a validation result */ protected ValidationResult validateSubjectConfirmations(final Assertion assertion, final List subjectConfirmations, final ValidationContext context) { final boolean hokProfileActive = Optional.ofNullable(context.getDynamicParameters().get(HOK_PROFILE_ACTIVE)) .map(Boolean.class::cast).orElse(Boolean.FALSE); for (final SubjectConfirmation confirmation : subjectConfirmations) { if (hokProfileActive && !SubjectConfirmation.METHOD_HOLDER_OF_KEY.equals(confirmation.getMethod())) { log.info("Holder-of-key profile is active - Ignoring SubjectConfirmation with method '{}'", confirmation.getMethod()); continue; } final SubjectConfirmationValidator validator = this.subjectConfirmationValidators.get(confirmation.getMethod()); if (validator != null) { try { final ValidationResult r = validator.validate(confirmation, assertion, context); if (r == ValidationResult.VALID) { context.getDynamicParameters().put( SAML2AssertionValidationParameters.CONFIRMED_SUBJECT_CONFIRMATION, confirmation); return ValidationResult.VALID; } else { if (context.getValidationFailureMessages().isEmpty()) { context.getValidationFailureMessages().add( String.format("Validation of SubjectConfirmation with method '%s' failed", confirmation.getMethod())); log.info("{}", String.join(", ", context.getValidationFailureMessages())); } else { log.info("Validation of SubjectConfirmation with method '{}' failed - {}", confirmation.getMethod(), String.join(", ", context.getValidationFailureMessages())); } if (hokProfileActive) { return ValidationResult.INVALID; } } } catch (final Exception e) { log.warn("Error while executing subject confirmation validation {}", validator.getClass().getName(), e); final String msg = String.format("Error during validation of subject confirmation data - %s", e.getMessage()); context.getValidationFailureMessages().add(msg); return ValidationResult.INVALID; } } else { log.info("No validator installed for SubjectConfirmation method '{}'", confirmation.getMethod()); } } final String msg = String.format("No subject confirmation methods were met for assertion with ID '%s'", assertion.getID()); log.debug(msg); context.getValidationFailureMessages().add(msg); return ValidationResult.INVALID; } /** * Validates the {@code Conditions} elements of the assertion. * * @param assertion the assertion * @param context the validation context * @return the validation result */ protected ValidationResult validateConditions(final Assertion assertion, final ValidationContext context) { final Conditions conditions = assertion.getConditions(); if (conditions == null) { log.debug("Assertion contained no Conditions element - allowed by default assertion validator"); return ValidationResult.VALID; } final ValidationResult timeboundsResult = this.validateConditionsTimeBounds(assertion, context); if (timeboundsResult != ValidationResult.VALID) { return timeboundsResult; } for (final Condition condition : conditions.getConditions()) { ConditionValidator validator = this.conditionValidators.get(condition.getElementQName()); if (validator == null && condition.getSchemaType() != null) { validator = this.conditionValidators.get(condition.getSchemaType()); } if (validator == null) { final String msg = String.format("Unknown Condition '%s' of type '%s' in assertion '%s'", condition.getElementQName(), condition.getSchemaType(), assertion.getID()); log.warn(msg); if (isStrictValidation(context)) { context.getValidationFailureMessages().add(msg); return ValidationResult.INDETERMINATE; } else { continue; } } ValidationResult r; try { r = validator.validate(condition, assertion, context); } catch (final AssertionValidationException e) { log.error("Failed Conditions validation - {}", e.getMessage()); log.debug("", e); context.getValidationFailureMessages().add(e.getMessage()); r = ValidationResult.INVALID; } if (r != ValidationResult.VALID) { final String msg = String.format("Condition '%s' of type '%s' in assertion '%s' was not valid - %s.", condition.getElementQName(), condition.getSchemaType(), assertion.getID(), String.join(", ", context.getValidationFailureMessages())); log.debug(msg); context.getValidationFailureMessages().add(msg); return ValidationResult.INVALID; } } return ValidationResult.VALID; } /** * Validates the NotBefore and NotOnOrAfter Conditions constraints on the assertion. * * @param assertion the assertion whose conditions will be validated * @param context current validation context * @return the result of the validation evaluation */ protected ValidationResult validateConditionsTimeBounds(final Assertion assertion, final ValidationContext context) { final Conditions conditions = assertion.getConditions(); if (conditions == null) { return ValidationResult.VALID; } final Duration clockSkew = getAllowedClockSkew(context); final Instant receiveInstant = getReceiveInstant(context); final Instant notBefore = conditions.getNotBefore(); log.debug("Evaluating Conditions NotBefore '{}' against 'skewed now' time '{}'", notBefore, receiveInstant.plus(clockSkew)); if (notBefore != null && notBefore.isAfter(receiveInstant.plus(clockSkew))) { context.getValidationFailureMessages().add(String.format( "Assertion '%s' with NotBefore condition of '%s' is not yet valid", assertion.getID(), notBefore)); return ValidationResult.INVALID; } final Instant notOnOrAfter = conditions.getNotOnOrAfter(); log.debug("Evaluating Conditions NotOnOrAfter '{}' against 'skewed now' time '{}'", notOnOrAfter, receiveInstant.minus(clockSkew)); if (notOnOrAfter != null && notOnOrAfter.isBefore(receiveInstant.minus(clockSkew))) { context.getValidationFailureMessages().add(String.format( "Assertion '%s' with NotOnOrAfter condition of '%s' is no longer valid", assertion.getID(), notOnOrAfter)); return ValidationResult.INVALID; } return ValidationResult.VALID; } /** * Validates the statements of the assertion using the registered {@link StatementValidator} instance. * * @param assertion the assertion to validate * @param context the validation context * @return validation result */ protected ValidationResult validateStatements(final Assertion assertion, final ValidationContext context) { final List statements = assertion.getStatements(); if (statements == null || statements.isEmpty()) { return ValidationResult.VALID; } StatementValidator validator; for (final Statement statement : statements) { validator = this.statementValidators.get(statement.getElementQName()); if (validator == null && statement.getSchemaType() != null) { validator = this.statementValidators.get(statement.getSchemaType()); } if (validator != null) { ValidationResult result; try { result = validator.validate(statement, assertion, context); } catch (final AssertionValidationException e) { log.error("Failed Statement validation - {}", e.getMessage()); log.debug("", e); context.getValidationFailureMessages().add(e.getMessage()); result = ValidationResult.INVALID; } if (result != ValidationResult.VALID) { return result; } } } return ValidationResult.VALID; } /** * Returns the Assertion issuer. */ @Override protected String getIssuer(final Assertion signableObject) { return signableObject.getIssuer() != null ? signableObject.getIssuer().getValue() : null; } /** * Returns the Assertion ID. */ @Override protected String getID(final Assertion signableObject) { return signableObject.getID(); } /** {@inheritDoc} */ @Override protected String getObjectName() { return "Assertion"; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy