org.aselect.server.request.handler.xsaml20.sp.Xsaml20_AssertionConsumer.java Source code

Java tutorial

Introduction

Here is the source code for org.aselect.server.request.handler.xsaml20.sp.Xsaml20_AssertionConsumer.java

Source

/*
 * * Copyright (c) Anoigo. All rights reserved.
 *
 * A-Select is a trademark registered by SURFnet bv.
 *
 * This program is distributed under the EUPL 1.0 (http://osor.eu/eupl)
 * See the included LICENSE file for details.
 *
 * If you did not receive a copy of the LICENSE
 * please contact Anoigo. (http://www.anoigo.nl) 
 */
package org.aselect.server.request.handler.xsaml20.sp;

import java.io.PrintWriter;
import java.io.StringReader;
import java.security.PublicKey;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.aselect.server.application.ApplicationManager;
import org.aselect.server.config.ASelectConfigManager;
import org.aselect.server.log.ASelectAuthProofLogger;
import org.aselect.server.log.ASelectAuthenticationLogger;
import org.aselect.server.request.HandlerTools;
import org.aselect.server.request.RequestState;
import org.aselect.server.request.handler.xsaml20.PartnerData;
import org.aselect.server.request.handler.xsaml20.Saml20_BaseHandler;
import org.aselect.server.request.handler.xsaml20.SamlTools;
import org.aselect.server.request.handler.xsaml20.SecurityLevel;
import org.aselect.server.request.handler.xsaml20.SoapManager;
import org.aselect.server.tgt.TGTManager;
import org.aselect.server.tgt.TGTIssuer;
import org.aselect.system.error.Errors;
import org.aselect.system.exception.ASelectCommunicationException;
import org.aselect.system.exception.ASelectException;
import org.aselect.system.utils.Base64Codec;
import org.aselect.system.utils.Utils;
import org.joda.time.DateTime;
import org.opensaml.Configuration;
import org.opensaml.common.SAMLObjectBuilder;
import org.opensaml.common.SAMLVersion;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.core.*;
import org.opensaml.saml2.metadata.ArtifactResolutionService;
import org.opensaml.ws.soap.soap11.Envelope;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.opensaml.xml.io.Unmarshaller;
import org.opensaml.xml.io.UnmarshallerFactory;
import org.opensaml.xml.util.XMLHelper;
import org.w3c.dom.*;
import org.xml.sax.InputSource;

/**
 * SAML2.0 AssertionConsumer for A-Select (Service Provider side). <br>
 * <br>
 * <b>Description:</b><br>
 * The SAML2.0 AssertionConsumer for the A-Select Server (Service Provider side).<br/>
 * HTTP GET containing the following items in the querystring<br/>
 * <ul>
 * <li><b>SAMLart</b> - The SAML2.0 artifact</li>
 * </ul>
 * The SAML2.0 artifact is resolved by the requesters ArtifactResolver. The resolved artifact contains a SAML2.0
 * Response which is futher handled by this A-Select server. <br>
 */
public class Xsaml20_AssertionConsumer extends Saml20_BaseHandler {
    private final static String MODULE = "Xsaml20_AssertionConsumer";
    private XMLObjectBuilderFactory _oBuilderFactory;
    private ASelectAuthenticationLogger _authenticationLogger;
    protected TGTManager _tgtManager;
    private String _sMyServerId;
    private String _sFederationUrl;
    private String _sRedirectUrl; // We use as Issuer in the send SAML message
    //private String _sRequestIssuer; // But it can be set explicitly
    private boolean signingRequired = false;
    // Get from aselect.xml <applications require_signing="false | true">
    private boolean localityAddressRequired = false; // Do we need to verify localityAddress in the AuthnStatement

    private boolean includeSessionindexes = false;
    //    should the SessionIndex(es) be included in the saml request, to be included the must be present in the tgt 
    //      and therefore previously been received in the assertion
    //    if null value is not specified in aselect.xml

    private boolean useBackchannelClientcertificate = false;

    // 20120712, Bauke: Store TGT in class variable to save on reads
    protected HashMap _htTGTContext = null;

    private boolean verifyArtifactResponseSignature = false;

    private boolean useNameIDAsAuthID = false;

    private boolean carryAuthProof = false;
    private boolean logAuthProof = false;

    //
    // Example configuration:
    // <handler id="saml20_assertionconsumer"
    // class="org.aselect.server.request.handler.xsaml20.Xsaml20_AssertionConsumer"
    // target="/saml20_assertion.*" >
    // </handler>
    //
    /**
     * Initializes the request handler by reading the configuration.
     * 
     * @param oServletConfig
     *            the o servlet config
     * @param oHandlerConfig
     *            the o handler config
     * @throws ASelectException
     *             the a select exception
     */
    public void init(ServletConfig oServletConfig, Object oHandlerConfig) throws ASelectException {
        String sMethod = "init";

        super.init(oServletConfig, oHandlerConfig);

        _oBuilderFactory = Configuration.getBuilderFactory();
        _sMyServerId = ASelectConfigManager.getParamFromSection(null, "aselect", "server_id", true);
        _sFederationUrl = ASelectConfigManager.getParamFromSection(null, "aselect", "federation_url", false);
        // Issuer in the send SAML message
        _sRedirectUrl = ASelectConfigManager.getParamFromSection(null, "aselect", "redirect_url", true);
        // 20100315: Specific issuer (eHerkenning) different from redirect_url
        // 20100429: removed: _sRequestIssuer = ASelectConfigManager.getSimpleParam(oHandlerConfig, "issuer", false);

        String sLocalityAddressRequired = ASelectConfigManager.getSimpleParam(oHandlerConfig,
                "locality_address_required", false);
        if ("true".equalsIgnoreCase(sLocalityAddressRequired)) {
            setLocalityAddressRequired(true);
        }
        // RH, 20120201, sn
        String sIncludeSessionindexes = ASelectConfigManager.getSimpleParam(oHandlerConfig,
                "include_sessionindexes", false);
        if ("true".equalsIgnoreCase(sIncludeSessionindexes)) {
            setIncludeSessionindexes(true);
        }
        _systemLogger.log(Level.INFO, MODULE, sMethod, "include_sessionindexes: " + isIncludeSessionindexes());
        // RH, 20120201, en

        // RH, 20120322, sn
        String sUseBackChannelClientCertificate = ASelectConfigManager.getSimpleParam(oHandlerConfig,
                "use_backchannelclientcertificate", false);
        //      if ("true".equalsIgnoreCase(sIncludeSessionindexes)) {      // RH, 20130923, o
        if ("true".equalsIgnoreCase(sUseBackChannelClientCertificate)) { // RH, 20130923, n
            setUseBackchannelClientcertificate(true);
        }
        _systemLogger.log(Level.INFO, MODULE, sMethod,
                "use_backchannelclientcertificate: " + isUseBackchannelClientcertificate());
        // RH, 20120322, en

        // RH, 20121205, sn
        String sVerifyArtifactresponseSignature = ASelectConfigManager.getSimpleParam(oHandlerConfig,
                "verify_artifactresponsesignature", false);
        if ("true".equalsIgnoreCase(sVerifyArtifactresponseSignature)) {
            setVerifyArtifactResponseSignature(true);
        }
        // RH, 20121205, en

        // RH, 20130923, sn
        String sUseNameIDAsAuthID = ASelectConfigManager.getSimpleParam(oHandlerConfig, "use_nameidasauthid",
                false);
        if ("true".equalsIgnoreCase(sUseNameIDAsAuthID)) {
            setUseNameIDAsAuthID(true);
        }
        // RH, 20130923, en

        // Because carrying and/or logging the quite large auth_proof may have some impact on resources we can enable/disable
        // RH, 20140327, sn
        String sCarryAuthProof = ASelectConfigManager.getSimpleParam(oHandlerConfig, "carry_auth_proof", false);
        if ("true".equalsIgnoreCase(sCarryAuthProof)) {
            setCarryAuthProof(true);
        }
        // RH, 20140327, en

        // RH, 20140327, sn
        String sLogAuthProof = ASelectConfigManager.getSimpleParam(oHandlerConfig, "log_auth_proof", false);
        if ("true".equalsIgnoreCase(sLogAuthProof)) {
            setLogAuthProof(true);
        }
        // RH, 20140327, en

        _tgtManager = TGTManager.getHandle();
        _authenticationLogger = ASelectAuthenticationLogger.getHandle();
    }

    /**
     * Assertion consumer. <br>
     * 
     * @param servletRequest
     *            HttpServletRequest.
     * @param servletResponse
     *            HttpServletResponse.
     * @return the request state
     * @throws ASelectException
     *             on failure
     */
    @SuppressWarnings("unchecked")
    public RequestState process(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
            throws ASelectException {
        String sMethod = "process";
        boolean checkAssertionSigning = false;
        Object samlResponseObject = null;
        String auth_proof = null;
        PrintWriter pwOut = null;

        try {
            pwOut = Utils.prepareForHtmlOutput(servletRequest, servletResponse);

            String sReceivedArtifact = servletRequest.getParameter("SAMLart");
            String sReceivedResponse = servletRequest.getParameter("SAMLResponse");
            String sRelayState = servletRequest.getParameter("RelayState");
            _systemLogger.log(Level.INFO, MODULE, sMethod,
                    "Received artifact: " + sReceivedArtifact + " RelayState=" + sRelayState);
            if (!(sReceivedArtifact == null || "".equals(sReceivedArtifact))) {
                String sFederationUrl = _sFederationUrl; // default, remove later on, can be null
                if (sRelayState.startsWith("idp=")) {
                    sFederationUrl = sRelayState.substring(4);
                } else { // Could be Base64 encoded
                    sRelayState = new String(Base64Codec.decode(sRelayState));
                    _systemLogger.log(Level.INFO, MODULE, sMethod, "RelayState=" + sRelayState);
                    sFederationUrl = Utils.getParameterValueFromUrl(sRelayState, "idp");
                }
                if (!Utils.hasValue(sFederationUrl)) {
                    _systemLogger.log(Level.WARNING, MODULE, sMethod,
                            "No idp value found in RelayState (or in <federation_url> config)");
                    throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                }

                _systemLogger.log(Level.INFO, MODULE, sMethod, "FederationUrl=" + sFederationUrl);
                // use metadata
                MetaDataManagerSp metadataManager = MetaDataManagerSp.getHandle();
                String sASelectServerUrl = metadataManager.getLocation(sFederationUrl,
                        ArtifactResolutionService.DEFAULT_ELEMENT_LOCAL_NAME,
                        SAMLConstants.SAML2_SOAP11_BINDING_URI);
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Artifact Resolution at " + sASelectServerUrl);

                if (sASelectServerUrl == null) {
                    _systemLogger.log(Level.INFO, MODULE, sMethod, "Artifact NOT found");
                    throw new ASelectException(Errors.ERROR_ASELECT_NOT_FOUND);
                }

                SAMLObjectBuilder<Artifact> artifactBuilder = (SAMLObjectBuilder<Artifact>) _oBuilderFactory
                        .getBuilder(Artifact.DEFAULT_ELEMENT_NAME);
                Artifact artifact = artifactBuilder.buildObject();
                artifact.setArtifact(sReceivedArtifact);

                SAMLObjectBuilder<ArtifactResolve> artifactResolveBuilder = (SAMLObjectBuilder<ArtifactResolve>) _oBuilderFactory
                        .getBuilder(ArtifactResolve.DEFAULT_ELEMENT_NAME);
                ArtifactResolve artifactResolve = artifactResolveBuilder.buildObject();

                artifactResolve.setID(SamlTools.generateIdentifier(_systemLogger, MODULE));
                artifactResolve.setVersion(SAMLVersion.VERSION_20);
                artifactResolve.setIssueInstant(new DateTime());

                // We decided that the other side could retrieve public key from metadata
                // by looking up the issuer as an entityID in the metadata
                // So we MUST supply an Issuer (which otherwise would be optional (by SAML standards))
                SAMLObjectBuilder<Issuer> assertionIssuerBuilder = (SAMLObjectBuilder<Issuer>) _oBuilderFactory
                        .getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
                Issuer assertionIssuer = assertionIssuerBuilder.buildObject();

                // 20100312, Bauke: eHerkenning, no assertion issuer format:
                // assertionIssuer.setFormat(NameIDType.ENTITY);
                // 20100311, Bauke: added for eHerkenning: Specific issuer id, independent of the Url
                PartnerData partnerData = MetaDataManagerSp.getHandle().getPartnerDataEntry(sFederationUrl);
                String specialSettings = (partnerData == null) ? null : partnerData.getSpecialSettings();
                if (partnerData != null && partnerData.getLocalIssuer() != null)
                    assertionIssuer.setValue(partnerData.getLocalIssuer());
                else
                    assertionIssuer.setValue(_sRedirectUrl);
                artifactResolve.setIssuer(assertionIssuer);
                artifactResolve.setArtifact(artifact);

                // Do some logging for testing
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Sign the artifactResolve >======");
                boolean useSha256 = (specialSettings != null && specialSettings.contains("sha256"));
                artifactResolve = (ArtifactResolve) SamlTools.signSamlObject(artifactResolve,
                        useSha256 ? "sha256" : "sha1");
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Signed the artifactResolve ======<");

                // Build the SOAP message
                SoapManager soapManager = null;
                if (isUseBackchannelClientcertificate()) {
                    soapManager = new SoapManager(getSslSocketFactory());
                } else {
                    soapManager = new SoapManager();
                }
                Envelope envelope = soapManager.buildSOAPMessage(artifactResolve);
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Marshall");
                Element envelopeElem = SamlTools.marshallMessage(envelope);
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Writing SOAP message:\n" + XMLHelper.nodeToString(envelopeElem));
                // XMLHelper.prettyPrintXML(envelopeElem));

                // ------------ Send/Receive the SOAP message
                String sSamlResponse = soapManager.sendSOAP(XMLHelper.nodeToString(envelopeElem),
                        sASelectServerUrl); // x_AssertionConsumer_x
                //byte[] sSamlResponseAsBytes = sSamlResponse.getBytes();
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Received response: " + sSamlResponse + " length=" + sSamlResponse.length());

                // save original, but, for (internal) transport, encode base64 
                auth_proof = new String(
                        org.apache.commons.codec.binary.Base64.encodeBase64(sSamlResponse.getBytes("UTF-8")));

                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                dbFactory.setNamespaceAware(true);
                // dbFactory.setExpandEntityReferences(false);
                // dbFactory.setIgnoringComments(true);
                DocumentBuilder builder = dbFactory.newDocumentBuilder();

                StringReader stringReader = new StringReader(sSamlResponse);
                InputSource inputSource = new InputSource(stringReader);
                Document docReceivedSoap = builder.parse(inputSource);
                _systemLogger.log(Level.INFO, MODULE, sMethod, "parsed=" + docReceivedSoap.toString());
                Element elementReceivedSoap = docReceivedSoap.getDocumentElement();
                _systemLogger.log(Level.INFO, MODULE, sMethod, "getdoc=" + elementReceivedSoap.toString());

                // Remove all SOAP elements
                Node eltArtifactResponse = SamlTools.getNode(elementReceivedSoap, "ArtifactResponse");

                // Unmarshall to the SAMLmessage
                UnmarshallerFactory factory = Configuration.getUnmarshallerFactory();
                Unmarshaller unmarshaller = factory.getUnmarshaller((Element) eltArtifactResponse);
                ArtifactResponse artifactResponse = (ArtifactResponse) unmarshaller
                        .unmarshall((Element) eltArtifactResponse);

                Issuer issuer = artifactResponse.getIssuer();
                String sIssuer = (issuer == null) ? null : issuer.getValue();
                // If issuer is not present in the response, use sASelectServerUrl value retrieved from metadata
                // else use value from the response
                String artifactResponseIssuer = (sIssuer == null || "".equals(sIssuer)) ? sASelectServerUrl
                        : sIssuer;

                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Do artifactResponse signature verification=" + is_bVerifySignature());
                //            if (is_bVerifySignature()) {   // RH, 20121205, o
                if (is_bVerifySignature() || isVerifyArtifactResponseSignature()) { // RH, 20121205, n
                    // Check signature of artifactResolve here
                    // We get the public key from the metadata
                    // Therefore we need a valid Issuer to lookup the entityID in the metadata
                    // We get the metadataURL from aselect.xml so we consider this safe and authentic
                    if (artifactResponseIssuer == null || "".equals(artifactResponseIssuer)) {
                        _systemLogger.log(Level.SEVERE, MODULE, sMethod,
                                "For signature verification the received message must have an Issuer");
                        throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                    }

                    PublicKey pkey = metadataManager.getSigningKeyFromMetadata(artifactResponseIssuer);
                    if (pkey == null || "".equals(pkey)) {
                        _systemLogger.log(Level.SEVERE, MODULE, sMethod, "No valid public key in metadata");
                        throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                    }

                    if (SamlTools.checkSignature(artifactResponse, pkey)) {
                        _systemLogger.log(Level.INFO, MODULE, sMethod, "artifactResponse was signed OK");
                    } else {
                        _systemLogger.log(Level.SEVERE, MODULE, sMethod, "artifactResponse was NOT signed OK");
                        throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                    }
                }
                samlResponseObject = artifactResponse.getMessage();
            } else if (!(sReceivedResponse == null || "".equals(sReceivedResponse))) {
                // Handle http-post, can be unsolicited POST as well
                // Could be Base64 encoded
                // RelayState should contain intended application resource URL
                sRelayState = new String(Base64Codec.decode(sRelayState));

                _systemLogger.log(Level.FINER, MODULE, sMethod, "Received Response: " + sReceivedResponse); //   RH, 20130924, n
                //            sReceivedResponse = new String(Base64Codec.decode(sReceivedResponse));   //   RH, 20130924, o
                auth_proof = sReceivedResponse; // save original

                sReceivedResponse = new String(
                        org.apache.commons.codec.binary.Base64.decodeBase64(sReceivedResponse.getBytes("UTF-8"))); //   RH, 20130924, n
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Received Response after base64 decoding: "
                        + sReceivedResponse + " RelayState=" + sRelayState); //   RH, 20130924, n
                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                dbFactory.setNamespaceAware(true);
                // dbFactory.setExpandEntityReferences(false);
                // dbFactory.setIgnoringComments(true);
                DocumentBuilder builder = dbFactory.newDocumentBuilder();

                StringReader stringReader = new StringReader(sReceivedResponse);
                InputSource inputSource = new InputSource(stringReader);
                Document docReceived = builder.parse(inputSource);
                Node eltSAMLResponse = SamlTools.getNode(docReceived, "Response");
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Found node Response: " + eltSAMLResponse + ((eltSAMLResponse == null) ? " NULL" : " ok"));

                // Unmarshall to the SAMLmessage
                UnmarshallerFactory factory = Configuration.getUnmarshallerFactory();
                Unmarshaller unmarshaller = factory.getUnmarshaller((Element) eltSAMLResponse);
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Unmarshaller" + ((unmarshaller == null) ? " NULL" : " ok"));
                samlResponseObject = (Response) unmarshaller.unmarshall((Element) eltSAMLResponse);
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Unmarshalling done, VerifySignature=" + is_bVerifySignature());

                // 20120308: Bauke added signature checking
                //   saml-profiles-2.0-os: The <Assertion> element(s) in the <Response> MUST be signed,
                //   if the HTTP POST binding is used, and MAY be signed if the HTTPArtifact binding is used.
                if (is_bVerifySignature())
                    checkAssertionSigning = true;

            } else {
                _systemLogger.log(Level.WARNING, MODULE, sMethod,
                        "No Artifact and no Response found in the message.");
                throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
            }

            ///////
            // The object can either a Response (SSO case) or a StatusResponseType (SLO case)
            ///////////////////////////////////////////////////////////////////////////
            if (samlResponseObject instanceof Response) {
                // SSO
                Response samlResponse = (Response) samlResponseObject;
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Processing 'Response'"); // +XMLHelper.prettyPrintXML(samlResponse.getDOM()));

                // RH, 20121205, sn
                MetaDataManagerSp metadataManager = MetaDataManagerSp.getHandle();
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "Do Response signature verification=" + isVerifyResponseSignature());
                if (isVerifyResponseSignature()) {
                    Issuer issuer = samlResponse.getIssuer();
                    String sIssuer = (issuer == null) ? null : issuer.getValue();
                    // If issuer is not present in the response, use sASelectServerUrl value retrieved from metadata
                    // else use value from the response
                    //               String responseIssuer = (sIssuer == null || "".equals(sIssuer))? sASelectServerUrl: sIssuer;
                    String responseIssuer = (sIssuer == null || "".equals(sIssuer)) ? null : sIssuer; // There must be an issuer for now
                    // Check signature of artifactResolve here
                    // We get the public key from the metadata
                    // Therefore we need a valid Issuer to lookup the entityID in the metadata
                    // We get the metadataURL from aselect.xml so we consider this safe and authentic
                    if (responseIssuer == null || "".equals(responseIssuer)) {
                        _systemLogger.log(Level.SEVERE, MODULE, sMethod,
                                "For signature verification the received response must have an Issuer");
                        throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                    }

                    PublicKey pkey = metadataManager.getSigningKeyFromMetadata(responseIssuer);
                    if (pkey == null || "".equals(pkey)) {
                        _systemLogger.log(Level.SEVERE, MODULE, sMethod, "No valid public key in metadata");
                        throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                    }

                    if (SamlTools.checkSignature(samlResponse, pkey)) {
                        _systemLogger.log(Level.INFO, MODULE, sMethod, "Response was signed OK");
                    } else {
                        _systemLogger.log(Level.SEVERE, MODULE, sMethod, "Response was NOT signed OK");
                        throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                    }
                }
                // RH, 20121205, en

                // Detect if this is a successful or an error Response      
                String sStatusCode = samlResponse.getStatus().getStatusCode().getValue();
                String sRemoteRid = samlResponse.getID();

                // 20100531, Bauke: Remove added timestamp to get our local RID
                String sLocalRid = samlResponse.getInResponseTo();
                int len = sLocalRid.length();
                if (len > 9)
                    sLocalRid = sLocalRid.substring(0, len - 9);
                _systemLogger.log(Level.INFO, MODULE, sMethod,
                        "RemoteRid=" + sRemoteRid + " LocalRid=" + sLocalRid + " StatusCode=" + sStatusCode);
                _htSessionContext = _oSessionManager.getSessionContext(sLocalRid);
                if (_htSessionContext == null) {
                    _systemLogger.log(Level.WARNING, MODULE, sMethod,
                            "Unknown session in response from cross aselect server");
                    throw new ASelectCommunicationException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                }

                if (sStatusCode.equals(StatusCode.SUCCESS_URI)) {
                    _systemLogger.log(Level.INFO, MODULE, sMethod,
                            "Response was successful " + samlResponse.toString());
                    _systemLogger.log(Level.INFO, MODULE, sMethod,
                            "Number of Assertions found:  " + samlResponse.getAssertions().size());
                    Assertion samlAssertion = samlResponse.getAssertions().get(0);
                    _systemLogger.log(Level.INFO, MODULE, sMethod, "Assertion ID:" + samlAssertion.getID());
                    String sAssertIssuer = samlAssertion.getIssuer().getValue();
                    _systemLogger.log(Level.INFO, MODULE, sMethod,
                            "Issuer:" + sAssertIssuer + " checkAssertionSigning=" + checkAssertionSigning);

                    // 20120308: Bauke added signature checking
                    //               if (checkAssertionSigning) {   // RH, 20121205, o
                    if (checkAssertionSigning || isVerifyAssertionSignature()) { // RH, 20121205, n
                        // Check signature of artifactResolve here. We get the public key from the metadata
                        // Therefore we need a valid Issuer to lookup the entityID in the metadata
                        // We get the metadataURL from aselect.xml so we consider this safe and authentic
                        _systemLogger.log(Level.INFO, MODULE, sMethod,
                                "Verify assertion signature, issuer=" + sAssertIssuer);
                        if (!Utils.hasValue(sAssertIssuer)) {
                            _systemLogger.log(Level.SEVERE, MODULE, sMethod, "No Issuer present in Assertion");
                            throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                        }

                        //                  MetaDataManagerSp metadataManager = MetaDataManagerSp.getHandle();   // RH, 20121205, n
                        PublicKey pkey = metadataManager.getSigningKeyFromMetadata(sAssertIssuer);
                        if (pkey == null || "".equals(pkey)) {
                            _systemLogger.log(Level.SEVERE, MODULE, sMethod, "No valid public key in metadata");
                            throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                        }
                        if (!SamlTools.checkSignature(samlAssertion, pkey)) {
                            _systemLogger.log(Level.SEVERE, MODULE, sMethod, "Assertion was NOT signed OK");
                            throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                        }
                        _systemLogger.log(Level.INFO, MODULE, sMethod, "Assertion was signed OK");
                    }
                    // 20120308

                    String sNameID = samlAssertion.getSubject().getNameID().getValue();
                    _systemLogger.log(Level.INFO, MODULE, sMethod, "NameID:" + sNameID);
                    String sNameIDQualifier = samlAssertion.getSubject().getNameID().getNameQualifier();
                    _systemLogger.log(Level.INFO, MODULE, sMethod, "NameIDQualifier:" + sNameIDQualifier);

                    // Now check for time interval validation
                    // We only check first object from the list
                    // First the assertion itself
                    if (is_bVerifyInterval() && !SamlTools.checkValidityInterval(samlAssertion)) {
                        _systemLogger.log(Level.SEVERE, MODULE, sMethod, "Assertion time interval was NOT valid");
                        throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                    }
                    // then the AuthnStatement
                    if (is_bVerifyInterval()
                            && !SamlTools.checkValidityInterval(samlAssertion.getAuthnStatements().get(0))) {
                        _systemLogger.log(Level.SEVERE, MODULE, sMethod,
                                "AuthnStatement time interval was NOT valid");
                        throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                    }
                    // check subjectlocalityaddress
                    if (isLocalityAddressRequired()
                            && !SamlTools.checkLocalityAddress(samlAssertion.getAuthnStatements().get(0),
                                    servletRequest.getRemoteAddr())) {
                        _systemLogger.log(Level.SEVERE, MODULE, sMethod,
                                "AuthnStatement subjectlocalityaddress was NOT valid");
                        throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                    }

                    // Get the (option) sessionindex from remote
                    String sSessionindex = samlAssertion.getAuthnStatements().get(0).getSessionIndex();
                    _systemLogger.log(Level.INFO, MODULE, sMethod, "Sessionindex:" + sSessionindex);

                    AuthnContext oAuthnContext = samlAssertion.getAuthnStatements().get(0).getAuthnContext();
                    List<AuthenticatingAuthority> authAuthorities = oAuthnContext.getAuthenticatingAuthorities();
                    String sAuthnAuthority = null;
                    if (authAuthorities != null && authAuthorities.size() > 0)
                        sAuthnAuthority = (String) authAuthorities.get(0).getURI();
                    String sAuthnContextClassRefURI = oAuthnContext.getAuthnContextClassRef()
                            .getAuthnContextClassRef();
                    _systemLogger.log(Level.INFO, MODULE, sMethod,
                            "AuthnContextClassRefURI:" + sAuthnContextClassRefURI);
                    ;
                    /////////////////////////   digid4   ///////////////////////////////////////////
                    /// Digid4 still has to decide how to provide a "face2face" declaration 
                    //   String sAuthnContextDeclRefIssueMethod = samlAssertion.getAuthnStatements().get(0).getAuthnContext().
                    /////////////////////////   digid4   ///////////////////////////////////////////
                    String sSelectedLevel = SecurityLevel
                            .convertAuthnContextClassRefURIToLevel(sAuthnContextClassRefURI, _systemLogger);

                    // Check returned security level
                    Integer intAppLevel = (Integer) _htSessionContext.get("level");
                    if (Integer.parseInt(sSelectedLevel) < intAppLevel) {
                        _systemLogger.log(Level.SEVERE, MODULE, sMethod, "Security level returned ("
                                + sSelectedLevel + ") must be at least: " + intAppLevel);
                        throw new ASelectException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
                    }

                    // Retrieve the embedded attributes
                    HashMap hmSamlAttributes = new HashMap();
                    String sEncodedAttributes = null;
                    List<AttributeStatement> lAttrStatList = samlAssertion.getAttributeStatements();
                    Iterator<AttributeStatement> iASList = lAttrStatList.iterator();
                    while (iASList.hasNext()) {
                        AttributeStatement sAttr = iASList.next();
                        List<Attribute> lAttr = sAttr.getAttributes();
                        Iterator<Attribute> iAttr = lAttr.iterator();
                        while (iAttr.hasNext()) {
                            Attribute attr = iAttr.next();
                            String sAttrName = attr.getName();

                            String sAttrValue = null;// RH, 20120124, sn
                            List<XMLObject> aValues = attr.getAttributeValues();
                            if (aValues != null && aValues.size() == 1) { // For now we only allow single valued simple type xs:string attributes
                                XMLObject xmlObj = aValues.get(0);
                                //                        XSStringImpl xsString = (XSStringImpl) attr.getOrderedChildren().get(0);// RH, 20120124, so
                                //                        String sAttrValue = xsString.getValue();// RH, 20120124, o
                                //                        sAttrValue = xsString.getValue();// RH, 20120124, eo
                                sAttrValue = xmlObj.getDOM().getFirstChild().getTextContent();
                                _systemLogger.log(Level.INFO, MODULE, sMethod,
                                        "Name=" + sAttrName + " Value=" + sAttrValue);
                            } else {
                                _systemLogger.log(Level.INFO, MODULE, sMethod,
                                        "Only single valued attributes allowed, skipped attribute Name="
                                                + sAttrName);
                            } // RH, 20120124, en
                            if ("attributes".equals(sAttrName))
                                sEncodedAttributes = sAttrValue;
                            else
                                hmSamlAttributes.put(sAttrName, sAttrValue);
                        }
                    }

                    // Since the "attributes" Attribute is used for gathering, add the Saml Attributes to it
                    HashMap<String, String> hmAttributes;
                    if (sEncodedAttributes != null) {
                        hmAttributes = org.aselect.server.utils.Utils.deserializeAttributes(sEncodedAttributes);
                    } else {
                        hmAttributes = new HashMap<String, String>();
                    }
                    // Add the serialized attributes and a few specials
                    hmSamlAttributes.putAll(hmAttributes);
                    hmSamlAttributes.put("name_id", sNameID); // "sel_level" was already set by the IdP
                    if (sAuthnAuthority != null)
                        hmSamlAttributes.put("authority", sAuthnAuthority);

                    // eHerkenning addition: OrgID = KvKnummer+Vestigingsnummer
                    // If EntityConcernedID = 00000003123456780000 and EntityConcernedSubID = ...0001,
                    // then orgid = 1234567800000001
                    //               String sEntityId = (String)hmSamlAttributes.get("urn:nl:eherkenning:0.8def:EntityConcernedID");
                    // RH, 20110523, add support for other versions of eHerk
                    String sEntityId = null;

                    Pattern p = Pattern.compile("urn:nl:eherkenning:(.*):EntityConcernedID");

                    Set<String> keys = hmSamlAttributes.keySet();
                    Iterator keyIter = keys.iterator();
                    String eHerkversion = null;
                    while (keyIter.hasNext()) {
                        Matcher m = p.matcher((String) keyIter.next());
                        if (m.find()) {
                            sEntityId = (String) hmSamlAttributes.get(m.group());
                            eHerkversion = m.group(1);
                            _systemLogger.log(Level.INFO, MODULE, sMethod,
                                    "Found sEntityId=" + sEntityId + " eHerkversion=" + eHerkversion);
                            break; // just take the first we find
                        }
                    }

                    if (sEntityId != null) {
                        int idx = sEntityId.length() - 12; // last 12 characters
                        if (idx > 0)
                            sEntityId = sEntityId.substring(idx);

                        //                  String sEntitySubId = (String)hmSamlAttributes.get("urn:nl:eherkenning:0.8def:EntityConcernedSubID");
                        String sEntitySubId = (String) hmSamlAttributes
                                .get("urn:nl:eherkenning:" + eHerkversion + ":EntityConcernedSubID");
                        if (sEntitySubId != null) {
                            _systemLogger.log(Level.INFO, MODULE, sMethod, "Found sEntitySubId=" + sEntitySubId);
                            idx = sEntitySubId.length() - 12; // last 12 characters to be on the safe side
                            if (idx > 0)
                                sEntitySubId = sEntitySubId.substring(idx);
                            sEntityId = sEntitySubId;
                        } else { // ditch the last 4 zeroes
                            idx = sEntityId.length() - 4;
                            if (idx > 0)
                                sEntityId = sEntityId.substring(0, idx);
                        }
                        hmSamlAttributes.put("orgid", sEntityId);
                    }

                    // eHerkenning: AuthID = Unique Persistent Identifier
                    if (isUseNameIDAsAuthID()) { // RH, 20130923, sn
                        hmSamlAttributes.put("authid", sNameID);
                    } else { // RH, 20130923, en
                        // Use the fifth word from sAuthnAuthority (split using :) and add sNameID
                        if (sNameIDQualifier != null) {
                            String sAuthID = "", sAuthSubID = "";
                            String[] tokens = sNameIDQualifier.split(":");
                            if (tokens.length > 4)
                                sAuthID = tokens[4];

                            //                  if (tokens.length > 5)
                            //                     sAuthSubID = tokens[5];
                            // Test  new layout of eherkenning
                            // Maybe do something with pattern search here
                            if (tokens.length > 6)
                                sAuthSubID = tokens[6];

                            sAuthID += "_" + sAuthSubID + "_" + sNameID; // add separator
                            hmSamlAttributes.put("authid", sAuthID);
                        }
                    } // RH, 20130923, n

                    if (isCarryAuthProof()) { // Put the original authentication proof in hmSamlAttributes before serialization in attributes
                                              // so they will be available for gatherer
                        hmSamlAttributes.put("auth_proof", auth_proof); // original response, still base64 encoded
                        //                  _systemLogger.log(Level.FINEST, MODULE, sMethod, "auth_proof=" + auth_proof);
                    }
                    // And serialize them back to where they came from
                    sEncodedAttributes = org.aselect.server.utils.Utils.serializeAttributes(hmSamlAttributes);
                    hmSamlAttributes.put("attributes", sEncodedAttributes);

                    if (!isCarryAuthProof() && isLogAuthProof()) { // Put the original authentication proof in hmSamlAttributes only temporarily to be removed later
                        // if isCarryAuthProof() true they were already there
                        hmSamlAttributes.put("auth_proof", auth_proof); // original response, still base64 encoded
                        //                  _systemLogger.log(Level.FINEST, MODULE, sMethod, "auth_proof=" + auth_proof);
                    }
                    // This is the quickest way to get "name_id" into the Context
                    hmSamlAttributes.put("name_id", sNameID); // also as plain attribute

                    ///////////// Digid4   //////////////////////////////
                    // must be made configurable and parameterized, still looking for some reference to identify the service (maybe issuer) 
                    String[] splittedNameId = sNameID.split(":");
                    if (splittedNameId.length == 2 && splittedNameId[0].toUpperCase().startsWith("S")
                            && splittedNameId[0].length() == 9) { // for now this identifies as digid4
                        hmSamlAttributes.put("uid", splittedNameId[1]);
                        // add special attributes for digid4
                        if ("S00000000".equalsIgnoreCase(splittedNameId[0])) {
                            hmSamlAttributes.put("bsn", splittedNameId[1]);

                        } else if ("S00000001".equalsIgnoreCase(splittedNameId[0])) {
                            hmSamlAttributes.put("sofi", splittedNameId[1]);

                        } else if ("S00000002".equalsIgnoreCase(splittedNameId[0])) {
                            hmSamlAttributes.put("anummer", splittedNameId[1]);

                        } else if ("S00000100".equalsIgnoreCase(splittedNameId[0])) {
                            hmSamlAttributes.put("oeb", splittedNameId[1]);
                        }
                    }
                    /////////////////////////////////////////////////////

                    // 20100422, Bauke: no uid, then use NameID
                    String sUid = (String) hmSamlAttributes.get("uid");
                    if (sUid == null || sUid.equals(""))
                        hmSamlAttributes.put("uid", sNameID);
                    _systemLogger.log(Level.INFO, MODULE, sMethod,
                            "NameID=" + sNameID + " remote_rid=" + sRemoteRid + " local_rid=" + sLocalRid
                                    + " sel_level=" + sSelectedLevel + " organization/authsp=" + sAssertIssuer);

                    // htRemoteAttributes.put("attributes", HandlerTools.serializeAttributes(htAttributes));
                    hmSamlAttributes.put("remote_rid", sRemoteRid);
                    hmSamlAttributes.put("local_rid", sLocalRid);

                    hmSamlAttributes.put("sel_level", sSelectedLevel);
                    hmSamlAttributes.put("authsp_level", sSelectedLevel); // default value, issueTGT will correct this
                    hmSamlAttributes.put("organization", sAssertIssuer);
                    hmSamlAttributes.put("authsp", sAssertIssuer);

                    // RH, 20120201, sn
                    // also save the provided session if present, saml2 specs say there might be more than one session to track
                    if (isIncludeSessionindexes() && sSessionindex != null && sSessionindex.length() > 0) {
                        Vector sessionindexes = new Vector<String>();
                        sessionindexes.add(sSessionindex);
                        hmSamlAttributes.put("remote_sessionlist", sessionindexes);
                    }
                    // RH, 20120201, en

                    // Bauke, 20081204: If we want to send the IdP token as an attribute
                    // to the application, we will need the following code:
                    /*
                     * String sAssertion = XMLHelper.nodeToString(samlAssertion.getDOM());
                     * _systemLogger.log(Level.INFO, MODULE, sMethod, "sAssertion="+sAssertion);
                     * BASE64Encoder b64Enc = new BASE64Encoder();
                     * sAssertion = b64Enc.encode(sAssertion.getBytes("UTF-8"));
                     * htRemoteAttributes.put("saml_remote_token", sAssertion);
                     */
                    // End of IdP token

                    _systemLogger.log(Level.INFO, MODULE, sMethod, "htRemoteAttributes=" + hmSamlAttributes);
                    handleSSOResponse(_htSessionContext, hmSamlAttributes, servletRequest, servletResponse);
                } else {
                    _systemLogger.log(Level.WARNING, MODULE, sMethod,
                            "Response was not successful: " + sStatusCode);
                    // Handle various error conditions here
                    String sErrorCode = Errors.ERROR_ASELECT_AUTHSP_COULD_NOT_AUTHENTICATE_USER; // default
                    String sErrorSubCode = null;
                    if (samlResponse.getStatus().getStatusCode().getStatusCode() != null) { // Get the subcode
                        sErrorSubCode = SamlTools
                                .mapStatus(samlResponse.getStatus().getStatusCode().getStatusCode().getValue());
                        _systemLogger.log(Level.FINER, MODULE, sMethod, "ErrorSubcode: " + sErrorSubCode);
                    }
                    StatusMessage statMsg = samlResponse.getStatus().getStatusMessage();
                    if (statMsg != null) {
                        sErrorCode = statMsg.getMessage();
                        _systemLogger.log(Level.FINER, MODULE, sMethod, "StatusMessage found: " + sErrorCode);
                    } else {
                        if (sErrorSubCode != null && !"".equals(sErrorSubCode)) {
                            sErrorCode = sErrorSubCode;
                        }
                    }
                    _systemLogger.log(Level.INFO, MODULE, sMethod, "ErrorCode=" + sErrorCode);
                    //else if (samlResponse.getStatus().getStatusCode().getStatusCode().getValue().equals(StatusCode.AUTHN_FAILED_URI))
                    //   sErrorCode = Errors.ERROR_ASELECT_AUTHSP_COULD_NOT_AUTHENTICATE_USER;
                    // Expect these codes: Errors.ERROR_ASELECT_SERVER_CANCEL,
                    // Errors.ERROR_ASELECT_AUTHSP_COULD_NOT_AUTHENTICATE_USER;

                    //HashMap htRemoteAttributes = new HashMap();
                    //htRemoteAttributes.put("remote_rid", sRemoteRid);
                    //htRemoteAttributes.put("local_rid", sLocalRid);
                    //htRemoteAttributes.put("result_code", sErrorCode);

                    // Choose your response (3rd is implemented below)
                    // 1. handleSSOResponse(htRemoteAttributes, request, response); // Lets application display error
                    // 2. throw new ASelectException(Errors.ERROR_ASELECT_AUTHSP_ACCESS_DENIED); // Standard server error
                    // 3. Show error page:
                    showErrorPage(sErrorCode, _htSessionContext, pwOut, servletRequest);
                }
            } else { // SLO
                _systemLogger.log(Level.WARNING, "Unexpected SAMLObject type: " + samlResponseObject.getClass());
                throw new ASelectException(Errors.ERROR_ASELECT_INTERNAL_ERROR);
            }
        } catch (ASelectException e) {
            throw e;
        } catch (Exception e) {
            _systemLogger.log(Level.WARNING, MODULE, sMethod, "Internal error", e);
            throw new ASelectException(Errors.ERROR_ASELECT_INTERNAL_ERROR, e);
        } finally {
            if (pwOut != null)
                pwOut.close();

            // 20130821, Bauke: save friendly name after session is gone
            if (_htSessionContext != null) {
                String sStatus = (String) _htSessionContext.get("status");
                String sAppId = (String) _htSessionContext.get("app_id");
                if ("del".equals(sStatus) && Utils.hasValue(sAppId)) {
                    String sUF = ApplicationManager.getHandle().getFriendlyName(sAppId);
                    HandlerTools.setEncryptedCookie(servletResponse, "requestor_friendly_name", sUF,
                            _configManager.getCookieDomain(), -1/*age*/, _systemLogger);
                }
            }
            _oSessionManager.finalSessionProcessing(_htSessionContext, true/*really do it*/);
        }
        return null;
    }

    /* (non-Javadoc)
     * @see org.aselect.server.request.handler.xsaml20.Saml20_BaseHandler#destroy()
     */
    public void destroy() {
        String sMethod = "destroy";
        _systemLogger.log(Level.INFO, MODULE, sMethod, "<--");
    }

    /**
     * Handle sso response.
     * 
     * @param htRemoteAttributes
     *            the ht remote attributes
     * @param servletRequest
     *            the servlet request
     * @param servletResponse
     *            the servlet response
     * @throws ASelectException
     *             the a select exception
     */
    private void handleSSOResponse(HashMap htSessionContext, HashMap htRemoteAttributes,
            HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ASelectException {
        String sMethod = "handleSSOResponse";
        _systemLogger.log(Level.INFO, MODULE, sMethod, "<--");

        try {
            // 20120712, Bauke: Stores TGT in class variable to save on reads:
            HashMap htServiceRequest = createServiceRequest(servletRequest);
            _systemLogger.log(Level.INFO, MODULE, sMethod, "htServiceRequest=" + htServiceRequest);

            String sLocalRid = (String) htRemoteAttributes.get("local_rid");
            if (sLocalRid == null) {
                _systemLogger.log(Level.INFO, MODULE, sMethod, "Missing remote attribute: 'local_rid'");
                throw new ASelectCommunicationException(Errors.ERROR_ASELECT_SERVER_INVALID_REQUEST);
            }

            // for authentication logging
            String sRemoteOrg = (String) htSessionContext.get("remote_organization");
            String sOrg = (String) htRemoteAttributes.get("organization");
            if (sRemoteOrg != null && sOrg != null && !sRemoteOrg.equals(sOrg))
                sRemoteOrg = sOrg + "@" + sRemoteOrg;

            String sResultCode = (String) htRemoteAttributes.get("result_code");
            String sUID = (String) htRemoteAttributes.get("uid");
            String sFederationId = (String) htSessionContext.get("federation_url");
            if (sResultCode != null) {
                if (sResultCode.equals(Errors.ERROR_ASELECT_SERVER_CANCEL)
                        || sResultCode.equals(Errors.ERROR_ASELECT_AUTHSP_COULD_NOT_AUTHENTICATE_USER)) {
                    _systemLogger.log(Level.WARNING, MODULE, sMethod, "Cancel");
                    _authenticationLogger.log(
                            new Object[] { "Saml", sUID, (String) htServiceRequest.get("client_ip"), sRemoteOrg,
                                    htSessionContext.get("app_id"), "denied", sFederationId, sResultCode });
                    // Issue 'CANCEL' TGT
                    TGTIssuer tgtIssuer = new TGTIssuer(_sMyServerId);
                    tgtIssuer.issueErrorTGTandRedirect(sLocalRid, htSessionContext, sResultCode, servletResponse);
                } else { // remote server returned error
                    _systemLogger.log(Level.WARNING, MODULE, sMethod, "Error");
                    _authenticationLogger.log(
                            new Object[] { "Saml", sUID, (String) htServiceRequest.get("client_ip"), sRemoteOrg,
                                    htSessionContext.get("app_id"), "denied", sFederationId, sResultCode });
                    throw new ASelectException(Errors.ERROR_ASELECT_AUTHSP_ACCESS_DENIED);
                }
            } else { // No result_code set, log successful authentication
                _authenticationLogger.log(new Object[] { "Saml", sUID, (String) htServiceRequest.get("client_ip"),
                        sRemoteOrg, htSessionContext.get("app_id"), "granted", sFederationId });
                if (isLogAuthProof()) { // Log auth_proof here if enabled
                    ASelectAuthProofLogger.getHandle().log(sUID, (String) htRemoteAttributes.get("client_ip"),
                            (String) htSessionContext.get("app_id"), (String) null,
                            (String) htRemoteAttributes.get("auth_proof"));
                    //               _systemLogger.log(Level.FINEST, MODULE, sMethod, "auth_proof logged after successful authentication=" + 
                    //                     htRemoteAttributes.get("auth_proof"));
                    if (!isCarryAuthProof()) { // We do not want to carry the auth_proof any  further
                        Object removed_auth_proof = htRemoteAttributes.remove("auth_proof");
                        if (removed_auth_proof != null) {
                            _systemLogger.log(Level.FINEST, MODULE, sMethod,
                                    "Successfully removed auth_proof from htRemoteAttributes");
                        } else {
                            _systemLogger.log(Level.FINEST, MODULE, sMethod,
                                    "Could not remove auth_proof from htRemoteAttributes");
                        }
                    }
                }

                HandlerTools.setRequestorFriendlyCookie(servletResponse, htSessionContext, _systemLogger); // 20130825

                // Issue a cross TGT since we do not know the AuthSP
                // and we might have received remote attributes.
                TGTIssuer oTGTIssuer = new TGTIssuer(_sMyServerId);
                String sOldTGT = (String) htServiceRequest.get("aselect_credentials_tgt");
                // Will also redirect the user, this call can update/delete the session in the local cache
                // Final session updates will be done by finalSessionProcessing()
                oTGTIssuer.issueTGTandRedirect(sLocalRid, htSessionContext, null, htRemoteAttributes,
                        servletRequest, servletResponse, sOldTGT, true);
                // 20090909: oTGTIssuer.issueCrossTGT(sLocalRid, null, htRemoteAttributes, servletResponse, sOldTGT);
            }
        } catch (ASelectException ae) {
            throw ae;
        } catch (Exception e) {
            _systemLogger.log(Level.WARNING, MODULE, sMethod, "Internal error", e);
            throw new ASelectException(Errors.ERROR_ASELECT_INTERNAL_ERROR, e);
        }
    }

    /**
     * This function converts a <code>servletRequest</code> to a <code>HashMap</code> by extracting the parameters from
     * the <code>servletRequest</code> and inserting them into a <code>HashMap</code>. <br>
     * <br>
     * 
     * @param servletRequest
     *            Contains request parameters
     * @return HashMap containing request parameters.
     */
    @SuppressWarnings("unchecked")
    private HashMap createServiceRequest(HttpServletRequest servletRequest) {
        // Extract parameters into htServiceRequest
        HashMap htServiceRequest = null;
        if (servletRequest.getMethod().equalsIgnoreCase("GET")) {
            htServiceRequest = Utils.convertCGIMessage(servletRequest.getQueryString(), false);
        } else {
            htServiceRequest = new HashMap();
            String sParameter, sValue;
            Enumeration eParameters = servletRequest.getParameterNames();
            while (eParameters.hasMoreElements()) {
                sParameter = (String) eParameters.nextElement();
                sValue = servletRequest.getParameter(sParameter);
                if (sValue != null) {
                    htServiceRequest.put(sParameter, sValue);
                }
            }
        }

        htServiceRequest.put("my_url", servletRequest.getRequestURL().toString());
        // Bauke 20081217: client_ip and user_agent should already be set
        // htServiceRequest.put("client_ip", servletRequest.getRemoteAddr());
        // String sAgent = servletRequest.getHeader("User-Agent");
        // if (sAgent != null) htServiceRequest.put("user_agent", sAgent);
        HashMap htCredentials = getASelectCredentials(servletRequest);
        if (htCredentials != null) {
            htServiceRequest.put("aselect_credentials_tgt", htCredentials.get("aselect_credentials_tgt"));
            htServiceRequest.put("aselect_credentials_uid", htCredentials.get("aselect_credentials_uid"));
            htServiceRequest.put("aselect_credentials_server_id", _sMyServerId);
        }

        return htServiceRequest;
    }

    /**
     * Retrieve A-Select credentials. Reads TGT in _htTGTContext.<br>
     * <br>
     * <b>Description:</b> <br>
     * Reads the A-Select credentials from a Cookie and put them into a <code>HashMap</code>. <br>
     * <br>
     * <b>Concurrency issues:</b> <br>
     * - <br>
     * <br>
     * <b>Preconditions:</b> <br>
     * <code>servletRequest != null</code> <br>
     * <br>
     * <b>Postconditions:</b> <br>
     * - <br>
     * 
     * @param servletRequest
     *            The Request which should contain the Cookie.
     * @return The A-Select credentials in a <code>HashMap</code>.
     */
    @SuppressWarnings("unchecked")
    protected HashMap getASelectCredentials(HttpServletRequest servletRequest) {
        // This method overrides the default from ProtoRequestHandler.java
        String sMethod = "getASelectCredentials";
        HashMap htCredentials = new HashMap();

        // Check for credentials that might be present
        // Bauke 20080618, we only store the tgt value from now on
        String sTgt = HandlerTools.getCookieValue(servletRequest, "aselect_credentials", _systemLogger);
        if (sTgt == null)
            return null;

        // 20120712, Bauke: Store TGT in class variable to save on reads
        _htTGTContext = _tgtManager.getTGT(sTgt);
        if (_htTGTContext == null)
            return null;

        String sUserId = (String) _htTGTContext.get("uid");
        if (sUserId != null)
            htCredentials.put("aselect_credentials_uid", sUserId);
        htCredentials.put("aselect_credentials_tgt", sTgt);
        htCredentials.put("aselect_credentials_server_id", _sMyServerId); // Bauke 200806128 was: sServerId);
        return htCredentials;
    }

    /**
     * Checks if is signing required.
     * 
     * @return true, if is signing required
     */
    public synchronized boolean isSigningRequired() {
        return signingRequired;
    }

    /**
     * Sets the signing required.
     * 
     * @param signingRequired
     *            the new signing required
     */
    public synchronized void setSigningRequired(boolean signingRequired) {
        this.signingRequired = signingRequired;
    }

    /**
     * Checks if is locality address required.
     * 
     * @return true, if is locality address required
     */
    public synchronized boolean isLocalityAddressRequired() {
        return localityAddressRequired;
    }

    /**
     * Sets the locality address required.
     * 
     * @param localityAddressRequired
     *            the new locality address required
     */
    public synchronized void setLocalityAddressRequired(boolean localityAddressRequired) {
        this.localityAddressRequired = localityAddressRequired;
    }

    /**
     * @return the includeSessionindexes
     */
    public synchronized boolean isIncludeSessionindexes() {
        return includeSessionindexes;
    }

    /**
     * @param includeSessionindexes the includeSessionindexes to set
     */
    public synchronized void setIncludeSessionindexes(boolean includeSessionindexes) {
        this.includeSessionindexes = includeSessionindexes;
    }

    /**
     * @return the useBackchannelClientcertificate
     */
    public boolean isUseBackchannelClientcertificate() {
        return useBackchannelClientcertificate;
    }

    /**
     * @param useBackchannelClientcertificate the useBackchannelClientcertificate to set
     */
    public void setUseBackchannelClientcertificate(boolean useBackchannelClientcertificate) {
        this.useBackchannelClientcertificate = useBackchannelClientcertificate;
    }

    public synchronized boolean isVerifyArtifactResponseSignature() {
        return verifyArtifactResponseSignature;
    }

    public synchronized void setVerifyArtifactResponseSignature(boolean verifyArtifactResponseSignature) {
        this.verifyArtifactResponseSignature = verifyArtifactResponseSignature;
    }

    public synchronized boolean isUseNameIDAsAuthID() {
        return useNameIDAsAuthID;
    }

    public synchronized void setUseNameIDAsAuthID(boolean useNameIDAsAuthID) {
        this.useNameIDAsAuthID = useNameIDAsAuthID;
    }

    public boolean isCarryAuthProof() {
        return carryAuthProof;
    }

    public void setCarryAuthProof(boolean carryAuthProof) {
        this.carryAuthProof = carryAuthProof;
    }

    public boolean isLogAuthProof() {
        return logAuthProof;
    }

    public void setLogAuthProof(boolean logAuthProof) {
        this.logAuthProof = logAuthProof;
    }
}