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