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