net.shibboleth.idp.cas.flow.BuildSamlValidationSuccessMessageAction.java Source code

Java tutorial

Introduction

Here is the source code for net.shibboleth.idp.cas.flow.BuildSamlValidationSuccessMessageAction.java

Source

/*
 * See LICENSE for licensing and NOTICE for copyright.
 */

package net.shibboleth.idp.cas.flow;

import javax.annotation.Nonnull;

import net.shibboleth.idp.authn.AuthenticationResult;
import net.shibboleth.idp.cas.protocol.TicketValidationRequest;
import net.shibboleth.idp.cas.protocol.TicketValidationResponse;
import net.shibboleth.idp.session.IdPSession;
import net.shibboleth.idp.session.context.SessionContext;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.security.IdentifierGenerationStrategy;
import org.joda.time.DateTime;
import org.opensaml.core.xml.schema.XSString;
import org.opensaml.core.xml.schema.impl.XSStringBuilder;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.saml.common.SAMLObject;
import org.opensaml.saml.common.SAMLVersion;
import org.opensaml.saml.saml1.core.Assertion;
import org.opensaml.saml.saml1.core.Attribute;
import org.opensaml.saml.saml1.core.AttributeStatement;
import org.opensaml.saml.saml1.core.AttributeValue;
import org.opensaml.saml.saml1.core.Audience;
import org.opensaml.saml.saml1.core.AudienceRestrictionCondition;
import org.opensaml.saml.saml1.core.AuthenticationStatement;
import org.opensaml.saml.saml1.core.Conditions;
import org.opensaml.saml.saml1.core.ConfirmationMethod;
import org.opensaml.saml.saml1.core.NameIdentifier;
import org.opensaml.saml.saml1.core.Response;
import org.opensaml.saml.saml1.core.Status;
import org.opensaml.saml.saml1.core.StatusCode;
import org.opensaml.saml.saml1.core.Subject;
import org.opensaml.saml.saml1.core.SubjectConfirmation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.webflow.execution.RequestContext;

/**
 * Creates the SAML response message for successful ticket validation at the <code>/samlValidate</code> URI.
 * The SAML message is bound to the outgoing message context as needed by the
 * {@link org.opensaml.profile.action.impl.EncodeMessage} action.
 *
 * @author Marvin S. Addison
 */
public class BuildSamlValidationSuccessMessageAction extends AbstractOutgoingSamlMessageAction {

    /** Attribute namespace. */
    private static final String NAMESPACE = "http://www.ja-sig.org/products/cas/";

    /** Class logger. */
    private final Logger log = LoggerFactory.getLogger(BuildSamlValidationSuccessMessageAction.class);

    /** Attribute value node builder. */
    private final XSStringBuilder attrValueBuilder = new XSStringBuilder();

    /** SAML identifier generation strategy. */
    @Nonnull
    private final IdentifierGenerationStrategy identifierGenerationStrategy;

    /** IdP entity ID used to set issuer field of generated assertions. */
    @Nonnull
    private final String entityID;

    /**
     * Creates a new instance with required parameters.
     *
     * @param strategy SAML identifier generation strategy.
     * @param entityID IdP entity ID.
     */
    public BuildSamlValidationSuccessMessageAction(final IdentifierGenerationStrategy strategy,
            final String entityID) {
        Constraint.isNotNull(strategy, "IdentifierGenerationStrategy cannot be null");
        Constraint.isNotNull(strategy, "EntityID cannot be null");
        this.identifierGenerationStrategy = strategy;
        this.entityID = entityID;
    }

    @Nonnull
    @Override
    protected Response buildSamlResponse(final @Nonnull RequestContext springRequestContext,
            final @Nonnull ProfileRequestContext<SAMLObject, SAMLObject> profileRequestContext) {

        final DateTime now = DateTime.now();

        final TicketValidationRequest request = FlowStateSupport.getTicketValidationRequest(springRequestContext);
        if (request == null) {
            log.info("TicketValidationRequest not found in flow state.");
            throw new IllegalStateException("TicketValidationRequest not found in flow state.");
        }

        final TicketValidationResponse ticketResponse = FlowStateSupport
                .getTicketValidationResponse(springRequestContext);
        if (ticketResponse == null) {
            log.info("TicketValidationResponse not found in flow state.");
            throw new IllegalStateException("TicketValidationResponse not found in flow state.");
        }

        final SessionContext sessionCtx = profileRequestContext.getSubcontext(SessionContext.class, false);
        if (sessionCtx == null || sessionCtx.getIdPSession() == null) {
            log.info("Cannot locate IdP session");
            throw new IllegalStateException("Cannot locate IdP session");
        }
        final IdPSession session = sessionCtx.getIdPSession();

        final Response response = newSAMLObject(Response.class, Response.DEFAULT_ELEMENT_NAME);
        final Status status = newSAMLObject(Status.class, Status.DEFAULT_ELEMENT_NAME);
        final StatusCode code = newSAMLObject(StatusCode.class, StatusCode.DEFAULT_ELEMENT_NAME);
        code.setValue(StatusCode.SUCCESS);
        status.setStatusCode(code);
        response.setStatus(status);

        final Assertion assertion = newSAMLObject(Assertion.class, Assertion.DEFAULT_ELEMENT_NAME);
        assertion.setID(identifierGenerationStrategy.generateIdentifier());
        assertion.setIssueInstant(now);
        assertion.setVersion(SAMLVersion.VERSION_11);
        assertion.setIssuer(entityID);

        final Conditions conditions = newSAMLObject(Conditions.class, Conditions.DEFAULT_ELEMENT_NAME);
        conditions.setNotBefore(now);
        conditions.setNotOnOrAfter(now.plusSeconds(60));
        final AudienceRestrictionCondition audienceRestriction = newSAMLObject(AudienceRestrictionCondition.class,
                AudienceRestrictionCondition.DEFAULT_ELEMENT_NAME);
        final Audience audience = newSAMLObject(Audience.class, Audience.DEFAULT_ELEMENT_NAME);
        audience.setUri(request.getService());
        audienceRestriction.getAudiences().add(audience);
        conditions.getAudienceRestrictionConditions().add(audienceRestriction);
        assertion.setConditions(conditions);

        // Create an AuthenticationStatement for every authentication bound to the IdP session
        // Use flow ID for authentication method
        for (AuthenticationResult result : session.getAuthenticationResults()) {
            assertion.getAuthenticationStatements().add(
                    newAuthenticationStatement(now, result.getAuthenticationFlowId(), session.getPrincipalName()));
        }

        final AttributeStatement attrStatement = newSAMLObject(AttributeStatement.class,
                AttributeStatement.DEFAULT_ELEMENT_NAME);
        attrStatement.setSubject(newSubject(session.getPrincipalName()));
        for (final String attrName : ticketResponse.getAttributes().keySet()) {
            final Attribute attribute = newSAMLObject(Attribute.class, Attribute.DEFAULT_ELEMENT_NAME);
            attribute.setAttributeName(attrName);
            attribute.setAttributeNamespace(NAMESPACE);
            for (String value : ticketResponse.getAttributes().get(attrName)) {
                attribute.getAttributeValues().add(newAttributeValue(value));
            }
            attrStatement.getAttributes().add(attribute);
        }
        assertion.getAttributeStatements().add(attrStatement);

        response.getAssertions().add(assertion);
        return response;
    }

    private Subject newSubject(final String identifier) {
        final SubjectConfirmation confirmation = newSAMLObject(SubjectConfirmation.class,
                SubjectConfirmation.DEFAULT_ELEMENT_NAME);
        final ConfirmationMethod method = newSAMLObject(ConfirmationMethod.class,
                ConfirmationMethod.DEFAULT_ELEMENT_NAME);
        method.setConfirmationMethod(ConfirmationMethod.METHOD_ARTIFACT);
        confirmation.getConfirmationMethods().add(method);
        final NameIdentifier nameIdentifier = newSAMLObject(NameIdentifier.class,
                NameIdentifier.DEFAULT_ELEMENT_NAME);
        nameIdentifier.setValue(identifier);
        final Subject subject = newSAMLObject(Subject.class, Subject.DEFAULT_ELEMENT_NAME);
        subject.setNameIdentifier(nameIdentifier);
        subject.setSubjectConfirmation(confirmation);
        return subject;
    }

    private AuthenticationStatement newAuthenticationStatement(final DateTime authnInstant,
            final String authnMethod, final String principal) {
        final AuthenticationStatement authnStatement = newSAMLObject(AuthenticationStatement.class,
                AuthenticationStatement.DEFAULT_ELEMENT_NAME);
        authnStatement.setAuthenticationInstant(authnInstant);
        authnStatement.setAuthenticationMethod(authnMethod);
        authnStatement.setSubject(newSubject(principal));
        return authnStatement;
    }

    private XSString newAttributeValue(final String value) {
        final XSString stringValue = this.attrValueBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME,
                XSString.TYPE_NAME);
        stringValue.setValue(value);
        return stringValue;
    }
}