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