/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2007 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: AttributeQueryUtil.java,v 1.11 2009/07/24 22:51:48 madan_ranganath Exp $ * * Portions copyright 2010-2016 ForgeRock AS. */ package com.sun.identity.saml2.profile; import static org.forgerock.openam.utils.Time.*; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.security.PrivateKey; import java.security.cert.X509Certificate; import javax.crypto.SecretKey; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPMessage; import com.sun.identity.saml2.common.SOAPCommunicator; import org.w3c.dom.Element; import com.sun.identity.plugin.datastore.DataStoreProviderException; import com.sun.identity.plugin.datastore.DataStoreProvider; import com.sun.identity.saml.xmlsig.KeyProvider; import com.sun.identity.saml2.assertion.Assertion; import com.sun.identity.saml2.assertion.AssertionFactory; import com.sun.identity.saml2.assertion.Attribute; import com.sun.identity.saml2.assertion.AttributeStatement; import com.sun.identity.saml2.assertion.Conditions; import com.sun.identity.saml2.assertion.EncryptedAssertion; import com.sun.identity.saml2.assertion.Issuer; import com.sun.identity.saml2.assertion.NameID; import com.sun.identity.saml2.assertion.EncryptedID; import com.sun.identity.saml2.assertion.Subject; import com.sun.identity.saml2.common.SAML2Constants; import com.sun.identity.saml2.common.SAML2Exception; import com.sun.identity.saml2.common.SAML2Utils; import com.sun.identity.saml2.jaxb.assertion.AttributeElement; import com.sun.identity.saml2.jaxb.assertion.AttributeValueElement; import com.sun.identity.saml2.jaxb.entityconfig.AttributeAuthorityConfigElement; import com.sun.identity.saml2.jaxb.entityconfig.AttributeQueryConfigElement; import com.sun.identity.saml2.jaxb.entityconfig.IDPSSOConfigElement; import com.sun.identity.saml2.jaxb.metadata.AttributeAuthorityDescriptorElement; import com.sun.identity.saml2.jaxb.metadata.AttributeServiceElement; import com.sun.identity.saml2.jaxb.metadataextquery.AttributeQueryDescriptorElement; import com.sun.identity.saml2.key.EncInfo; import com.sun.identity.saml2.key.KeyUtil; import com.sun.identity.saml2.meta.SAML2MetaException; import com.sun.identity.saml2.meta.SAML2MetaManager; import com.sun.identity.saml2.meta.SAML2MetaUtils; import com.sun.identity.saml2.plugins.AttributeAuthorityMapper; import com.sun.identity.saml2.plugins.SPAttributeMapper; import com.sun.identity.saml2.protocol.AttributeQuery; import com.sun.identity.saml2.protocol.ProtocolFactory; import com.sun.identity.saml2.protocol.Response; import com.sun.identity.saml2.protocol.Status; import com.sun.identity.saml2.protocol.StatusCode; import com.sun.identity.saml2.xmlenc.EncManager; /** * This class provides methods to send or process AttributeQuery. * * @supported.api */ public class AttributeQueryUtil { private static final String DEFAULT_ATTRIBUTE_NAME_FORMAT = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"; static KeyProvider keyProvider = KeyUtil.getKeyProviderInstance(); static Hashtable attrAuthorityMapperCache = new Hashtable(); static DataStoreProvider dsProvider = null; static SAML2MetaManager metaManager = SAML2Utils.getSAML2MetaManager(); static { try { dsProvider = SAML2Utils.getDataStoreProvider(); } catch (SAML2Exception se) { SAML2Utils.debug.error("AttributeQueryUtil.static:", se); } } private AttributeQueryUtil() { } /** * Sends the AttributeQuery to specified * attribute authority and returns Response coming * from the attribute authority. * * @param attrQuery the AttributeQuery object * @param attrAuthorityEntityID entity ID of attribute authority * @param realm the realm of hosted entity * @param attrQueryProfile the attribute query profile or null to ignore * @param attrProfile the attribute profile * @param binding the binding * * @return the Response object * @exception SAML2Exception if the operation is not successful * * @supported.api */ public static Response sendAttributeQuery(AttributeQuery attrQuery, String attrAuthorityEntityID, String realm, String attrQueryProfile, String attrProfile, String binding) throws SAML2Exception { AttributeAuthorityDescriptorElement aad = null; try { aad = metaManager.getAttributeAuthorityDescriptor( realm, attrAuthorityEntityID); } catch (SAML2MetaException sme) { SAML2Utils.debug.error("AttributeQueryUtil.sendAttributeQuery:", sme); throw new SAML2Exception( SAML2Utils.bundle.getString("metaDataError")); } if (aad == null) { throw new SAML2Exception( SAML2Utils.bundle.getString("attrAuthorityNotFound")); } if (binding == null) { throw new SAML2Exception( SAML2Utils.bundle.getString("unsupportedBinding")); } String location = findLocation(aad, binding, attrQueryProfile, attrProfile); if (location == null) { throw new SAML2Exception( SAML2Utils.bundle.getString("attrAuthorityNotFound")); } if (binding.equalsIgnoreCase(SAML2Constants.SOAP)) { signAttributeQuery(attrQuery, realm, false); return sendAttributeQuerySOAP(attrQuery, location, attrAuthorityEntityID, aad); } else { throw new SAML2Exception( SAML2Utils.bundle.getString("unsupportedBinding")); } } /** * Sends the AttributeQuery to specified * attribute authority and returns Response coming * from the attribute authority. * * @param attrQuery the AttributeQuery object * @param request the HTTP Request * @param response the HTTP Response * @param attrAuthorityEntityID entity ID of attribute authority * @param realm the realm of hosted entity * @param attrQueryProfile the attribute query profile or null to ignore * @param attrProfile the attribute profile * @param binding the binding * * @exception SAML2Exception if the operation is not successful * * @supported.api */ public static void sendAttributeQuery(AttributeQuery attrQuery, HttpServletRequest request, HttpServletResponse response, String attrAuthorityEntityID, String realm, String attrQueryProfile, String attrProfile, String binding) throws SAML2Exception { AttributeAuthorityDescriptorElement aad = null; try { aad = metaManager.getAttributeAuthorityDescriptor( realm, attrAuthorityEntityID); } catch (SAML2MetaException sme) { SAML2Utils.debug.error("AttributeQueryUtil.sendAttributeQuery:", sme); throw new SAML2Exception( SAML2Utils.bundle.getString("metaDataError")); } if (aad == null) { throw new SAML2Exception( SAML2Utils.bundle.getString("attrAuthorityNotFound")); } if (binding == null) { throw new SAML2Exception( SAML2Utils.bundle.getString("unsupportedBinding")); } String location = findLocation(aad, binding, attrQueryProfile, attrProfile); if (location == null) { throw new SAML2Exception( SAML2Utils.bundle.getString("attrAuthorityNotFound")); } if (binding.equalsIgnoreCase(SAML2Constants.HTTP_POST)) { signAttributeQuery(attrQuery, realm, false); String encodedReqMsg = SAML2Utils.encodeForPOST(attrQuery.toXMLString(true, true)); SAML2Utils.postToTarget(request, response, "SAMLRequest", encodedReqMsg, null, null, location); } else { throw new SAML2Exception( SAML2Utils.bundle.getString("unsupportedBinding")); } } /** * Processes the AttributeQuery coming * from a requester. * * @param attrQuery the AttributeQuery object * @param request the HttpServletRequest object * @param response the HttpServletResponse object * @param attrAuthorityEntityID entity ID of attribute authority * @param realm the realm of hosted entity * @param attrQueryProfileAlias the attribute query profile alias * * @return the Response object * @exception SAML2Exception if the operation is not successful */ public static Response processAttributeQuery(AttributeQuery attrQuery, HttpServletRequest request, HttpServletResponse response, String attrAuthorityEntityID, String realm, String attrQueryProfileAlias) throws SAML2Exception { AttributeAuthorityMapper attrAuthorityMapper = getAttributeAuthorityMapper(realm, attrAuthorityEntityID, attrQueryProfileAlias); String attrQueryProfile = AttributeQueryUtil.getAttributeQueryProfile( attrQueryProfileAlias); try { attrAuthorityMapper.authenticateRequester(request, response, attrQuery, attrAuthorityEntityID, realm); } catch(SAML2Exception se) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message("AttributeQueryUtil." + "processAttributeQuery: ", se); } return SAML2Utils.getErrorResponse(attrQuery, SAML2Constants.REQUESTER, null, se.getMessage(), null); } try { attrAuthorityMapper.validateAttributeQuery(request, response, attrQuery, attrAuthorityEntityID, realm); } catch(SAML2Exception se) { SAML2Utils.debug.error("AttributeQueryUtil.processAttributeQuery:", se); return SAML2Utils.getErrorResponse(attrQuery, SAML2Constants.REQUESTER, null, se.getMessage(), null); } Issuer issuer = attrQuery.getIssuer(); String requesterEntityID = issuer.getValue(); AttributeAuthorityDescriptorElement aad = null; try { aad = metaManager.getAttributeAuthorityDescriptor( realm, attrAuthorityEntityID); } catch (SAML2MetaException sme) { SAML2Utils.debug.error("AttributeQueryUtil.processAttributeQuery:", sme); return SAML2Utils.getErrorResponse(attrQuery, SAML2Constants.RESPONDER, null, SAML2Utils.bundle.getString("metaDataError"), null); } if (aad == null) { return SAML2Utils.getErrorResponse(attrQuery, SAML2Constants.REQUESTER, null, SAML2Utils.bundle.getString("attrAuthorityNotFound"), null); } Object identity = null; try { identity = attrAuthorityMapper.getIdentity(request, response, attrQuery, attrAuthorityEntityID, realm); } catch (SAML2Exception se) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message("AttributeQueryUtil." + "processAttributeQuery: ", se); } return SAML2Utils.getErrorResponse(attrQuery, SAML2Constants.REQUESTER, SAML2Constants.UNKNOWN_PRINCIPAL, se.getMessage(), null); } if (identity == null) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message("AttributeQueryUtil." + "processAttributeQuery: unable to find identity."); } return SAML2Utils.getErrorResponse(attrQuery, SAML2Constants.REQUESTER, SAML2Constants.UNKNOWN_PRINCIPAL, null, null); } // Addition to support changing of desired attributes list List desiredAttrs = (List)request.getAttribute("AttributeQueryUtil-desiredAttrs"); if (desiredAttrs == null) { desiredAttrs = attrQuery.getAttributes(); } try { desiredAttrs = verifyDesiredAttributes(aad.getAttribute(), desiredAttrs); } catch (SAML2Exception se) { return SAML2Utils.getErrorResponse(attrQuery, SAML2Constants.REQUESTER, SAML2Constants.INVALID_ATTR_NAME_OR_VALUE, null, null); } List attributes = attrAuthorityMapper.getAttributes(identity, attrQuery, attrAuthorityEntityID, realm); if (request.getAttribute("AttributeQueryUtil-storeAllAttributes") != null) { request.setAttribute("AttributeQueryUtil-allAttributes", attributes); } attributes = filterAttributes(attributes, desiredAttrs); ProtocolFactory protocolFactory = ProtocolFactory.getInstance(); Response samlResp = protocolFactory.createResponse(); List assertionList = new ArrayList(); Assertion assertion = null; try { assertion = getAssertion(attrQuery, attrAuthorityEntityID, requesterEntityID, realm, attrQueryProfileAlias, attributes); } catch (SAML2Exception se) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.processAttributeQuery:", se); } return SAML2Utils.getErrorResponse(attrQuery, SAML2Constants.RESPONDER, null, se.getMessage(), null); } EncryptedID encryptedID = attrQuery.getSubject().getEncryptedID(); if (encryptedID != null) { EncryptedAssertion encryptedAssertion = null; try { signAssertion(assertion, realm, attrAuthorityEntityID, false); encryptedAssertion = encryptAssertion(assertion, encryptedID, attrAuthorityEntityID, requesterEntityID, realm, attrQueryProfileAlias); } catch (SAML2Exception se) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.processAttributeQuery:", se); } return SAML2Utils.getErrorResponse(attrQuery, SAML2Constants.RESPONDER, null, se.getMessage(), null); } assertionList.add(encryptedAssertion); samlResp.setEncryptedAssertion(assertionList); } else { assertionList.add(assertion); samlResp.setAssertion(assertionList); } samlResp.setID(SAML2Utils.generateID()); samlResp.setInResponseTo(attrQuery.getID()); samlResp.setVersion(SAML2Constants.VERSION_2_0); samlResp.setIssueInstant(newDate()); Status status = protocolFactory.createStatus(); StatusCode statusCode = protocolFactory.createStatusCode(); statusCode.setValue(SAML2Constants.SUCCESS); status.setStatusCode(statusCode); samlResp.setStatus(status); Issuer respIssuer = AssertionFactory.getInstance().createIssuer(); respIssuer.setValue(attrAuthorityEntityID); samlResp.setIssuer(respIssuer); signResponse(samlResp, attrAuthorityEntityID, realm, false); return samlResp; } /** * Converts attribute query profile alias to attribute query profile. * * @param attrQueryProfileAlias attribute query profile alias * * @return attribute query profile */ public static String getAttributeQueryProfile( String attrQueryProfileAlias) { if (attrQueryProfileAlias == null) { return null; } else if (attrQueryProfileAlias.equals( SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE_ALIAS)) { return SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE; } else if (attrQueryProfileAlias.equals( SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE_ALIAS)) { return SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE; } return null; } private static void signAttributeQuery(AttributeQuery attrQuery, String realm, boolean includeCert) throws SAML2Exception { String requesterEntityID = attrQuery.getIssuer().getValue(); String alias = SAML2Utils.getSigningCertAlias(realm, requesterEntityID, SAML2Constants.ATTR_QUERY_ROLE); PrivateKey signingKey = keyProvider.getPrivateKey(alias); if (signingKey == null) { throw new SAML2Exception( SAML2Utils.bundle.getString("missingSigningCertAlias")); } X509Certificate signingCert = null; if (includeCert) { signingCert = keyProvider.getX509Certificate(alias); } if (signingKey != null) { attrQuery.sign(signingKey, signingCert); } } public static void validateEntityRequester(AttributeQuery attrQuery, String attrAuthorityEntityID, String realm) throws SAML2Exception { Issuer issuer = attrQuery.getIssuer(); String format = issuer.getFormat(); if ((format == null) || (format.length() == 0) || (format.equals(SAML2Constants.UNSPECIFIED)) || (format.equals(SAML2Constants.ENTITY))) { String requestedEntityID = issuer.getValue(); if (!SAML2Utils.isSourceSiteValid(issuer, realm, attrAuthorityEntityID)) { throw new SAML2Exception(SAML2Utils.bundle.getString( "attrQueryIssuerInvalid")); } } else { throw new SAML2Exception(SAML2Utils.bundle.getString( "attrQueryIssuerInvalid")); } } /** * Checks if the attribute query signature is valid. * * @param attrQuery attribute query * @param attrAuthorityEntityID entity ID of attribute authority * @param realm the realm of hosted entity * * @exception SAML2Exception if the attribute query signature is not valid. */ public static void verifyAttrQuerySignature(AttributeQuery attrQuery, String attrAuthorityEntityID, String realm) throws SAML2Exception { if (!attrQuery.isSigned()) { throw new SAML2Exception(SAML2Utils.bundle.getString( "attrQueryNotSigned")); } String requestedEntityID = attrQuery.getIssuer().getValue(); AttributeQueryDescriptorElement attrqDesc = metaManager.getAttributeQueryDescriptor(realm, requestedEntityID); if (attrqDesc == null) { throw new SAML2Exception(SAML2Utils.bundle.getString( "attrQueryIssuerNotFound")); } Set signingCerts = KeyUtil.getVerificationCerts(attrqDesc, requestedEntityID, SAML2Constants.ATTR_QUERY_ROLE); if (!signingCerts.isEmpty()) { boolean valid = attrQuery.isSignatureValid(signingCerts); if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.verifyAttributeQuery: " + "Signature validity is : " + valid); } if (!valid) { throw new SAML2Exception(SAML2Utils.bundle.getString( "invalidSignatureAttrQuery")); } } else { throw new SAML2Exception( SAML2Utils.bundle.getString("missingSigningCertAlias")); } } public static String getIdentityFromDataStoreX509Subject( AttributeQuery attrQuery, String attrAuthorityEntityID, String realm) throws SAML2Exception { Subject subject = attrQuery.getSubject(); NameID nameID = null; EncryptedID encryptedID = subject.getEncryptedID(); if (encryptedID != null) { nameID = encryptedID.decrypt(KeyUtil.getDecryptionKeys(realm, attrAuthorityEntityID, SAML2Constants.ATTR_AUTH_ROLE)); } else { nameID = subject.getNameID(); } if (!SAML2Constants.X509_SUBJECT_NAME.equals(nameID.getFormat())) { throw new SAML2Exception(SAML2Utils.bundle.getString( "unsupportedAttrQuerySubjectNameID")); } String mappingAttrName = getAttributeValueFromAttrAuthorityConfig( realm, attrAuthorityEntityID, SAML2Constants.X509_SUBJECT_DATA_STORE_ATTR_NAME); if ((mappingAttrName == null) || (mappingAttrName.length() == 0)) { throw new SAML2Exception(SAML2Utils.bundle.getString( "x509SubjectMappingNotConfigured")); } String x509SubjectDN = nameID.getValue(); Map attrMap = new HashMap(); Set values = new HashSet(); values.add(x509SubjectDN); attrMap.put(mappingAttrName, values); if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.getIdentityFromDataStoreX509Subject: " + "mappingAttrName = " + mappingAttrName + ", X509 subject DN = " + x509SubjectDN); } try { return dsProvider.getUserID(realm, attrMap); } catch (DataStoreProviderException dse) { SAML2Utils.debug.error( "AttributeQueryUtil.getIdentityFromDataStoreX509Subject:",dse); throw new SAML2Exception(dse.getMessage()); } } public static String getIdentity(AttributeQuery attrQuery, String attrAuthorityEntityID, String realm) throws SAML2Exception { Subject subject = attrQuery.getSubject(); NameID nameID = null; EncryptedID encryptedID = subject.getEncryptedID(); if (encryptedID != null) { nameID = encryptedID.decrypt(KeyUtil.getDecryptionKeys(realm, attrAuthorityEntityID, SAML2Constants.ATTR_AUTH_ROLE)); } else { nameID = subject.getNameID(); } String nameIDFormat = nameID.getFormat(); // NameIDFormat is "transient" if (SAML2Constants.NAMEID_TRANSIENT_FORMAT.equals(nameIDFormat)) { return (String)IDPCache.userIDByTransientNameIDValue.get( nameID.getValue()); } else // NameIDFormat is "unspecified" if (SAML2Constants.UNSPECIFIED.equals(nameIDFormat)) { Map userIDsSearchMap = new HashMap(); Set userIDValuesSet = new HashSet(); userIDValuesSet.add(nameID.getValue()); String userId = "uid"; IDPSSOConfigElement config = SAML2Utils.getSAML2MetaManager().getIDPSSOConfig( realm, attrAuthorityEntityID); Map attrs = SAML2MetaUtils.getAttributes(config); List nimAttrs = (List)attrs.get(SAML2Constants.NAME_ID_FORMAT_MAP); for (Iterator i = nimAttrs.iterator(); i.hasNext(); ) { String attrName = (String)i.next(); if (attrName != null && attrName.length()>2 && attrName.startsWith(nameIDFormat)) { int eqPos = attrName.indexOf('='); if (eqPos != -1 && eqPos verifyDesiredAttributes(List supportedAttrs, List desiredAttrs) throws SAML2Exception { if (supportedAttrs == null || supportedAttrs.isEmpty()) { return desiredAttrs; } if (desiredAttrs == null || desiredAttrs.isEmpty()) { return convertAttributes(supportedAttrs); } for (Attribute desiredAttr : desiredAttrs) { boolean isAttrValid = false; Iterator supportedAttrIterator = supportedAttrs.iterator(); while (supportedAttrIterator.hasNext()) { AttributeElement supportedAttr = supportedAttrIterator.next(); if (isSameAttribute(desiredAttr, supportedAttr)) { if (isValueValid(desiredAttr, supportedAttr)) { isAttrValid = true; //By removing the attribute from the supported list we make sure that an AttributeQuery can //not request the same Attribute more than once, see SAML core 3.3.2.3. supportedAttrIterator.remove(); break; } else { throw new SAML2Exception("Attribute value not supported"); } } } if (!isAttrValid) { throw new SAML2Exception("Attribute name not supported"); } } return desiredAttrs; } private static List convertAttributes(List jaxbAttrs) throws SAML2Exception { List resultAttrs = new ArrayList(); for(Iterator iter = jaxbAttrs.iterator(); iter.hasNext(); ) { AttributeElement jaxbAttr = (AttributeElement)iter.next(); Attribute attr = AssertionFactory.getInstance().createAttribute(); attr.setName(jaxbAttr.getName()); attr.setNameFormat(jaxbAttr.getNameFormat()); attr.setFriendlyName(jaxbAttr.getFriendlyName()); List jaxbValues = jaxbAttr.getAttributeValue(); if ((jaxbValues != null) && (!jaxbValues.isEmpty())) { List newValues = new ArrayList(); for(Iterator iterV = jaxbValues.iterator(); iterV.hasNext();) { AttributeValueElement jaxbValeu = (AttributeValueElement)iter.next(); List content = jaxbValeu.getContent(); if ((content != null) && (!content.isEmpty())) { newValues.add(content.get(0)); } } if (!newValues.isEmpty()) { attr.setAttributeValueString(newValues); } } resultAttrs.add(attr); } return resultAttrs; } private static List filterAttributes(List attributes, List desiredAttrs) { if (attributes == null || attributes.isEmpty()) { SAML2Utils.debug.message("AttributeQueryUtil.filterAttributes: attributes are null"); return attributes; } if (desiredAttrs == null || desiredAttrs.isEmpty()) { SAML2Utils.debug.message("AttributeQueryUtil.filterAttributes: desired attributes are null"); return attributes; } List returnAttributes = new ArrayList(); if (!desiredAttrs.isEmpty()) { for (Attribute attrD : desiredAttrs) { for (Attribute attr : attributes) { if (isSameAttribute(attr, attrD) ) { attr = filterAttributeValues(attr, attrD); if (attr != null) { //let's copy FriendlyName if exists String fName = attrD.getFriendlyName(); if (fName != null && fName.length() > 0){ try { attr.setFriendlyName(fName); } catch (SAML2Exception e) { //do nothing, attribute will be sent without //friendlyName set } } returnAttributes.add(attr); } break; } } } } return returnAttributes; } private static boolean isSameAttribute(Attribute attribute, Attribute desired) { return desired.getName().equals(attribute.getName()) && isNameFormatMatching(desired.getNameFormat(), attribute.getNameFormat()); } private static Attribute filterAttributeValues(Attribute attr, Attribute desiredAttr) { List valuesD = desiredAttr.getAttributeValueString(); if ((valuesD == null) || (valuesD.isEmpty())) { return attr; } List values = attr.getAttributeValueString(); if ((values == null) || (values.isEmpty())) { return null; } List newValuesD = new ArrayList(); for(Iterator iter = valuesD.iterator(); iter.hasNext(); ) { String valueD = (String)iter.next(); if (values.contains(valueD)) { newValuesD.add(valueD); } } if (newValuesD.isEmpty()) { return null; } if (newValuesD.size() == valuesD.size()) { return desiredAttr; } try { Attribute newAttr = AssertionFactory.getInstance().createAttribute(); newAttr.setName(desiredAttr.getName()); newAttr.setNameFormat(desiredAttr.getNameFormat()); newAttr.setFriendlyName(desiredAttr.getFriendlyName()); newAttr.setAnyAttribute(desiredAttr.getAnyAttribute()); newAttr.setAttributeValueString(newValuesD); return newAttr; } catch(SAML2Exception se) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.filterAttributeValues:", se); } return null; } } private static boolean isSameAttribute(Attribute desired, AttributeElement supported) { return desired.getName().equals(supported.getName()) && isNameFormatMatching(desired.getNameFormat(), supported.getNameFormat()); } /** * Determines whether the desired Attribute NameFormat matches with the available attribute's NameFormat. When * the NameFormat isn't specified in the request, the * urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified default NameFormat needs to be used (see * SAML core spec 2.7.3.1). * The different attribute profiles (SAML profiles spec section 8) each determine how the attribute comparison * should be performed, however there is no clear way to actually determine which attribute profile is being used * when the Attribute Authority supports more than one profile. Because of this, the unspecified Attribute * NameFormat has been implemented as a wildcard match, much similarly to how requesting the unspecified * NameID-Format allows the IdP to choose an arbitrary NameID-Format when generating the assertion for an SP. * * @param desiredNameFormat The NameFormat of the Attribute defined in the AttributeQuery request. * @param availableNameFormat The NameFormat of the Attribute defined in the server configuration. * @return true if the desired NameFormat is unspecified, or if it is the same as the NameFormat * defined in the server configuration. */ private static boolean isNameFormatMatching(String desiredNameFormat, String availableNameFormat) { return desiredNameFormat == null || DEFAULT_ATTRIBUTE_NAME_FORMAT.equals(desiredNameFormat) || desiredNameFormat.equals(availableNameFormat); } private static boolean isValueValid(Attribute desiredAttr, AttributeElement supportedAttr) { List valuesD = desiredAttr.getAttributeValueString(); if ((valuesD == null) || (valuesD.isEmpty())) { return true; } List attrValuesS = supportedAttr.getAttributeValue(); if ((attrValuesS == null) || (attrValuesS.isEmpty())) { return true; } List valuesS = new ArrayList(); for(Iterator iter = attrValuesS.iterator(); iter.hasNext(); ) { AttributeValueElement attrValueElem = (AttributeValueElement)iter.next(); valuesS.addAll(attrValueElem.getContent()); } try { return valuesS.containsAll(valuesD); } catch (Exception ex) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.isValueValid:", ex); } return false; } } private static Response sendAttributeQuerySOAP(AttributeQuery attrQuery, String attributeServiceURL, String attrAuthorityEntityID, AttributeAuthorityDescriptorElement aad) throws SAML2Exception { String attrQueryXMLString = attrQuery.toXMLString(true, true); if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.sendAttributeQuerySOAP: " + "attrQueryXMLString = " + attrQueryXMLString); SAML2Utils.debug.message( "AttributeQueryUtil.sendAttributeQuerySOAP: " + "attributeServiceURL = " + attributeServiceURL); } SOAPMessage resMsg = null; try { resMsg = SOAPCommunicator.getInstance().sendSOAPMessage(attrQueryXMLString, attributeServiceURL, true); } catch (SOAPException se) { SAML2Utils.debug.error( "AttributeQueryUtil.sendAttributeQuerySOAP: ", se); throw new SAML2Exception( SAML2Utils.bundle.getString("errorSendingAttributeQuery")); } Element respElem = SOAPCommunicator.getInstance().getSamlpElement(resMsg, "Response"); Response response = ProtocolFactory.getInstance().createResponse(respElem); Status status = response.getStatus(); if (!SAML2Constants.SUCCESS.equals(status.getStatusCode().getValue())) { String message = status.getStatusMessage() == null ? "" : status.getStatusMessage(); String detail = status.getStatusDetail() == null ? "" : status.getStatusDetail().toXMLString(); SAML2Utils.debug.error( "AttributeQueryUtil.sendAttributeQuerySOAP: " + "Non-Success status " + status.getStatusCode().getValue() + ", message: " + message + ", detail: " + detail); Object[] args = { status.getStatusCode().getValue(), message, detail }; throw new SAML2Exception(SAML2Utils.BUNDLE_NAME, "failureStatusAttributeQuery", args); } if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.sendAttributeQuerySOAP: " + "response = " + response.toXMLString(true, true)); } verifyResponse(response, attrQuery, attrAuthorityEntityID, aad); return response; } private static void verifyResponse(Response response, AttributeQuery attrQuery, String attrAuthorityEntityID, AttributeAuthorityDescriptorElement aad) throws SAML2Exception { String attrQueryID = attrQuery.getID(); if ((attrQueryID != null) && (!attrQueryID.equals(response.getInResponseTo()))) { throw new SAML2Exception( SAML2Utils.bundle.getString("invalidInResponseToAttrQuery")); } Issuer respIssuer = response.getIssuer(); if (respIssuer == null) { return; } if (!attrAuthorityEntityID.equals(respIssuer.getValue())) { throw new SAML2Exception(SAML2Utils.bundle.getString( "responseIssuerMismatch")); } if (!response.isSigned()) { throw new SAML2Exception(SAML2Utils.bundle.getString( "responseNotSigned")); } Set signingCerts = KeyUtil.getVerificationCerts(aad, attrAuthorityEntityID, SAML2Constants.ATTR_AUTH_ROLE); if (!signingCerts.isEmpty()) { boolean valid = response.isSignatureValid(signingCerts); if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.verifyResponse: " + "Signature validity is : " + valid); } if (!valid) { throw new SAML2Exception(SAML2Utils.bundle.getString( "invalidSignatureOnResponse")); } } else { throw new SAML2Exception( SAML2Utils.bundle.getString("missingSigningCertAlias")); } } private static String findLocation( AttributeAuthorityDescriptorElement aad, String binding, String attrQueryProfile, String attrProfile) { SAML2Utils.debug.message("AttributeQueryUtil.findLocation entering..."); List attrProfiles = aad.getAttributeProfile(); if ((attrProfiles == null) || (attrProfiles.isEmpty())) { SAML2Utils.debug.message("AttributeQueryUtil.findLocation: attrProfiles is null or empty"); if (attrProfile != null) { SAML2Utils.debug.message("AttributeQueryUtil.findLocation: attrProfiles is null or empty and attrProfile is null"); return null; } } else if (!attrProfiles.contains(attrProfile)) { SAML2Utils.debug.message("AttributeQueryUtil.findLocation: attrProfile not found in the attrProfiles"); return null; } SAML2Utils.debug.message("AttributeQueryUtil.findLocation: entering..."); List attrServices = aad.getAttributeService(); for(Iterator iter = attrServices.iterator(); iter.hasNext(); ) { AttributeServiceElement attrService = (AttributeServiceElement)iter.next(); if (isValidAttributeService(binding, attrService, attrQueryProfile)) { SAML2Utils.debug.message("AttributeQueryUtil.findLocation: found valid service"); return attrService.getLocation(); } } SAML2Utils.debug.message("AttributeQueryUtil.findLocation: nothing found, leaving last line with null"); return null; } private static boolean isValidAttributeService(String binding, AttributeServiceElement attrService, String attrQueryProfile) { if (!binding.equalsIgnoreCase(attrService.getBinding())) { return false; } if (attrQueryProfile == null) { return false; } return ((attrQueryProfile.equals( SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE)) || (SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE.equals( attrQueryProfile) && attrService.isSupportsX509Query())); } /** * Returns an AttributeAuthorityMapper * * @param realm the realm name * @param attrAuthorityEntityID the entity id of the attribute authority * @param attrQueryProfileAlias attribute profile alias * * @return the AttributeAuthorityMapper * @exception SAML2Exception if the operation is not successful */ static AttributeAuthorityMapper getAttributeAuthorityMapper(String realm, String attrAuthorityEntityID, String attrQueryProfileAlias) throws SAML2Exception { String attrAuthorityMapperName = null; AttributeAuthorityMapper attrAuthorityMapper = null; try { attrAuthorityMapperName = getAttributeValueFromAttrAuthorityConfig( realm, attrAuthorityEntityID, attrQueryProfileAlias + "_" + SAML2Constants.ATTRIBUTE_AUTHORITY_MAPPER); if (attrAuthorityMapperName == null) { attrAuthorityMapperName = SAML2Constants.DEFAULT_ATTRIBUTE_AUTHORITY_MAPPER_CLASS; if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.getAttributeAuthorityMapper: use "+ attrAuthorityMapperName); } } attrAuthorityMapper = (AttributeAuthorityMapper) attrAuthorityMapperCache.get(attrAuthorityMapperName); if (attrAuthorityMapper == null) { attrAuthorityMapper = (AttributeAuthorityMapper) Class.forName(attrAuthorityMapperName).newInstance(); attrAuthorityMapperCache.put(attrAuthorityMapperName, attrAuthorityMapper); } else { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( "AttributeQueryUtil.getAttributeAuthorityMapper: " + "got the AttributeAuthorityMapper from cache"); } } } catch (Exception ex) { SAML2Utils.debug.error( "AttributeQueryUtil.getAttributeAuthorityMapper: " + "Unable to get IDP Attribute Mapper.", ex); throw new SAML2Exception(ex); } return attrAuthorityMapper; } private static String getAttributeValueFromAttrAuthorityConfig( String realm, String attrAuthorityEntityID, String attrName) { try { AttributeAuthorityConfigElement config = metaManager.getAttributeAuthorityConfig(realm, attrAuthorityEntityID); Map attrs = SAML2MetaUtils.getAttributes(config); String value = null; List values = (List) attrs.get(attrName); if ((values != null) && (!values.isEmpty())) { value = ((String)values.iterator().next()).trim(); } return value; } catch (SAML2MetaException sme) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message("AttributeQueryUtil." + "getAttributeValueFromAttrAuthorityConfig: " + "get AttributeAuthorityConfig failed", sme); } } return null; } /** * Sends the AttributeQuery to specified attribute authority, * validates the response and returns the attribute map * Map<String, String> to the Fedlet * * @param spEntityID SP entity ID * @param idpEntityID IDP entity ID * @param nameIDValue NameID value * @param attrsList The list of attributes whose values need to be * fetched from IDP * @param attrQueryProfileAlias Attribute Query Profile Alias * @param subjectDN Attribute name which contains X.509 subject DN * * @return the Map object * @exception SAML2Exception if the operation is not successful * * @deprecated Use {@link #getAttributesForFedlet(String, String, String, List, String, String)} */ public static Map getAttributeMapForFedlet(String spEntityID, String idpEntityID, String nameIDValue, List attrsList, String attrQueryProfileAlias, String subjectDN) throws SAML2Exception { Map> attrMap = getAttributesForFedlet(spEntityID, idpEntityID, nameIDValue, attrsList, attrQueryProfileAlias, subjectDN); Map newAttrMap = new HashMap(); for (Map.Entry> entry : attrMap.entrySet()) { String attrName = entry.getKey(); Set attrValue = entry.getValue(); StringBuilder pipedValue = new StringBuilder(); for (String value : attrValue) { // Multiple attribute values // are seperated with "|" if (pipedValue.length() > 0) { pipedValue.append('|'); } pipedValue.append(value); } newAttrMap.put(attrName, pipedValue.toString()); } return newAttrMap; } /** * Sends the AttributeQuery to specified attribute authority, * validates the response and returns the attribute map * Map<String, Set<String>> to the Fedlet * * @param spEntityID SP entity ID * @param idpEntityID IDP entity ID * @param nameIDValue NameID value * @param attrsList The list of attributes whose values need to be * fetched from IDP * @param attrQueryProfileAlias Attribute Query Profile Alias * @param subjectDN Attribute name which contains X.509 subject DN * * @return the Map object * @exception SAML2Exception if the operation is not successful * * @supported.api */ public static Map> getAttributesForFedlet(String spEntityID, String idpEntityID, String nameIDValue, List attrsList, String attrQueryProfileAlias, String subjectDN) throws SAML2Exception { final String classMethod = "AttributeQueryUtil.getAttributesForFedlet: "; AttributeQueryConfigElement attrQueryConfig = metaManager.getAttributeQueryConfig("/", spEntityID); if (attrQueryConfig == null) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message(classMethod + "Attribute Query Config is null"); } return null; } String attrqMetaAlias = attrQueryConfig.getMetaAlias(); if (attrqMetaAlias == null) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message(classMethod + "Attribute Query MetaAlias is null"); } return null; } boolean wantNameIDEncrypted = SAML2Utils.getWantNameIDEncrypted("/", spEntityID, SAML2Constants.ATTR_QUERY_ROLE); AttributeQuery attrQuery = constructAttrQueryForFedlet(spEntityID, idpEntityID, nameIDValue, attrsList, attrqMetaAlias, attrQueryProfileAlias, subjectDN, wantNameIDEncrypted); String attrQueryProfile = null; if (attrQueryProfileAlias.equals(SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE_ALIAS)) { attrQueryProfile = SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE; } else if (attrQueryProfileAlias.equals(SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE_ALIAS)) { attrQueryProfile = SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE; } Response samlResp = sendAttributeQuery(attrQuery, idpEntityID, "/", attrQueryProfile, SAML2Constants.BASIC_ATTRIBUTE_PROFILE, SAML2Constants.SOAP); // Validate the response boolean validResp = validateSAMLResponseForFedlet(samlResp, spEntityID, wantNameIDEncrypted); Map> attrMap = new HashMap>(); if (validResp) { // Return back the AttributeMap if (samlResp != null) { List assertions; if (wantNameIDEncrypted) { assertions = samlResp.getEncryptedAssertion(); } else { assertions = samlResp.getAssertion(); } for (Object currentAssertion : assertions) { Assertion assertion; if (wantNameIDEncrypted) { assertion = getDecryptedAssertion((EncryptedAssertion) currentAssertion, spEntityID); } else { assertion = (Assertion) currentAssertion; } if (assertion != null) { List statements = assertion.getAttributeStatements(); if (statements != null && statements.size() > 0) { for (AttributeStatement statement : statements) { List attributes = statement.getAttribute(); attrMap.putAll(mapAttributes("/", spEntityID, idpEntityID, nameIDValue, attributes)); } } else { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message(classMethod + "Empty Statement present in SAML response"); } } } else { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message(classMethod + "Empty Assertion present in SAML response"); } } } if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message(classMethod + "attributes received from Attribute Query: " + attrMap); } } } else { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message(classMethod + "Invalid response obtained from Attribute Authority"); } } // Return the attribute map and to the fedlet return attrMap; } private static Map> mapAttributes(String realm, String spEntityID, String idpEntityID, String userID, List attributes) throws SAML2Exception { SPAttributeMapper spAttributeMapper = SAML2Utils.getSPAttributeMapper(realm, spEntityID); return spAttributeMapper.getAttributes(attributes, userID, spEntityID, idpEntityID, realm); } /** * Constructs the Attribute Query used by the Fedlet to retrieve the * values from IDP * * @param samlResp saml response * * @exception SAML2Exception if the operation is not successful * * @supported.api */ private static AttributeQuery constructAttrQueryForFedlet( String spEntityID, String idpEntityID, String nameIDValue, List attrsList, String attrqMetaAlias, String attrProfileNameAlias, String subjectDN, boolean wantNameIDEncrypted) throws SAML2Exception { String attrqEntityID = SAML2Utils.getSAML2MetaManager().getEntityByMetaAlias(attrqMetaAlias); ProtocolFactory protocolFactory = ProtocolFactory.getInstance(); AssertionFactory assertionFactory = AssertionFactory.getInstance(); AttributeQuery attrQuery = protocolFactory.createAttributeQuery(); Issuer issuer = assertionFactory.createIssuer(); issuer.setValue(attrqEntityID); attrQuery.setIssuer(issuer); attrQuery.setID(SAML2Utils.generateID()); attrQuery.setVersion(SAML2Constants.VERSION_2_0); attrQuery.setIssueInstant(newDate()); List attrs = new ArrayList(); for (String attributeName : attrsList) { Attribute attr = assertionFactory.createAttribute(); attr.setName(attributeName); attr.setNameFormat(SAML2Constants.BASIC_NAME_FORMAT); attrs.add(attr); } attrQuery.setAttributes(attrs); Subject subject = assertionFactory.createSubject(); NameID nameID = assertionFactory.createNameID(); nameID.setNameQualifier(idpEntityID); nameID.setSPNameQualifier(spEntityID); if (attrProfileNameAlias.equals( SAML2Constants.DEFAULT_ATTR_QUERY_PROFILE_ALIAS)) { nameID.setFormat(SAML2Constants.NAMEID_TRANSIENT_FORMAT); nameID.setValue(nameIDValue); } if (attrProfileNameAlias.equals( SAML2Constants.X509_SUBJECT_ATTR_QUERY_PROFILE_ALIAS)) { nameID.setFormat(SAML2Constants.X509_SUBJECT_NAME); nameID.setValue(subjectDN); } if (!wantNameIDEncrypted) { subject.setNameID(nameID); } else { AttributeAuthorityDescriptorElement aad = metaManager.getAttributeAuthorityDescriptor("/", idpEntityID); EncInfo encInfo = KeyUtil.getEncInfo(aad, idpEntityID, SAML2Constants.ATTR_AUTH_ROLE); EncryptedID encryptedID = nameID.encrypt(encInfo.getWrappingKey(), encInfo.getDataEncAlgorithm(), encInfo.getDataEncStrength(), idpEntityID); subject.setEncryptedID(encryptedID); } attrQuery.setSubject(subject); return attrQuery; } /** * Validates the SAML response obtained from Attribute Authortity * * @param samlResp saml response * * @exception SAML2Exception if the operation is not successful * * @supported.api */ private static boolean validateSAMLResponseForFedlet( Response samlResp, String spEntityID, boolean wantNameIDEncrypted) throws SAML2Exception { boolean resp = true; if (samlResp != null && samlResp.isSigned()) { List assertions = null; if (wantNameIDEncrypted) { assertions = samlResp.getEncryptedAssertion(); } else { assertions = samlResp.getAssertion(); } if (assertions == null) { return false; } for (Iterator asserIter = assertions.iterator(); asserIter.hasNext();) { Assertion assertion = null; if (wantNameIDEncrypted) { assertion = getDecryptedAssertion( (EncryptedAssertion)asserIter.next(), spEntityID); } else { assertion = (Assertion)asserIter.next(); } if (assertion != null) { Conditions conditions = assertion.getConditions(); if (conditions != null) { List audienceRes = conditions. getAudienceRestrictions(); if (audienceRes.size() > 1) { resp = false; break; } } List statements = assertion.getAttributeStatements(); if (statements.size() > 1) { resp = false; break; } } } } else { resp = false; } return resp; } /** * Returns the decrypted assertion * * @param samlResp saml response * * @exception SAML2Exception if the operation is not successful * * @supported.api */ private static Assertion getDecryptedAssertion( EncryptedAssertion eAssertion, String spEntityID) throws SAML2Exception { if (eAssertion != null) { return eAssertion.decrypt(KeyUtil.getDecryptionKeys("/", spEntityID, SAML2Constants.ATTR_QUERY_ROLE)); } return null; } }