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

se.litsec.opensaml.saml2.common.assertion.AuthnStatementValidator Maven / Gradle / Ivy

There is a newer version: 1.4.5
Show newest version
/*
 * Copyright 2016-2018 Litsec AB
 *
 * 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.litsec.opensaml.saml2.common.assertion;

import static se.litsec.opensaml.common.validation.ValidationSupport.check;

import javax.xml.namespace.QName;

import org.joda.time.DateTime;
import org.joda.time.chrono.ISOChronology;
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.saml2.assertion.StatementValidator;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.AuthnStatement;
import org.opensaml.saml.saml2.core.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import se.litsec.opensaml.common.validation.AbstractObjectValidator;
import se.litsec.opensaml.common.validation.CoreValidatorParameters;
import se.litsec.opensaml.common.validation.ValidationSupport.ValidationResultException;

/**
 * Core statement validator for {@link AuthnStatement}s.
 * 
 * 

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

*
    *
  • {@link CoreValidatorParameters#AUTHN_REQUEST}: Optional. If supplied will be used in a number of validations when * information from the corresponding {@code AuthnRequest} is needed. If not supplied, other, more detailed parameters * must be given.
  • *
  • {@link #AUTHN_REQUEST_FORCE_AUTHN}: If the above {@link CoreValidatorParameters#AUTHN_REQUEST} is not assigned, * this parameter gives the {@code ForceAuthn} flag. This is used to determine if a valid assertion was issued based on * SSO/non-SSO.
  • *
  • {@link #AUTHN_REQUEST_ISSUE_INSTANT}: If the above {@link CoreValidatorParameters#AUTHN_REQUEST} is not assigned, * this parameter gives the issue instant of the authentication request. This is used to determine if a valid assertion * was issued based on SSO/non-SSO.
  • *
  • {@link #MAX_ACCEPTED_SSO_SESSION_TIME}: For SSO, we may want to assert that the authentication is not too old. If * so, this parameter gives the maximum accepted session time.
  • *
* * @author Martin Lindström ([email protected]) */ public class AuthnStatementValidator implements StatementValidator { /** * Key for a validation context parameter. Carries a {@link Boolean} holding the value of the ForceAuthn flag from the * AuthnRequest. */ public static final String AUTHN_REQUEST_FORCE_AUTHN = CoreValidatorParameters.STD_PREFIX + ".AuthnRequestForceAuthn"; /** * Key for a validation context parameter. Carries a {@link Long} holding the issuance time for the AuthnRequest. */ public static final String AUTHN_REQUEST_ISSUE_INSTANT = CoreValidatorParameters.STD_PREFIX + ".AuthnRequestIssueInstant"; /** * Key for a validation context parameter. Carries a {@link Long} holding the maximum session time that we can accept * for SSO. */ public static final String MAX_ACCEPTED_SSO_SESSION_TIME = CoreValidatorParameters.STD_PREFIX + ".MaxAcceptedSsoSessionTime"; /** Class logger. */ private final Logger log = LoggerFactory.getLogger(AuthnStatementValidator.class); /** {@inheritDoc} */ @Override public QName getServicedStatement() { return AuthnStatement.DEFAULT_ELEMENT_NAME; } /** {@inheritDoc} */ @Override public final ValidationResult validate(Statement statement, Assertion assertion, ValidationContext context) throws AssertionValidationException { if (statement instanceof AuthnStatement) { return this.validate((AuthnStatement) statement, assertion, context); } else { throw new AssertionValidationException("Illegal call - statement is of type " + statement.getClass().getSimpleName()); } } /** * Validates the {@link AuthnStatement}. * * @param statement * the statement to validate * @param assertion * the assertion containing the statement * @param context * validation context * @return validation result */ protected ValidationResult validate(AuthnStatement statement, Assertion assertion, ValidationContext context) { try { check(this.validateAuthnInstant(statement, assertion, context)); check(this.validateSessionIndex(statement, assertion, context)); check(this.validateSessionNotOnOrAfter(statement, assertion, context)); check(this.validateSubjectLocality(statement, assertion, context)); check(this.validateAuthnContext(statement, assertion, context)); } catch (ValidationResultException e) { return e.getResult(); } return ValidationResult.VALID; } /** * Validates the {@code AuthnInstant} of the {@code AuthnStatement}. * * @param statement * the statement * @param assertion * the assertion containing the statement * @param context * validation context * @return validation result */ protected ValidationResult validateAuthnInstant(AuthnStatement statement, Assertion assertion, ValidationContext context) { if (statement.getAuthnInstant() == null) { context.setValidationFailureMessage("AuthnInstant of Assertion/@AuthnStatement is missing"); return ValidationResult.INVALID; } // Assert the the authentication instant is not newer than the assertion issuance time. // if (statement.getAuthnInstant().isAfter(assertion.getIssueInstant())) { context.setValidationFailureMessage("AuthnInstant is after assertion issue instant - invalid"); return ValidationResult.INVALID; } // Make checks regarding SSO and session length ... // return this.validateSsoAndSession(statement.getAuthnInstant(), statement, assertion, context); } /** * Makes checks for SSO and session lengths. * * @param authnInstant * the authentication instant * @param statement * the statement * @param assertion * the assertion containing the statement * @param context * validation context * @return validation result */ protected ValidationResult validateSsoAndSession(DateTime authnInstant, AuthnStatement statement, Assertion assertion, ValidationContext context) { // If we requested a forced authentication, we check that the authentication instant is not before // the issuance time of the request. // Boolean forceAuthn = this.getForceAuthnFlag(context); Long authnRequestIssueInstant = this.getAuthnRequestIssueInstant(context); if (forceAuthn != null && forceAuthn.booleanValue()) { if (authnRequestIssueInstant != null) { if (authnInstant.getMillis() + AbstractObjectValidator.getAllowedClockSkew(context) < authnRequestIssueInstant.longValue()) { String msg = String.format("Invalid Assertion. Force authentication was requested, but authentication " + "instant (%s) is before the issuance time of the authentication request (%s)", authnInstant, new DateTime(authnRequestIssueInstant, ISOChronology.getInstanceUTC())); context.setValidationFailureMessage(msg); return ValidationResult.INVALID; } } else { log.warn("%s (or %s) not suppplied - cannot check SSO", AUTHN_REQUEST_ISSUE_INSTANT, CoreValidatorParameters.AUTHN_REQUEST); } } else { // Forced authentication was not requested. // If we have specified the MAX_ACCEPTED_SSO_SESSION_TIME parameter we make a check that the // SSO session at the issuing IdP is not greater than what we can accept. // Long maxSessionTime = (Long) context.getStaticParameters().get(MAX_ACCEPTED_SSO_SESSION_TIME); if (maxSessionTime != null) { if (authnInstant.getMillis() + maxSessionTime.longValue() < AbstractObjectValidator.getReceiveInstant(context)) { String msg = String.format( "Session length violation. Authentication instant (%s) is too far back in time to be accepted by SP SSO policy", authnInstant); context.setValidationFailureMessage(msg); return ValidationResult.INVALID; } } } return ValidationResult.VALID; } /** * Gets the {@code ForceAuthn} flag from the validation context. The method primarily checks for the * {@link #AUTHN_REQUEST_FORCE_AUTHN} parameter, and that does not exist, tries with the * {@link CoreValidatorParameters#AUTHN_REQUEST} parameter. * * @param context * the validation context * @return the {@code ForceAuthn} flag or {@code null} if this is not set */ protected Boolean getForceAuthnFlag(ValidationContext context) { Boolean forceAuthn = (Boolean) context.getStaticParameters().get(AUTHN_REQUEST_FORCE_AUTHN); if (forceAuthn == null) { AuthnRequest authnRequest = (AuthnRequest) context.getStaticParameters().get(CoreValidatorParameters.AUTHN_REQUEST); if (authnRequest != null) { forceAuthn = authnRequest.isForceAuthn(); } } return forceAuthn; } /** * Gets the issue instant of the {@code AuthnRequest} from the validation context. The method primarily checks for the * {@link #AUTHN_REQUEST_ISSUE_INSTANT} parameter, and that does not exist, tries with the * {@link CoreValidatorParameters#AUTHN_REQUEST} parameter. * * @param context * the validation context * @return the issuance time */ protected Long getAuthnRequestIssueInstant(ValidationContext context) { Long issueInstant = (Long) context.getStaticParameters().get(AUTHN_REQUEST_ISSUE_INSTANT); if (issueInstant == null) { AuthnRequest authnRequest = (AuthnRequest) context.getStaticParameters().get(CoreValidatorParameters.AUTHN_REQUEST); if (authnRequest != null) { issueInstant = authnRequest.getIssueInstant().getMillis(); } } return issueInstant; } /** * Default implementation does not perform any checks and returns {@link ValidationResult#VALID}. * * @param statement * the statement * @param assertion * the assertion * @param context * the validation context * @return validation result */ protected ValidationResult validateSessionIndex(AuthnStatement statement, Assertion assertion, ValidationContext context) { return ValidationResult.VALID; } /** * Default implementation does not perform any checks and returns {@link ValidationResult#VALID}. * * @param statement * the statement * @param assertion * the assertion * @param context * the validation context * @return validation result */ protected ValidationResult validateSessionNotOnOrAfter(AuthnStatement statement, Assertion assertion, ValidationContext context) { return ValidationResult.VALID; } /** * Default implementation does not perform any checks and returns {@link ValidationResult#VALID}. * * @param statement * the statement * @param assertion * the assertion * @param context * the validation context * @return validation result */ protected ValidationResult validateSubjectLocality(AuthnStatement statement, Assertion assertion, ValidationContext context) { return ValidationResult.VALID; } /** * Default implementation will only assert that the {@code AuthnContext} element is present. * * @param statement * the statement * @param assertion * the assertion * @param context * the validation context * @return validation result */ protected ValidationResult validateAuthnContext(AuthnStatement statement, Assertion assertion, ValidationContext context) { if (statement.getAuthnContext() == null) { context.setValidationFailureMessage("AuthnContext element is missing in Assertion/@AuthnStatement"); return ValidationResult.INVALID; } return ValidationResult.VALID; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy