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




























































Copyright © 2010-2017, ForgeRock All Rights Reserved.