org.guanxi.sp.engine.service.shibboleth.AuthConsumerService.java Source code

Java tutorial

Introduction

Here is the source code for org.guanxi.sp.engine.service.shibboleth.AuthConsumerService.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.service.shibboleth;

import java.io.IOException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.guanxi.common.GuanxiException;
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.common.metadata.IdPMetadata;
import org.guanxi.sp.Util;
import org.guanxi.sp.engine.Config;
import org.guanxi.xal.saml2.metadata.GuardRoleDescriptorExtensions;
import org.guanxi.xal.saml_1_0.protocol.ResponseType;
import org.guanxi.xal.saml_2_0.metadata.EntityDescriptorType;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
import org.springframework.context.MessageSource;

/**
 * Shibboleth AuthenticationStatement consumer service. This service accepts an AuthenticationStatement
 * from a Shibboleth Identity Provider and requests attributes for the subject. It then passes those
 * attributes to the appropriate Guard that started the session that resulted in the
 * AuthenticationStatement being sent here.
 * By the time this service reached, the Identity Provider will have been verified.
 *
 * @author Alistair Young alistair@codebrane.com
 * @author Marcin Mielnicki mielniczu@o2.pl - bug fixing
 */
public class AuthConsumerService extends MultiActionController implements ServletContextAware {
    private static final Logger logger = Logger.getLogger(AuthConsumerService.class.getName());

    /** The view to redirect to if no error occur */
    private String podderView = null;
    /** The view to use to display any errors */
    private String errorView = null;
    /** The variable to use in the error view to display the error */
    private String errorViewDisplayVar = null;
    /**
     * The variable to use in the error view to display a simple version
     * of the error explaining the likely cause.
     */
    private String errorViewSimpleVar;
    /**
     * This is the map of the processing thread associated with each session.
     */
    private static Map<HttpSession, AuthConsumerServiceThread> threads;
    /** The localised messages to use */
    private MessageSource messages = null;

    /**
     * This initialises the threads map which will be used to hold the AA conversation
     * threads by session.
     */
    public void init() {
        threads = new TreeMap<HttpSession, AuthConsumerServiceThread>(new Comparator<HttpSession>() {
            public int compare(HttpSession one, HttpSession two) {
                return one.getId().compareTo(two.getId());
            }
        });
    } //init

    /**
     * Cleans up when the system shuts down
     */
    public void destroy() {
    } // destroy

    /**
     * This is the handler for the initial /shibb/acs page. This receives the
     * browser after it has visited the IdP and it spawns a thread associated
     * with the collection of attributes. It then redirects the user to the
     * process page which checks the status of the thread and displays a please
     * wait message, or forwards the user, as appropriate.
     *
     * @param request Servlet request
     * @param response Servlet response
     * @throws IOException if an error occurs
     * @throws GuanxiException if an error occurs
     * @throws KeyStoreException if an error occurs
     * @throws NoSuchAlgorithmException if an error occurs
     * @throws CertificateException if an error occurs
     */
    public void acs(HttpServletRequest request, HttpServletResponse response)
            throws IOException, GuanxiException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
        /* When a Guard initially set up a session with the Engine, it passed its session ID to
        * the Engine's WAYF Location web service. The Guard then passed the session ID to the
        * WAYF/IdP via the target parameter. So now it should come back here and we can
        * identify the Guard that we're working on behalf of.
        */
        String guardSession = request.getParameter(Shibboleth.TARGET_FORM_PARAM);

        Config config = (Config) getServletContext().getAttribute(Guanxi.CONTEXT_ATTR_ENGINE_CONFIG);

        /* When the Engine received the Guard's session, it munged it to an Engine session and
         * associated the Guard session ID with the Guard's ID. So now dereference the Guard's
         * session ID to get its ID and load it's metadata
         */
        EntityDescriptorType guardEntityDescriptor = (EntityDescriptorType) getServletContext()
                .getAttribute(guardSession.replaceAll("GUARD", "ENGINE"));
        GuardRoleDescriptorExtensions guardNativeMetadata = Util.getGuardNativeMetadata(guardEntityDescriptor);

        IdPMetadata idpMetadata = (IdPMetadata) request.getAttribute(Config.REQUEST_ATTRIBUTE_IDP_METADATA);
        EntityFarm farm = (EntityFarm) getServletContext().getAttribute(Guanxi.CONTEXT_ATTR_ENGINE_ENTITY_FARM);
        EntityManager manager = farm.getEntityManagerForID(idpMetadata.getEntityID());

        AuthConsumerServiceThread thread = null;
        thread = new AuthConsumerServiceThread(this, guardSession,
                guardNativeMetadata.getAttributeConsumerServiceURL(), idpMetadata.getAttributeAuthorityURL(),
                getPodderURL(guardSession, config), guardEntityDescriptor.getEntityID(),
                guardNativeMetadata.getKeystore(), guardNativeMetadata.getKeystorePassword(),
                config.getTrustStore(), config.getTrustStorePassword(),
                (String) request.getAttribute(Config.REQUEST_ATTRIBUTE_IDP_PROVIDER_ID),
                (String) request.getAttribute(Config.REQUEST_ATTRIBUTE_IDP_NAME_IDENTIFIER),
                (ResponseType) request.getAttribute(Config.REQUEST_ATTRIBUTE_SAML_RESPONSE), messages, request,
                manager);
        new Thread(thread).start();
        threads.put(request.getSession(true), thread);

        response.sendRedirect("process");
    }

    /**
     * Opportunity for extending classes to do some work to generate the podder URL
     *
     * @param sessionID the current Engine session ID
     * @param config the Engine config
     * @return the Podder URL for the Guard identified by sessionID
     * @throws GuanxiException if an error occurs
     */
    protected String getPodderURL(String sessionID, Config config) throws GuanxiException {
        EntityDescriptorType guardEntityDescriptor = (EntityDescriptorType) getServletContext()
                .getAttribute(sessionID.replaceAll("GUARD", "ENGINE"));
        GuardRoleDescriptorExtensions guardNativeMetadata = Util.getGuardNativeMetadata(guardEntityDescriptor);
        return guardNativeMetadata.getPodderURL();
    }

    /**
     * This checks the status of the thread associated with this request. This will display
     * either a please wait message (with progress bar) or will forward the user to the
     * Podder.
     *
     * @param request  the HttpServletRequest
     * @param response the HttpServletResponse
     * @return the ModelAndView
     */
    @SuppressWarnings("unchecked")
    public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
        AuthConsumerServiceThread thread;
        HttpSession session;

        session = request.getSession(false);
        if (session == null) {
            ModelAndView mAndV;

            mAndV = new ModelAndView();
            mAndV.setViewName(errorView);
            mAndV.getModel().put(errorViewDisplayVar, "Your session has expired");

            return mAndV;
        }

        thread = threads.get(session);
        if (thread == null) {
            ModelAndView mAndV;

            mAndV = new ModelAndView();
            mAndV.setViewName(errorView);
            mAndV.getModel().put(errorViewDisplayVar, "Processing thread cannot be found");

            return mAndV;
        }
        if (thread.isCompleted()) {
            threads.remove(session); // TODO: Periodic unloading of expired threads?
        }
        return thread.getStatus();
    }

    /**
     * This is the name of the podder jsp page. This will be used
     * to set the Guard cookie.
     *
     * @return the podderView
     */
    public String getPodderView() {
        return podderView;
    }

    /**
     * This is the name of the podder jsp page. This will be used
     * to set the Guard cookie.
     *
     * @param podderView the podderView to set
     */
    public void setPodderView(String podderView) {
        this.podderView = podderView;
    }

    /**
     * This is the name of the error jsp page. This will be used
     * to display any issues that arise during the AA process.
     *
     * @return the errorView
     */
    public String getErrorView() {
        return errorView;
    }

    /**
     * This is the name of the error jsp page. This will be used
     * to display any issues that arise during the AA process.
     *
     * @param errorView the errorView to set
     */
    public void setErrorView(String errorView) {
        this.errorView = errorView;
    }

    /**
     * This is the key that is used to store the exception stack
     * trace and display it on the error page.
     *
     * @return the errorViewDisplayVar
     */
    public String getErrorViewDisplayVar() {
        return errorViewDisplayVar;
    }

    /**
     * This is the key that is used to store the exception stack
     * trace and display it on the error page.
     *
     * @param errorViewDisplayVar the errorViewDisplayVar to set
     */
    public void setErrorViewDisplayVar(String errorViewDisplayVar) {
        this.errorViewDisplayVar = errorViewDisplayVar;
    }

    /**
     * This is the key that is used to store the more understandable
     * error message intended to reduce the amount of support required.
     *
     * @return the errorViewSimpleVar
     */
    public String getErrorViewSimpleVar() {
        return errorViewSimpleVar;
    }

    /**
     * This is the key that is used to store the brief description
     * of the error and display it on the error page.
     *
     * @param errorViewSimpleVar the errorViewSimpleVar to set
     */
    public void setErrorViewSimpleVar(String errorViewSimpleVar) {
        this.errorViewSimpleVar = errorViewSimpleVar;
    }

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