at.gv.egovernment.moa.id.protocols.pvp2x.SingleLogOutAction.java Source code

Java tutorial

Introduction

Here is the source code for at.gv.egovernment.moa.id.protocols.pvp2x.SingleLogOutAction.java

Source

/*
 * Copyright 2014 Federal Chancellery Austria
 * MOA-ID has been developed in a cooperation between BRZ, the Federal
 * Chancellery Austria - ICT staff unit, and Graz University of Technology.
 *
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
 * the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 * http://www.osor.eu/eupl/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 *
 * This product combines work with different licenses. See the "NOTICE" text
 * file for details on the various modules and licenses.
 * The "NOTICE" text file is part of the distribution. Any derivative works
 * that you distribute must include a readable copy of the "NOTICE" text file.
 */
package at.gv.egovernment.moa.id.protocols.pvp2x;

import java.io.Serializable;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

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

import org.apache.commons.lang.SerializationUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.opensaml.common.SAMLObject;
import org.opensaml.common.binding.BasicSAMLMessageContext;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.core.LogoutRequest;
import org.opensaml.saml2.core.LogoutResponse;
import org.opensaml.saml2.core.RequestAbstractType;
import org.opensaml.saml2.core.Status;
import org.opensaml.saml2.core.StatusCode;
import org.opensaml.saml2.core.StatusResponseType;
import org.opensaml.saml2.metadata.SingleLogoutService;
import org.opensaml.saml2.metadata.impl.SingleLogoutServiceBuilder;
import org.opensaml.ws.message.encoder.MessageEncodingException;
import org.opensaml.ws.soap.common.SOAPException;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.security.SecurityException;
import org.opensaml.xml.security.x509.X509Credential;

import at.gv.egovernment.moa.id.auth.data.AuthenticationSession;
import at.gv.egovernment.moa.id.auth.exception.AuthenticationException;
import at.gv.egovernment.moa.id.auth.exception.MOAIDException;
import at.gv.egovernment.moa.id.auth.servlet.RedirectServlet;
import at.gv.egovernment.moa.id.commons.db.MOASessionDBUtils;
import at.gv.egovernment.moa.id.commons.db.dao.session.AssertionStore;
import at.gv.egovernment.moa.id.commons.db.dao.session.InterfederationSessionStore;
import at.gv.egovernment.moa.id.commons.db.dao.session.OASessionStore;
import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException;
import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider;
import at.gv.egovernment.moa.id.data.IAuthData;
import at.gv.egovernment.moa.id.data.SLOInformationContainer;
import at.gv.egovernment.moa.id.data.SLOInformationImpl;
import at.gv.egovernment.moa.id.data.SLOInformationInterface;
import at.gv.egovernment.moa.id.moduls.AuthenticationManager;
import at.gv.egovernment.moa.id.moduls.IAction;
import at.gv.egovernment.moa.id.moduls.IRequest;
import at.gv.egovernment.moa.id.moduls.SSOManager;
import at.gv.egovernment.moa.id.opemsaml.MOAStringRedirectDeflateEncoder;
import at.gv.egovernment.moa.id.protocols.pvp2x.binding.IEncoder;
import at.gv.egovernment.moa.id.protocols.pvp2x.binding.PostBinding;
import at.gv.egovernment.moa.id.protocols.pvp2x.binding.RedirectBinding;
import at.gv.egovernment.moa.id.protocols.pvp2x.builder.SingleLogOutBuilder;
import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.BindingNotSupportedException;
import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.SLOException;
import at.gv.egovernment.moa.id.protocols.pvp2x.messages.MOARequest;
import at.gv.egovernment.moa.id.protocols.pvp2x.messages.MOAResponse;
import at.gv.egovernment.moa.id.protocols.pvp2x.signer.CredentialProvider;
import at.gv.egovernment.moa.id.protocols.pvp2x.utils.MOASAMLSOAPClient;
import at.gv.egovernment.moa.id.storage.AssertionStorage;
import at.gv.egovernment.moa.id.storage.AuthenticationSessionStoreage;
import at.gv.egovernment.moa.id.util.Random;
import at.gv.egovernment.moa.id.util.VelocityProvider;
import at.gv.egovernment.moa.logging.Logger;
import at.gv.egovernment.moa.util.MessageProvider;
import at.gv.egovernment.moa.util.MiscUtil;
import at.gv.egovernment.moa.util.URLEncoder;

/**
 * @author tlenz
 *
 */
public class SingleLogOutAction implements IAction {

    /* (non-Javadoc)
     * @see at.gv.egovernment.moa.id.moduls.IAction#processRequest(at.gv.egovernment.moa.id.moduls.IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, at.gv.egovernment.moa.id.data.IAuthData)
     */
    @Override
    public SLOInformationInterface processRequest(IRequest req, HttpServletRequest httpReq,
            HttpServletResponse httpResp, IAuthData authData) throws MOAIDException {

        PVPTargetConfiguration pvpReq = (PVPTargetConfiguration) req;

        if (pvpReq.getRequest() instanceof MOARequest
                && ((MOARequest) pvpReq.getRequest()).getSamlRequest() instanceof LogoutRequest) {
            Logger.debug("Process Single LogOut request");
            MOARequest samlReq = (MOARequest) pvpReq.getRequest();
            LogoutRequest logOutReq = (LogoutRequest) samlReq.getSamlRequest();

            AuthenticationSession session = AuthenticationSessionStoreage.searchMOASessionWithNameIDandOAID(
                    logOutReq.getIssuer().getValue(), logOutReq.getNameID().getValue());

            if (session == null) {
                Logger.warn("Can not find active SSO session with nameID " + logOutReq.getNameID().getValue()
                        + " and OA " + logOutReq.getIssuer().getValue());
                Logger.info("Search active SSO session with SSO session cookie");
                SSOManager ssomanager = SSOManager.getInstance();
                String ssoID = ssomanager.getSSOSessionID(httpReq);
                if (MiscUtil.isEmpty(ssoID)) {
                    Logger.warn("Can not find active Session. Single LogOut not possible!");
                    SingleLogoutService sloService = SingleLogOutBuilder.getResponseSLODescriptor(pvpReq);
                    //LogoutResponse message = SingleLogOutBuilder.buildSLOErrorResponse(sloService, pvpReq, StatusCode.RESPONDER_URI);
                    LogoutResponse message = SingleLogOutBuilder.buildSLOResponseMessage(sloService, pvpReq, null);
                    Logger.info("Sending SLO success message to requester ...");
                    SingleLogOutBuilder.sendFrontChannelSLOMessage(sloService, message, httpReq, httpResp,
                            samlReq.getRelayState());
                    return null;

                } else {
                    String moasession = ssomanager.getMOASession(ssoID);
                    try {
                        session = AuthenticationSessionStoreage.getSession(moasession);

                    } catch (MOADatabaseException e) {
                        Logger.warn("Can not find active Session. Single LogOut not possible!");
                        SingleLogoutService sloService = SingleLogOutBuilder.getResponseSLODescriptor(pvpReq);
                        //LogoutResponse message = SingleLogOutBuilder.buildSLOErrorResponse(sloService, pvpReq, StatusCode.RESPONDER_URI);
                        LogoutResponse message = SingleLogOutBuilder.buildSLOResponseMessage(sloService, pvpReq,
                                null);
                        Logger.info("Sending SLO success message to requester ...");
                        SingleLogOutBuilder.sendFrontChannelSLOMessage(sloService, message, httpReq, httpResp,
                                samlReq.getRelayState());
                        return null;

                    }
                }
            }

            AuthenticationManager authManager = AuthenticationManager.getInstance();
            authManager.performSingleLogOut(httpReq, httpResp, session, pvpReq);

        } else if (pvpReq.getRequest() instanceof MOAResponse
                && ((MOAResponse) pvpReq.getRequest()).getResponse() instanceof LogoutResponse) {
            Logger.debug("Process Single LogOut response");
            LogoutResponse logOutResp = (LogoutResponse) ((MOAResponse) pvpReq.getRequest()).getResponse();

            Transaction tx = null;

            try {
                String relayState = pvpReq.getRequest().getRelayState();
                if (MiscUtil.isEmpty(relayState)) {
                    Logger.warn(
                            "SLO Response from " + logOutResp.getIssuer().getValue() + " has no SAML2 RelayState.");
                    throw new SLOException("pvp2.19", null);

                }

                Session session = MOASessionDBUtils.getCurrentSession();
                boolean storageSuccess = false;
                int counter = 0;

                //TODO: add counter to prevent deadlock

                while (!storageSuccess) {
                    tx = session.beginTransaction();

                    List result;
                    Query query = session.getNamedQuery("getAssertionWithArtifact");
                    query.setParameter("artifact", relayState);
                    result = query.list();
                    Logger.trace("Found entries: " + result.size());

                    //Assertion requires an unique artifact
                    if (result.size() != 1) {
                        Logger.trace("No entries found.");
                        throw new MOADatabaseException("No sessioninformation found with this ID");
                    }

                    AssertionStore element = (AssertionStore) result.get(0);
                    Object data = SerializationUtils.deserialize(element.getAssertion());

                    if (data instanceof SLOInformationContainer) {
                        SLOInformationContainer sloContainer = (SLOInformationContainer) data;

                        //check status
                        SingleLogOutBuilder.checkStatusCode(sloContainer, logOutResp);

                        if (sloContainer.hasFrontChannelOA()) {
                            try {
                                //some response are open
                                byte[] serializedSLOContainer = SerializationUtils
                                        .serialize((Serializable) sloContainer);
                                element.setAssertion(serializedSLOContainer);
                                element.setType(sloContainer.getClass().getName());

                                session.saveOrUpdate(element);
                                tx.commit();

                                //sloContainer could be stored to database
                                storageSuccess = true;

                            } catch (HibernateException e) {
                                tx.rollback();

                                counter++;
                                Logger.debug(
                                        "SLOContainter could not stored to database. Wait some time and restart storage process ... ");
                                java.util.Random rand = new java.util.Random();

                                try {
                                    Thread.sleep(rand.nextInt(20) * 10);

                                } catch (InterruptedException e1) {
                                    Logger.warn("Thread could not stopped. ReStart storage process immediately",
                                            e1);
                                }
                            }

                        } else {
                            //last response received.
                            try {
                                session.delete(element);
                                tx.commit();

                            } catch (HibernateException e) {
                                tx.rollback();
                                Logger.error("SLOContainter could not deleted from database. ");

                            }

                            storageSuccess = true;
                            String redirectURL = null;
                            if (sloContainer.getSloRequest() != null) {
                                //send SLO response to SLO request issuer
                                SingleLogoutService sloService = SingleLogOutBuilder
                                        .getResponseSLODescriptor(sloContainer.getSloRequest());
                                LogoutResponse message = SingleLogOutBuilder.buildSLOResponseMessage(sloService,
                                        sloContainer.getSloRequest(), sloContainer.getSloFailedOAs());
                                redirectURL = SingleLogOutBuilder.getFrontChannelSLOMessageURL(sloService, message,
                                        httpReq, httpResp,
                                        sloContainer.getSloRequest().getRequest().getRelayState());

                            } else {
                                //print SLO information directly
                                redirectURL = AuthConfigurationProvider.getInstance().getPublicURLPrefix()
                                        + "/idpSingleLogout";

                                String artifact = Random.nextRandom();

                                String statusCode = null;
                                if (sloContainer.getSloFailedOAs() == null
                                        || sloContainer.getSloFailedOAs().size() == 0)
                                    statusCode = SLOSTATUS_SUCCESS;
                                else
                                    statusCode = SLOSTATUS_ERROR;

                                AssertionStorage.getInstance().put(artifact, statusCode);
                                redirectURL = addURLParameter(redirectURL, PARAM_SLOSTATUS, artifact);

                            }
                            //redirect to Redirect Servlet
                            String url = AuthConfigurationProvider.getInstance().getPublicURLPrefix()
                                    + "/RedirectServlet";
                            url = addURLParameter(url, RedirectServlet.REDIRCT_PARAM_URL,
                                    URLEncoder.encode(redirectURL, "UTF-8"));
                            url = httpResp.encodeRedirectURL(url);

                            httpResp.setContentType("text/html");
                            httpResp.setStatus(302);
                            httpResp.addHeader("Location", url);

                        }
                    } else {
                        Logger.warn("Sessioninformation Cast-Exception by using Artifact=" + relayState);
                        throw new MOADatabaseException("Sessioninformation Cast-Exception");

                    }
                }

            } catch (MOADatabaseException e) {
                Logger.error("MOA AssertionDatabase ERROR", e);
                throw new SLOException("pvp2.19", null);

            } catch (UnsupportedEncodingException e) {
                Logger.error("Finale SLO redirct not possible.", e);
                throw new AuthenticationException("pvp2.13", new Object[] {});

            } finally {
                if (tx != null && !tx.wasCommitted()) {
                    tx.commit();

                }
            }

        } else {
            Logger.error("Process SingleLogOutAction but request is NOT of type LogoutRequest or LogoutResponse.");
            throw new MOAIDException("pvp2.13", null);

        }

        return null;
    }

    /* (non-Javadoc)
     * @see at.gv.egovernment.moa.id.moduls.IAction#needAuthentication(at.gv.egovernment.moa.id.moduls.IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    @Override
    public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) {
        return false;
    }

    /* (non-Javadoc)
     * @see at.gv.egovernment.moa.id.moduls.IAction#getDefaultActionName()
     */
    @Override
    public String getDefaultActionName() {
        return PVP2XProtocol.SINGLELOGOUT;
    }

    protected static String addURLParameter(String url, String paramname, String paramvalue) {
        String param = paramname + "=" + paramvalue;
        if (url.indexOf("?") < 0)
            return url + "?" + param;
        else
            return url + "&" + param;
    }
}