org.guanxi.sp.engine.security.shibboleth.IdPVerifier.java Source code

Java tutorial

Introduction

Here is the source code for org.guanxi.sp.engine.security.shibboleth.IdPVerifier.java

Source

//: "The contents of this file are subject to the Mozilla Public License
//: Version 1.1 (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.mozilla.org/MPL/
//:
//: Software distributed under the License is distributed on an "AS IS"
//: basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//: License for the specific language governing rights and limitations
//: under the License.
//:
//: The Original Code is Guanxi (http://www.guanxi.uhi.ac.uk).
//:
//: The Initial Developer of the Original Code is Alistair Young alistair@codebrane.com
//: All Rights Reserved.
//:

package org.guanxi.sp.engine.security.shibboleth;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.context.ServletContextAware;
import org.springframework.context.MessageSource;
import org.guanxi.common.Utils;
import org.guanxi.common.entity.EntityFarm;
import org.guanxi.common.entity.EntityManager;
import org.guanxi.common.definitions.Guanxi;
import org.guanxi.common.definitions.Shibboleth;
import org.guanxi.xal.saml_1_0.protocol.ResponseDocument;
import org.guanxi.xal.saml_1_0.protocol.ResponseType;
import org.guanxi.xal.saml_1_0.assertion.AssertionType;
import org.guanxi.xal.saml_1_0.assertion.AuthenticationStatementType;
import org.guanxi.sp.engine.Config;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlOptions;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletContext;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.IOException;
import java.util.HashMap;

/**
 * Security interceptor that verifies whether the Engine will trust the IdentityProvider
 * that is sending an AuthenticationStatement to the Engine.
 * The interceptor will use the metadata registered with the Engine to make this decision.
 */
public class IdPVerifier extends HandlerInterceptorAdapter implements ServletContextAware {
    private static final Logger logger = Logger.getLogger(IdPVerifier.class.getName());
    /** The ServletContext, passed to us by Spring as we are ServletContextAware */
    private ServletContext servletContext = null;
    /** The localised messages to use */
    private MessageSource messages = null;
    /** The error page to use */
    private String errorPage = null;

    // Called by Spring as we are ServletContextAware
    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    /**
     * Initialise the interceptor
     */
    public void init() {
    }

    /**
     * Blocks access to an Engine's REST web service based on resident Engine metadata which defines what
     * entities can access that service.
     *
     * @param request Standard HttpServletRequest
     * @param response Standard HttpServletResponse
     * @param object handler
     * @return true if the caller is authorised to use the service
     * @throws Exception if an error occurs
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object)
            throws Exception {
        String idpProviderID = null;
        ResponseDocument responseDocument = null;
        ResponseType samlResponse = null;

        try {
            if (request.getParameter("SAMLResponse") == null) {
                logger.error("Could not process the AuthenticatonStatement from the IdP as there isn't one!");
                request.setAttribute("error",
                        messages.getMessage("engine.error.cannot.parse.authnstmnt", null, request.getLocale()));
                request.setAttribute("message",
                        messages.getMessage("engine.error.no.authn.stmnt", null, request.getLocale()));
                request.getRequestDispatcher(errorPage).forward(request, response);
                return false;
            }

            // Parse the SAML Response containing the AuthenticationStatement coming from the IdP
            responseDocument = ResponseDocument.Factory
                    .parse(new StringReader(Utils.decodeBase64(request.getParameter("SAMLResponse"))));

            dumpSAML(responseDocument);

            samlResponse = responseDocument.getResponse();
            AssertionType assertion = samlResponse.getAssertionArray()[0];
            idpProviderID = assertion.getIssuer();
            AuthenticationStatementType authStatement = assertion.getAuthenticationStatementArray()[0];

            request.setAttribute(Config.REQUEST_ATTRIBUTE_SAML_RESPONSE, samlResponse);
            request.setAttribute(Config.REQUEST_ATTRIBUTE_IDP_PROVIDER_ID, idpProviderID);
            request.setAttribute(Config.REQUEST_ATTRIBUTE_IDP_NAME_IDENTIFIER,
                    authStatement.getSubject().getNameIdentifier().getStringValue());
        } catch (Exception e) {
            logger.error("Could not process the AuthenticatonStatement from the IdP", e);
            request.setAttribute("error",
                    messages.getMessage("engine.error.cannot.parse.authnstmnt", null, request.getLocale()));
            request.setAttribute("message", e.getMessage());
            request.getRequestDispatcher(errorPage).forward(request, response);
            return false;
        }

        /* Find the IdP's metadata from our store. This is based on it's providerId, which is matched
         * against the entityID in the IdP's EntityDescriptor file.
         */
        EntityFarm farm = (EntityFarm) servletContext.getAttribute(Guanxi.CONTEXT_ATTR_ENGINE_ENTITY_FARM);
        EntityManager manager = farm.getEntityManagerForID(idpProviderID);
        if (manager == null) {
            logger.error("Could not find manager for IdP '" + idpProviderID + "' in the metadata repository");
            request.setAttribute("error",
                    messages.getMessage("engine.error.no.idp.metadata", null, request.getLocale()));
            request.setAttribute("message", idpProviderID);
            request.getRequestDispatcher(errorPage).forward(request, response);
            return false;
        }
        if (manager.getMetadata(idpProviderID) != null) {
            // Apply the trust rules to the entity
            if (manager.getTrustEngine() != null) {
                if (manager.getTrustEngine().trustEntity(manager.getMetadata(idpProviderID), responseDocument)) {
                    request.setAttribute(Config.REQUEST_ATTRIBUTE_IDP_METADATA, manager.getMetadata(idpProviderID));
                } else {
                    logger.error("Trust failed for the IdP");
                    request.setAttribute("error", messages.getMessage("engine.error.idp.sig.failed.verification",
                            null, request.getLocale()));
                    request.setAttribute("message", idpProviderID);
                    request.getRequestDispatcher(errorPage).forward(request, response);
                    return false;
                }
            }
        } else {
            logger.error("Could not get IdP '" + idpProviderID + "' from the manager");
            request.setAttribute("error",
                    messages.getMessage("engine.error.no.idp.metadata", null, request.getLocale()));
            request.setAttribute("message", idpProviderID);
            request.getRequestDispatcher(errorPage).forward(request, response);
            return false;
        }

        return true;
    }

    /**
     * Dumps the SAML response from the IdP to the logs
     *
     * @param samlResponseDoc Response from the IdP containing the AuthenticationStatement
     */
    private void dumpSAML(ResponseDocument samlResponseDoc) {
        // Sort out the namespaces for saving the Response
        HashMap<String, String> namespaces = new HashMap<String, String>();
        namespaces.put(Shibboleth.NS_SAML_10_PROTOCOL, Shibboleth.NS_PREFIX_SAML_10_PROTOCOL);
        namespaces.put(Shibboleth.NS_SAML_10_ASSERTION, Shibboleth.NS_PREFIX_SAML_10_ASSERTION);
        XmlOptions xmlOptions = new XmlOptions();
        xmlOptions.setSavePrettyPrint();
        xmlOptions.setSavePrettyPrintIndent(2);
        xmlOptions.setUseDefaultNamespace();
        xmlOptions.setSaveAggressiveNamespaces();
        xmlOptions.setSaveSuggestedPrefixes(namespaces);
        xmlOptions.setSaveNamespacesFirst();

        StringWriter sw = new StringWriter();
        try {
            samlResponseDoc.save(sw, xmlOptions);
        } catch (IOException ioe) {
            // Do I care?
        }

        logger.debug(sw.toString());
    }

    public void setMessages(MessageSource messages) {
        this.messages = messages;
    }

    public void setErrorPage(String errorPage) {
        this.errorPage = errorPage;
    }
}