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