com.alfaariss.oa.util.saml2.binding.artifact.ImplementedHTTPArtifactDecoder.java Source code

Java tutorial

Introduction

Here is the source code for com.alfaariss.oa.util.saml2.binding.artifact.ImplementedHTTPArtifactDecoder.java

Source

/*
 * Asimba Server
 * 
 * Copyright (C) 2012 Asimba
 * Copyright (C) 2007-2009 Alfa & Ariss B.V.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see www.gnu.org/licenses
 * 
 * Asimba - Serious Open Source SSO - More information on www.asimba.org
 * 
 */
package com.alfaariss.oa.util.saml2.binding.artifact;

import java.security.NoSuchAlgorithmException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.DateTime;
import org.opensaml.Configuration;
import org.opensaml.common.SAMLObject;
import org.opensaml.common.SAMLVersion;
import org.opensaml.common.binding.SAMLMessageContext;
import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.binding.artifact.SAML2ArtifactType0004;
import org.opensaml.saml2.binding.artifact.SAML2ArtifactType0004Builder;
import org.opensaml.saml2.binding.decoding.HTTPArtifactDecoder;
import org.opensaml.saml2.core.Artifact;
import org.opensaml.saml2.core.ArtifactResolve;
import org.opensaml.saml2.core.ArtifactResponse;
import org.opensaml.saml2.core.Issuer;
import org.opensaml.saml2.core.impl.ArtifactBuilder;
import org.opensaml.saml2.core.impl.ArtifactResolveBuilder;
import org.opensaml.saml2.core.impl.IssuerBuilder;
import org.opensaml.saml2.metadata.ArtifactResolutionService;
import org.opensaml.saml2.metadata.IDPSSODescriptor;
import org.opensaml.saml2.metadata.SPSSODescriptor;
import org.opensaml.saml2.metadata.SSODescriptor;
import org.opensaml.saml2.metadata.provider.MetadataProvider;
import org.opensaml.saml2.metadata.provider.MetadataProviderException;
import org.opensaml.ws.message.decoder.MessageDecodingException;
import org.opensaml.ws.soap.client.BasicSOAPMessageContext;
import org.opensaml.ws.soap.client.http.HttpClientBuilder;
import org.opensaml.ws.soap.client.http.HttpSOAPClient;
import org.opensaml.ws.soap.soap11.Body;
import org.opensaml.ws.soap.soap11.Envelope;
import org.opensaml.ws.soap.soap11.impl.BodyBuilder;
import org.opensaml.ws.soap.soap11.impl.EnvelopeBuilder;
import org.opensaml.ws.transport.http.HTTPInTransport;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.opensaml.xml.io.Marshaller;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.parse.ParserPool;
import org.opensaml.xml.util.Base64;
import org.opensaml.xml.util.DatatypeHelper;
import org.opensaml.xml.util.XMLHelper;
import org.w3c.dom.Element;

import com.alfaariss.oa.util.saml2.binding.BindingProperties;

/**
 * OA implemented artifact decoder. Partially replaces the OpenSAML 
 * HTTPArtifactDecoder. This version may need to be made more independent, 
 * since some of the data necessary to retrieve the artifacts are now put in the
 * context beforehand.
 * 
 *  This artifact decoder supports 
 *  
 * @see org.opensaml.saml2.binding.decoding.HTTPArtifactDecoder
 * @author jre
 * @author Alfa & Ariss
 * @since 1.0
 */
public class ImplementedHTTPArtifactDecoder extends HTTPArtifactDecoder {
    //DD Signature in message within ArtifactResponse types is currently not verified, because there currently is no opportunity to do so.

    private final Log _logger = LogFactory.getLog(ImplementedHTTPArtifactDecoder.class);
    private XMLObjectBuilderFactory _builderFactory;
    private String _sSSODescriptor;

    /**
     * Default constructor.
     *  
     * @param pool Basic parser pool.
     * @param bindingProperties The binding properties configuration.
     */
    public ImplementedHTTPArtifactDecoder(ParserPool pool, BindingProperties bindingProperties) {
        super(pool);
        _builderFactory = Configuration.getBuilderFactory();
        _sSSODescriptor = bindingProperties.getProperty(SAMLConstants.SAML2_ARTIFACT_BINDING_URI, "SSODescriptor");
    }

    /**
     * @see org.opensaml.saml2.binding.decoding.HTTPArtifactDecoder#processArtifact(org.opensaml.common.binding.SAMLMessageContext)
     */
    @SuppressWarnings("unchecked")
    protected void processArtifact(SAMLMessageContext samlMsgCtx) throws MessageDecodingException {
        HTTPInTransport inTransport = (HTTPInTransport) samlMsgCtx.getInboundMessageTransport();
        String encodedArtifact = DatatypeHelper.safeTrimOrNullString(inTransport.getParameterValue("SAMLart"));
        if (encodedArtifact == null) {
            _logger.error("URL SAMLart parameter was missing or did not contain a value");
            throw new MessageDecodingException("URL TARGET parameter was missing or did not contain a value");
        }

        ArtifactBuilder artifactBuilder = (ArtifactBuilder) _builderFactory
                .getBuilder(Artifact.DEFAULT_ELEMENT_NAME);
        Artifact artifact = artifactBuilder.buildObject();
        artifact.setArtifact(encodedArtifact);

        ArtifactResolveBuilder artifactResolveBuilder = (ArtifactResolveBuilder) _builderFactory
                .getBuilder(ArtifactResolve.DEFAULT_ELEMENT_NAME);
        ArtifactResolve artifactResolve = artifactResolveBuilder.buildObject();

        SecureRandomIdentifierGenerator idgen = null;
        try {
            idgen = new SecureRandomIdentifierGenerator();
        } catch (NoSuchAlgorithmException e) {
            String msg = "Could not generate ID for artifact resolve request";
            _logger.debug(msg);
            throw new MessageDecodingException(msg, e);
        }

        String id = idgen.generateIdentifier();

        artifactResolve.setID(id);
        artifactResolve.setVersion(SAMLVersion.VERSION_20);
        artifactResolve.setIssueInstant(new DateTime());
        artifactResolve.setArtifact(artifact);

        IssuerBuilder issuerBuilder = (IssuerBuilder) _builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
        Issuer issuer = issuerBuilder.buildObject();
        issuer.setValue(samlMsgCtx.getOutboundMessageIssuer());

        artifactResolve.setIssuer(issuer);

        MetadataProvider mp = samlMsgCtx.getMetadataProvider();
        if (mp == null) {
            _logger.debug("No MetadataProvider available in message context");
            throw new MessageDecodingException("No MetadataProvider available in message context");
        }

        String entID = samlMsgCtx.getInboundMessageIssuer();
        String endpoint = null;

        try {
            SSODescriptor rd = null;
            if (_sSSODescriptor != null) {
                if ("sp".equalsIgnoreCase(_sSSODescriptor)) {
                    rd = (SPSSODescriptor) mp.getRole(entID, SPSSODescriptor.DEFAULT_ELEMENT_NAME,
                            SAMLConstants.SAML20P_NS);
                } else if ("idp".equalsIgnoreCase(_sSSODescriptor)) {
                    rd = (IDPSSODescriptor) mp.getRole(entID, IDPSSODescriptor.DEFAULT_ELEMENT_NAME,
                            SAMLConstants.SAML20P_NS);
                } else {
                    StringBuffer sbDebug = new StringBuffer("Unknown SSODescriptor configured '");
                    sbDebug.append(_sSSODescriptor);
                    sbDebug.append("'; using IDPSSODescriptor");
                    _logger.debug(sbDebug.toString());
                }
            }

            if (rd == null) {//default use IDP role
                rd = (IDPSSODescriptor) mp.getRole(entID, IDPSSODescriptor.DEFAULT_ELEMENT_NAME,
                        SAMLConstants.SAML20P_NS);
            }

            if (rd != null) {
                SAML2ArtifactType0004 b = null;
                SAML2ArtifactType0004Builder bf = new SAML2ArtifactType0004Builder();
                b = bf.buildArtifact(Base64.decode(encodedArtifact));

                String defaultEndpoint = null;
                String indexedEndpoint = null;
                String firstEndpoint = null;

                for (ArtifactResolutionService ars : rd.getArtifactResolutionServices()) {
                    if (firstEndpoint == null)
                        firstEndpoint = ars.getLocation();
                    if (ars.isDefault())
                        defaultEndpoint = ars.getLocation();

                    int i = 0;
                    byte[] ba = b.getEndpointIndex();

                    for (int ia = ba.length - 1; ia >= 0; ia--) {
                        i = i + (ba[ia] * Byte.SIZE);
                    }

                    if (ars.getIndex() == i) {
                        indexedEndpoint = ars.getLocation();
                    }
                }

                //choose right endpoint:
                if (indexedEndpoint != null)
                    endpoint = indexedEndpoint;
                else if (defaultEndpoint != null)
                    endpoint = defaultEndpoint;
                else
                    endpoint = firstEndpoint;
            }
        } catch (MetadataProviderException e1) {
            String msg = "Exception while fetching metadata for requestor while decoding artifact";
            _logger.debug(msg);
            throw new MessageDecodingException(msg, e1);
        }

        if (endpoint == null) {
            String msg = "Could not fetch endpoint for requestor while decoding artifact";
            _logger.debug(msg);
            throw new MessageDecodingException(msg);
        }

        BodyBuilder bodyBuilder = (BodyBuilder) _builderFactory.getBuilder(Body.DEFAULT_ELEMENT_NAME);
        Body body = bodyBuilder.buildObject();
        body.getUnknownXMLObjects().add(artifactResolve);

        EnvelopeBuilder envelopeBuilder = (EnvelopeBuilder) _builderFactory
                .getBuilder(Envelope.DEFAULT_ELEMENT_NAME);
        Envelope env = envelopeBuilder.buildObject();
        env.setBody(body);

        BasicSOAPMessageContext soapContext = new BasicSOAPMessageContext();
        soapContext.setOutboundMessage(env);

        HttpClientBuilder clientBuilder = new HttpClientBuilder();
        clientBuilder.setConnectionTimeout(5000);

        HttpSOAPClient soapClient = new HttpSOAPClient(clientBuilder.buildClient(), super.getParserPool());

        if (_logger.isDebugEnabled())
            logXML(env);

        try {
            soapClient.send(endpoint, soapContext);
        } catch (Exception e) {
            String msg = "Could not resolve artifact";
            _logger.debug(msg, e);
            throw new MessageDecodingException(msg, e);
        }

        Envelope envelope = (Envelope) soapContext.getInboundMessage();

        if (_logger.isDebugEnabled())
            logXML(envelope);

        XMLObject samlResponseMessage = null;
        XMLObject responseMessage = soapContext.getInboundMessage();
        if (responseMessage != null && responseMessage instanceof Envelope) {
            Envelope responseEnvelope = (Envelope) responseMessage;
            Body responseBody = responseEnvelope.getBody();
            if (responseBody != null) {
                samlResponseMessage = responseBody.getUnknownXMLObjects().get(0);
            } else {
                _logger.debug("No body in response message");
            }
        } else {
            _logger.debug("No envelope in response message");
        }

        if (samlResponseMessage != null && samlResponseMessage instanceof ArtifactResponse) {
            ArtifactResponse artResp = (ArtifactResponse) samlResponseMessage;
            SAMLObject message = artResp.getMessage();
            if (message != null)
                samlMsgCtx.setInboundSAMLMessage(message);
            else
                _logger.debug("No message found in artifact: " + artResp);
        } else {
            _logger.debug("Response doesn't contain an ArtifactResponse object");
        }
    }

    /**
     * Log XML to logger.
     * 
     * @param xmlObject the Object to be logged.
     */
    protected void logXML(XMLObject xmlObject) {
        assert _logger.isDebugEnabled() : "Logger debug state not checked";
        Element eDOM = xmlObject.getDOM();
        if (eDOM == null) {
            Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(xmlObject);
            if (marshaller != null) {
                try {
                    eDOM = marshaller.marshall(xmlObject);
                } catch (MarshallingException e) {
                    _logger.debug("Could not prettyPrint XML object", e);
                }
            }
        }

        if (eDOM != null) {
            String sXML = XMLHelper.prettyPrintXML(eDOM);
            _logger.debug(sXML);
        }

    }

}