001/*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: SPACSUtils.java,v 1.48 2009/11/20 21:41:16 exu Exp $
026 *
027 * Portions Copyrighted 2010-2017 ForgeRock AS.
028 */
029package com.sun.identity.saml2.profile;
030
031import static org.forgerock.http.util.Uris.urlEncodeQueryParameterNameOrValue;
032import static org.forgerock.openam.utils.Time.*;
033
034import com.sun.identity.common.SystemConfigurationUtil;
035import com.sun.identity.liberty.ws.soapbinding.Message;
036import com.sun.identity.liberty.ws.soapbinding.SOAPBindingException;
037import com.sun.identity.liberty.ws.soapbinding.SOAPFaultException;
038import com.sun.identity.plugin.datastore.DataStoreProviderException;
039import com.sun.identity.plugin.monitoring.FedMonAgent;
040import com.sun.identity.plugin.monitoring.FedMonSAML2Svc;
041import com.sun.identity.plugin.monitoring.MonitorManager;
042import com.sun.identity.plugin.session.SessionException;
043import com.sun.identity.plugin.session.SessionManager;
044import com.sun.identity.plugin.session.SessionProvider;
045import com.sun.identity.saml.common.SAMLConstants;
046import com.sun.identity.saml.common.SAMLUtils;
047import com.sun.identity.saml.xmlsig.KeyProvider;
048import com.sun.identity.saml2.assertion.Advice;
049import com.sun.identity.saml2.assertion.Assertion;
050import com.sun.identity.saml2.assertion.AssertionFactory;
051import com.sun.identity.saml2.assertion.Attribute;
052import com.sun.identity.saml2.assertion.AttributeStatement;
053import com.sun.identity.saml2.assertion.EncryptedAttribute;
054import com.sun.identity.saml2.assertion.EncryptedID;
055import com.sun.identity.saml2.assertion.Issuer;
056import com.sun.identity.saml2.assertion.NameID;
057import com.sun.identity.saml2.assertion.Subject;
058import com.sun.identity.saml2.common.AccountUtils;
059import com.sun.identity.saml2.common.NameIDInfo;
060import com.sun.identity.saml2.common.NameIDInfoKey;
061import com.sun.identity.saml2.common.SAML2Constants;
062import com.sun.identity.saml2.common.SAML2Exception;
063import com.sun.identity.saml2.common.SAML2FailoverUtils;
064import com.sun.identity.saml2.common.SAML2SDKUtils;
065import com.sun.identity.saml2.common.SAML2Utils;
066import com.sun.identity.saml2.common.SOAPCommunicator;
067import com.sun.identity.saml2.ecp.ECPFactory;
068import com.sun.identity.saml2.ecp.ECPRelayState;
069import com.sun.identity.saml2.jaxb.entityconfig.IDPSSOConfigElement;
070import com.sun.identity.saml2.jaxb.entityconfig.SPSSOConfigElement;
071import com.sun.identity.saml2.jaxb.metadata.AffiliationDescriptorType;
072import com.sun.identity.saml2.jaxb.metadata.ArtifactResolutionServiceElement;
073import com.sun.identity.saml2.jaxb.metadata.IDPSSODescriptorElement;
074import com.sun.identity.saml2.jaxb.metadata.SPSSODescriptorElement;
075import com.sun.identity.saml2.key.KeyUtil;
076import com.sun.identity.saml2.logging.LogUtil;
077import com.sun.identity.saml2.meta.SAML2MetaException;
078import com.sun.identity.saml2.meta.SAML2MetaManager;
079import com.sun.identity.saml2.meta.SAML2MetaUtils;
080import com.sun.identity.saml2.plugins.SAML2PluginsUtils;
081import com.sun.identity.saml2.plugins.SAML2ServiceProviderAdapter;
082import com.sun.identity.saml2.plugins.SPAccountMapper;
083import com.sun.identity.saml2.plugins.SPAttributeMapper;
084import com.sun.identity.saml2.protocol.Artifact;
085import com.sun.identity.saml2.protocol.ArtifactResolve;
086import com.sun.identity.saml2.protocol.ArtifactResponse;
087import com.sun.identity.saml2.protocol.AuthnRequest;
088import com.sun.identity.saml2.protocol.ProtocolFactory;
089import com.sun.identity.saml2.protocol.Response;
090import com.sun.identity.saml2.protocol.Status;
091import com.sun.identity.shared.encode.Base64;
092import com.sun.identity.shared.xml.XMLUtils;
093import java.io.ByteArrayInputStream;
094import java.io.IOException;
095import java.io.PrintWriter;
096import java.security.PrivateKey;
097import java.security.cert.X509Certificate;
098import java.text.MessageFormat;
099import java.util.ArrayList;
100import java.util.HashMap;
101import java.util.HashSet;
102import java.util.Iterator;
103import java.util.List;
104import java.util.Map;
105import java.util.Set;
106import java.util.logging.Level;
107import javax.servlet.ServletException;
108import javax.servlet.http.HttpServletRequest;
109import javax.servlet.http.HttpServletResponse;
110import javax.xml.soap.SOAPConnection;
111import javax.xml.soap.SOAPException;
112import javax.xml.soap.SOAPMessage;
113import org.forgerock.openam.federation.saml2.SAML2TokenRepositoryException;
114import org.forgerock.openam.saml2.audit.SAML2EventLogger;
115import org.forgerock.openam.utils.ClientUtils;
116import org.forgerock.openam.utils.CollectionUtils;
117import org.forgerock.openam.utils.StringUtils;
118import org.w3c.dom.Document;
119import org.w3c.dom.Element;
120
121/**
122 * This class is used by a service provider (SP) to process the response from  
123 * an identity provider for the SP's Assertion Consumer Service.
124 *
125 * @supported.api
126 */
127public class SPACSUtils {
128
129    private static FedMonAgent agent = MonitorManager.getAgent();
130    private static FedMonSAML2Svc saml2Svc = MonitorManager.getSAML2Svc();
131
132    private SPACSUtils() {}
133
134    /**
135     * Retrieves <code>SAML</code> <code>Response</code> from http request.
136     * It handles three cases:
137     * <pre>
138     * 1. using http method get using request parameter "resID".
139     *    This is the case after local login is done.
140     * 2. using http method get using request parameter "SAMLart".
141     *    This is the case for artifact profile.
142     * 3. using http method post. This is the case for post profile.
143     * </pre>
144     * 
145     * @param request http servlet request
146     * @param response http servlet response
147     * @param orgName realm or organization name the service provider resides in
148     * @param hostEntityId Entity ID of the hosted service provider
149     * @param metaManager <code>SAML2MetaManager</code> instance.
150     * @return <code>ResponseInfo</code> instance.
151     * @throws SAML2Exception,IOException if it fails in the process.
152     */
153    public static ResponseInfo getResponse(
154                                HttpServletRequest request,
155                                HttpServletResponse response,
156                                String orgName,
157                                String hostEntityId,
158                                SAML2MetaManager metaManager)
159                throws SAML2Exception,IOException
160    {
161        ResponseInfo respInfo = null;
162
163        String method = request.getMethod();
164        if (method.equals("GET")) {
165            if (!SAML2Utils.isSPProfileBindingSupported(
166                orgName, hostEntityId, SAML2Constants.ACS_SERVICE,
167                SAML2Constants.HTTP_ARTIFACT))
168            {
169                SAMLUtils.sendError(request, response, 
170                    response.SC_BAD_REQUEST,
171                    "unsupportedBinding", 
172                    SAML2Utils.bundle.getString("unsupportedBinding"));
173                throw new SAML2Exception(
174                    SAML2Utils.bundle.getString("unsupportedBinding"));
175            }
176            respInfo = getResponseFromGet(request, response, orgName,
177                                hostEntityId, metaManager);
178        } else if (method.equals("POST")) {
179            String pathInfo = request.getPathInfo();
180            if ((pathInfo != null) && (pathInfo.startsWith("/ECP"))) {
181                if (!SAML2Utils.isSPProfileBindingSupported(
182                    orgName, hostEntityId, SAML2Constants.ACS_SERVICE,
183                    SAML2Constants.PAOS))
184                {
185                SAMLUtils.sendError(request, response, 
186                    response.SC_BAD_REQUEST,
187                    "unsupportedBinding", 
188                    SAML2Utils.bundle.getString("unsupportedBinding"));
189                throw new SAML2Exception(
190                    SAML2Utils.bundle.getString("unsupportedBinding"));
191            }
192                respInfo = getResponseFromPostECP(request, response, orgName,
193                    hostEntityId, metaManager);
194            } else {
195                if (!SAML2Utils.isSPProfileBindingSupported(
196                    orgName, hostEntityId, SAML2Constants.ACS_SERVICE,
197                    SAML2Constants.HTTP_POST))
198                {
199                    SAMLUtils.sendError(request, response, 
200                        response.SC_BAD_REQUEST,
201                        "unsupportedBinding", 
202                        SAML2Utils.bundle.getString("unsupportedBinding"));
203                    throw new SAML2Exception(
204                        SAML2Utils.bundle.getString("unsupportedBinding"));
205                }
206                respInfo = getResponseFromPost(request, response, orgName,
207                    hostEntityId, metaManager);
208            }
209        } else {
210            // not supported
211            SAMLUtils.sendError(request, response, 
212                response.SC_METHOD_NOT_ALLOWED,
213                "notSupportedHTTPMethod", 
214                SAML2Utils.bundle.getString("notSupportedHTTPMethod"));
215            throw new SAML2Exception(
216                        SAML2Utils.bundle.getString("notSupportedHTTPMethod"));
217        }
218        if (SAML2Utils.debug.messageEnabled()) {
219            SAML2Utils.debug.message("SPACSUtils.getResponse: got response="
220                    + respInfo.getResponse().toXMLString(true, true));
221        }
222        return respInfo;
223    }
224
225    /**
226     * Retrieves <code>SAML Response</code> from http Get. 
227     * It first uses parameter resID to retrieve <code>Response</code>. This is
228     * the case after local login;
229     * If resID is not defined, it then uses <code>SAMLart</code> http 
230     * parameter to retrieve <code>Response</code>.
231     */
232    private static ResponseInfo getResponseFromGet(
233                                HttpServletRequest request,
234                                HttpServletResponse response,
235                                String orgName,
236                                String hostEntityId,
237                                SAML2MetaManager metaManager)
238                throws SAML2Exception,IOException
239    {
240        ResponseInfo respInfo = null;
241        String resID = request.getParameter("resID");
242        if (resID != null && resID.length() != 0) {
243            if (SAML2Utils.debug.messageEnabled()) {
244                SAML2Utils.debug.message("SPACSUtils.getResponseFromGet: resID="
245                        + resID);
246            }
247            synchronized (SPCache.responseHash) {
248                respInfo = (ResponseInfo) SPCache.responseHash.remove(resID);
249            }
250            if (respInfo == null) {
251                if (SAML2Utils.debug.messageEnabled()) {
252                    SAML2Utils.debug.message("SPACSUtils.getResponseFromGet: "
253                        + "couldn't find Response from resID.");
254                }
255                String[] data = {resID};
256                LogUtil.error(Level.INFO,
257                                LogUtil.RESPONSE_NOT_FOUND_FROM_CACHE,
258                                data,
259                                null);
260                SAMLUtils.sendError(request, response, 
261                    response.SC_INTERNAL_SERVER_ERROR, "SSOFailed",
262                    SAML2Utils.bundle.getString("SSOFailed"));
263                throw new SAML2Exception(
264                        SAML2Utils.bundle.getString("SSOFailed"));
265            }
266            return respInfo;
267        }
268
269        String samlArt = request.getParameter(SAML2Constants.SAML_ART);
270        if (samlArt == null || samlArt.trim().length() == 0) {
271            SAML2Utils.debug.error("SPACSUtils.getResponseFromGet: Artifact "
272                + "string is empty.");
273            LogUtil.error(Level.INFO,
274                        LogUtil.MISSING_ARTIFACT,
275                        null,
276                        null);
277            SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST,
278                    "missingArtifact",
279                    SAML2Utils.bundle.getString("missingArtifact"));
280            throw new SAML2Exception(
281                        SAML2Utils.bundle.getString("missingArtifact"));
282        }
283
284        return new ResponseInfo(getResponseFromArtifact(samlArt, hostEntityId,
285            request, response, orgName, metaManager), 
286            SAML2Constants.HTTP_ARTIFACT, null);
287    }
288
289    // Retrieves response using artifact profile.
290    private static Response getResponseFromArtifact(String samlArt,
291        String hostEntityId, HttpServletRequest request,
292        HttpServletResponse response, String orgName,
293        SAML2MetaManager sm) throws SAML2Exception,IOException
294    {
295
296        // Try to get source ID and endpointIndex, and then
297        // decide which IDP and which artifact resolution service
298        if (SAML2Utils.debug.messageEnabled()) {
299            SAML2Utils.debug.message("SPACSUtils.getResponseFromArtifact: " +
300                "samlArt = " + samlArt);
301        }
302
303        Artifact art = null;
304        try {
305            art = ProtocolFactory.getInstance().createArtifact(samlArt.trim());
306            String[] data = {samlArt.trim()};
307            LogUtil.access(Level.INFO,
308                        LogUtil.RECEIVED_ARTIFACT,
309                        data,
310                        null);
311        } catch (SAML2Exception se) {
312            SAML2Utils.debug.error("SPACSUtils.getResponseFromArtifact: "
313                 + "Unable to decode and parse artifact string:" + samlArt);
314            SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST,
315                "errorObtainArtifact",
316                SAML2Utils.bundle.getString("errorObtainArtifact"));
317            throw se;
318        }
319
320        String idpEntityID = getIDPEntityID(art, request, response, orgName, sm);
321        IDPSSODescriptorElement idp = null;
322        try {
323            idp = sm.getIDPSSODescriptor(orgName, idpEntityID);
324        } catch (SAML2MetaException se) {
325            String[] data = {orgName, idpEntityID};
326            LogUtil.error(Level.INFO,
327                        LogUtil.IDP_META_NOT_FOUND,
328                        data,
329                        null);
330            SAMLUtils.sendError(request, response, 
331                response.SC_INTERNAL_SERVER_ERROR,
332                "failedToGetIDPSSODescriptor", se.getMessage());
333            throw se;
334        }
335
336        String location = getIDPArtifactResolutionServiceUrl(
337            art.getEndpointIndex(), idpEntityID, idp, request, response);
338
339        // create ArtifactResolve message
340        ArtifactResolve resolve = null;
341        SOAPMessage resMsg = null;
342        try {
343            resolve = ProtocolFactory.getInstance().createArtifactResolve();
344            resolve.setID(SAML2Utils.generateID());
345            resolve.setVersion(SAML2Constants.VERSION_2_0);
346            resolve.setIssueInstant(newDate());
347            resolve.setArtifact(art);
348            resolve.setDestination(XMLUtils.escapeSpecialCharacters(location));
349            Issuer issuer = AssertionFactory.getInstance().createIssuer();
350            issuer.setValue(hostEntityId);
351            resolve.setIssuer(issuer);
352            String needArtiResolveSigned =
353                SAML2Utils.getAttributeValueFromSSOConfig(
354                                orgName,
355                                idpEntityID,
356                                SAML2Constants.IDP_ROLE,
357                                SAML2Constants.WANT_ARTIFACT_RESOLVE_SIGNED);
358                                                        
359            if (needArtiResolveSigned != null &&
360                needArtiResolveSigned.equals("true")) {
361                // or save it somewhere?
362                String signAlias = getAttributeValueFromSPSSOConfig(
363                                orgName,
364                                hostEntityId,
365                                sm,
366                                SAML2Constants.SIGNING_CERT_ALIAS);
367                if (signAlias == null) {
368                    throw new SAML2Exception(
369                        SAML2Utils.bundle.getString("missingSigningCertAlias"));
370                }
371                KeyProvider kp = KeyUtil.getKeyProviderInstance();
372                if (kp == null) {
373                    throw new SAML2Exception(
374                        SAML2Utils.bundle.getString("nullKeyProvider"));
375                }
376                resolve.sign(kp.getPrivateKey(signAlias),
377                                kp.getX509Certificate(signAlias));
378            }
379
380            String resolveString = resolve.toXMLString(true, true);
381            if (SAML2Utils.debug.messageEnabled()) {
382                SAML2Utils.debug.message("SPACSUtils.getResponseFromArtifact: "
383                    + "ArtifactResolve=" + resolveString);
384            }
385
386            SOAPConnection con = SOAPCommunicator.getInstance().openSOAPConnection();
387            SOAPMessage msg = SOAPCommunicator.getInstance().createSOAPMessage(resolveString, true);
388
389            IDPSSOConfigElement config = null;
390            config = sm.getIDPSSOConfig(orgName, idpEntityID);
391            location = SAML2Utils.fillInBasicAuthInfo(
392                config, location);
393            resMsg = con.call(msg, location);
394        } catch (SAML2Exception s2e) {
395            SAML2Utils.debug.error("SPACSUtils.getResponseFromArtifact: "
396                + "couldn't create ArtifactResolve:", s2e);
397            String[] data = {hostEntityId, art.getArtifactValue()};
398            LogUtil.error(Level.INFO,
399                        LogUtil.CANNOT_CREATE_ARTIFACT_RESOLVE,
400                        data,
401                        null);
402            SAMLUtils.sendError(request, response, 
403                response.SC_INTERNAL_SERVER_ERROR,
404                "errorCreateArtifactResolve",
405                SAML2Utils.bundle.getString("errorCreateArtifactResolve"));
406            throw s2e;
407        } catch (SOAPException se) {
408            SAML2Utils.debug.error("SPACSUtils.getResponseFromGet: "
409                + "couldn't get ArtifactResponse. SOAP error:",se);
410            String[] data = {hostEntityId, location};
411            LogUtil.error(Level.INFO,
412                        LogUtil.CANNOT_GET_SOAP_RESPONSE,
413                        data,
414                        null);
415            SAMLUtils.sendError(request, response, 
416                response.SC_INTERNAL_SERVER_ERROR,
417                "errorInSOAPCommunication",
418                SAML2Utils.bundle.getString("errorInSOAPCommunication"));
419            throw new SAML2Exception(se.getMessage());
420        }
421
422        Response result = getResponseFromSOAP(resMsg, resolve, request, 
423            response, idpEntityID, idp, orgName, hostEntityId, sm);
424        String[] data = {hostEntityId, idpEntityID,
425                        art.getArtifactValue(), ""};
426        if (LogUtil.isAccessLoggable(Level.FINE)) {
427            data[3] = result.toXMLString();
428        }
429        LogUtil.access(Level.INFO,
430                        LogUtil.GOT_RESPONSE_FROM_ARTIFACT,
431                        data,
432                        null);
433        return result;
434    }
435
436    // Finds the IDP who sends the artifact;
437    private static String getIDPEntityID(
438                Artifact art,
439                HttpServletRequest request,
440                HttpServletResponse response,
441                String orgName,
442                SAML2MetaManager metaManager)
443                throws SAML2Exception,IOException
444    {
445        String sourceID = art.getSourceID();
446        // find the idp
447        String idpEntityID = null;
448        try {
449            Iterator iter =
450                metaManager.getAllRemoteIdentityProviderEntities(orgName).
451                        iterator();
452            String tmpSourceID = null;
453            while (iter.hasNext()) {
454                idpEntityID = (String) iter.next();
455                tmpSourceID = SAML2Utils.generateSourceID(idpEntityID);
456                if (sourceID.equals(tmpSourceID)) {
457                    break;
458                }
459                idpEntityID = null;
460            }
461            if (idpEntityID == null) {
462                SAML2Utils.debug.error("SPACSUtils.getResponseFromGet: Unable "
463                    + "to find the IDP based on the SourceID in the artifact");
464                String[] data = {art.getArtifactValue(), orgName};
465                LogUtil.error(Level.INFO,
466                        LogUtil.IDP_NOT_FOUND,
467                        data,
468                        null);
469                throw new SAML2Exception(
470                    SAML2Utils.bundle.getString("cannotFindIDP"));
471            }
472        } catch (SAML2Exception se) {
473            String[] data = {art.getArtifactValue(), orgName};
474            LogUtil.error(Level.INFO,
475                        LogUtil.IDP_NOT_FOUND,
476                        data,
477                        null);
478            SAMLUtils.sendError(request, response, 
479                response.SC_INTERNAL_SERVER_ERROR,
480                "cannotFindIDP", se.getMessage());
481            throw se;
482        }
483        return idpEntityID;
484    }
485
486    // Retrieves the ArtifactResolutionServiceURL for an IDP.
487    private static String getIDPArtifactResolutionServiceUrl(
488                int endpointIndex,
489                String idpEntityID,
490                IDPSSODescriptorElement idp,
491                HttpServletRequest request,
492                HttpServletResponse response)
493                throws SAML2Exception,IOException
494    {
495        // find the artifact resolution service url
496        List arsList=idp.getArtifactResolutionService();
497        ArtifactResolutionServiceElement ars = null;
498        String location = null;
499        String defaultLocation = null;
500        String firstLocation = null;
501        int index;
502        boolean isDefault = false;
503        for (int i=0; i<arsList.size(); i++) {
504            ars = (ArtifactResolutionServiceElement)arsList.get(i);
505            location = ars.getLocation();
506            //String binding = ars.getBinding();
507            index = ars.getIndex();
508            isDefault = ars.isIsDefault();
509            if (index == endpointIndex) {
510                break;
511            }
512            if (isDefault) {
513                defaultLocation = location;
514            }
515            if (i==0) {
516                firstLocation = location;
517            }
518            location = null;
519        }
520        if (location == null || location.length() == 0) {
521            location = defaultLocation;
522            if (location == null || location.length() == 0) {
523                location = firstLocation;
524                if (location == null || location.length() == 0) {
525                    SAML2Utils.debug.error("SPACSUtils: Unable to get the "
526                        + "location of artifact resolution service for "
527                        + idpEntityID);
528                    String[] data = {idpEntityID};
529                    LogUtil.error(Level.INFO,
530                                LogUtil.ARTIFACT_RESOLUTION_URL_NOT_FOUND,
531                                data,
532                                null);
533                    SAMLUtils.sendError(request, response, 
534                        response.SC_INTERNAL_SERVER_ERROR,
535                        "cannotFindArtifactResolutionUrl",
536                        SAML2Utils.bundle.getString(
537                            "cannotFindArtifactResolutionUrl"));
538                    throw new SAML2Exception(
539                        SAML2Utils.bundle.getString(
540                            "cannotFindArtifactResolutionUrl"));
541                }
542            }
543        }
544        if (SAML2Utils.debug.messageEnabled()) {
545            SAML2Utils.debug.message("SPACSUtils: IDP artifact resolution "
546                    + "service url =" + location);
547        }
548        return location;
549    }
550
551    /**
552     * Obtains <code>SAML Response</code> from <code>SOAPBody</code>.
553     * Used by Artifact profile.
554     */
555    private static Response getResponseFromSOAP(SOAPMessage resMsg,
556                                                ArtifactResolve resolve,
557                                                HttpServletRequest request,
558                                                HttpServletResponse response,
559                                                String idpEntityID,
560                                                IDPSSODescriptorElement idp,
561                                                String orgName,
562                                                String hostEntityId,
563                                                SAML2MetaManager sm)
564                throws SAML2Exception,IOException
565    {
566        String method = "SPACSUtils.getResponseFromSOAP:";
567        Element resElem = null;
568        try {
569            resElem = SOAPCommunicator.getInstance().getSamlpElement(resMsg, "ArtifactResponse");
570        } catch (SAML2Exception se) {
571            String[] data = {idpEntityID};
572            LogUtil.error(Level.INFO,
573                        LogUtil.SOAP_ERROR,
574                        data,
575                        null);
576            SAMLUtils.sendError(request, response, 
577                response.SC_INTERNAL_SERVER_ERROR,
578                "soapError", se.getMessage());
579            throw se; 
580        }
581        ArtifactResponse artiResp = null;
582        try {
583            artiResp = ProtocolFactory.getInstance().
584                createArtifactResponse(resElem);
585        } catch (SAML2Exception se) {
586            if (SAML2Utils.debug.messageEnabled()) {
587                SAML2Utils.debug.message(method + "Couldn't create "
588                        + "ArtifactResponse:", se);
589            }
590            String[] data = {idpEntityID};
591            LogUtil.error(Level.INFO,
592                        LogUtil.CANNOT_INSTANTIATE_ARTIFACT_RESPONSE,
593                        data,
594                        null);
595            SAMLUtils.sendError(request, response, 
596                response.SC_INTERNAL_SERVER_ERROR,
597                "failedToCreateArtifactResponse", se.getMessage());
598            throw se;
599        }
600
601        if (artiResp == null) {
602            String[] data = {idpEntityID};
603            LogUtil.error(Level.INFO,
604                        LogUtil.MISSING_ARTIFACT_RESPONSE,
605                        data,
606                        null);
607            SAMLUtils.sendError(request, response, 
608                response.SC_INTERNAL_SERVER_ERROR,
609                "missingArtifactResponse",
610                SAML2Utils.bundle.getString("missingArtifactResponse"));
611            throw new SAML2Exception(
612                SAML2Utils.bundle.getString("missingArtifactResponse"));
613        } else {
614            if (SAML2Utils.debug.messageEnabled()) {
615                SAML2Utils.debug.message(method + "Received ArtifactResponse:"
616                        + artiResp.toXMLString(true, true));
617            }
618        }
619
620        // verify ArtifactResponse
621        String wantArtiRespSigned = getAttributeValueFromSPSSOConfig(
622                                orgName,
623                                hostEntityId,
624                                sm,
625                                SAML2Constants.WANT_ARTIFACT_RESPONSE_SIGNED);
626        if (wantArtiRespSigned != null && wantArtiRespSigned.equals("true")) {
627            Set<X509Certificate> verificationCerts = KeyUtil.getVerificationCerts(idp, idpEntityID,
628                    SAML2Constants.IDP_ROLE);
629            if (!artiResp.isSigned() || !artiResp.isSignatureValid(verificationCerts)) {
630                if (SAML2Utils.debug.messageEnabled()) {
631                   SAML2Utils.debug.message(method 
632                        + "ArtifactResponse's signature is invalid.");
633                }
634                String[] data = {idpEntityID};
635                LogUtil.error(Level.INFO,
636                        LogUtil.ARTIFACT_RESPONSE_INVALID_SIGNATURE,
637                        data,
638                        null);
639                SAMLUtils.sendError(request, response, 
640                    response.SC_INTERNAL_SERVER_ERROR, "invalidSignature",
641                    SAML2Utils.bundle.getString("invalidSignature"));
642                throw new SAML2Exception(
643                    SAML2Utils.bundle.getString("invalidSignature"));
644            }
645        }
646
647        String inResponseTo = artiResp.getInResponseTo();
648        if (inResponseTo == null || !inResponseTo.equals(resolve.getID())) {
649            if (SAML2Utils.debug.messageEnabled()) {
650                SAML2Utils.debug.message(method 
651                    + "ArtifactResponse's InResponseTo is invalid.");
652            }
653            String[] data = {idpEntityID};
654            LogUtil.error(Level.INFO,
655                        LogUtil.ARTIFACT_RESPONSE_INVALID_INRESPONSETO,
656                        data,
657                        null);
658            SAMLUtils.sendError(request, response, 
659                response.SC_INTERNAL_SERVER_ERROR, "invalidInResponseTo",
660                SAML2Utils.bundle.getString("invalidInResponseTo"));
661            throw new SAML2Exception(
662                SAML2Utils.bundle.getString("invalidInResponseTo"));
663        }
664
665        Issuer idpIssuer = artiResp.getIssuer();
666        if (idpIssuer == null || !idpIssuer.getValue().equals(idpEntityID)) {
667            if (SAML2Utils.debug.messageEnabled()) {
668                SAML2Utils.debug.message(method 
669                    + "ArtifactResponse's Issuer is invalid.");
670            }
671            String[] data = {idpEntityID};
672            LogUtil.error(Level.INFO,
673                        LogUtil.ARTIFACT_RESPONSE_INVALID_ISSUER,
674                        data,
675                        null);
676            SAMLUtils.sendError(request, response, 
677                response.SC_INTERNAL_SERVER_ERROR, "invalidIssuer",
678                SAML2Utils.bundle.getString("invalidIssuer"));
679            throw new SAML2Exception(
680                SAML2Utils.bundle.getString("invalidIssuer"));
681        }
682
683        // check time?
684
685        Status status = artiResp.getStatus();
686        if (status == null || !status.getStatusCode().getValue().equals(
687                                        SAML2Constants.SUCCESS))
688        {
689            String statusCode =
690                (status == null)?"":status.getStatusCode().getValue();
691            if (SAML2Utils.debug.messageEnabled()) {
692                SAML2Utils.debug.message(method 
693                    + "ArtifactResponse's status code is not success."
694                    + statusCode);
695            }
696            String[] data = {idpEntityID, ""};
697            if (LogUtil.isErrorLoggable(Level.FINE)) {
698                data[1] = statusCode;
699            }
700            LogUtil.error(Level.INFO,
701                        LogUtil.ARTIFACT_RESPONSE_INVALID_STATUS_CODE,
702                        data,
703                        null);
704            SAMLUtils.sendError(request, response,
705                    response.SC_INTERNAL_SERVER_ERROR, "invalidStatusCode",
706                    SAML2Utils.bundle.getString("invalidStatusCode"));
707            throw new SAML2Exception(
708                SAML2Utils.bundle.getString("invalidStatusCode"));
709        } 
710
711        try {
712            return ProtocolFactory.getInstance().createResponse(
713                                artiResp.getAny());
714        } catch (SAML2Exception se) {
715            if (SAML2Utils.debug.messageEnabled()) {
716                SAML2Utils.debug.message(method 
717                    + "couldn't instantiate Response:", se);
718            }
719            String[] data = {idpEntityID};
720            LogUtil.error(Level.INFO,
721                        LogUtil.CANNOT_INSTANTIATE_RESPONSE_ARTIFACT,
722                        data,
723                        null);
724            SAMLUtils.sendError(request, response, 
725                response.SC_INTERNAL_SERVER_ERROR, 
726                "failedToCreateResponse", se.getMessage());
727            throw se;
728        }
729    }
730
731    /**
732     * Obtains <code>SAML Response</code> from <code>SOAPBody</code>.
733     * Used by ECP profile.
734     */
735    private static ResponseInfo getResponseFromPostECP(
736        HttpServletRequest request, HttpServletResponse response,
737        String orgName, String hostEntityId, SAML2MetaManager metaManager)
738            throws SAML2Exception,IOException
739    {
740        Message message = null;
741        try {
742            message = new Message(SOAPCommunicator.getInstance().getSOAPMessage(request));
743        } catch (SOAPException soapex) {
744            String[] data = { hostEntityId } ;
745            LogUtil.error(Level.INFO,
746                LogUtil.CANNOT_INSTANTIATE_SOAP_MESSAGE_ECP, data, null);
747            SAMLUtils.sendError(request, response, 
748                response.SC_INTERNAL_SERVER_ERROR,
749                "failedToCreateSOAPMessage", soapex.getMessage());
750            throw new SAML2Exception(soapex.getMessage()); 
751        } catch (SOAPBindingException soapex) {
752            String[] data = { hostEntityId } ;
753            LogUtil.error(Level.INFO,
754                LogUtil.CANNOT_INSTANTIATE_SOAP_MESSAGE_ECP, data, null);
755            SAMLUtils.sendError(request, response, 
756                response.SC_INTERNAL_SERVER_ERROR,
757                "failedToCreateSOAPMessage", soapex.getMessage());
758            throw new SAML2Exception(soapex.getMessage()); 
759        } catch(SOAPFaultException sfex) {
760            String[] data = { hostEntityId } ;
761            LogUtil.error(Level.INFO, LogUtil.RECEIVE_SOAP_FAULT_ECP,
762                data, null);
763            String faultString =
764                sfex.getSOAPFaultMessage().getSOAPFault().getFaultString();
765            SAMLUtils.sendError(request, response, 
766                response.SC_INTERNAL_SERVER_ERROR, 
767                "failedToCreateSOAPMessage", faultString);
768            throw new SAML2Exception(faultString);
769        }
770
771        List soapHeaders = message.getOtherSOAPHeaders();
772        ECPRelayState ecpRelayState = null;
773        if ((soapHeaders != null) && (!soapHeaders.isEmpty())) {
774            for(Iterator iter = soapHeaders.iterator(); iter.hasNext();) {
775                Element headerEle = (Element)iter.next();
776                try {
777                    ecpRelayState =
778                        ECPFactory.getInstance().createECPRelayState(headerEle);
779                    break;
780                } catch (SAML2Exception saml2ex) {
781                    // not ECP RelayState
782                }
783            }
784        }
785        String relayState = null;
786        if (ecpRelayState != null) {
787            relayState = ecpRelayState.getValue();
788        }
789
790        List soapBodies = message.getBodies();
791        if ((soapBodies == null) || (soapBodies.isEmpty())) {
792            String[] data = { hostEntityId } ;
793            LogUtil.error(Level.INFO,
794                LogUtil.CANNOT_INSTANTIATE_SAML_RESPONSE_FROM_ECP, data, null);
795            SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST,
796                "missingSAMLResponse",
797                SAML2Utils.bundle.getString("missingSAMLResponse"));
798            throw new SAML2Exception(
799                SAML2Utils.bundle.getString("missingSAMLResponse"));
800        }
801
802        Element resElem = (Element)soapBodies.get(0);
803
804        Response resp = null;
805        try {
806            resp = ProtocolFactory.getInstance().createResponse(resElem);
807        } catch (SAML2Exception se) {
808            if (SAML2Utils.debug.messageEnabled()) {
809                SAML2Utils.debug.message("SPACSUtils.getResponseFromPostECP:" +
810                    "Couldn't create Response:", se);
811            }
812            String[] data = { hostEntityId } ;
813            LogUtil.error(Level.INFO,
814                LogUtil.CANNOT_INSTANTIATE_SAML_RESPONSE_FROM_ECP, data, null);
815            SAMLUtils.sendError(request, response, 
816                response.SC_INTERNAL_SERVER_ERROR,
817                "failedToCreateResponse", se.getMessage());
818            throw se;
819        }
820
821        String idpEntityID = resp.getIssuer().getValue();
822        IDPSSODescriptorElement idpDesc = null;
823        try {
824            idpDesc = metaManager.getIDPSSODescriptor(orgName, idpEntityID);
825        } catch (SAML2MetaException se) {
826            String[] data = { orgName, idpEntityID };
827            LogUtil.error(Level.INFO, LogUtil.IDP_META_NOT_FOUND, data, null);
828            SAMLUtils.sendError(request, response, 
829                response.SC_INTERNAL_SERVER_ERROR,
830                "failedToGetIDPSSODescriptor", se.getMessage());
831            throw se;
832        }
833
834        Set<X509Certificate> certificates = KeyUtil.getVerificationCerts(idpDesc, idpEntityID, SAML2Constants.IDP_ROLE);
835        List assertions = resp.getAssertion();
836        if ((assertions != null) && (!assertions.isEmpty())) {
837            for(Iterator iter = assertions.iterator(); iter.hasNext(); ) {
838                Assertion assertion = (Assertion)iter.next();
839                if (!assertion.isSigned()) {
840                    if (SAML2Utils.debug.messageEnabled()) {
841                        SAML2Utils.debug.message(
842                            "SPACSUtils.getResponseFromPostECP: " + 
843                            " Assertion is not signed.");
844                    }
845                    String[] data = { idpEntityID };
846                    LogUtil.error(Level.INFO,
847                        LogUtil.ECP_ASSERTION_NOT_SIGNED, data, null);
848                    SAMLUtils.sendError(request, response, 
849                        response.SC_INTERNAL_SERVER_ERROR,
850                        "assertionNotSigned",
851                        SAML2Utils.bundle.getString("assertionNotSigned"));
852                    throw new SAML2Exception(
853                        SAML2Utils.bundle.getString("assertionNotSigned"));
854                } else if (!assertion.isSignatureValid(certificates)) {
855                    if (SAML2Utils.debug.messageEnabled()) {
856                        SAML2Utils.debug.message(
857                            "SPACSUtils.getResponseFromPostECP: " + 
858                            " Assertion signature is invalid.");
859                    }
860                    String[] data = { idpEntityID };
861                    LogUtil.error(Level.INFO,
862                        LogUtil.ECP_ASSERTION_INVALID_SIGNATURE, data, null);
863                    SAMLUtils.sendError(request, response, 
864                        response.SC_INTERNAL_SERVER_ERROR,
865                        "invalidSignature",
866                        SAML2Utils.bundle.getString("invalidSignature"));
867                    throw new SAML2Exception(
868                        SAML2Utils.bundle.getString("invalidSignature"));
869                }
870            }
871        }
872
873        return new ResponseInfo(resp, SAML2Constants.PAOS, relayState);
874    }
875
876    // Obtains SAML Response from POST.
877    private static ResponseInfo getResponseFromPost(HttpServletRequest request, HttpServletResponse response,
878                                                    String orgName, String hostEntityId, SAML2MetaManager metaManager)
879            throws SAML2Exception,IOException {
880
881        String classMethod = "SPACSUtils:getResponseFromPost:";
882        SAML2Utils.debug.message(classMethod);
883
884        String samlArt = request.getParameter(SAML2Constants.SAML_ART);
885        if (StringUtils.isNotBlank(samlArt)) {
886            return new ResponseInfo(getResponseFromArtifact(samlArt, hostEntityId, request, response,
887                    orgName, metaManager), SAML2Constants.HTTP_ARTIFACT, null);
888        }
889
890        String samlResponse = request.getParameter(SAML2Constants.SAML_RESPONSE);
891        if (samlResponse == null) {
892            LogUtil.error(Level.INFO, LogUtil.MISSING_SAML_RESPONSE_FROM_POST, null, null);
893            SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, "missingSAMLResponse",
894                    SAML2Utils.bundle.getString("missingSAMLResponse"));
895            throw new SAML2Exception(SAML2Utils.bundle.getString("missingSAMLResponse"));
896        }
897
898        // Get Response back
899        // decode the Response
900        Response resp = null;
901        try {
902            byte[] raw = Base64.decode(samlResponse);
903            if (raw != null) {
904                try (ByteArrayInputStream bis = new ByteArrayInputStream(raw)) {
905                    Document doc = XMLUtils.toDOMDocument(bis, SAML2Utils.debug);
906                    if (doc != null) {
907                        resp = ProtocolFactory.getInstance().createResponse(doc.getDocumentElement());
908                    }
909                }
910            }
911        } catch (SAML2Exception se) {
912            SAML2Utils.debug.error("{} Exception when instantiating SAMLResponse: {}", classMethod, samlResponse, se);
913            SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, "errorObtainResponse",
914                    SAML2Utils.bundle.getString("errorObtainResponse"));
915            LogUtil.error(Level.INFO, LogUtil.CANNOT_INSTANTIATE_RESPONSE_POST, null, null);
916            throw new SAML2Exception(SAML2Utils.bundle.getString("errorObtainResponse"));
917        }
918
919        if (resp != null) {
920            String[] data = {""};
921            if (LogUtil.isAccessLoggable(Level.FINE)) {
922                data[0] = resp.toXMLString();
923            }
924            LogUtil.access(Level.INFO, LogUtil.GOT_RESPONSE_FROM_POST, data,null);
925            return (new ResponseInfo(resp, SAML2Constants.HTTP_POST, null));
926        }
927
928        SAML2Utils.debug.message("{} Decoded response is null for SAMLResponse: {}", classMethod, samlResponse);
929        LogUtil.error(Level.INFO, LogUtil.CANNOT_DECODE_RESPONSE, null, null);
930        throw new SAML2Exception(SAML2Utils.bundle.getString("errorDecodeResponse"));
931    }
932
933    /**
934     * Authenticates user with <code>Response</code>.
935     * Auth session upgrade will be called if input session is
936     * not null.
937     * Otherwise, saml2 auth module is called. The name of the auth module
938     * is retrieved from <code>SPSSOConfig</code>. If not found, "SAML2" will
939     * be used.
940     *
941     * @param request HTTP Servlet request
942     * @param response HTTP Servlet response.
943     * @param out the print writer for writing out presentation
944     * @param metaAlias metaAlias for the service provider
945     * @param session input session object. It could be null.
946     * @param respInfo <code>ResponseInfo</code> to be verified.
947     * @param realm realm or organization name of the service provider.
948     * @param hostEntityId hosted service provider Entity ID.
949     * @param metaManager <code>SAML2MetaManager</code> instance for meta operation.
950     * @param auditor a <code>SAML2EventLogger</code> auditor object to hook into
951     *                tracking information for the saml request
952     * @return <code>Object</code> which holds result of the session.
953     * @throws SAML2Exception if the processing failed.
954     */
955    public static Object processResponse(
956        HttpServletRequest request, HttpServletResponse response, PrintWriter out,
957        String metaAlias, Object session, ResponseInfo respInfo,
958        String realm, String hostEntityId, SAML2MetaManager metaManager, SAML2EventLogger auditor
959    ) throws SAML2Exception {
960
961        String classMethod = "SPACSUtils.processResponse: ";
962        if (SAML2Utils.debug.messageEnabled()) {
963            SAML2Utils.debug.message(classMethod + "Response : " +
964                                     respInfo.getResponse());
965        }        
966        Map smap = null;
967        try {
968            // check Response/Assertion and get back a Map of relevant data
969            smap = SAML2Utils.verifyResponse(request, response,
970                respInfo.getResponse(), realm, hostEntityId,
971                respInfo.getProfileBinding());
972        } catch (SAML2Exception se) {
973            // invoke SPAdapter for failure
974            invokeSPAdapterForSSOFailure(hostEntityId, realm,
975                request, response, smap, respInfo, 
976                SAML2ServiceProviderAdapter.INVALID_RESPONSE, se);
977            throw se;
978        }
979        
980        com.sun.identity.saml2.assertion.Subject assertionSubject =
981            (com.sun.identity.saml2.assertion.Subject)
982            smap.get(SAML2Constants.SUBJECT);
983        NameID nameId = assertionSubject.getNameID();
984        EncryptedID encId = assertionSubject.getEncryptedID();
985        Assertion authnAssertion =
986            (Assertion) smap.get(SAML2Constants.POST_ASSERTION);
987        String sessionIndex = (String)smap.get(SAML2Constants.SESSION_INDEX);
988        respInfo.setSessionIndex(sessionIndex);
989        Integer authLevel = (Integer) smap.get(SAML2Constants.AUTH_LEVEL);
990        Long maxSessionTime = (Long) smap.get(SAML2Constants.MAX_SESSION_TIME);
991        String inRespToResp = (String) smap.get(SAML2Constants.IN_RESPONSE_TO);
992        List assertions = (List) smap.get(SAML2Constants.ASSERTIONS);
993        
994        if (SAML2Utils.debug.messageEnabled()) {
995            SAML2Utils.debug.message(classMethod + "Assertions : " +
996                                     assertions);
997        }
998       
999        SPSSOConfigElement spssoconfig =
1000            metaManager.getSPSSOConfig(realm, hostEntityId);
1001
1002        // get mappers
1003        SPAccountMapper acctMapper = SAML2Utils.getSPAccountMapper(realm, hostEntityId);
1004        SPAttributeMapper attrMapper = SAML2Utils.getSPAttributeMapper(realm, hostEntityId);
1005
1006        boolean needAssertionEncrypted =
1007                Boolean.parseBoolean(SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig,
1008                        SAML2Constants.WANT_ASSERTION_ENCRYPTED));
1009
1010        boolean needAttributeEncrypted = getNeedAttributeEncrypted(needAssertionEncrypted, spssoconfig);
1011        boolean needNameIDEncrypted = getNeedNameIDEncrypted(needAssertionEncrypted, spssoconfig);
1012
1013        Set<PrivateKey> decryptionKeys = KeyUtil.getDecryptionKeys(spssoconfig);
1014        if (needNameIDEncrypted && encId == null) {
1015            SAML2Utils.debug.error(classMethod +
1016                                   "process: NameID was not encrypted.");
1017            SAML2Exception se = new SAML2Exception(SAML2Utils.bundle.getString(
1018                "nameIDNotEncrypted"));
1019            // invoke SPAdapter for failure
1020            invokeSPAdapterForSSOFailure(hostEntityId, realm,
1021                request, response, smap, respInfo,
1022                SAML2ServiceProviderAdapter.INVALID_RESPONSE, se);
1023            throw se;
1024        }
1025        if (encId != null) {
1026            try {
1027                nameId = encId.decrypt(decryptionKeys);
1028            } catch (SAML2Exception se) {
1029                // invoke SPAdapter for failure
1030                invokeSPAdapterForSSOFailure(hostEntityId, realm,
1031                    request, response, smap, respInfo,
1032                    SAML2ServiceProviderAdapter.INVALID_RESPONSE, se);
1033                throw se;
1034            }
1035        }
1036        respInfo.setNameId(nameId);
1037
1038        SPSSODescriptorElement spDesc = null;
1039        try {
1040            spDesc = metaManager.getSPSSODescriptor(realm, hostEntityId);
1041        } catch (SAML2MetaException ex) {
1042            SAML2Utils.debug.error(classMethod, ex);
1043        }
1044        if (spDesc == null) {
1045            SAML2Exception se = new SAML2Exception(SAML2Utils.bundle.getString(
1046                "metaDataError"));
1047            invokeSPAdapterForSSOFailure(hostEntityId, realm, request,
1048                response, smap, respInfo,
1049                SAML2ServiceProviderAdapter.SSO_FAILED_META_DATA_ERROR, se);
1050            throw se;
1051        }
1052        String nameIDFormat = nameId.getFormat();
1053        if (nameIDFormat != null) {
1054            List spNameIDFormatList = spDesc.getNameIDFormat();
1055
1056            if ((spNameIDFormatList != null) && (!spNameIDFormatList.isEmpty())
1057                && (!spNameIDFormatList.contains(nameIDFormat))) {
1058
1059                Object[] args = { nameIDFormat };
1060                SAML2Exception se = new SAML2Exception(SAML2Utils.BUNDLE_NAME,
1061                    "unsupportedNameIDFormatSP", args);
1062
1063                invokeSPAdapterForSSOFailure(hostEntityId, realm, request,
1064                    response, smap, respInfo,
1065                    SAML2ServiceProviderAdapter.INVALID_RESPONSE, se);
1066                throw se;
1067            }
1068        }
1069
1070        boolean isTransient = SAML2Constants.NAMEID_TRANSIENT_FORMAT.equals(nameIDFormat);
1071        boolean ignoreProfile = SAML2PluginsUtils.isIgnoredProfile(session, realm);
1072        String existUserName = null;
1073        SessionProvider sessionProvider = null;
1074        try {
1075            sessionProvider = SessionManager.getProvider();
1076        } catch (SessionException se) {
1077            // invoke SPAdapter for failure
1078            SAML2Exception se2 = new SAML2Exception(se);
1079            invokeSPAdapterForSSOFailure(hostEntityId, realm,
1080                request, response, smap, respInfo, 
1081                SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2);
1082            throw se2;
1083        }
1084        if (session != null) {
1085            try {
1086                existUserName = sessionProvider.getPrincipalName(session);
1087            } catch (SessionException se) {
1088                // invoke SPAdapter for failure
1089                SAML2Exception se2 = new SAML2Exception(se);
1090                invokeSPAdapterForSSOFailure(hostEntityId, realm, request, response, smap, respInfo,
1091                        SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2);
1092                throw se2;
1093            }
1094        }
1095
1096        String remoteHostId = authnAssertion.getIssuer().getValue();
1097        String userName = null;
1098        boolean isNewAccountLink = false;
1099        boolean shouldPersistNameID = !isTransient && !ignoreProfile
1100                && acctMapper.shouldPersistNameIDFormat(realm, hostEntityId, remoteHostId, nameIDFormat);
1101        try {
1102            if (shouldPersistNameID) {
1103                if (SAML2Utils.debug.messageEnabled()) {
1104                    SAML2Utils.debug.message(classMethod + "querying data store for existing federation links: realm = "
1105                            + realm + " hostEntityID = " + hostEntityId + " remoteEntityID = " + remoteHostId);
1106                }
1107
1108                try {
1109                    userName = SAML2Utils.getDataStoreProvider().getUserID(realm, SAML2Utils.getNameIDKeyMap(
1110                            nameId, hostEntityId, remoteHostId, realm, SAML2Constants.SP_ROLE));
1111                } catch (DataStoreProviderException dse) {
1112                    SAML2Utils.debug.error(classMethod + "DataStoreProviderException whilst retrieving NameID " +
1113                            "information", dse);
1114                    throw new SAML2Exception(dse.getMessage());
1115                }
1116            }
1117            if (userName == null) {
1118                userName = acctMapper.getIdentity(authnAssertion, hostEntityId, realm);
1119                isNewAccountLink = true;
1120            }
1121        } catch (SAML2Exception se) {
1122            // invoke SPAdapter for failure
1123            invokeSPAdapterForSSOFailure(hostEntityId, realm, request, response, smap, respInfo,
1124                    SAML2ServiceProviderAdapter.SSO_FAILED_NO_USER_MAPPING, se);
1125            throw se;
1126        }
1127
1128        if (userName == null && respInfo.isLocalLogin()) {
1129            // In case we just got authenticated locally, we should accept the freshly authenticated session's principal
1130            // as the username corresponding to the received assertion.
1131            userName = existUserName;
1132        }
1133        if (null != auditor) {
1134            auditor.setUserId(userName);
1135        }
1136        if (SAML2Utils.debug.messageEnabled()) {
1137            SAML2Utils.debug.message(
1138                classMethod + "process: userName =[" + userName + "]");
1139        }
1140        List attrs = null;
1141        for (Iterator it = assertions.iterator(); it.hasNext(); ) {
1142            Assertion assertion = (Assertion)it.next();
1143            List origAttrs = getSAMLAttributes(assertion, needAttributeEncrypted, decryptionKeys);
1144            if (origAttrs != null && !origAttrs.isEmpty()) {
1145                if (attrs == null) {
1146                    attrs = new ArrayList();
1147                }
1148                attrs.addAll(origAttrs);
1149            }
1150        }
1151        Map attrMap = null;
1152        if (attrs != null) {
1153            try {
1154                attrMap = attrMapper.getAttributes(attrs, userName,
1155                    hostEntityId, remoteHostId, realm);
1156            } catch (SAML2Exception se) {
1157                // invoke SPAdapter for failure
1158                invokeSPAdapterForSSOFailure(hostEntityId, realm,
1159                    request, response, smap, respInfo, 
1160                    SAML2ServiceProviderAdapter.SSO_FAILED_ATTRIBUTE_MAPPING, 
1161                    se);
1162                throw se;
1163            }
1164        }
1165        if (SAML2Utils.debug.messageEnabled()) {
1166            SAML2Utils.debug.message(
1167                classMethod + "process: remoteHostId = " + remoteHostId);
1168            SAML2Utils.debug.message(
1169                classMethod + "process: attrMap = " + attrMap);
1170        }
1171        respInfo.setAttributeMap(attrMap);
1172
1173        // return error code for local user login
1174        if (StringUtils.isEmpty(userName)) {
1175            // If we couldn't determine the username based on the incoming assertion, then we shouldn't automatically
1176            // map the user to the existing session.
1177            if (session != null) {
1178                try {
1179                    sessionProvider.invalidateSession(session, request, response);
1180                } catch (SessionException se) {
1181                    SAML2Utils.debug.error("An error occurred while trying to invalidate session", se);
1182                }
1183            }
1184            throw new SAML2Exception(SAML2Utils.bundle.getString("noUserMapping"));
1185        }
1186
1187        boolean writeFedInfo = isNewAccountLink && shouldPersistNameID;
1188
1189        if (SAML2Utils.debug.messageEnabled()) {
1190            SAML2Utils.debug.message(
1191                classMethod + "userName : " + userName);
1192            SAML2Utils.debug.message(
1193                classMethod + "writeFedInfo : " + writeFedInfo);
1194        }
1195        AuthnRequest authnRequest = null;
1196        if (smap != null) {
1197            authnRequest = (AuthnRequest) 
1198                smap.get(SAML2Constants.AUTHN_REQUEST);
1199        }
1200        if (inRespToResp != null && inRespToResp.length() != 0) {
1201            SPCache.requestHash.remove(inRespToResp);
1202        }
1203        Map sessionInfoMap = new HashMap();
1204        sessionInfoMap.put(SessionProvider.REALM, realm);
1205        sessionInfoMap.put(SessionProvider.PRINCIPAL_NAME, userName);
1206        // set client info. always use client IP address to prevent
1207        // reverse host lookup
1208        String clientAddr = ClientUtils.getClientIPAddress(request);
1209        sessionInfoMap.put(SessionProvider.HOST, clientAddr);
1210        sessionInfoMap.put(SessionProvider.HOST_NAME, clientAddr);
1211        sessionInfoMap.put(SessionProvider.AUTH_LEVEL, 
1212            String.valueOf(authLevel));
1213        request.setAttribute(SessionProvider.ATTR_MAP, attrMap);
1214        try {
1215            session = sessionProvider.createSession(
1216                sessionInfoMap, request, response, null);
1217        } catch (SessionException se) {
1218            // invoke SPAdapter for failure
1219            int failureCode = 
1220                SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_GENERATION;
1221            int sessCode =  se.getErrCode();
1222            if (sessCode == SessionException.AUTH_USER_INACTIVE) {
1223                failureCode =
1224                    SAML2ServiceProviderAdapter.SSO_FAILED_AUTH_USER_INACTIVE;
1225            } else if (sessCode == SessionException.AUTH_USER_LOCKED) {
1226                failureCode =
1227                    SAML2ServiceProviderAdapter.SSO_FAILED_AUTH_USER_LOCKED;
1228            } else if (sessCode == SessionException.AUTH_ACCOUNT_EXPIRED) {
1229                failureCode =
1230                    SAML2ServiceProviderAdapter.SSO_FAILED_AUTH_ACCOUNT_EXPIRED;
1231            }
1232            if (SAML2Utils.debug.messageEnabled()) {
1233                SAML2Utils.debug.message(
1234                    "SPACSUtils.processResponse : error code=" + sessCode, se);
1235            }
1236            SAML2Exception se2 = new SAML2Exception(se);
1237            invokeSPAdapterForSSOFailure(hostEntityId, realm,
1238                request, response, smap, respInfo, failureCode, se2);
1239            throw se2;
1240        }
1241
1242        // set metaAlias
1243        String[] values = { metaAlias };
1244        try {
1245            setAttrMapInSession(sessionProvider, attrMap, session);
1246            setDiscoBootstrapCredsInSSOToken(sessionProvider, authnAssertion,
1247                session);
1248            sessionProvider.setProperty(
1249                session, SAML2Constants.SP_METAALIAS, values);
1250        } catch (SessionException se) {
1251            // invoke SPAdapter for failure
1252            SAML2Exception se2 = new SAML2Exception(se);
1253            invokeSPAdapterForSSOFailure(hostEntityId, realm,
1254                request, response, smap, respInfo, 
1255                SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2);
1256            throw se2;
1257        }
1258
1259        NameIDInfo info = null;
1260        String affiID = nameId.getSPNameQualifier();
1261        boolean isDualRole = SAML2Utils.isDualRole(hostEntityId, realm);
1262        AffiliationDescriptorType affiDesc = null;
1263        if (affiID != null && !affiID.isEmpty()) {
1264            affiDesc = metaManager.getAffiliationDescriptor(realm, affiID);
1265        }
1266
1267        if (affiDesc != null) {
1268            if (!affiDesc.getAffiliateMember().contains(hostEntityId)) {
1269                throw new SAML2Exception(SAML2Utils.bundle.getString(
1270                    "spNotAffiliationMember"));
1271            }
1272            if (isDualRole) {
1273                info = new NameIDInfo(affiID, remoteHostId, nameId,
1274                    SAML2Constants.DUAL_ROLE, true);
1275            } else {
1276                info = new NameIDInfo(affiID, remoteHostId, nameId,
1277                    SAML2Constants.SP_ROLE, true);
1278            }
1279        } else {
1280            if (isDualRole) {
1281                info = new NameIDInfo(hostEntityId, remoteHostId, nameId,
1282                    SAML2Constants.DUAL_ROLE, false);
1283            } else {
1284                info = new NameIDInfo(hostEntityId, remoteHostId, nameId,
1285                    SAML2Constants.SP_ROLE, false);
1286            }
1287        }
1288        Map props = new HashMap();
1289        String nameIDValueString = info.getNameIDValue();
1290        props.put(LogUtil.NAME_ID, info.getNameIDValue());
1291        try {
1292            userName = sessionProvider.getPrincipalName(session);
1293        } catch (SessionException se) {
1294            // invoke SPAdapter for failure
1295            SAML2Exception se2 = new SAML2Exception(se);
1296            invokeSPAdapterForSSOFailure(hostEntityId, realm,
1297                request, response, smap, respInfo, 
1298                SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2);
1299            throw se2;
1300        }
1301        String[] data1 = {userName, nameIDValueString};
1302        LogUtil.access(Level.INFO, LogUtil.SUCCESS_FED_SSO, data1, session,
1303            props);
1304        // write fed info into data store
1305        if (writeFedInfo) {
1306            try {
1307                AccountUtils.setAccountFederation(info, userName);
1308            } catch (SAML2Exception se) {
1309                // invoke SPAdapter for failure
1310                invokeSPAdapterForSSOFailure(hostEntityId, realm,
1311                    request, response, smap, respInfo, 
1312                    SAML2ServiceProviderAdapter.FEDERATION_FAILED_WRITING_ACCOUNT_INFO, se);
1313               throw se;
1314            }
1315            String[] data = {userName, ""};
1316            if (LogUtil.isAccessLoggable(Level.FINE)) {
1317                data[1] = info.toValueString();
1318            }
1319            LogUtil.access(Level.INFO,
1320                           LogUtil.FED_INFO_WRITTEN,
1321                           data,
1322                           session,
1323                           props);
1324        }
1325        String requestID = respInfo.getResponse().getInResponseTo();
1326        // save info in memory for logout
1327        saveInfoInMemory(sessionProvider, session, sessionIndex, metaAlias,
1328            info, IDPProxyUtil.isIDPProxyEnabled(requestID), isTransient);
1329
1330        // invoke SP Adapter
1331        SAML2ServiceProviderAdapter spAdapter =
1332            SAML2Utils.getSPAdapterClass(hostEntityId, realm);
1333        if (spAdapter != null) {
1334            boolean redirected = spAdapter.postSingleSignOnSuccess(
1335                hostEntityId, realm, request, 
1336                response, out, session, authnRequest, respInfo.getResponse(),
1337                respInfo.getProfileBinding(), writeFedInfo);
1338            String[] value = null;
1339            if (redirected) {
1340                value = new String[] {"true"};
1341            } else {
1342                value = new String[] {"false"};
1343            }
1344            try {
1345                sessionProvider.setProperty(session, 
1346                    SAML2Constants.RESPONSE_REDIRECTED, value);
1347            } catch (SessionException ex) {
1348                SAML2Utils.debug.warning("SPSingleLogout.processResp", ex);
1349            } catch (UnsupportedOperationException ex) {
1350                SAML2Utils.debug.warning("SPSingleLogout.processResp", ex);
1351            }
1352        }
1353     
1354        String assertionID=authnAssertion.getID();
1355        if (respInfo.getProfileBinding().equals(SAML2Constants.HTTP_POST)) {
1356            SPCache.assertionByIDCache.put(assertionID, SAML2Constants.ONETIME);
1357            try {
1358                if (SAML2FailoverUtils.isSAML2FailoverEnabled()) {
1359                    SAML2FailoverUtils.saveSAML2TokenWithoutSecondaryKey(
1360                            assertionID,
1361                            SAML2Constants.ONETIME,
1362                            ((Long) smap.get(SAML2Constants.NOTONORAFTER)).longValue() / 1000);
1363                }
1364            } catch (SAML2TokenRepositoryException se) {
1365                SAML2Utils.debug.error(classMethod +
1366                        "There was a problem saving the assertionID to the SAML2 Token Repository for assertionID:"
1367                        + assertionID, se);
1368            }
1369        }
1370        respInfo.setAssertion(authnAssertion);
1371 
1372        return session;
1373    }
1374
1375    private static boolean getNeedNameIDEncrypted(boolean needAssertionEncrypted, SPSSOConfigElement spssoconfig) {
1376        if (!needAssertionEncrypted) {
1377            return Boolean.parseBoolean(SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig,
1378                    SAML2Constants.WANT_NAMEID_ENCRYPTED));
1379        }
1380
1381        return false;
1382    }
1383
1384    public static boolean getNeedAttributeEncrypted(boolean needAssertionEncrypted, SPSSOConfigElement spssoconfig) {
1385        if (!needAssertionEncrypted) {
1386            return Boolean.parseBoolean(SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig,
1387                    SAML2Constants.WANT_ATTRIBUTE_ENCRYPTED));
1388        }
1389
1390        return false;
1391    }
1392
1393    private static void invokeSPAdapterForSSOFailure(String hostEntityId,
1394        String realm, HttpServletRequest request, HttpServletResponse response,
1395        Map smap, ResponseInfo respInfo, int errorCode, 
1396        SAML2Exception se) { 
1397        SAML2ServiceProviderAdapter spAdapter = null;
1398        try {
1399            spAdapter = SAML2Utils.getSPAdapterClass(hostEntityId, realm);
1400        } catch (SAML2Exception e) {
1401            if (SAML2Utils.debug.messageEnabled()) {
1402                SAML2Utils.debug.message(
1403                    "SPACSUtils.invokeSPAdapterForSSOFailure", e);
1404            }
1405        }
1406        if (spAdapter != null) {
1407            AuthnRequest authnRequest = null;
1408            if (smap != null) {
1409                authnRequest = (AuthnRequest) 
1410                    smap.get(SAML2Constants.AUTHN_REQUEST);
1411            }
1412            boolean redirected = spAdapter.postSingleSignOnFailure(
1413                hostEntityId, realm, request, response, authnRequest,
1414                respInfo.getResponse(), respInfo.getProfileBinding(),
1415                errorCode);
1416            se.setRedirectionDone(redirected);
1417        }
1418    }
1419
1420    public static void saveInfoInMemory(SessionProvider sessionProvider,
1421        Object session, String sessionIndex, String metaAlias,
1422        NameIDInfo info, boolean isIDPProxy, boolean isTransient)
1423        throws SAML2Exception {
1424        
1425        String infoKeyString = (new NameIDInfoKey(
1426            info.getNameIDValue(),
1427            info.getHostEntityID(),
1428            info.getRemoteEntityID())).toValueString();
1429        String infoKeyAttribute =
1430            AccountUtils.getNameIDInfoKeyAttribute();
1431        String[] fromToken = null;
1432        try {
1433            fromToken = sessionProvider.
1434                    getProperty(session, infoKeyAttribute);
1435            if (fromToken == null || fromToken.length == 0 ||
1436                fromToken[0] == null || fromToken[0].length() == 0) {
1437                String[] values = { infoKeyString };
1438                sessionProvider.setProperty(
1439                    session, infoKeyAttribute, values);
1440            } else {
1441                if (fromToken[0].indexOf(infoKeyString) == -1) {
1442                    String[] values = { fromToken[0] +
1443                                        SAML2Constants.SECOND_DELIM +
1444                                        infoKeyString }; 
1445                    sessionProvider.setProperty(
1446                        session, infoKeyAttribute, values);
1447                }
1448            }
1449            if (isTransient) {
1450                String nameIDInfoStr = info.toValueString();
1451                String infoAttribute = AccountUtils.getNameIDInfoAttribute();
1452                String[] nameIDInfoStrs = sessionProvider.getProperty(session,
1453                    infoAttribute);
1454                if (nameIDInfoStrs == null) {
1455                    nameIDInfoStrs = new String[1];
1456                    nameIDInfoStrs[0] = nameIDInfoStr;
1457                } else {
1458                    Set nameIDInfoStrSet = new HashSet();
1459                    for(int i=0; i<nameIDInfoStrs.length; i++) {
1460                        nameIDInfoStrSet.add(nameIDInfoStrs[i]);
1461                    }
1462                    nameIDInfoStrSet.add(nameIDInfoStr);
1463                    nameIDInfoStrs = (String[])nameIDInfoStrSet.toArray(
1464                        new String[nameIDInfoStrSet.size()]);
1465                }
1466                sessionProvider.setProperty(session, infoAttribute,
1467                    nameIDInfoStrs);
1468            }
1469        } catch (SessionException sessE) {
1470            throw new SAML2Exception(sessE);
1471        }
1472        String tokenID = sessionProvider.getSessionID(session);
1473        if (!SPCache.isFedlet) {
1474            List fedSessions = (List) SPCache.fedSessionListsByNameIDInfoKey.get(infoKeyString);
1475            if (isIDPProxy) {
1476                IDPSession idpSess = IDPCache.idpSessionsBySessionID.get(tokenID);
1477                if (idpSess == null) {
1478                    idpSess = new IDPSession(session);
1479                    IDPCache.idpSessionsBySessionID.put(tokenID, idpSess);
1480                }
1481                SAML2Utils.debug.message("Add Session Partner: {}", info.getRemoteEntityID());
1482                idpSess.addSessionPartner(new SAML2SessionPartner(info.getRemoteEntityID(), true));
1483            }
1484
1485            if (fedSessions == null) {
1486                synchronized (SPCache.fedSessionListsByNameIDInfoKey) {
1487                    fedSessions = (List)
1488                    SPCache.fedSessionListsByNameIDInfoKey.get(infoKeyString);
1489                    if (fedSessions == null) {
1490                        fedSessions = new ArrayList();
1491                    }
1492                }  
1493                synchronized (fedSessions) {
1494                    fedSessions.add(new SPFedSession(sessionIndex, tokenID,
1495                        info, metaAlias));
1496                    SPCache.fedSessionListsByNameIDInfoKey.put(
1497                        infoKeyString, fedSessions);
1498                }
1499                if ((agent != null) && agent.isRunning() && (saml2Svc != null)){
1500                    saml2Svc.setFedSessionCount(
1501                        (long)SPCache.fedSessionListsByNameIDInfoKey.size());
1502                }
1503            } else {
1504                synchronized (fedSessions) {
1505                    Iterator iter = fedSessions.iterator();
1506                    boolean found = false;
1507                    while (iter.hasNext()) {
1508                        SPFedSession temp = (SPFedSession) iter.next();
1509                        String idpSessionIndex = null;
1510                        if(temp != null) {
1511                           idpSessionIndex = temp.idpSessionIndex; 
1512                        }
1513                        if ((idpSessionIndex != null) &&
1514                                (idpSessionIndex.equals(sessionIndex))) {
1515                            temp.spTokenID = tokenID;
1516                            temp.info = info;
1517                            found = true;
1518                            break;
1519                        }
1520                    }    
1521                    if (!found) {
1522                        fedSessions.add(
1523                            new SPFedSession(sessionIndex, tokenID, info,
1524                                             metaAlias));
1525                        SPCache.fedSessionListsByNameIDInfoKey.put(
1526                            infoKeyString, fedSessions);
1527                        if ((agent != null) &&
1528                            agent.isRunning() &&
1529                            (saml2Svc != null))
1530                        {
1531                            saml2Svc.setFedSessionCount(
1532                                (long)SPCache.fedSessionListsByNameIDInfoKey.
1533                                    size());
1534                        }
1535                    }
1536               }    
1537            }
1538            SPCache.fedSessionListsByNameIDInfoKey.put(infoKeyString,
1539                    fedSessions);
1540            if ((agent != null) && agent.isRunning() && (saml2Svc != null)) {
1541                saml2Svc.setFedSessionCount(
1542                    (long)SPCache.fedSessionListsByNameIDInfoKey.size());
1543            }
1544        }
1545        try {
1546            sessionProvider.addListener(
1547                session, new SPSessionListener(infoKeyString, tokenID));
1548        } catch (SessionException e) {
1549            SAML2Utils.debug.error(
1550                "SPACSUtils.saveInfoInMemory: "+
1551                "Unable to add session listener.");
1552        }
1553    }
1554    
1555    /** Sets the attribute map in the session
1556     *
1557     *  @param sessionProvider Session provider
1558     *  @param attrMap the Attribute Map
1559     *  @param session the valid session object
1560     *  @throws com.sun.identity.plugin.session.SessionException 
1561     */
1562    public static void setAttrMapInSession(
1563        SessionProvider sessionProvider,
1564        Map attrMap, Object session)
1565        throws SessionException {
1566        if (attrMap != null && !attrMap.isEmpty()) {
1567            Set entrySet = attrMap.entrySet();
1568            for(Iterator iter = entrySet.iterator(); iter.hasNext();) {
1569                Map.Entry entry = (Map.Entry)iter.next();
1570                String attrName = (String)entry.getKey();
1571                Set attrValues = (Set)entry.getValue();
1572                if(attrValues != null && !attrValues.isEmpty()) {
1573                   sessionProvider.setProperty(
1574                       session, attrName,
1575                       (String[]) attrValues.toArray(
1576                       new String[attrValues.size()]));
1577                   if (SAML2Utils.debug.messageEnabled()) {
1578                       SAML2Utils.debug.message(
1579                           "SPACSUtils.setAttrMapInSessioin: AttrMap:" +
1580                           attrName + " , " + attrValues);
1581                   }
1582                }
1583            }
1584        }
1585    }
1586
1587    /** Sets Discovery bootstrap credentials in the SSOToken
1588     *
1589     *  @param sessionProvider session provider.
1590     *  @param assertion assertion.
1591     *  @param session the valid session object.
1592     */
1593    private static void setDiscoBootstrapCredsInSSOToken(
1594        SessionProvider sessionProvider, Assertion assertion, Object session)
1595        throws SessionException {
1596
1597        if (assertion == null) {
1598            return;
1599        }
1600
1601        Set discoBootstrapCreds = null;
1602        Advice advice = assertion.getAdvice();
1603        if (advice != null) {
1604            List creds = advice.getAdditionalInfo();
1605            if ((creds != null) && !creds.isEmpty()) {
1606                if (discoBootstrapCreds == null) {
1607                    discoBootstrapCreds = new HashSet();
1608                }
1609                discoBootstrapCreds.addAll(creds);
1610            }
1611        }
1612
1613        if (discoBootstrapCreds != null) {
1614            sessionProvider.setProperty(session,
1615                SAML2Constants.DISCOVERY_BOOTSTRAP_CREDENTIALS,
1616                (String[])discoBootstrapCreds.toArray(
1617                new String[discoBootstrapCreds.size()]));
1618        }
1619    }
1620
1621    /**
1622     * Obtains relay state. Retrieves the relay state from relay state cache.
1623     * If input relay state is null, retrieve it from <code>SPSSOConfig</code>.
1624     *
1625     * @param relayStateID relay state value received from http request.
1626     * @param orgName realm or organization name the service provider resides in
1627     * @param hostEntityId Entity ID of the hosted service provider
1628     * @param sm <code>SAML2MetaManager</code> instance.
1629     * @return final relay state. Or <code>null</code> if the input 
1630     *         relayStateID is null and no default relay state is configured.
1631     */
1632    public static String getRelayState(
1633        String relayStateID,
1634        String orgName,
1635        String hostEntityId,
1636        SAML2MetaManager sm
1637    ) {
1638        String relayStateUrl = null;
1639
1640        if ((relayStateID != null) && (relayStateID.trim().length() != 0)) {
1641            CacheObject cache = (CacheObject)SPCache.relayStateHash.remove(
1642                relayStateID);
1643
1644            if (cache != null) {
1645                relayStateUrl = (String)cache.getObject();
1646            } else if (SAML2FailoverUtils.isSAML2FailoverEnabled()) {
1647                // The key is this way to make it unique compared to when
1648                // the same key is used to store a copy of the AuthnRequestInfo
1649                String key = relayStateID + relayStateID;
1650                try {
1651                    // Try and retrieve the value from the SAML2 repository
1652                    String relayState = (String) SAML2FailoverUtils.retrieveSAML2Token(key);
1653                    if (relayState != null) {
1654                        // Get back the relayState
1655                        relayStateUrl = relayState;
1656                        if (SAML2Utils.debug.messageEnabled()) {
1657                            SAML2Utils.debug.message("SPACUtils.getRelayState: relayState"
1658                                + " retrieved from SAML2 repository for key: " + key);
1659                        }
1660                    }
1661                } catch (SAML2TokenRepositoryException se) {
1662                    SAML2Utils.debug.error("SPACUtils.getRelayState: Unable to retrieve relayState for key "
1663                            + key, se);
1664                }
1665            } else {
1666                if (SAML2Utils.debug.messageEnabled()) {
1667                    SAML2Utils.debug.message("SPACUtils.getRelayState: relayState"
1668                        + " is null for relayStateID: " + relayStateID + ", SAML2 failover is disabled");
1669                }
1670            }
1671            
1672            if (relayStateUrl == null || relayStateUrl.trim().length() == 0) {
1673                relayStateUrl = relayStateID;
1674            }
1675        }
1676        
1677        if (relayStateUrl == null || relayStateUrl.trim().length() == 0) {
1678            relayStateUrl = getAttributeValueFromSPSSOConfig(
1679                orgName, hostEntityId, sm, SAML2Constants.DEFAULT_RELAY_STATE);
1680        }
1681        
1682        return relayStateUrl;
1683    }
1684
1685    /**
1686     * Retrieves intermediate redirect url from SP sso config. This url is used
1687     * if you want to goto some place before the final relay state.
1688     *
1689     * @param orgName realm or organization name the service provider resides in
1690     * @param hostEntityId Entity ID of the hosted service provider
1691     * @param sm <code>SAML2MetaManager</code> instance.
1692     * @return intermediate redirect url; or <code>null</code> if the url is
1693     *                is not configured or an error occured during the retrieval
1694     *                process.
1695     */
1696    public static String getIntermediateURL(String orgName,
1697                                        String hostEntityId,
1698                                        SAML2MetaManager sm)
1699    {
1700        return getAttributeValueFromSPSSOConfig(orgName, hostEntityId, sm,
1701                                        SAML2Constants.INTERMEDIATE_URL);
1702    }
1703
1704    /**
1705     * Saves response for later retrieval and retrieves local auth url from <code>SPSSOConfig</code>.
1706     * If the url does not exist, generate one from request URI.
1707     * If still cannot get it, (shouldn't happen), get it from {@link SystemConfigurationUtil}.
1708     *
1709     * @param realm Realm or organization name the service provider resides in.
1710     * @param hostEntityId Entity ID of the hosted service provider.
1711     * @param sm <code>SAML2MetaManager</code> instance to perform metadata operations.
1712     * @param respInfo The to be cached <code>ResponseInfo</code>.
1713     * @param requestURI The HTTP request URI.
1714     * @return The local login url.
1715     */
1716    public static String prepareForLocalLogin(String realm, String hostEntityId, SAML2MetaManager sm,
1717            ResponseInfo respInfo, String requestURI) {
1718        String localLoginUrl = getAttributeValueFromSPSSOConfig(realm, hostEntityId, sm, SAML2Constants.LOCAL_AUTH_URL);
1719        if (StringUtils.isEmpty(localLoginUrl)) {
1720            // get it from request
1721            try {
1722                int index = requestURI.indexOf("Consumer/metaAlias");
1723                if (index != -1) {
1724                    localLoginUrl = requestURI.substring(0, index) + "UI/Login?realm=" + realm;
1725                }
1726            } catch (IndexOutOfBoundsException e) {
1727                localLoginUrl = null;
1728            }
1729            if (StringUtils.isEmpty(localLoginUrl)) {
1730                // shouldn't be here, but in case
1731                localLoginUrl =
1732                        SystemConfigurationUtil.getProperty(SAMLConstants.SERVER_PROTOCOL)
1733                        + "://"
1734                        + SystemConfigurationUtil.getProperty(SAMLConstants.SERVER_HOST)
1735                        + SystemConfigurationUtil.getProperty(SAMLConstants.SERVER_PORT)
1736                        + "/UI/Login?realm="
1737                        + realm;
1738            }
1739        }
1740
1741        respInfo.setIsLocalLogin(true);
1742        synchronized (SPCache.responseHash) {
1743           SPCache.responseHash.put(respInfo.getResponse().getID(), respInfo);
1744        }
1745        SAML2Utils.debug.message("SPACSUtils:prepareForLocalLogin: localLoginUrl = {}", localLoginUrl);
1746
1747        return localLoginUrl;
1748    }
1749
1750    /**
1751     * Retrieves attribute value for a given attribute name from 
1752     * <code>SPSSOConfig</code>.
1753     * @param orgName realm or organization name the service provider resides in
1754     * @param hostEntityId hosted service provider's Entity ID.
1755     * @param sm <code>SAML2MetaManager</code> instance to perform meta
1756     *                operations.
1757     * @param attrName name of the attribute whose value ot be retrived.
1758     * @return value of the attribute; or <code>null</code> if the attribute
1759     *                if not configured, or an error occured in the process.
1760     */ 
1761    private static String getAttributeValueFromSPSSOConfig(String orgName,
1762                                                        String hostEntityId,
1763                                                        SAML2MetaManager sm,
1764                                                        String attrName)
1765    {
1766        String result = null;
1767        try {
1768            SPSSOConfigElement config = sm.getSPSSOConfig(orgName,
1769                                                        hostEntityId);
1770            if (config == null) {
1771                return null;
1772            }
1773            Map attrs = SAML2MetaUtils.getAttributes(config);
1774            List value = (List) attrs.get(attrName);
1775            if (value != null && value.size() != 0) {
1776                result = ((String) value.iterator().next()).trim();
1777            }
1778        } catch (SAML2MetaException sme) {
1779            if (SAML2Utils.debug.messageEnabled()) {
1780                SAML2Utils.debug.message("SPACSUtils.getAttributeValueFromSPSSO"
1781                        + "Config:", sme);
1782            }
1783            result = null;
1784        }
1785        return result;
1786    }
1787
1788    /**
1789     * Gets the attributes from an assert's AttributeStates.
1790     *
1791     * @param assertion The assertion from which to pull the AttributeStates.
1792     * @param needAttributeEncrypted Whether attributes must be encrypted (or else rejected).
1793     * @param privateKeys Private keys used to decrypt those encrypted attributes.
1794     * @return a list of attributes pulled from the provided assertion.
1795     */
1796    public static List<Attribute> getSAMLAttributes(Assertion assertion, boolean needAttributeEncrypted,
1797                                                     Set<PrivateKey> privateKeys) {
1798        List<Attribute> attrList = null;
1799        if (assertion != null) {
1800            List<AttributeStatement> statements = assertion.getAttributeStatements();
1801            if (CollectionUtils.isNotEmpty(statements)) {
1802                for (AttributeStatement statement : statements) {
1803                    List<Attribute> attributes = statement.getAttribute();
1804                    if (needAttributeEncrypted && attributes != null && !attributes.isEmpty()) {
1805                        SAML2Utils.debug.error("Attribute not encrypted.");
1806                        return null;
1807                    }
1808                    if (attributes != null) {
1809                        if (attrList == null) {
1810                            attrList = new ArrayList<>();
1811                        }
1812                        attrList.addAll(attributes);
1813                    }
1814                    List<EncryptedAttribute> encAttrs = statement.getEncryptedAttribute();
1815                    if (encAttrs != null) {
1816                        for (EncryptedAttribute encAttr : encAttrs) {
1817                            if (attrList == null) {
1818                                attrList = new ArrayList<>();
1819                            }
1820                            try {
1821                                attrList.add((encAttr).decrypt(privateKeys));
1822                            } catch (SAML2Exception se) {
1823                                SAML2Utils.debug.error("Decryption error:", se);
1824                                return null;
1825                            }
1826                        }
1827                    }
1828                }
1829            }
1830        }
1831        return attrList;
1832    }
1833
1834    /**
1835     * Processes response from Identity Provider to Fedlet (SP).
1836     * This will do all required protocol processing, include signature,
1837     * issuer and audience validation etc. A map containing processing
1838     * result will be returned. <br>
1839     * Here is a list of keys and values for the returned map: <br>
1840     * SAML2Constants.ATTRIBUTE_MAP -- Attribute map containing all attributes
1841     *                                 passed down from IDP inside the 
1842     *                                 Assertion. The value is a 
1843     *                                 <code>java.util.Map</code> whose keys 
1844     *                                 are attribute names and values are 
1845     *                                 <code>java.util.Set</code> of string 
1846     *                                 values for the attributes. <br>
1847     * SAML2Constants.RELAY_STATE -- Relay state, value is a string <br>
1848     * SAML2Constants.IDPENTITYID -- IDP entity ID, value is a string<br>
1849     * SAML2Constants.RESPONSE    -- Response object, value is an instance of 
1850     *                               com.sun.identity.saml2.protocol.Response
1851     * SAML2Constants.ASSERTION   -- Assertion object, value is an instance of 
1852     *                               com.sun.identity.saml2.assertion.Assertion
1853     * SAML2Constants.SUBJECT     -- Subject object, value is an instance of 
1854     *                               com.sun.identity.saml2.assertion.Subject
1855     * SAML2Constants.NAMEID      -- NameID object, value is an instance of 
1856     *                               com.sun.identity.saml2.assertion.NameID
1857     *
1858     * @param request HTTP Servlet request
1859     * @param response HTTP Servlet response.
1860     * @param out the print writer for writing out presentation
1861     *
1862     * @return <code>Map</code> which holds result of the processing.
1863     * @throws SAML2Exception if the processing failed due to server error.
1864     * @throws IOException if the processing failed due to IO error.
1865     * @throws SessionException if the processing failed due to session error.
1866     * @throws ServletException if the processing failed due to request error.
1867     *
1868     * @supported.api
1869     */  
1870    public static Map processResponseForFedlet (HttpServletRequest request,
1871        HttpServletResponse response, PrintWriter out) throws SAML2Exception, IOException,
1872        SessionException, ServletException {
1873        if (request == null) {
1874            String message =
1875                    MessageFormat.format(SAML2SDKUtils.bundle.getString("nullInputMessage"), new String[]{"request"});
1876            SAML2SDKUtils.debug.error("SPACSUtils.processResponseForFedlet: " + message);
1877            throw new ServletException(message);
1878        }
1879        if (response == null) {
1880            String message =
1881                    MessageFormat.format(SAML2SDKUtils.bundle.getString("nullInputMessage"), new String[]{"response"});
1882            SAML2SDKUtils.debug.error("SPACSUtils.processResponseForFedlet: " + message);
1883            throw new ServletException(message);
1884        }
1885        
1886        String requestURL = request.getRequestURL().toString();
1887        SAML2MetaManager metaManager = new SAML2MetaManager();
1888        if (metaManager == null) {
1889            throw new SAML2Exception(
1890                    SAML2SDKUtils.bundle.getString("errorMetaManager"));
1891        }
1892        String metaAlias = SAML2MetaUtils.getMetaAliasByUri(requestURL);
1893        if ((metaAlias ==  null) || (metaAlias.length() == 0)) {
1894            // Check in case metaAlias has been supplied as a parameter
1895            metaAlias = request.getParameter(SAML2MetaManager.NAME_META_ALIAS_IN_URI);
1896            if (metaAlias == null || metaAlias.length() == 0) {
1897                // pick the first available one
1898                List spMetaAliases =
1899                        metaManager.getAllHostedServiceProviderMetaAliases("/");
1900                if ((spMetaAliases != null) && !spMetaAliases.isEmpty()) {
1901                    // get first one
1902                    metaAlias = (String) spMetaAliases.get(0);
1903                }
1904                if ((metaAlias ==  null) || (metaAlias.length() == 0)) {
1905                    throw new ServletException(
1906                            SAML2SDKUtils.bundle.getString("nullSPEntityID"));
1907                }
1908            }
1909        }
1910        String hostEntityId = null;
1911        try {
1912            hostEntityId = metaManager.getEntityByMetaAlias(metaAlias);
1913        } catch (SAML2MetaException sme) {
1914            SAML2SDKUtils.debug.error("SPACSUtils.processResponseForFedlet",
1915                sme);
1916            throw new SAML2Exception( 
1917                    SAML2SDKUtils.bundle.getString("metaDataError"));
1918        }
1919        if (hostEntityId == null) {
1920            // logging?
1921            throw new SAML2Exception( 
1922                    SAML2SDKUtils.bundle.getString("metaDataError"));
1923        }
1924        // organization is always root org
1925        String orgName = "/";
1926        String relayState = request.getParameter(SAML2Constants.RELAY_STATE);
1927        SessionProvider sessionProvider = null;
1928        ResponseInfo respInfo = null;
1929        try {
1930            sessionProvider = SessionManager.getProvider();
1931        } catch (SessionException se) {
1932            SAML2SDKUtils.debug.error("SPACSUtils.processResponseForFedlet",
1933                se);
1934            throw new SAML2Exception(se);
1935        }
1936        respInfo = SPACSUtils.getResponse(
1937                request, response, orgName, hostEntityId, metaManager);
1938        
1939        Object newSession = null;
1940
1941        // Throws a SAML2Exception if the response cannot be validated
1942        // or contains a non-Success StatusCode, invoking the SPAdapter SPI
1943        // for taking action on the failed validation.
1944        // The resulting exception has its redirectionDone flag set if
1945        // the SPAdapter issued a HTTP redirect.
1946        newSession = SPACSUtils.processResponse(
1947                    request, response, out, metaAlias, null, respInfo,
1948                    orgName, hostEntityId, metaManager, null);
1949        
1950        SAML2SDKUtils.debug.message("SSO SUCCESS");
1951        String[] redirected = sessionProvider.getProperty(newSession,
1952                SAML2Constants.RESPONSE_REDIRECTED);
1953        if ((redirected != null) && (redirected.length != 0) &&
1954                redirected[0].equals("true")) {
1955            SAML2SDKUtils.debug.message("Already redirected in SPAdapter.");
1956            // response redirected already in SPAdapter
1957            return createMapForFedlet(respInfo, null, hostEntityId);
1958        }
1959        // redirect to relay state
1960        String finalUrl = SPACSUtils.getRelayState(
1961                relayState, orgName, hostEntityId, metaManager);
1962        String realFinalUrl = finalUrl;
1963        if (finalUrl != null && finalUrl.length() != 0) {
1964            try {
1965                realFinalUrl =
1966                    sessionProvider.rewriteURL(newSession, finalUrl);
1967            } catch (SessionException se) {
1968                SAML2SDKUtils.debug.message("SPACSUtils.processRespForFedlet",
1969                    se);
1970                realFinalUrl = finalUrl;
1971            }
1972        }
1973        String redirectUrl = SPACSUtils.getIntermediateURL(
1974                orgName, hostEntityId, metaManager);
1975        String realRedirectUrl = null;
1976        if (redirectUrl != null && redirectUrl.length() != 0) {
1977            if (realFinalUrl != null && realFinalUrl.length() != 0) {
1978                if (redirectUrl.indexOf("?") != -1) {
1979                    redirectUrl += "&goto=";
1980                } else {
1981                    redirectUrl += "?goto=";
1982                }
1983                redirectUrl += urlEncodeQueryParameterNameOrValue(realFinalUrl);
1984                try {
1985                    realRedirectUrl = sessionProvider.rewriteURL(
1986                            newSession, redirectUrl);
1987                } catch (SessionException se) {
1988                    SAML2SDKUtils.debug.message(
1989                      "SPACSUtils.processRespForFedlet: rewriting failed.", se);
1990                    realRedirectUrl = redirectUrl;
1991                }
1992            } else {
1993                realRedirectUrl = redirectUrl;
1994            }
1995        } else {
1996            realRedirectUrl = finalUrl;
1997        }
1998        return createMapForFedlet(respInfo, realRedirectUrl, hostEntityId); 
1999    }
2000
2001    private static Map createMapForFedlet(
2002        ResponseInfo respInfo, String relayUrl, String hostedEntityId) {
2003        Map map = new HashMap();
2004        if (relayUrl != null) {
2005            map.put(SAML2Constants.RELAY_STATE, relayUrl);
2006        }
2007        Response samlResp = respInfo.getResponse();
2008        map.put(SAML2Constants.RESPONSE, samlResp);
2009        Assertion assertion = respInfo.getAssertion();
2010        map.put(SAML2Constants.ASSERTION, assertion); 
2011        map.put(SAML2Constants.SUBJECT, assertion.getSubject());
2012        map.put(SAML2Constants.IDPENTITYID, assertion.getIssuer().getValue()); 
2013        map.put(SAML2Constants.SPENTITYID, hostedEntityId);
2014        map.put(SAML2Constants.NAMEID, respInfo.getNameId());
2015        map.put(SAML2Constants.ATTRIBUTE_MAP, respInfo.getAttributeMap());
2016        map.put(SAML2Constants.SESSION_INDEX, respInfo.getSessionIndex());
2017        return map;
2018    }
2019
2020    /**
2021     * Returns the username if there was one from the Assertion we were able to map into a local user account. Returns
2022     * null if not. Should only be used from the SP side. Should only be called in conjuncture with the Auth Module.
2023     * In addition, it performs what attribute federation it can.
2024     *
2025     * This method is a picked apart version of the "processResponse" function.
2026     */
2027    public static String getPrincipalWithoutLogin(Subject assertionSubject, Assertion authnAssertion, String realm,
2028                                                  String spEntityId, SAML2MetaManager metaManager, String idpEntityId,
2029                                                  String storageKey)
2030            throws SAML2Exception {
2031
2032        final EncryptedID encId = assertionSubject.getEncryptedID();
2033        final SPSSOConfigElement spssoconfig = metaManager.getSPSSOConfig(realm, spEntityId);
2034        final Set<PrivateKey> decryptionKeys = KeyUtil.getDecryptionKeys(spssoconfig);
2035        final SPAccountMapper acctMapper = SAML2Utils.getSPAccountMapper(realm, spEntityId);
2036
2037        boolean needNameIDEncrypted = false;
2038        NameID nameId = assertionSubject.getNameID();
2039
2040        String assertionEncryptedAttr =
2041                SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, SAML2Constants.WANT_ASSERTION_ENCRYPTED);
2042        if (!Boolean.parseBoolean(assertionEncryptedAttr)) {
2043            String idEncryptedStr =
2044                    SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, SAML2Constants.WANT_NAMEID_ENCRYPTED);
2045            needNameIDEncrypted = Boolean.parseBoolean(idEncryptedStr);
2046        }
2047
2048        if (needNameIDEncrypted && encId == null) {
2049            throw new SAML2Exception(SAML2Utils.bundle.getString("nameIDNotEncrypted"));
2050        }
2051        if (encId != null) {
2052            nameId = encId.decrypt(decryptionKeys);
2053        }
2054
2055        SPSSODescriptorElement spDesc = null;
2056        try {
2057            spDesc = metaManager.getSPSSODescriptor(realm, spEntityId);
2058        } catch (SAML2MetaException ex) {
2059            SAML2Utils.debug.error("Unable to read SPSSODescription", ex);
2060        }
2061
2062        if (spDesc == null) {
2063            throw new SAML2Exception(SAML2Utils.bundle.getString("metaDataError"));
2064        }
2065
2066        final String nameIDFormat = nameId.getFormat();
2067
2068        if (nameIDFormat != null) {
2069            List spNameIDFormatList = spDesc.getNameIDFormat();
2070
2071            if (CollectionUtils.isNotEmpty(spNameIDFormatList) && !spNameIDFormatList.contains(nameIDFormat)) {
2072                Object[] args = {nameIDFormat};
2073                throw new SAML2Exception(SAML2Utils.BUNDLE_NAME, "unsupportedNameIDFormatSP", args);
2074            }
2075        }
2076
2077        final boolean isTransient = SAML2Constants.NAMEID_TRANSIENT_FORMAT.equals(nameIDFormat);
2078        final boolean ignoreProfile = SAML2PluginsUtils.isIgnoredProfile(null, realm);
2079
2080        final boolean shouldPersistNameID = !isTransient && !ignoreProfile
2081                && acctMapper.shouldPersistNameIDFormat(realm, spEntityId, idpEntityId, nameIDFormat);
2082
2083        String userName = null;
2084        boolean isNewAccountLink = false;
2085
2086        try {
2087            if (shouldPersistNameID) {
2088                try {
2089                    userName = SAML2Utils.getDataStoreProvider().getUserID(realm, SAML2Utils.getNameIDKeyMap(
2090                            nameId, spEntityId, idpEntityId, realm, SAML2Constants.SP_ROLE));
2091                } catch (DataStoreProviderException dse) {
2092                    throw new SAML2Exception(dse.getMessage());
2093                }
2094            }
2095
2096            //if we can't get an already linked account, see if we'll be generating a new one based on federated data
2097            if (userName == null) {
2098                userName = acctMapper.getIdentity(authnAssertion, spEntityId, realm);
2099                isNewAccountLink = true; //we'll use this later to inform us
2100            }
2101        } catch (SAML2Exception se) {
2102            return null;
2103        }
2104
2105        //if we're new and we're persistent, store the federation data in the user pref
2106        if (isNewAccountLink && shouldPersistNameID) {
2107            try {
2108                writeFedData(nameId, spEntityId, realm, metaManager, idpEntityId, userName, storageKey);
2109            } catch (SAML2Exception se) {
2110                return userName;
2111            }
2112        }
2113
2114        return userName;
2115    }
2116
2117    private static void writeFedData(NameID nameId, String spEntityId, String realm, SAML2MetaManager metaManager,
2118                                     String idpEntityId, String userName, String storageKey) throws SAML2Exception {
2119        final NameIDInfo info;
2120        final String affiID = nameId.getSPNameQualifier();
2121        boolean isDualRole = SAML2Utils.isDualRole(spEntityId, realm);
2122        AffiliationDescriptorType affiDesc = null;
2123
2124        if (affiID != null && !affiID.isEmpty()) {
2125            affiDesc = metaManager.getAffiliationDescriptor(realm, affiID);
2126        }
2127
2128        if (affiDesc != null) {
2129            if (!affiDesc.getAffiliateMember().contains(spEntityId)) {
2130                throw new SAML2Exception("Unable to locate SP Entity ID in the affiliate descriptor.");
2131            }
2132            if (isDualRole) {
2133                info = new NameIDInfo(affiID, idpEntityId, nameId, SAML2Constants.DUAL_ROLE, true);
2134            } else {
2135                info = new NameIDInfo(affiID, idpEntityId, nameId, SAML2Constants.SP_ROLE, true);
2136            }
2137        } else {
2138            if (isDualRole) {
2139                info = new NameIDInfo(spEntityId, idpEntityId, nameId, SAML2Constants.DUAL_ROLE, false);
2140            } else {
2141                info = new NameIDInfo(spEntityId, idpEntityId, nameId, SAML2Constants.SP_ROLE, false);
2142            }
2143        }
2144
2145        // write fed info into data store
2146        SPCache.fedAccountHash.put(storageKey, "true");
2147        AccountUtils.setAccountFederation(info, userName);
2148    }
2149
2150    /**
2151     * Gets the attributes for this assertion in a new List.
2152     * @param authnAssertion Assertion from which to reead the attributes.
2153     * @param needAttributeEncrypted Whether the attributes must be encrypted.
2154     * @param decryptionKeys The keys used to decrypt the attributes, if they're encrypted.
2155     * @return a List of the attributes in this assertion.
2156     */
2157    public static List<Attribute> getAttrs(Assertion authnAssertion, boolean needAttributeEncrypted,
2158                                           Set<PrivateKey> decryptionKeys) {
2159        final List<Attribute> origAttrs = getSAMLAttributes(authnAssertion, needAttributeEncrypted, decryptionKeys);
2160
2161        List<Attribute> attrs = null;
2162        if (origAttrs != null && !origAttrs.isEmpty()) {
2163            attrs = new ArrayList<>();
2164            attrs.addAll(origAttrs);
2165        }
2166
2167        return attrs;
2168    }
2169
2170}