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

Java tutorial

Introduction

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

Source

/*
 * Asimba Server
 * 
 * Copyright (C) 2012 Asimba
 * Copyright (C) 2007-2008 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.io.IOException;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opensaml.Configuration;
import org.opensaml.common.SAMLObject;
import org.opensaml.common.binding.SAMLMessageContext;
import org.opensaml.common.binding.artifact.SAMLArtifactMap;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.binding.artifact.AbstractSAML2Artifact;
import org.opensaml.saml2.binding.artifact.SAML2ArtifactBuilder;
import org.opensaml.saml2.binding.artifact.SAML2ArtifactType0004;
import org.opensaml.saml2.binding.encoding.BaseSAML2MessageEncoder;
import org.opensaml.saml2.core.NameID;
import org.opensaml.util.URLBuilder;
import org.opensaml.ws.message.MessageContext;
import org.opensaml.ws.message.encoder.MessageEncodingException;
import org.opensaml.ws.transport.InTransport;
import org.opensaml.ws.transport.http.HTTPOutTransport;
import org.opensaml.ws.transport.http.HTTPTransportUtils;
import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
import org.opensaml.ws.transport.http.HttpServletResponseAdapter;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.util.Pair;

import com.alfaariss.oa.util.saml2.SAML2Constants;

/**
 * JSP base HTTP Artifact encoder.
 * 
 * Encodes artifacts using HTTP redirect or HTTP POST using a JSP template 
 * instead of the velocity template engine.
 * 
 * <br><br><i>Partitially based on sources from OpenSAML (www.opensaml.org).</i>
 * 
 * @author EVB
 * @author Alfa & Ariss
 */
public class JSPHTTPArtifactEncoder extends BaseSAML2MessageEncoder {
    /** logger */
    private static final Log _logger = LogFactory.getLog(JSPHTTPArtifactEncoder.class);
    private final String _sTemplateLocation;

    /** Use POST encoding instead of GET. */
    private boolean _bPostEncoding;
    /** Artifact map. */
    private final SAMLArtifactMap _artifactMap;

    /**
     * Create a new <code>JSPHTTPArtifactEncoder</code> with the given template 
     * and redirect encoding.
        
     * @param sTemplateLocation The JSp template for POST encoding.
     * @param map The artifact map to store artifact/message bindings.
     */
    public JSPHTTPArtifactEncoder(String sTemplateLocation, SAMLArtifactMap map) {
        super();
        _bPostEncoding = false;
        _sTemplateLocation = sTemplateLocation;
        _artifactMap = map;
    }

    /**
     * Retrieve the post binding value.
     * @return <code>true</code> if POST encoding will be used, 
     *  otherwise <code>false</code>.
     */
    public boolean isPostEncoding() {
        return _bPostEncoding;
    }

    /**
     * Set the post binding value.
     * 
     * @param post <code>true</code> if POST encoding shuld be used, 
     *  otherwise <code>false</code>.
     */
    public void setPostEncoding(boolean post) {
        _bPostEncoding = post;
    }

    /**
     * Returns return {@link SAMLConstants#SAML2_ARTIFACT_BINDING_URI}.
     * @see org.opensaml.common.binding.encoding.SAMLMessageEncoder#getBindingURI()
     */
    @Override
    public String getBindingURI() {
        return SAMLConstants.SAML2_ARTIFACT_BINDING_URI;
    }

    /**
     * No message confidentiality.
     * @see org.opensaml.ws.message.encoder.MessageEncoder#providesMessageConfidentiality(org.opensaml.ws.message.MessageContext)
     */
    @Override
    public boolean providesMessageConfidentiality(MessageContext arg0) throws MessageEncodingException {
        return false;
    }

    /**
     * No message integrity.
     * @see org.opensaml.ws.message.encoder.MessageEncoder#providesMessageIntegrity(org.opensaml.ws.message.MessageContext)
     */
    @Override
    public boolean providesMessageIntegrity(MessageContext arg0) throws MessageEncodingException {
        return false;
    }

    /**
     * Encodes the message using Artifact binding.
     * @see org.opensaml.ws.message.encoder.BaseMessageEncoder#doEncode(org.opensaml.ws.message.MessageContext)
     */
    @SuppressWarnings("unchecked")
    @Override
    protected void doEncode(MessageContext messageContext) throws MessageEncodingException {
        assert messageContext != null : "messageContext is empty";
        assert messageContext.getOutboundMessageTransport() != null : "outboundMessageTransport is empty";
        try {
            if (!(messageContext instanceof SAMLMessageContext)) {
                _logger.error("Invalid message context type:" + messageContext.getClass().getSimpleName());
                throw new MessageEncodingException("Invalid message context type");
            }

            if (!(messageContext.getOutboundMessageTransport() instanceof HTTPOutTransport)) {
                _logger.error("Invalid outbound message transport type:"
                        + messageContext.getOutboundMessageTransport().getClass().getSimpleName());
                throw new MessageEncodingException("Invalid outbound message transport type");
            }

            SAMLMessageContext<SAMLObject, SAMLObject, NameID> artifactContext = (SAMLMessageContext<SAMLObject, SAMLObject, NameID>) messageContext;
            HTTPOutTransport outTransport = (HTTPOutTransport) artifactContext.getOutboundMessageTransport();
            outTransport.setCharacterEncoding(SAML2Constants.CHARSET);

            if (_bPostEncoding) {
                postEncode(artifactContext, outTransport);
            } else {
                getEncode(artifactContext, outTransport);
            }
        } catch (MessageEncodingException e) {
            throw e;
        } catch (Exception e) {
            _logger.error("Could not encode messagecontext", e);
            throw new MessageEncodingException("Internal error while encoding");
        }

    }

    private void postEncode(SAMLMessageContext<SAMLObject, SAMLObject, NameID> artifactContext,
            HTTPOutTransport outTransport) throws MessageEncodingException {
        InTransport inTransport = artifactContext.getInboundMessageTransport();
        HttpServletRequest request = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
        HttpServletResponse response = ((HttpServletResponseAdapter) outTransport).getWrappedResponse();

        request.setAttribute("action", getEndpointURL(artifactContext));
        request.setAttribute("SAMLArt", buildArtifact(artifactContext).base64Encode());

        if (checkRelayState(artifactContext.getRelayState())) {
            request.setAttribute("RelayState", HTTPTransportUtils.urlEncode(artifactContext.getRelayState()));
        }
        try {
            _logger.debug("Forward caller to JSP template");
            RequestDispatcher oDispatcher = request.getRequestDispatcher(_sTemplateLocation);

            oDispatcher.forward(request, response);
        } catch (ServletException e) {
            _logger.warn("Could not process forward to JSP due to Servlet Error", e);
            throw new MessageEncodingException("Could not process forward to JSP");
        } catch (IOException e) {
            _logger.warn("Could not process forward to JSP due to I/O Error", e);
            throw new MessageEncodingException("Could not process forward to JSP");
        }
    }

    private void getEncode(SAMLMessageContext<SAMLObject, SAMLObject, NameID> artifactContext,
            HTTPOutTransport outTransport) throws MessageEncodingException {
        URLBuilder urlBuilder = getEndpointURL(artifactContext);

        List<Pair<String, String>> params = urlBuilder.getQueryParams();

        params.add(new Pair<String, String>("SAMLart", buildArtifact(artifactContext).base64Encode()));

        if (checkRelayState(artifactContext.getRelayState())) {
            params.add(new Pair<String, String>("RelayState", artifactContext.getRelayState()));
        }

        outTransport.sendRedirect(urlBuilder.buildURL());
    }

    private AbstractSAML2Artifact buildArtifact(SAMLMessageContext<SAMLObject, SAMLObject, NameID> artifactContext)
            throws MessageEncodingException {

        SAML2ArtifactBuilder<?> artifactBuilder;
        if (artifactContext.getOutboundMessageArtifactType() != null) {
            artifactBuilder = Configuration.getSAML2ArtifactBuilderFactory()
                    .getArtifactBuilder(artifactContext.getOutboundMessageArtifactType());
        } else {
            artifactBuilder = Configuration.getSAML2ArtifactBuilderFactory()
                    .getArtifactBuilder(SAML2ArtifactType0004.TYPE_CODE);
            artifactContext.setOutboundMessageArtifactType(SAML2ArtifactType0004.TYPE_CODE);
        }

        AbstractSAML2Artifact artifact = artifactBuilder.buildArtifact(artifactContext);
        String encodedArtifact = artifact.base64Encode();
        try {
            _artifactMap.put(encodedArtifact, artifactContext.getInboundMessageIssuer(),
                    artifactContext.getOutboundMessageIssuer(), artifactContext.getOutboundSAMLMessage());
        } catch (MarshallingException e) {
            _logger.error("Error while marshalling assertion to be represented as an artifact", e);
            throw new MessageEncodingException("Error while marshalling assertion");
        }
        return artifact;
    }
}