001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2007 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: AttributeQueryUtil.java,v 1.11 2009/07/24 22:51:48 madan_ranganath Exp $
026 *
027 */
028
029/*
030 * Portions copyright 2010-2013 ForgeRock, Inc.
031 */
032
033package com.sun.identity.saml2.profile;
034
035import java.util.ArrayList;
036import java.util.Date;
037import java.util.HashMap;
038import java.util.HashSet;
039import java.util.Hashtable;
040import java.util.Iterator;
041import java.util.List;
042import java.util.Map;
043import java.util.Set;
044import java.security.PrivateKey;
045import java.security.PublicKey;
046import java.security.cert.X509Certificate;
047import javax.crypto.SecretKey;
048import javax.servlet.http.HttpServletRequest;
049import javax.servlet.http.HttpServletResponse;
050import javax.xml.soap.SOAPException;
051import javax.xml.soap.SOAPMessage;
052import org.w3c.dom.Element;
053
054import com.sun.identity.plugin.datastore.DataStoreProviderException;
055import com.sun.identity.plugin.datastore.DataStoreProvider;
056import com.sun.identity.plugin.session.SessionException;
057import com.sun.identity.plugin.session.SessionManager;
058import com.sun.identity.saml.xmlsig.KeyProvider;
059import com.sun.identity.saml2.assertion.Assertion;
060import com.sun.identity.saml2.assertion.AssertionFactory;
061import com.sun.identity.saml2.assertion.Attribute;
062import com.sun.identity.saml2.assertion.AttributeStatement;
063import com.sun.identity.saml2.assertion.Conditions;
064import com.sun.identity.saml2.assertion.EncryptedAssertion;
065import com.sun.identity.saml2.assertion.Issuer;
066import com.sun.identity.saml2.assertion.NameID;
067import com.sun.identity.saml2.assertion.EncryptedID;
068import com.sun.identity.saml2.assertion.Subject;
069import com.sun.identity.saml2.common.AccountUtils;
070import com.sun.identity.saml2.common.SAML2Constants;
071import com.sun.identity.saml2.common.SAML2Exception;
072import com.sun.identity.saml2.common.SAML2Utils;
073import com.sun.identity.saml2.jaxb.assertion.AttributeElement;
074import com.sun.identity.saml2.jaxb.assertion.AttributeValueElement;
075import com.sun.identity.saml2.jaxb.entityconfig.AttributeAuthorityConfigElement;
076import com.sun.identity.saml2.jaxb.entityconfig.AttributeQueryConfigElement;
077import com.sun.identity.saml2.jaxb.entityconfig.IDPSSOConfigElement;
078import com.sun.identity.saml2.jaxb.metadata.AttributeAuthorityDescriptorElement;
079import com.sun.identity.saml2.jaxb.metadata.AttributeServiceElement;
080import com.sun.identity.saml2.jaxb.metadata.IDPSSODescriptorElement;
081import com.sun.identity.saml2.jaxb.metadata.NameIDMappingServiceElement;
082import com.sun.identity.saml2.jaxb.metadataextquery.AttributeQueryDescriptorElement;
083import com.sun.identity.saml2.key.EncInfo;
084import com.sun.identity.saml2.key.KeyUtil;
085import com.sun.identity.saml2.meta.SAML2MetaException;
086import com.sun.identity.saml2.meta.SAML2MetaManager;
087import com.sun.identity.saml2.meta.SAML2MetaUtils;
088import com.sun.identity.saml2.plugins.AttributeAuthorityMapper;
089import com.sun.identity.saml2.protocol.AttributeQuery;
090import com.sun.identity.saml2.protocol.ProtocolFactory;
091import com.sun.identity.saml2.protocol.Response;
092import com.sun.identity.saml2.protocol.Status;
093import com.sun.identity.saml2.protocol.StatusCode;
094import com.sun.identity.saml2.xmlenc.EncManager;
095import java.io.IOException;
096
097
098/**
099 * This class provides methods to send or process <code>AttributeQuery</code>.
100 *
101 * @supported.api
102 */
103
104public class AttributeQueryUtil {
105
106    static KeyProvider keyProvider = KeyUtil.getKeyProviderInstance(); 
107    static Hashtable attrAuthorityMapperCache = new Hashtable(); 
108    static DataStoreProvider dsProvider = null;
109    static SAML2MetaManager metaManager = SAML2Utils.getSAML2MetaManager();
110
111    static {
112        try {
113            dsProvider = SAML2Utils.getDataStoreProvider(); 
114        } catch (SAML2Exception se) {
115            SAML2Utils.debug.error("AttributeQueryUtil.static:", se);
116        }
117    }
118
119    private AttributeQueryUtil() {
120    }
121
122    /**
123     * Sends the <code>AttributeQuery</code> to specified
124     * attribute authority and returns <code>Response</code> coming
125     * from the attribute authority.
126     *
127     * @param attrQuery the <code>AttributeQuery</code> object
128     * @param attrAuthorityEntityID entity ID of attribute authority
129     * @param realm the realm of hosted entity
130     * @param attrQueryProfile the attribute query profile or null to ignore
131     * @param attrProfile the attribute profile
132     * @param binding the binding
133     *
134     * @return the <code>Response</code> object
135     * @exception SAML2Exception if the operation is not successful
136     *
137     * @supported.api
138     */
139    public static Response sendAttributeQuery(AttributeQuery attrQuery,
140        String attrAuthorityEntityID, String realm, String attrQueryProfile,
141        String attrProfile, String binding) throws SAML2Exception {
142
143        AttributeAuthorityDescriptorElement aad = null;
144        try {
145             aad = metaManager.getAttributeAuthorityDescriptor(
146                realm, attrAuthorityEntityID);
147        } catch (SAML2MetaException sme) {
148            SAML2Utils.debug.error("AttributeQueryUtil.sendAttributeQuery:",
149                sme);
150            throw new SAML2Exception(
151                SAML2Utils.bundle.getString("metaDataError"));
152        }
153
154        if (aad == null) {
155            throw new SAML2Exception(
156                SAML2Utils.bundle.getString("attrAuthorityNotFound"));
157        }
158
159        if (binding == null) {
160            throw new SAML2Exception(
161                SAML2Utils.bundle.getString("unsupportedBinding"));
162        }
163
164        String location = findLocation(aad, binding, attrQueryProfile,
165             attrProfile);
166
167        if (location == null) {
168            throw new SAML2Exception(
169                SAML2Utils.bundle.getString("attrAuthorityNotFound"));
170        }
171
172        if (binding.equalsIgnoreCase(SAML2Constants.SOAP)) {
173            signAttributeQuery(attrQuery, realm, false);
174            return sendAttributeQuerySOAP(attrQuery, location,
175                attrAuthorityEntityID, aad);
176        } else {
177            throw new SAML2Exception(
178                SAML2Utils.bundle.getString("unsupportedBinding"));
179        }
180    }
181
182     /**
183     * Sends the <code>AttributeQuery</code> to specified
184     * attribute authority and returns <code>Response</code> coming
185     * from the attribute authority.
186     *
187     * @param attrQuery the <code>AttributeQuery</code> object
188     * @param request the HTTP Request
189     * @param  response the HTTP Response
190     * @param attrAuthorityEntityID entity ID of attribute authority
191     * @param realm the realm of hosted entity
192     * @param attrQueryProfile the attribute query profile or null to ignore
193     * @param attrProfile the attribute profile
194     * @param binding the binding
195     *
196     * @exception SAML2Exception if the operation is not successful
197     *
198     * @supported.api
199     */
200     public static void sendAttributeQuery(AttributeQuery attrQuery,
201        HttpServletRequest request, HttpServletResponse response,
202        String attrAuthorityEntityID, String realm, String attrQueryProfile,
203        String attrProfile, String binding) throws SAML2Exception {
204
205        AttributeAuthorityDescriptorElement aad = null;
206        try {
207             aad = metaManager.getAttributeAuthorityDescriptor(
208                realm, attrAuthorityEntityID);
209        } catch (SAML2MetaException sme) {
210            SAML2Utils.debug.error("AttributeQueryUtil.sendAttributeQuery:",
211                sme);
212            throw new SAML2Exception(
213                SAML2Utils.bundle.getString("metaDataError"));
214        }
215
216        if (aad == null) {
217            throw new SAML2Exception(
218                SAML2Utils.bundle.getString("attrAuthorityNotFound"));
219        }
220
221        if (binding == null) {
222            throw new SAML2Exception(
223                SAML2Utils.bundle.getString("unsupportedBinding"));
224        }
225
226        String location = findLocation(aad, binding, attrQueryProfile,
227             attrProfile);
228
229        if (location == null) {
230            throw new SAML2Exception(
231                SAML2Utils.bundle.getString("attrAuthorityNotFound"));
232        }
233
234        if (binding.equalsIgnoreCase(SAML2Constants.HTTP_POST)) {
235            signAttributeQuery(attrQuery, realm, false);
236            String encodedReqMsg = SAML2Utils.encodeForPOST(attrQuery.toXMLString(true, true));
237            try {
238                SAML2Utils.postToTarget(response, "SAMLRequest",
239                                encodedReqMsg, null, null, location);
240            } catch (IOException ptte) {
241                SAML2Utils.bundle.getBundle("errorSendingAttributeQuery");
242            }
243        } else {
244            throw new SAML2Exception(
245                SAML2Utils.bundle.getString("unsupportedBinding"));
246        }
247    }
248
249
250    /**
251     * Processes the <code>AttributeQuery</code> coming
252     * from a requester.
253     *
254     * @param attrQuery the <code>AttributeQuery</code> object
255     * @param request the <code>HttpServletRequest</code> object
256     * @param response the <code>HttpServletResponse</code> object
257     * @param attrAuthorityEntityID entity ID of attribute authority
258     * @param realm the realm of hosted entity
259     * @param attrQueryProfileAlias the attribute query profile alias
260     *
261     * @return the <code>Response</code> object
262     * @exception SAML2Exception if the operation is not successful
263     */
264    public static Response processAttributeQuery(AttributeQuery attrQuery,
265        HttpServletRequest request, HttpServletResponse response,
266        String attrAuthorityEntityID, String realm,
267        String attrQueryProfileAlias) throws SAML2Exception {
268
269        AttributeAuthorityMapper attrAuthorityMapper = 
270            getAttributeAuthorityMapper(realm, attrAuthorityEntityID,
271            attrQueryProfileAlias);
272
273        String attrQueryProfile = AttributeQueryUtil.getAttributeQueryProfile(
274            attrQueryProfileAlias);
275
276        try {
277            attrAuthorityMapper.authenticateRequester(request, response,
278                attrQuery, attrAuthorityEntityID, realm);
279        } catch(SAML2Exception se) {
280            if (SAML2Utils.debug.messageEnabled()) {
281                SAML2Utils.debug.message("AttributeQueryUtil." +
282                "processAttributeQuery: ", se);
283            }
284            return SAML2Utils.getErrorResponse(attrQuery,
285                SAML2Constants.REQUESTER, null, se.getMessage(), null);
286        }
287
288        try {
289            attrAuthorityMapper.validateAttributeQuery(request, response,
290                attrQuery, attrAuthorityEntityID, realm);
291        } catch(SAML2Exception se) {
292            SAML2Utils.debug.error("AttributeQueryUtil.processAttributeQuery:",
293                se);
294            return SAML2Utils.getErrorResponse(attrQuery,
295                SAML2Constants.REQUESTER, null, se.getMessage(), null);
296        }
297
298        Issuer issuer = attrQuery.getIssuer();
299        String requesterEntityID = issuer.getValue();        
300        AttributeAuthorityDescriptorElement aad = null;
301        try {
302             aad = metaManager.getAttributeAuthorityDescriptor(
303                realm, attrAuthorityEntityID);
304        } catch (SAML2MetaException sme) {
305            SAML2Utils.debug.error("AttributeQueryUtil.processAttributeQuery:",
306                sme);
307            return SAML2Utils.getErrorResponse(attrQuery,
308                SAML2Constants.RESPONDER, null,
309                SAML2Utils.bundle.getString("metaDataError"), null);
310        } 
311
312        if (aad == null) {
313            return SAML2Utils.getErrorResponse(attrQuery,
314                SAML2Constants.REQUESTER, null,
315                SAML2Utils.bundle.getString("attrAuthorityNotFound"), null);
316        }
317
318        Object identity = null;
319        try {
320            identity = attrAuthorityMapper.getIdentity(request, response,
321                attrQuery, attrAuthorityEntityID, realm);
322        } catch (SAML2Exception se) {
323            if (SAML2Utils.debug.messageEnabled()) {
324                SAML2Utils.debug.message("AttributeQueryUtil." +
325                "processAttributeQuery: ", se);
326            }
327            return SAML2Utils.getErrorResponse(attrQuery,
328                SAML2Constants.REQUESTER, SAML2Constants.UNKNOWN_PRINCIPAL,
329                se.getMessage(), null);
330        }
331
332        if (identity == null) {
333            if (SAML2Utils.debug.messageEnabled()) {
334                SAML2Utils.debug.message("AttributeQueryUtil." +
335                "processAttributeQuery: unable to find identity.");
336            }
337            return SAML2Utils.getErrorResponse(attrQuery,
338                SAML2Constants.REQUESTER, SAML2Constants.UNKNOWN_PRINCIPAL,
339                null, null);
340        }
341
342        // Addition to support changing of desired attributes list
343        List desiredAttrs = (List)request.getAttribute("AttributeQueryUtil-desiredAttrs");
344        if (desiredAttrs == null) {
345            desiredAttrs = attrQuery.getAttributes();
346        }
347        try {
348            desiredAttrs = verifyDesiredAttributes(aad.getAttribute(),
349                desiredAttrs);
350        } catch (SAML2Exception se) {
351            return SAML2Utils.getErrorResponse(attrQuery,
352                SAML2Constants.REQUESTER, 
353                SAML2Constants.INVALID_ATTR_NAME_OR_VALUE, null, null);
354        }
355
356        List attributes = attrAuthorityMapper.getAttributes(identity,
357            attrQuery, attrAuthorityEntityID, realm);
358
359        if (request.getAttribute("AttributeQueryUtil-storeAllAttributes") != null) {
360            request.setAttribute("AttributeQueryUtil-allAttributes", attributes);
361        }
362
363        attributes = filterAttributes(attributes, desiredAttrs);
364
365        ProtocolFactory protocolFactory = ProtocolFactory.getInstance();
366        Response samlResp = protocolFactory.createResponse();
367        List assertionList = new ArrayList();
368
369        Assertion assertion = null;
370        try {
371            assertion = getAssertion(attrQuery, attrAuthorityEntityID,
372                requesterEntityID, realm, attrQueryProfileAlias, attributes);
373        } catch (SAML2Exception se) {
374            if (SAML2Utils.debug.messageEnabled()) {
375                SAML2Utils.debug.message(
376                    "AttributeQueryUtil.processAttributeQuery:", se);
377            }
378            return SAML2Utils.getErrorResponse(attrQuery,
379                SAML2Constants.RESPONDER, null, se.getMessage(), null);
380        }
381
382        EncryptedID encryptedID = attrQuery.getSubject().getEncryptedID();
383        if (encryptedID != null) {
384            EncryptedAssertion encryptedAssertion = null;
385            try {
386                signAssertion(assertion, realm, attrAuthorityEntityID, false);
387                encryptedAssertion = encryptAssertion(assertion,
388                        encryptedID, attrAuthorityEntityID, requesterEntityID,
389                        realm, attrQueryProfileAlias);
390            } catch (SAML2Exception se) {
391                if (SAML2Utils.debug.messageEnabled()) {
392                        SAML2Utils.debug.message(
393                            "AttributeQueryUtil.processAttributeQuery:", se);
394                }
395                return SAML2Utils.getErrorResponse(attrQuery,
396                        SAML2Constants.RESPONDER, null, se.getMessage(), null);
397            }
398            assertionList.add(encryptedAssertion);        
399            samlResp.setEncryptedAssertion(assertionList);
400        } else {
401            assertionList.add(assertion);        
402            samlResp.setAssertion(assertionList);
403        }
404
405        samlResp.setID(SAML2Utils.generateID());
406        samlResp.setInResponseTo(attrQuery.getID());
407
408        samlResp.setVersion(SAML2Constants.VERSION_2_0);
409        samlResp.setIssueInstant(new Date());
410    
411        Status status = protocolFactory.createStatus();
412        StatusCode statusCode = protocolFactory.createStatusCode();
413        statusCode.setValue(SAML2Constants.SUCCESS);
414        status.setStatusCode(statusCode);
415        samlResp.setStatus(status);
416
417        Issuer respIssuer = AssertionFactory.getInstance().createIssuer();
418        respIssuer.setValue(attrAuthorityEntityID);
419        samlResp.setIssuer(respIssuer);
420
421        signResponse(samlResp, attrAuthorityEntityID, realm, false);
422
423        return samlResp;
424    }
425
426    /**
427     * Converts attribute query profile alias to attribute query profile.
428     *
429     * @param attrQueryProfileAlias attribute query profile alias
430     *
431     * @return attribute query profile
432     */
433    public static String getAttributeQueryProfile(
434        String attrQueryProfileAlias) {
435
436        if (attrQueryProfileAlias == null) {
437            return null;
438        } else if (attrQueryProfileAlias.equals(
439            SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE_ALIAS)) {
440            return  SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE;
441        } else if (attrQueryProfileAlias.equals(
442            SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE_ALIAS)) {
443            return  SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE;
444        }
445
446        return null;
447    }
448
449    private static void signAttributeQuery(AttributeQuery attrQuery,
450        String realm, boolean includeCert) throws SAML2Exception {
451        String requesterEntityID = attrQuery.getIssuer().getValue();
452        
453        String alias = SAML2Utils.getSigningCertAlias(realm, requesterEntityID,
454            SAML2Constants.ATTR_QUERY_ROLE);
455
456        PrivateKey signingKey = keyProvider.getPrivateKey(alias);
457        if (signingKey == null) {
458            throw new SAML2Exception(
459                SAML2Utils.bundle.getString("missingSigningCertAlias"));
460        }
461
462        X509Certificate signingCert = null;
463        if (includeCert) {
464            signingCert = keyProvider.getX509Certificate(alias);
465        }
466        
467        if (signingKey != null) {
468            attrQuery.sign(signingKey, signingCert);
469        }
470    }
471
472    public static void validateEntityRequester(AttributeQuery attrQuery,
473        String attrAuthorityEntityID, String realm) throws SAML2Exception {
474
475        Issuer issuer = attrQuery.getIssuer();
476        String format = issuer.getFormat();
477        if ((format == null) || (format.length() == 0) ||
478            (format.equals(SAML2Constants.UNSPECIFIED)) ||
479            (format.equals(SAML2Constants.ENTITY))) {
480
481            String requestedEntityID = issuer.getValue();
482
483            if (!SAML2Utils.isSourceSiteValid(issuer, realm,
484                attrAuthorityEntityID)) {
485                throw new SAML2Exception(SAML2Utils.bundle.getString(
486                    "attrQueryIssuerInvalid"));
487            }
488        } else {
489            throw new SAML2Exception(SAML2Utils.bundle.getString(
490                "attrQueryIssuerInvalid"));
491        }
492    }
493
494    /**
495     * Checks if the attribute query signature is valid.
496     *
497     * @param attrQuery attribute query
498     * @param attrAuthorityEntityID entity ID of attribute authority
499     * @param realm the realm of hosted entity
500     *
501     * @exception SAML2Exception if the attribute query signature is not valid.
502     */
503    public static void verifyAttrQuerySignature(AttributeQuery attrQuery,
504        String attrAuthorityEntityID, String realm)
505        throws SAML2Exception {
506
507        if (!attrQuery.isSigned()) {
508            throw new SAML2Exception(SAML2Utils.bundle.getString(
509                "attrQueryNotSigned"));
510        }
511
512        String requestedEntityID = attrQuery.getIssuer().getValue();
513
514        AttributeQueryDescriptorElement attrqDesc =
515            metaManager.getAttributeQueryDescriptor(realm, requestedEntityID);
516        if (attrqDesc == null) {
517            throw new SAML2Exception(SAML2Utils.bundle.getString(
518                "attrQueryIssuerNotFound"));
519        }
520        X509Certificate signingCert =
521            KeyUtil.getVerificationCert(attrqDesc, requestedEntityID,
522            SAML2Constants.ATTR_QUERY_ROLE);
523
524        if (signingCert != null) {
525            boolean valid = attrQuery.isSignatureValid(signingCert);
526            if (SAML2Utils.debug.messageEnabled()) {
527                SAML2Utils.debug.message(
528                    "AttributeQueryUtil.verifyAttributeQuery: " +
529                    "Signature validity is : " + valid);
530            }
531            if (!valid) {
532                throw new SAML2Exception(SAML2Utils.bundle.getString(
533                    "invalidSignatureAttrQuery"));
534            }
535        } else {
536            throw new SAML2Exception(
537                SAML2Utils.bundle.getString("missingSigningCertAlias"));
538        }
539    }
540
541    public static String getIdentityFromDataStoreX509Subject(
542        AttributeQuery attrQuery, String attrAuthorityEntityID, String realm)
543        throws SAML2Exception {
544
545        Subject subject = attrQuery.getSubject();
546        NameID nameID = null;
547        EncryptedID encryptedID = subject.getEncryptedID();
548
549        if (encryptedID != null) {
550            String alias = SAML2Utils.getEncryptionCertAlias(realm, 
551                attrAuthorityEntityID, SAML2Constants.ATTR_AUTH_ROLE); 
552            PrivateKey privateKey = keyProvider.getPrivateKey(alias);
553            
554            nameID = encryptedID.decrypt(privateKey);
555        } else {
556            nameID = subject.getNameID();
557        }
558
559        if (!SAML2Constants.X509_SUBJECT_NAME.equals(nameID.getFormat())) {
560            throw new SAML2Exception(SAML2Utils.bundle.getString(
561                "unsupportedAttrQuerySubjectNameID"));
562        }
563
564        String mappingAttrName = getAttributeValueFromAttrAuthorityConfig(
565            realm, attrAuthorityEntityID,
566            SAML2Constants.X509_SUBJECT_DATA_STORE_ATTR_NAME);
567
568        if ((mappingAttrName == null) || (mappingAttrName.length() == 0)) {
569            throw new SAML2Exception(SAML2Utils.bundle.getString(
570                "x509SubjectMappingNotConfigured"));
571        }
572
573        String x509SubjectDN = nameID.getValue();
574        Map attrMap = new HashMap();
575        Set values = new HashSet();
576        values.add(x509SubjectDN);
577        attrMap.put(mappingAttrName, values);
578
579        if (SAML2Utils.debug.messageEnabled()) {
580            SAML2Utils.debug.message(
581                "AttributeQueryUtil.getIdentityFromDataStoreX509Subject: " +
582                "mappingAttrName = " + mappingAttrName +
583                ", X509 subject DN = " + x509SubjectDN);
584        }
585
586        try {
587            return dsProvider.getUserID(realm, attrMap);
588        } catch (DataStoreProviderException dse) {
589            SAML2Utils.debug.error(
590                "AttributeQueryUtil.getIdentityFromDataStoreX509Subject:",dse);
591            throw new SAML2Exception(dse.getMessage());
592        }
593    }
594
595    public static String getIdentity(AttributeQuery attrQuery,
596        String attrAuthorityEntityID, String realm) throws SAML2Exception {
597
598        Subject subject = attrQuery.getSubject();
599        NameID nameID = null;
600        EncryptedID encryptedID = subject.getEncryptedID();
601
602        if (encryptedID != null) {
603            String alias = SAML2Utils.getEncryptionCertAlias(realm, 
604                attrAuthorityEntityID, SAML2Constants.ATTR_AUTH_ROLE); 
605            PrivateKey privateKey = keyProvider.getPrivateKey(alias);
606            
607            nameID = encryptedID.decrypt(privateKey);
608        } else {
609            nameID = subject.getNameID();
610        }
611
612        String nameIDFormat = nameID.getFormat();
613        // NameIDFormat is "transient"
614        if (SAML2Constants.NAMEID_TRANSIENT_FORMAT.equals(nameIDFormat)) {
615            return (String)IDPCache.userIDByTransientNameIDValue.get(
616                nameID.getValue());
617        } else  
618          // NameIDFormat is "unspecified"
619          if (SAML2Constants.UNSPECIFIED.equals(nameIDFormat)) {
620            Map userIDsSearchMap = new HashMap();
621            Set userIDValuesSet = new HashSet();
622            userIDValuesSet.add(nameID.getValue());
623            String userId = "uid";
624
625            IDPSSOConfigElement config = SAML2Utils.getSAML2MetaManager().getIDPSSOConfig(
626                    realm, attrAuthorityEntityID);
627            Map attrs = SAML2MetaUtils.getAttributes(config);
628
629            List nimAttrs = (List)attrs.get(SAML2Constants.NAME_ID_FORMAT_MAP);
630
631
632            for (Iterator i = nimAttrs.iterator(); i.hasNext(); ) {
633                String attrName = (String)i.next();
634                if (attrName != null && attrName.length()>2 && attrName.startsWith(nameIDFormat)) {
635                    int eqPos = attrName.indexOf('=');
636                    if (eqPos != -1 && eqPos<attrName.length()-2) {
637                        userId = attrName.substring(eqPos+1);
638                        SAML2Utils.debug.message("AttributeQueryUtil.getIdentity: NameID attribute from map: " + userId);
639                        break;
640                    }
641                }
642            }
643            userIDsSearchMap.put(userId, userIDValuesSet);
644            try {
645                return dsProvider.getUserID(realm, userIDsSearchMap);
646            } catch (DataStoreProviderException dse) {
647                SAML2Utils.debug.error(
648                    "AttributeQueryUtil.getIdentityFromDataStore1:", dse);
649                throw new SAML2Exception(dse.getMessage());
650            }
651        } else {
652            String requestedEntityID = attrQuery.getIssuer().getValue();
653
654            try {
655                return dsProvider.getUserID(realm, SAML2Utils.getNameIDKeyMap(
656                    nameID, attrAuthorityEntityID, requestedEntityID, realm,
657                    SAML2Constants.IDP_ROLE));
658            } catch (DataStoreProviderException dse) {
659                SAML2Utils.debug.error(
660                    "AttributeQueryUtil.getIdentityFromDataStore:", dse);
661                throw new SAML2Exception(dse.getMessage());
662            }
663        }
664    }
665
666    public static List getUserAttributes(String userId,
667        AttributeQuery attrQuery, String attrAuthorityEntityID, String realm)
668        throws SAML2Exception {
669 
670        String requestedEntityID = attrQuery.getIssuer().getValue();
671
672        Map configMap = SAML2Utils.getConfigAttributeMap(realm,
673            requestedEntityID, SAML2Constants.SP_ROLE);
674        if (SAML2Utils.debug.messageEnabled()) {
675            SAML2Utils.debug.message(
676                "AttributeQueryUtil.getUserAttributes: " +
677                "remote SP attribute map = " + configMap);
678        }
679        if (configMap == null || configMap.isEmpty()) {
680            configMap = SAML2Utils.getConfigAttributeMap(realm,
681                attrAuthorityEntityID, SAML2Constants.IDP_ROLE);
682            if (configMap == null || configMap.isEmpty()) {
683                if (SAML2Utils.debug.messageEnabled()) {
684                    SAML2Utils.debug.message(
685                        "AttributeQueryUtil.getUserAttributes:" +
686                        "Configuration map is not defined.");
687                }
688                return null;
689            }
690            if (SAML2Utils.debug.messageEnabled()) {
691                SAML2Utils.debug.message(
692                    "AttributeQueryUtil.getUserAttributes: " +
693                    "hosted IDP attribute map=" + configMap);
694            }
695        }
696
697        List attributes = new ArrayList();
698
699        Set localAttributes = new HashSet();
700        localAttributes.addAll(configMap.values());
701        Map valueMap = null;
702
703        try {
704            valueMap = dsProvider.getAttributes(userId, localAttributes);
705        } catch (DataStoreProviderException dse) {
706            if (SAML2Utils.debug.warningEnabled()) {
707                SAML2Utils.debug.warning(
708                    "AttributeQueryUtil.getUserAttributes:", dse);
709            }
710        }
711
712        Iterator iter = configMap.keySet().iterator();
713        while(iter.hasNext()) {
714            String samlAttribute = (String)iter.next();
715            String localAttribute = (String)configMap.get(samlAttribute);
716            String[] localAttributeValues = null;
717            if ((valueMap != null) && (!valueMap.isEmpty())) {
718                Set values = (Set)valueMap.get(localAttribute);
719                if ((values == null) || values.isEmpty()) {
720                    if (SAML2Utils.debug.messageEnabled()) {
721                        SAML2Utils.debug.message(
722                            "AttributeQueryUtil.getUserAttributes:" +
723                            " user profile does not have value for " +
724                            localAttribute);
725                    }
726                } else {
727                    localAttributeValues = (String[])
728                        values.toArray(new String[values.size()]);
729                }
730            }
731
732            if ((localAttributeValues == null) ||
733                (localAttributeValues.length == 0)) {
734                if (SAML2Utils.debug.messageEnabled()) {
735                    SAML2Utils.debug.message(
736                        "AttributeQueryUtil.getUserAttributes:" +
737                        " user does not have " + localAttribute);
738                }
739                continue;
740            }
741
742            Attribute attr = SAML2Utils.getSAMLAttribute(samlAttribute,
743                localAttributeValues);
744            attributes.add(attr);
745        }
746        return attributes;
747    }
748
749    public static void signResponse(Response response,
750        String attrAuthorityEntityID, String realm, boolean includeCert)
751        throws SAML2Exception {
752        
753        String alias = SAML2Utils.getSigningCertAlias(realm,
754            attrAuthorityEntityID, SAML2Constants.ATTR_AUTH_ROLE);
755
756        PrivateKey signingKey = keyProvider.getPrivateKey(alias);
757        if (signingKey == null) {
758            throw new SAML2Exception(
759                SAML2Utils.bundle.getString("missingSigningCertAlias"));
760        }
761
762        X509Certificate signingCert = null;
763        if (includeCert) {
764            signingCert = keyProvider.getX509Certificate(alias);
765        }
766        
767        if (signingKey != null) {
768            response.sign(signingKey, signingCert);
769        }
770    }
771
772    private static Assertion getAssertion(AttributeQuery attrQuery,
773        String attrAuthorityEntityID, String requesterEntityID, String realm,
774        String attrQueryProfileAlias, List attributes) throws SAML2Exception {
775
776        AssertionFactory assertionFactory = AssertionFactory.getInstance();
777        Assertion assertion = assertionFactory.createAssertion();
778        assertion.setID(SAML2Utils.generateID());    
779        assertion.setVersion(SAML2Constants.VERSION_2_0);
780        assertion.setIssueInstant(new Date());
781        Issuer issuer = assertionFactory.createIssuer();
782        issuer.setValue(attrAuthorityEntityID);
783        assertion.setIssuer(issuer);
784
785        Subject subjectQ = attrQuery.getSubject();
786        Subject subject = assertionFactory.createSubject();
787        subject.setEncryptedID(subjectQ.getEncryptedID());
788        subject.setNameID(subjectQ.getNameID());
789        subject.setBaseID(subjectQ.getBaseID());
790        subject.setSubjectConfirmation(subjectQ.getSubjectConfirmation());
791        assertion.setSubject(subject);
792
793        if ((attributes != null) && (!attributes.isEmpty())) {
794            AttributeStatement attrStatement =
795                assertionFactory.createAttributeStatement();
796
797            attrStatement.setAttribute(attributes);
798            List attrStatementList = new ArrayList();
799            attrStatementList.add(attrStatement);
800            assertion.setAttributeStatements(attrStatementList); 
801        }
802
803        int effectiveTime = IDPSSOUtil.getEffectiveTime(realm,
804            attrAuthorityEntityID);
805        int notBeforeSkewTime = IDPSSOUtil.getNotBeforeSkewTime(realm,
806            attrAuthorityEntityID);
807        Conditions conditions = IDPSSOUtil.getConditions(requesterEntityID, 
808            notBeforeSkewTime, effectiveTime);
809        assertion.setConditions(conditions);
810
811        return assertion;
812    }
813
814    private static void signAssertion(Assertion assertion, String realm,
815        String attrAuthorityEntityID, boolean includeCert)
816        throws SAML2Exception {
817
818        String alias = SAML2Utils.getSigningCertAlias(realm,
819            attrAuthorityEntityID, SAML2Constants.ATTR_AUTH_ROLE);
820
821        PrivateKey signingKey = keyProvider.getPrivateKey(alias);
822        X509Certificate signingCert = null;
823        if (includeCert) {
824            signingCert = keyProvider.getX509Certificate(alias);
825        }
826        
827        if (signingKey != null) {
828            assertion.sign(signingKey, signingCert);
829        }
830    }
831
832    private static EncryptedAssertion encryptAssertion(Assertion assertion,
833        EncryptedID encryptedID, String attrAuthorityEntityID,
834        String requesterEntityID, String realm, String attrQueryProfileAlias)
835        throws SAML2Exception {
836
837        String alias = SAML2Utils.getEncryptionCertAlias(realm, 
838            attrAuthorityEntityID, SAML2Constants.ATTR_AUTH_ROLE); 
839        PrivateKey privateKey = keyProvider.getPrivateKey(alias);
840
841        SecretKey secretKey = EncManager.getEncInstance().getSecretKey(
842            encryptedID.toXMLString(true, true), privateKey);
843
844        AttributeQueryDescriptorElement aqd =
845            metaManager.getAttributeQueryDescriptor(realm, requesterEntityID);
846        EncInfo encInfo = KeyUtil.getEncInfo(aqd, requesterEntityID,
847            SAML2Constants.ATTR_QUERY_ROLE);
848
849        Element el = EncManager.getEncInstance().encrypt(
850            assertion.toXMLString(true, true), encInfo.getWrappingKey(),
851            secretKey, encInfo.getDataEncAlgorithm(),
852            encInfo.getDataEncStrength(), requesterEntityID,
853            "EncryptedAssertion");
854
855        return AssertionFactory.getInstance().createEncryptedAssertion(el);
856    }
857
858    private static List verifyDesiredAttributes(List supportedAttrs,
859        List desiredAttrs) throws SAML2Exception {
860
861        if ((supportedAttrs == null) || (supportedAttrs.isEmpty())) {
862            return desiredAttrs;
863        }
864
865        if ((desiredAttrs == null) || (desiredAttrs.isEmpty())) {
866            return convertAttributes(supportedAttrs);
867        }
868
869        for(Iterator iterD = desiredAttrs.iterator(); iterD.hasNext(); ) {
870            Attribute attrD = (Attribute)iterD.next();
871            boolean isAttrValid = false;
872            Iterator iterS = supportedAttrs.iterator();
873            while(iterS.hasNext()) {
874                AttributeElement attrS = (AttributeElement)iterS.next();
875                if (isSameAttribute(attrD, attrS)) {
876                    if (isValueValid(attrD, attrS)) {
877                        isAttrValid = true;
878                        break;
879                    } else {
880                        throw new SAML2Exception(
881                            "Attribute value not suppoted");
882                    }
883                }
884            }
885            if (isAttrValid) {
886                iterS.remove();
887            } else {
888                throw new SAML2Exception("Attribute name not suppoted");
889            }
890        }
891        return desiredAttrs;
892    }
893
894    private static List convertAttributes(List jaxbAttrs)
895        throws SAML2Exception {
896
897        List resultAttrs = new ArrayList();
898        for(Iterator iter = jaxbAttrs.iterator(); iter.hasNext(); ) {
899            AttributeElement jaxbAttr = (AttributeElement)iter.next();
900            Attribute attr = AssertionFactory.getInstance().createAttribute();
901            attr.setName(jaxbAttr.getName());
902            attr.setNameFormat(jaxbAttr.getNameFormat());
903            attr.setFriendlyName(jaxbAttr.getFriendlyName());
904
905            List jaxbValues = jaxbAttr.getAttributeValue();
906            if ((jaxbValues != null) && (!jaxbValues.isEmpty())) {
907                List newValues = new ArrayList();
908                for(Iterator iterV = jaxbValues.iterator(); iterV.hasNext();) {
909                    AttributeValueElement jaxbValeu =
910                        (AttributeValueElement)iter.next();
911                    List content = jaxbValeu.getContent();
912                    if ((content != null) && (!content.isEmpty())) {
913                        newValues.add(content.get(0));
914                    }
915                }
916                if (!newValues.isEmpty()) {
917                    attr.setAttributeValueString(newValues);
918                }
919            }
920            resultAttrs.add(attr);
921        }
922        return resultAttrs;
923    }
924
925    private static List filterAttributes(List attributes, List desiredAttrs) {
926
927        if ((attributes == null) || (attributes.isEmpty())) {
928            SAML2Utils.debug.message("AttributeQueryUtil.filterAttributes: attributes are null");
929            return attributes;
930        }
931        if ((desiredAttrs == null) || (desiredAttrs.isEmpty())) {
932            SAML2Utils.debug.message("AttributeQueryUtil.filterAttributes: desired attributes are null");
933            return attributes;
934        }
935
936        List returnAttributes = new ArrayList();
937        if ((desiredAttrs != null) && (!desiredAttrs.isEmpty())) {
938            for(Iterator iterD = desiredAttrs.iterator(); iterD.hasNext();){
939
940                Attribute attrD = (Attribute)iterD.next();
941                for(Iterator iter = attributes.iterator(); iter.hasNext();) {
942
943                    Attribute attr = (Attribute)iter.next();
944                    if (isSameAttribute(attr, attrD) ) {
945                        attr = filterAttributeValues(attr, attrD);
946                        if (attr != null) {
947                            //let's copy FriendlyName if exists
948                            String fName = attrD.getFriendlyName();
949                            if (fName != null && fName.length() > 0){
950                                try {
951                                    attr.setFriendlyName(fName);
952                                } catch (SAML2Exception e) {
953                                    //do nothing, attribute will be sent without
954                                    //friendlyName set
955                                }
956                            }
957                            returnAttributes.add(attr);
958                        }
959                        break;
960                    }
961                }
962            }
963
964        }
965        return returnAttributes;
966
967    }
968
969    private static boolean isSameAttribute(Attribute attr1, Attribute attr2) {
970        if (!attr1.getName().equals(attr2.getName())) {
971            return false;
972        }
973
974        String nameFormat1 = attr1.getNameFormat();
975
976        if (nameFormat1 == null) {
977            return (attr2.getNameFormat() == null);
978        } else {
979            return (nameFormat1.equals(attr2.getNameFormat()));
980        }
981
982    }
983
984    private static Attribute filterAttributeValues(Attribute attr,
985        Attribute desiredAttr) {
986
987        List valuesD = desiredAttr.getAttributeValueString();
988        if ((valuesD == null) || (valuesD.isEmpty())) {
989            return attr;
990        }
991
992        List values = attr.getAttributeValueString();
993        if ((values == null) || (values.isEmpty())) {
994            return null;
995        }
996
997        List newValuesD = new ArrayList();
998        for(Iterator iter = valuesD.iterator(); iter.hasNext(); ) {
999            String valueD = (String)iter.next();
1000            if (values.contains(valueD)) {
1001                newValuesD.add(valueD);
1002            }
1003        }
1004
1005        if (newValuesD.isEmpty()) {
1006            return null;
1007        }
1008
1009        if (newValuesD.size() == valuesD.size()) {
1010            return desiredAttr;
1011        }
1012
1013        try {
1014            Attribute newAttr =
1015                AssertionFactory.getInstance().createAttribute();
1016            newAttr.setName(desiredAttr.getName());
1017            newAttr.setNameFormat(desiredAttr.getNameFormat());
1018            newAttr.setFriendlyName(desiredAttr.getFriendlyName());
1019            newAttr.setAnyAttribute(desiredAttr.getAnyAttribute());
1020            newAttr.setAttributeValueString(newValuesD);
1021
1022            return newAttr;
1023        } catch(SAML2Exception se) {
1024            if (SAML2Utils.debug.messageEnabled()) {
1025                SAML2Utils.debug.message(
1026                    "AttributeQueryUtil.filterAttributeValues:", se);
1027            }
1028            return null;
1029        }
1030    }
1031
1032    private static boolean isSameAttribute(Attribute attr1,
1033        AttributeElement attr2) {
1034
1035        if (!attr1.getName().equals(attr2.getName())) {
1036            return false;
1037        }
1038
1039        String nameFormat1 = attr1.getNameFormat();
1040
1041        if (nameFormat1 == null) {
1042            return (attr2.getNameFormat() == null);
1043        } else {
1044            return (nameFormat1.equals(attr2.getNameFormat()));
1045        }
1046
1047    }
1048
1049    private static boolean isValueValid(Attribute desiredAttr,
1050        AttributeElement supportedAttr) {
1051
1052        List valuesD = desiredAttr.getAttributeValueString();
1053        if ((valuesD == null) || (valuesD.isEmpty())) {
1054            return true;
1055        }
1056        List attrValuesS = supportedAttr.getAttributeValue();
1057        if ((attrValuesS == null) || (attrValuesS.isEmpty())) {
1058            return true;
1059        }
1060
1061        List valuesS = new ArrayList();
1062        for(Iterator iter = attrValuesS.iterator(); iter.hasNext(); ) {
1063            AttributeValueElement attrValueElem =
1064                (AttributeValueElement)iter.next();
1065            valuesS.addAll(attrValueElem.getContent());
1066        }
1067
1068        try {
1069            return valuesS.containsAll(valuesD);
1070        } catch (Exception ex) {
1071            if (SAML2Utils.debug.messageEnabled()) {
1072                SAML2Utils.debug.message(
1073                    "AttributeQueryUtil.isValueValid:", ex);
1074            }
1075            return false;
1076        }
1077    }
1078
1079    private static Response sendAttributeQuerySOAP(AttributeQuery attrQuery,
1080        String attributeServiceURL, String attrAuthorityEntityID,
1081        AttributeAuthorityDescriptorElement aad) throws SAML2Exception {
1082
1083        String attrQueryXMLString = attrQuery.toXMLString(true, true);
1084        if (SAML2Utils.debug.messageEnabled()) {
1085            SAML2Utils.debug.message(
1086                "AttributeQueryUtil.sendAttributeQuerySOAP: " +
1087                "attrQueryXMLString = " + attrQueryXMLString);
1088            SAML2Utils.debug.message(
1089                "AttributeQueryUtil.sendAttributeQuerySOAP: " +
1090                "attributeServiceURL = " + attributeServiceURL);
1091        }
1092        
1093        SOAPMessage resMsg = null;
1094        try {
1095            resMsg = SAML2Utils.sendSOAPMessage(attrQueryXMLString,
1096                attributeServiceURL, true);
1097        } catch (SOAPException se) {
1098            SAML2Utils.debug.error(
1099                "AttributeQueryUtil.sendAttributeQuerySOAP: ", se);
1100            throw new SAML2Exception(
1101                SAML2Utils.bundle.getString("errorSendingAttributeQuery"));
1102        }
1103        
1104        Element respElem = SAML2Utils.getSamlpElement(resMsg, "Response");
1105        Response response =
1106            ProtocolFactory.getInstance().createResponse(respElem);
1107        
1108        if (SAML2Utils.debug.messageEnabled()) {
1109            SAML2Utils.debug.message(
1110                "AttributeQueryUtil.sendAttributeQuerySOAP: " +
1111                "response = " + response.toXMLString(true, true));
1112        }
1113
1114        verifyResponse(response, attrQuery, attrAuthorityEntityID, aad);
1115
1116        return response;
1117    }
1118
1119    private static void verifyResponse(Response response,
1120        AttributeQuery attrQuery, String attrAuthorityEntityID,
1121        AttributeAuthorityDescriptorElement aad)
1122        throws SAML2Exception {
1123
1124        String attrQueryID = attrQuery.getID();
1125        if ((attrQueryID != null) &&
1126            (!attrQueryID.equals(response.getInResponseTo()))) {
1127
1128            throw new SAML2Exception(
1129                SAML2Utils.bundle.getString("invalidInResponseToAttrQuery"));
1130        }
1131
1132        Issuer respIssuer = response.getIssuer();
1133        if (respIssuer == null) {
1134            return;
1135        }
1136
1137        if (!attrAuthorityEntityID.equals(respIssuer.getValue())) {
1138            throw new SAML2Exception(SAML2Utils.bundle.getString(
1139                "responseIssuerMismatch"));
1140        }
1141
1142        if (!response.isSigned()) {
1143            throw new SAML2Exception(SAML2Utils.bundle.getString(
1144                "responseNotSigned"));
1145        }
1146
1147        X509Certificate signingCert =
1148            KeyUtil.getVerificationCert(aad, attrAuthorityEntityID,
1149                SAML2Constants.ATTR_AUTH_ROLE);
1150
1151        if (signingCert != null) {
1152            boolean valid = response.isSignatureValid(signingCert);
1153            if (SAML2Utils.debug.messageEnabled()) {
1154                SAML2Utils.debug.message(
1155                    "AttributeQueryUtil.verifyResponse: " +
1156                    "Signature validity is : " + valid);
1157            }
1158            if (!valid) {
1159                throw new SAML2Exception(SAML2Utils.bundle.getString(
1160                    "invalidSignatureOnResponse"));
1161            }
1162        } else {
1163            throw new SAML2Exception(
1164                    SAML2Utils.bundle.getString("missingSigningCertAlias"));
1165        }
1166
1167    }
1168
1169    private static String findLocation(
1170        AttributeAuthorityDescriptorElement aad, String binding,
1171        String attrQueryProfile, String attrProfile) {
1172        SAML2Utils.debug.message("AttributeQueryUtil.findLocation entering...");
1173        List attrProfiles = aad.getAttributeProfile();
1174        if ((attrProfiles == null) || (attrProfiles.isEmpty())) {
1175            SAML2Utils.debug.message("AttributeQueryUtil.findLocation: attrProfiles is null or empty");
1176            if (attrProfile != null) {
1177                SAML2Utils.debug.message("AttributeQueryUtil.findLocation: attrProfiles is null or empty and attrProfile is null");
1178                return null;
1179            }
1180        } else if (!attrProfiles.contains(attrProfile)) {
1181            SAML2Utils.debug.message("AttributeQueryUtil.findLocation: attrProfile not found in the attrProfiles");
1182            return null;
1183        }
1184        SAML2Utils.debug.message("AttributeQueryUtil.findLocation: entering...");
1185
1186        List attrServices = aad.getAttributeService();
1187        for(Iterator iter = attrServices.iterator(); iter.hasNext(); ) {
1188            AttributeServiceElement attrService =
1189                (AttributeServiceElement)iter.next();
1190            if (isValidAttributeService(binding, attrService,
1191                attrQueryProfile)) {
1192                SAML2Utils.debug.message("AttributeQueryUtil.findLocation: found valid service");
1193                return attrService.getLocation();
1194            }
1195        }
1196        SAML2Utils.debug.message("AttributeQueryUtil.findLocation: nothing found, leaving last line with null");
1197
1198        return null;
1199    }
1200
1201    private static boolean isValidAttributeService(String binding,
1202        AttributeServiceElement attrService, String attrQueryProfile) {
1203    
1204        if (!binding.equalsIgnoreCase(attrService.getBinding())) {
1205            return false;
1206        }
1207
1208        if (attrQueryProfile == null) {
1209            return false;
1210        }
1211
1212        return ((attrQueryProfile.equals(
1213            SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE)) ||
1214            (SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE.equals(
1215            attrQueryProfile) && attrService.isSupportsX509Query()));
1216    }
1217
1218    /** 
1219     * Returns an <code>AttributeAuthorityMapper</code>
1220     *
1221     * @param realm the realm name
1222     * @param attrAuthorityEntityID the entity id of the attribute authority
1223     * @param attrQueryProfileAlias attribute profile alias
1224     *
1225     * @return the <code>AttributeAuthorityMapper</code>
1226     * @exception SAML2Exception if the operation is not successful
1227     */
1228    static AttributeAuthorityMapper getAttributeAuthorityMapper(String realm,
1229        String attrAuthorityEntityID, String attrQueryProfileAlias)
1230        throws SAML2Exception {
1231
1232        String attrAuthorityMapperName = null;
1233        AttributeAuthorityMapper attrAuthorityMapper = null;
1234        try {
1235            attrAuthorityMapperName = getAttributeValueFromAttrAuthorityConfig(
1236                realm, attrAuthorityEntityID, attrQueryProfileAlias + "_" +
1237                SAML2Constants.ATTRIBUTE_AUTHORITY_MAPPER);
1238
1239            if (attrAuthorityMapperName == null) {
1240                attrAuthorityMapperName = 
1241                    SAML2Constants.DEFAULT_ATTRIBUTE_AUTHORITY_MAPPER_CLASS;
1242                if (SAML2Utils.debug.messageEnabled()) {
1243                    SAML2Utils.debug.message(
1244                        "AttributeQueryUtil.getAttributeAuthorityMapper: use "+
1245                        attrAuthorityMapperName);
1246                }
1247            }
1248            attrAuthorityMapper = (AttributeAuthorityMapper)
1249                attrAuthorityMapperCache.get(attrAuthorityMapperName);
1250            if (attrAuthorityMapper == null) {
1251                attrAuthorityMapper = (AttributeAuthorityMapper)
1252                    Class.forName(attrAuthorityMapperName).newInstance();
1253                attrAuthorityMapperCache.put(attrAuthorityMapperName,
1254                       attrAuthorityMapper);
1255            } else {
1256                if (SAML2Utils.debug.messageEnabled()) {
1257                    SAML2Utils.debug.message(
1258                        "AttributeQueryUtil.getAttributeAuthorityMapper: " +
1259                        "got the AttributeAuthorityMapper from cache");
1260                }
1261            }
1262        } catch (Exception ex) {
1263            SAML2Utils.debug.error(
1264                "AttributeQueryUtil.getAttributeAuthorityMapper: " +
1265                "Unable to get IDP Attribute Mapper.", ex);
1266            throw new SAML2Exception(ex);
1267        }
1268
1269        return attrAuthorityMapper;
1270    }
1271
1272    private static String getAttributeValueFromAttrAuthorityConfig(
1273        String realm, String attrAuthorityEntityID, String attrName)
1274    {
1275        try {
1276            AttributeAuthorityConfigElement config =
1277                metaManager.getAttributeAuthorityConfig(realm,
1278                attrAuthorityEntityID);
1279            Map attrs = SAML2MetaUtils.getAttributes(config);
1280            String value = null;
1281            List values = (List) attrs.get(attrName);
1282            if ((values != null) && (!values.isEmpty())) {
1283                value = ((String)values.iterator().next()).trim();
1284            }
1285            return value;
1286        } catch (SAML2MetaException sme) {
1287            if (SAML2Utils.debug.messageEnabled()) {
1288                SAML2Utils.debug.message("AttributeQueryUtil." +
1289                   "getAttributeValueFromAttrAuthorityConfig: " +
1290                   "get AttributeAuthorityConfig failed", sme);
1291            }
1292        }
1293        return null;
1294    }
1295
1296    /**
1297     * Sends the AttributeQuery to specified attribute authority,
1298     * validates the response and returns the attribute map
1299     * <code>Map&lt;String, String&gt;</code> to the Fedlet
1300     *
1301     * @param spEntityID SP entity ID
1302     * @param idpEntityID IDP entity ID
1303     * @param nameIDValue  NameID value 
1304     * @param attrsList The list of attributes whose values need to be
1305     *                  fetched from IDP
1306     * @param attrQueryProfileAlias  Attribute Query Profile Alias
1307     * @param subjectDN  Attribute name which contains X.509 subject DN
1308     *
1309     * @return the <code>Map</code> object
1310     * @exception SAML2Exception if the operation is not successful
1311     *
1312     * @deprecated Use #getAttributesForFedlet(String, String, String, List, String, String)
1313     */
1314    public static Map<String, String> getAttributeMapForFedlet(String spEntityID,
1315                                               String idpEntityID,
1316                                               String nameIDValue,
1317                                               List attrsList,
1318                                               String attrQueryProfileAlias,
1319                                               String subjectDN)
1320                                               throws SAML2Exception {
1321        Map<String, Set<String>> attrMap = getAttributesForFedlet(spEntityID,
1322                        idpEntityID,
1323                        nameIDValue,
1324                        attrsList,
1325                        attrQueryProfileAlias,
1326                        subjectDN);
1327        
1328        Map<String, String> newAttrMap = new HashMap<String, String>(); 
1329        for (Map.Entry<String, Set<String>> entry : attrMap.entrySet()) {
1330                String attrName = entry.getKey();
1331                Set<String> attrValue = entry.getValue();
1332                StringBuilder pipedValue = new StringBuilder();
1333                for(String value : attrValue) {
1334                        // Multiple attribute values
1335                        // are seperated with "|"
1336                        if (pipedValue.length() > 0 ) {
1337                                pipedValue.append('|');
1338                        }
1339                        pipedValue.append(value);
1340                }
1341                newAttrMap.put(attrName, pipedValue.toString());
1342                
1343        }
1344        
1345        return newAttrMap;
1346    }
1347    
1348    /**
1349     * Sends the AttributeQuery to specified attribute authority,
1350     * validates the response and returns the attribute map
1351     * <code>Map&lt;String, Set&lt;String&gt;&gt;</code> to the Fedlet
1352     *
1353     * @param spEntityID SP entity ID
1354     * @param idpEntityID IDP entity ID
1355     * @param nameIDValue  NameID value 
1356     * @param attrsList The list of attributes whose values need to be
1357     *                  fetched from IDP
1358     * @param attrQueryProfileAlias  Attribute Query Profile Alias
1359     * @param subjectDN  Attribute name which contains X.509 subject DN
1360     *
1361     * @return the <code>Map</code> object
1362     * @exception SAML2Exception if the operation is not successful
1363     *
1364     * @supported.api
1365     */
1366    public static Map<String, Set<String>> getAttributesForFedlet(String spEntityID,
1367                                               String idpEntityID,
1368                                               String nameIDValue,
1369                                               List attrsList,
1370                                               String attrQueryProfileAlias,
1371                                               String subjectDN)
1372                                               throws SAML2Exception {
1373
1374        AttributeQueryConfigElement attrQueryConfig =
1375                metaManager.getAttributeQueryConfig("/", spEntityID);
1376        if (attrQueryConfig == null) {
1377            if (SAML2Utils.debug.messageEnabled()) {
1378                SAML2Utils.debug.message("AttributeQueryUtil." +
1379                                          "getAttributeMapForFedlet: " +
1380                                          "Attribute Query Config is null");
1381             }
1382            return null;
1383        }
1384
1385        String attrqMetaAlias = attrQueryConfig.getMetaAlias();
1386        if (attrqMetaAlias == null) {
1387            if (SAML2Utils.debug.messageEnabled()) {
1388                SAML2Utils.debug.message("AttributeQueryUtil." +
1389                                       "getAttributeMapForFedlet: " +
1390                                       "Attribute Query MetaAlias is null");
1391             }
1392            return null;
1393        }
1394        
1395        boolean wantNameIDEncrypted = SAML2Utils.getWantNameIDEncrypted("/",
1396                                              spEntityID,
1397                                              SAML2Constants.ATTR_QUERY_ROLE);
1398        
1399        AttributeQuery attrQuery = constructAttrQueryForFedlet(spEntityID,
1400                                                          idpEntityID,
1401                                                          nameIDValue,
1402                                                          attrsList,
1403                                                          attrqMetaAlias,
1404                                                          attrQueryProfileAlias,
1405                                                          subjectDN,
1406                                                          wantNameIDEncrypted);
1407        
1408        String attrQueryProfile = null;
1409        if (attrQueryProfileAlias.equals(
1410                SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE_ALIAS)) {
1411            attrQueryProfile = SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE;
1412        } else if (attrQueryProfileAlias.equals(
1413                SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE_ALIAS)) {
1414            attrQueryProfile = SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE;
1415        }
1416
1417        Response samlResp = sendAttributeQuery(attrQuery, idpEntityID,
1418                                  "/",
1419                                  attrQueryProfile,
1420                                  SAML2Constants.BASIC_ATTRIBUTE_PROFILE,
1421                                  SAML2Constants.SOAP);
1422
1423        // Validate the response
1424        boolean validResp = validateSAMLResponseForFedlet(samlResp, 
1425                                                          spEntityID,
1426                                                          wantNameIDEncrypted);
1427        Map<String, Set<String>> attrMap = new HashMap();
1428        if (validResp) {
1429            // Return back the AttributeMap
1430            if (samlResp != null) {
1431                List assertions = null;
1432                if (wantNameIDEncrypted) {
1433                    assertions = samlResp.getEncryptedAssertion();
1434                } else {
1435                    assertions = samlResp.getAssertion();
1436                }
1437                for (Iterator asserIter = assertions.iterator();
1438                    asserIter.hasNext();) {
1439                    Assertion assertion = null;
1440                    if (wantNameIDEncrypted) {
1441                        assertion = getDecryptedAssertion(
1442                                           (EncryptedAssertion)asserIter.next(),
1443                                            spEntityID);
1444                    } else {
1445                        assertion = (Assertion)asserIter.next();
1446                    }
1447                    if (assertion != null) {
1448                        List statements = assertion.getAttributeStatements();
1449                        if (statements != null && statements.size() > 0 ) {
1450                            for (Iterator stmtIter = statements.iterator();
1451                                stmtIter.hasNext();) {
1452                                AttributeStatement statement =
1453                                    (AttributeStatement)stmtIter.next();
1454                                List attributes = statement.getAttribute();
1455                                if (attributes != null) {
1456                                    for (Iterator attribIter =
1457                                        attributes.iterator();
1458                                        attribIter.hasNext(); ) {
1459                                        Attribute attr =
1460                                            (Attribute)attribIter.next();
1461                                        String attrName = attr.getName();
1462                                        List attrValueList =
1463                                            attr.getAttributeValueString();
1464                                        Set<String> attrValue = new HashSet(); 
1465                                        for (Iterator attrValueIter =
1466                                             attrValueList.iterator();
1467                                             attrValueIter.hasNext(); ) {
1468                                            String value =
1469                                                (String)attrValueIter.next();
1470                                            attrValue.add(value);
1471                                        }
1472                                        attrMap.put(attrName,attrValue);
1473                                    }
1474                                } else {
1475                                    if (SAML2Utils.debug.messageEnabled()) {
1476                                        SAML2Utils.debug.message(
1477                                            "AttributeQueryUtil." +
1478                                            "getAttributeMapForFedlet: " +
1479                                            "No Attributes present in " +
1480                                            "SAML response");
1481                                    }
1482                                }
1483                            }
1484                        } else {
1485                            if (SAML2Utils.debug.messageEnabled()) {
1486                                SAML2Utils.debug.message("AttributeQueryUtil." +
1487                                "getAttributeMapForFedlet: " +
1488                                "Empty Statement present in SAML response");
1489                            }
1490                        }
1491                    } else {
1492                        if (SAML2Utils.debug.messageEnabled()) {
1493                            SAML2Utils.debug.message("AttributeQueryUtil." +
1494                            "getAttributeMapForFedlet: " +
1495                            "Empty Assertion present in SAML response");
1496                        }
1497                    }
1498                }
1499            }
1500        } else {
1501            if (SAML2Utils.debug.messageEnabled()) {
1502                SAML2Utils.debug.message("AttributeQueryUtil." +
1503                   "getAttributeMapForFedlet: " +
1504                   "Invalid response obtained from Attribute Authority");
1505            }
1506        }
1507        // Return the attribute map and to the fedlet
1508        return attrMap;
1509    }
1510
1511    /**
1512     * Constructs the Attribute Query used by the Fedlet to retrieve the 
1513     * values from IDP
1514     *
1515     * @param samlResp saml response
1516     *
1517     * @exception SAML2Exception if the operation is not successful
1518     *
1519     * @supported.api
1520     */
1521    private static AttributeQuery constructAttrQueryForFedlet(
1522                             String spEntityID,
1523                             String idpEntityID,
1524                             String nameIDValue,
1525                             List attrsList,
1526                             String attrqMetaAlias,
1527                             String attrProfileNameAlias,
1528                             String subjectDN,
1529                             boolean wantNameIDEncrypted) throws SAML2Exception
1530    {
1531        String attrqEntityID =
1532          SAML2Utils.getSAML2MetaManager().getEntityByMetaAlias(attrqMetaAlias);
1533
1534        ProtocolFactory protocolFactory = ProtocolFactory.getInstance();
1535        AssertionFactory assertionFactory = AssertionFactory.getInstance();        
1536
1537        AttributeQuery attrQuery = protocolFactory.createAttributeQuery();
1538
1539        Issuer issuer = assertionFactory.createIssuer();
1540        issuer.setValue(attrqEntityID);
1541
1542        attrQuery.setIssuer(issuer);
1543        attrQuery.setID(SAML2Utils.generateID());
1544        attrQuery.setVersion(SAML2Constants.VERSION_2_0);
1545        attrQuery.setIssueInstant(new Date());
1546
1547        List attrs = new ArrayList();
1548        for (Iterator attrIter = attrsList.iterator();
1549                                attrIter.hasNext();) {
1550            Attribute attr = assertionFactory.createAttribute();
1551            String attributeName = (String)attrIter.next();
1552            attr.setName(attributeName);
1553            attr.setNameFormat(SAML2Constants.BASIC_NAME_FORMAT);
1554            attrs.add(attr);
1555        }        
1556        attrQuery.setAttributes(attrs);
1557
1558        Subject subject = assertionFactory.createSubject();
1559        NameID nameID = assertionFactory.createNameID();
1560        nameID.setNameQualifier(idpEntityID);
1561        nameID.setSPNameQualifier(spEntityID);
1562
1563        if (attrProfileNameAlias.equals(
1564                    SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE_ALIAS)) {
1565            nameID.setFormat(SAML2Constants.NAMEID_TRANSIENT_FORMAT);
1566            nameID.setValue(nameIDValue);
1567        }
1568
1569        if (attrProfileNameAlias.equals(
1570                    SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE_ALIAS)) {
1571            nameID.setFormat(SAML2Constants.X509_SUBJECT_NAME);
1572            nameID.setValue(subjectDN);            
1573        }
1574
1575        if (!wantNameIDEncrypted) {
1576            subject.setNameID(nameID);
1577        } else {
1578            AttributeAuthorityDescriptorElement aad =
1579                  metaManager.getAttributeAuthorityDescriptor("/", idpEntityID);
1580
1581            EncInfo encInfo = KeyUtil.getEncInfo(aad, idpEntityID,
1582                                                 SAML2Constants.ATTR_AUTH_ROLE);            
1583
1584            EncryptedID encryptedID = nameID.encrypt(encInfo.getWrappingKey(),
1585                                                  encInfo.getDataEncAlgorithm(),
1586                                                  encInfo.getDataEncStrength(),
1587                                                  idpEntityID);
1588            subject.setEncryptedID(encryptedID);
1589        }
1590    
1591        attrQuery.setSubject(subject);
1592
1593        return attrQuery;
1594    }
1595
1596     /**
1597     * Validates the SAML response obtained from Attribute Authortity
1598     *
1599     * @param samlResp saml response
1600     *
1601     * @exception SAML2Exception if the operation is not successful
1602     *
1603     * @supported.api
1604     */
1605    private static boolean validateSAMLResponseForFedlet(
1606                              Response samlResp,
1607                              String spEntityID, 
1608                              boolean wantNameIDEncrypted) throws SAML2Exception
1609    {
1610        boolean resp = true;
1611        if (samlResp != null && samlResp.isSigned()) {
1612            List assertions = null;
1613            if (wantNameIDEncrypted) {
1614                assertions = samlResp.getEncryptedAssertion();
1615            } else {
1616                assertions = samlResp.getAssertion();
1617            }
1618            if (assertions == null) {
1619                return false;
1620            }
1621            for (Iterator asserIter = assertions.iterator();
1622                 asserIter.hasNext();) {
1623                Assertion assertion = null;
1624                if (wantNameIDEncrypted) {
1625                    assertion = getDecryptedAssertion(
1626                                         (EncryptedAssertion)asserIter.next(),
1627                                          spEntityID);
1628                } else {
1629                    assertion = (Assertion)asserIter.next();
1630                }
1631                if (assertion != null) {
1632                    Conditions conditions = assertion.getConditions();
1633                    if (conditions != null) {
1634                        List audienceRes = conditions.
1635                                               getAudienceRestrictions();
1636                        if (audienceRes.size() > 1) {
1637                                resp = false;
1638                                break;
1639                        }
1640                     }
1641                     List statements = assertion.getAttributeStatements();
1642                     if (statements.size() > 1) {
1643                         resp = false;
1644                         break;
1645                     }
1646                }
1647            }
1648        } else {
1649            resp = false;
1650        }
1651        return resp;
1652    }
1653
1654     /**
1655     * Returns the decrypted assertion
1656     *
1657     * @param samlResp saml response
1658     *
1659     * @exception SAML2Exception if the operation is not successful
1660     *
1661     * @supported.api
1662     */
1663    private static Assertion getDecryptedAssertion(
1664                                      EncryptedAssertion eAssertion,
1665                                      String spEntityID) throws SAML2Exception 
1666    {
1667        if (eAssertion != null) {
1668            String alias = SAML2Utils.getEncryptionCertAlias("/", spEntityID,
1669                                      SAML2Constants.ATTR_QUERY_ROLE);
1670            PrivateKey privateKey = 
1671                       KeyUtil.getKeyProviderInstance().getPrivateKey(alias);
1672            if (privateKey != null) {
1673                Assertion assertion = eAssertion.decrypt(privateKey);
1674                return assertion;
1675            }
1676        }        
1677        return null;
1678    }
1679}