001/* 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2006 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: SPACSUtils.java,v 1.48 2009/11/20 21:41:16 exu Exp $ 026 * 027 * Portions Copyrighted 2010-2017 ForgeRock AS. 028 */ 029package com.sun.identity.saml2.profile; 030 031import static org.forgerock.http.util.Uris.urlEncodeQueryParameterNameOrValue; 032import static org.forgerock.openam.utils.Time.*; 033 034import com.sun.identity.common.SystemConfigurationUtil; 035import com.sun.identity.liberty.ws.soapbinding.Message; 036import com.sun.identity.liberty.ws.soapbinding.SOAPBindingException; 037import com.sun.identity.liberty.ws.soapbinding.SOAPFaultException; 038import com.sun.identity.plugin.datastore.DataStoreProviderException; 039import com.sun.identity.plugin.monitoring.FedMonAgent; 040import com.sun.identity.plugin.monitoring.FedMonSAML2Svc; 041import com.sun.identity.plugin.monitoring.MonitorManager; 042import com.sun.identity.plugin.session.SessionException; 043import com.sun.identity.plugin.session.SessionManager; 044import com.sun.identity.plugin.session.SessionProvider; 045import com.sun.identity.saml.common.SAMLConstants; 046import com.sun.identity.saml.common.SAMLUtils; 047import com.sun.identity.saml.xmlsig.KeyProvider; 048import com.sun.identity.saml2.assertion.Advice; 049import com.sun.identity.saml2.assertion.Assertion; 050import com.sun.identity.saml2.assertion.AssertionFactory; 051import com.sun.identity.saml2.assertion.Attribute; 052import com.sun.identity.saml2.assertion.AttributeStatement; 053import com.sun.identity.saml2.assertion.EncryptedAttribute; 054import com.sun.identity.saml2.assertion.EncryptedID; 055import com.sun.identity.saml2.assertion.Issuer; 056import com.sun.identity.saml2.assertion.NameID; 057import com.sun.identity.saml2.assertion.Subject; 058import com.sun.identity.saml2.common.AccountUtils; 059import com.sun.identity.saml2.common.NameIDInfo; 060import com.sun.identity.saml2.common.NameIDInfoKey; 061import com.sun.identity.saml2.common.SAML2Constants; 062import com.sun.identity.saml2.common.SAML2Exception; 063import com.sun.identity.saml2.common.SAML2FailoverUtils; 064import com.sun.identity.saml2.common.SAML2SDKUtils; 065import com.sun.identity.saml2.common.SAML2Utils; 066import com.sun.identity.saml2.common.SOAPCommunicator; 067import com.sun.identity.saml2.ecp.ECPFactory; 068import com.sun.identity.saml2.ecp.ECPRelayState; 069import com.sun.identity.saml2.jaxb.entityconfig.IDPSSOConfigElement; 070import com.sun.identity.saml2.jaxb.entityconfig.SPSSOConfigElement; 071import com.sun.identity.saml2.jaxb.metadata.AffiliationDescriptorType; 072import com.sun.identity.saml2.jaxb.metadata.ArtifactResolutionServiceElement; 073import com.sun.identity.saml2.jaxb.metadata.IDPSSODescriptorElement; 074import com.sun.identity.saml2.jaxb.metadata.SPSSODescriptorElement; 075import com.sun.identity.saml2.key.KeyUtil; 076import com.sun.identity.saml2.logging.LogUtil; 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.SAML2PluginsUtils; 081import com.sun.identity.saml2.plugins.SAML2ServiceProviderAdapter; 082import com.sun.identity.saml2.plugins.SPAccountMapper; 083import com.sun.identity.saml2.plugins.SPAttributeMapper; 084import com.sun.identity.saml2.protocol.Artifact; 085import com.sun.identity.saml2.protocol.ArtifactResolve; 086import com.sun.identity.saml2.protocol.ArtifactResponse; 087import com.sun.identity.saml2.protocol.AuthnRequest; 088import com.sun.identity.saml2.protocol.ProtocolFactory; 089import com.sun.identity.saml2.protocol.Response; 090import com.sun.identity.saml2.protocol.Status; 091import com.sun.identity.shared.encode.Base64; 092import com.sun.identity.shared.xml.XMLUtils; 093import java.io.ByteArrayInputStream; 094import java.io.IOException; 095import java.io.PrintWriter; 096import java.security.PrivateKey; 097import java.security.cert.X509Certificate; 098import java.text.MessageFormat; 099import java.util.ArrayList; 100import java.util.HashMap; 101import java.util.HashSet; 102import java.util.Iterator; 103import java.util.List; 104import java.util.Map; 105import java.util.Set; 106import java.util.logging.Level; 107import javax.servlet.ServletException; 108import javax.servlet.http.HttpServletRequest; 109import javax.servlet.http.HttpServletResponse; 110import javax.xml.soap.SOAPConnection; 111import javax.xml.soap.SOAPException; 112import javax.xml.soap.SOAPMessage; 113import org.forgerock.openam.federation.saml2.SAML2TokenRepositoryException; 114import org.forgerock.openam.saml2.audit.SAML2EventLogger; 115import org.forgerock.openam.utils.ClientUtils; 116import org.forgerock.openam.utils.CollectionUtils; 117import org.forgerock.openam.utils.StringUtils; 118import org.w3c.dom.Document; 119import org.w3c.dom.Element; 120 121/** 122 * This class is used by a service provider (SP) to process the response from 123 * an identity provider for the SP's Assertion Consumer Service. 124 * 125 * @supported.api 126 */ 127public class SPACSUtils { 128 129 private static FedMonAgent agent = MonitorManager.getAgent(); 130 private static FedMonSAML2Svc saml2Svc = MonitorManager.getSAML2Svc(); 131 132 private SPACSUtils() {} 133 134 /** 135 * Retrieves <code>SAML</code> <code>Response</code> from http request. 136 * It handles three cases: 137 * <pre> 138 * 1. using http method get using request parameter "resID". 139 * This is the case after local login is done. 140 * 2. using http method get using request parameter "SAMLart". 141 * This is the case for artifact profile. 142 * 3. using http method post. This is the case for post profile. 143 * </pre> 144 * 145 * @param request http servlet request 146 * @param response http servlet response 147 * @param orgName realm or organization name the service provider resides in 148 * @param hostEntityId Entity ID of the hosted service provider 149 * @param metaManager <code>SAML2MetaManager</code> instance. 150 * @return <code>ResponseInfo</code> instance. 151 * @throws SAML2Exception,IOException if it fails in the process. 152 */ 153 public static ResponseInfo getResponse( 154 HttpServletRequest request, 155 HttpServletResponse response, 156 String orgName, 157 String hostEntityId, 158 SAML2MetaManager metaManager) 159 throws SAML2Exception,IOException 160 { 161 ResponseInfo respInfo = null; 162 163 String method = request.getMethod(); 164 if (method.equals("GET")) { 165 if (!SAML2Utils.isSPProfileBindingSupported( 166 orgName, hostEntityId, SAML2Constants.ACS_SERVICE, 167 SAML2Constants.HTTP_ARTIFACT)) 168 { 169 SAMLUtils.sendError(request, response, 170 response.SC_BAD_REQUEST, 171 "unsupportedBinding", 172 SAML2Utils.bundle.getString("unsupportedBinding")); 173 throw new SAML2Exception( 174 SAML2Utils.bundle.getString("unsupportedBinding")); 175 } 176 respInfo = getResponseFromGet(request, response, orgName, 177 hostEntityId, metaManager); 178 } else if (method.equals("POST")) { 179 String pathInfo = request.getPathInfo(); 180 if ((pathInfo != null) && (pathInfo.startsWith("/ECP"))) { 181 if (!SAML2Utils.isSPProfileBindingSupported( 182 orgName, hostEntityId, SAML2Constants.ACS_SERVICE, 183 SAML2Constants.PAOS)) 184 { 185 SAMLUtils.sendError(request, response, 186 response.SC_BAD_REQUEST, 187 "unsupportedBinding", 188 SAML2Utils.bundle.getString("unsupportedBinding")); 189 throw new SAML2Exception( 190 SAML2Utils.bundle.getString("unsupportedBinding")); 191 } 192 respInfo = getResponseFromPostECP(request, response, orgName, 193 hostEntityId, metaManager); 194 } else { 195 if (!SAML2Utils.isSPProfileBindingSupported( 196 orgName, hostEntityId, SAML2Constants.ACS_SERVICE, 197 SAML2Constants.HTTP_POST)) 198 { 199 SAMLUtils.sendError(request, response, 200 response.SC_BAD_REQUEST, 201 "unsupportedBinding", 202 SAML2Utils.bundle.getString("unsupportedBinding")); 203 throw new SAML2Exception( 204 SAML2Utils.bundle.getString("unsupportedBinding")); 205 } 206 respInfo = getResponseFromPost(request, response, orgName, 207 hostEntityId, metaManager); 208 } 209 } else { 210 // not supported 211 SAMLUtils.sendError(request, response, 212 response.SC_METHOD_NOT_ALLOWED, 213 "notSupportedHTTPMethod", 214 SAML2Utils.bundle.getString("notSupportedHTTPMethod")); 215 throw new SAML2Exception( 216 SAML2Utils.bundle.getString("notSupportedHTTPMethod")); 217 } 218 if (SAML2Utils.debug.messageEnabled()) { 219 SAML2Utils.debug.message("SPACSUtils.getResponse: got response=" 220 + respInfo.getResponse().toXMLString(true, true)); 221 } 222 return respInfo; 223 } 224 225 /** 226 * Retrieves <code>SAML Response</code> from http Get. 227 * It first uses parameter resID to retrieve <code>Response</code>. This is 228 * the case after local login; 229 * If resID is not defined, it then uses <code>SAMLart</code> http 230 * parameter to retrieve <code>Response</code>. 231 */ 232 private static ResponseInfo getResponseFromGet( 233 HttpServletRequest request, 234 HttpServletResponse response, 235 String orgName, 236 String hostEntityId, 237 SAML2MetaManager metaManager) 238 throws SAML2Exception,IOException 239 { 240 ResponseInfo respInfo = null; 241 String resID = request.getParameter("resID"); 242 if (resID != null && resID.length() != 0) { 243 if (SAML2Utils.debug.messageEnabled()) { 244 SAML2Utils.debug.message("SPACSUtils.getResponseFromGet: resID=" 245 + resID); 246 } 247 synchronized (SPCache.responseHash) { 248 respInfo = (ResponseInfo) SPCache.responseHash.remove(resID); 249 } 250 if (respInfo == null) { 251 if (SAML2Utils.debug.messageEnabled()) { 252 SAML2Utils.debug.message("SPACSUtils.getResponseFromGet: " 253 + "couldn't find Response from resID."); 254 } 255 String[] data = {resID}; 256 LogUtil.error(Level.INFO, 257 LogUtil.RESPONSE_NOT_FOUND_FROM_CACHE, 258 data, 259 null); 260 SAMLUtils.sendError(request, response, 261 response.SC_INTERNAL_SERVER_ERROR, "SSOFailed", 262 SAML2Utils.bundle.getString("SSOFailed")); 263 throw new SAML2Exception( 264 SAML2Utils.bundle.getString("SSOFailed")); 265 } 266 return respInfo; 267 } 268 269 String samlArt = request.getParameter(SAML2Constants.SAML_ART); 270 if (samlArt == null || samlArt.trim().length() == 0) { 271 SAML2Utils.debug.error("SPACSUtils.getResponseFromGet: Artifact " 272 + "string is empty."); 273 LogUtil.error(Level.INFO, 274 LogUtil.MISSING_ARTIFACT, 275 null, 276 null); 277 SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, 278 "missingArtifact", 279 SAML2Utils.bundle.getString("missingArtifact")); 280 throw new SAML2Exception( 281 SAML2Utils.bundle.getString("missingArtifact")); 282 } 283 284 return new ResponseInfo(getResponseFromArtifact(samlArt, hostEntityId, 285 request, response, orgName, metaManager), 286 SAML2Constants.HTTP_ARTIFACT, null); 287 } 288 289 // Retrieves response using artifact profile. 290 private static Response getResponseFromArtifact(String samlArt, 291 String hostEntityId, HttpServletRequest request, 292 HttpServletResponse response, String orgName, 293 SAML2MetaManager sm) throws SAML2Exception,IOException 294 { 295 296 // Try to get source ID and endpointIndex, and then 297 // decide which IDP and which artifact resolution service 298 if (SAML2Utils.debug.messageEnabled()) { 299 SAML2Utils.debug.message("SPACSUtils.getResponseFromArtifact: " + 300 "samlArt = " + samlArt); 301 } 302 303 Artifact art = null; 304 try { 305 art = ProtocolFactory.getInstance().createArtifact(samlArt.trim()); 306 String[] data = {samlArt.trim()}; 307 LogUtil.access(Level.INFO, 308 LogUtil.RECEIVED_ARTIFACT, 309 data, 310 null); 311 } catch (SAML2Exception se) { 312 SAML2Utils.debug.error("SPACSUtils.getResponseFromArtifact: " 313 + "Unable to decode and parse artifact string:" + samlArt); 314 SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, 315 "errorObtainArtifact", 316 SAML2Utils.bundle.getString("errorObtainArtifact")); 317 throw se; 318 } 319 320 String idpEntityID = getIDPEntityID(art, request, response, orgName, sm); 321 IDPSSODescriptorElement idp = null; 322 try { 323 idp = sm.getIDPSSODescriptor(orgName, idpEntityID); 324 } catch (SAML2MetaException se) { 325 String[] data = {orgName, idpEntityID}; 326 LogUtil.error(Level.INFO, 327 LogUtil.IDP_META_NOT_FOUND, 328 data, 329 null); 330 SAMLUtils.sendError(request, response, 331 response.SC_INTERNAL_SERVER_ERROR, 332 "failedToGetIDPSSODescriptor", se.getMessage()); 333 throw se; 334 } 335 336 String location = getIDPArtifactResolutionServiceUrl( 337 art.getEndpointIndex(), idpEntityID, idp, request, response); 338 339 // create ArtifactResolve message 340 ArtifactResolve resolve = null; 341 SOAPMessage resMsg = null; 342 try { 343 resolve = ProtocolFactory.getInstance().createArtifactResolve(); 344 resolve.setID(SAML2Utils.generateID()); 345 resolve.setVersion(SAML2Constants.VERSION_2_0); 346 resolve.setIssueInstant(newDate()); 347 resolve.setArtifact(art); 348 resolve.setDestination(XMLUtils.escapeSpecialCharacters(location)); 349 Issuer issuer = AssertionFactory.getInstance().createIssuer(); 350 issuer.setValue(hostEntityId); 351 resolve.setIssuer(issuer); 352 String needArtiResolveSigned = 353 SAML2Utils.getAttributeValueFromSSOConfig( 354 orgName, 355 idpEntityID, 356 SAML2Constants.IDP_ROLE, 357 SAML2Constants.WANT_ARTIFACT_RESOLVE_SIGNED); 358 359 if (needArtiResolveSigned != null && 360 needArtiResolveSigned.equals("true")) { 361 // or save it somewhere? 362 String signAlias = getAttributeValueFromSPSSOConfig( 363 orgName, 364 hostEntityId, 365 sm, 366 SAML2Constants.SIGNING_CERT_ALIAS); 367 if (signAlias == null) { 368 throw new SAML2Exception( 369 SAML2Utils.bundle.getString("missingSigningCertAlias")); 370 } 371 KeyProvider kp = KeyUtil.getKeyProviderInstance(); 372 if (kp == null) { 373 throw new SAML2Exception( 374 SAML2Utils.bundle.getString("nullKeyProvider")); 375 } 376 resolve.sign(kp.getPrivateKey(signAlias), 377 kp.getX509Certificate(signAlias)); 378 } 379 380 String resolveString = resolve.toXMLString(true, true); 381 if (SAML2Utils.debug.messageEnabled()) { 382 SAML2Utils.debug.message("SPACSUtils.getResponseFromArtifact: " 383 + "ArtifactResolve=" + resolveString); 384 } 385 386 SOAPConnection con = SOAPCommunicator.getInstance().openSOAPConnection(); 387 SOAPMessage msg = SOAPCommunicator.getInstance().createSOAPMessage(resolveString, true); 388 389 IDPSSOConfigElement config = null; 390 config = sm.getIDPSSOConfig(orgName, idpEntityID); 391 location = SAML2Utils.fillInBasicAuthInfo( 392 config, location); 393 resMsg = con.call(msg, location); 394 } catch (SAML2Exception s2e) { 395 SAML2Utils.debug.error("SPACSUtils.getResponseFromArtifact: " 396 + "couldn't create ArtifactResolve:", s2e); 397 String[] data = {hostEntityId, art.getArtifactValue()}; 398 LogUtil.error(Level.INFO, 399 LogUtil.CANNOT_CREATE_ARTIFACT_RESOLVE, 400 data, 401 null); 402 SAMLUtils.sendError(request, response, 403 response.SC_INTERNAL_SERVER_ERROR, 404 "errorCreateArtifactResolve", 405 SAML2Utils.bundle.getString("errorCreateArtifactResolve")); 406 throw s2e; 407 } catch (SOAPException se) { 408 SAML2Utils.debug.error("SPACSUtils.getResponseFromGet: " 409 + "couldn't get ArtifactResponse. SOAP error:",se); 410 String[] data = {hostEntityId, location}; 411 LogUtil.error(Level.INFO, 412 LogUtil.CANNOT_GET_SOAP_RESPONSE, 413 data, 414 null); 415 SAMLUtils.sendError(request, response, 416 response.SC_INTERNAL_SERVER_ERROR, 417 "errorInSOAPCommunication", 418 SAML2Utils.bundle.getString("errorInSOAPCommunication")); 419 throw new SAML2Exception(se.getMessage()); 420 } 421 422 Response result = getResponseFromSOAP(resMsg, resolve, request, 423 response, idpEntityID, idp, orgName, hostEntityId, sm); 424 String[] data = {hostEntityId, idpEntityID, 425 art.getArtifactValue(), ""}; 426 if (LogUtil.isAccessLoggable(Level.FINE)) { 427 data[3] = result.toXMLString(); 428 } 429 LogUtil.access(Level.INFO, 430 LogUtil.GOT_RESPONSE_FROM_ARTIFACT, 431 data, 432 null); 433 return result; 434 } 435 436 // Finds the IDP who sends the artifact; 437 private static String getIDPEntityID( 438 Artifact art, 439 HttpServletRequest request, 440 HttpServletResponse response, 441 String orgName, 442 SAML2MetaManager metaManager) 443 throws SAML2Exception,IOException 444 { 445 String sourceID = art.getSourceID(); 446 // find the idp 447 String idpEntityID = null; 448 try { 449 Iterator iter = 450 metaManager.getAllRemoteIdentityProviderEntities(orgName). 451 iterator(); 452 String tmpSourceID = null; 453 while (iter.hasNext()) { 454 idpEntityID = (String) iter.next(); 455 tmpSourceID = SAML2Utils.generateSourceID(idpEntityID); 456 if (sourceID.equals(tmpSourceID)) { 457 break; 458 } 459 idpEntityID = null; 460 } 461 if (idpEntityID == null) { 462 SAML2Utils.debug.error("SPACSUtils.getResponseFromGet: Unable " 463 + "to find the IDP based on the SourceID in the artifact"); 464 String[] data = {art.getArtifactValue(), orgName}; 465 LogUtil.error(Level.INFO, 466 LogUtil.IDP_NOT_FOUND, 467 data, 468 null); 469 throw new SAML2Exception( 470 SAML2Utils.bundle.getString("cannotFindIDP")); 471 } 472 } catch (SAML2Exception se) { 473 String[] data = {art.getArtifactValue(), orgName}; 474 LogUtil.error(Level.INFO, 475 LogUtil.IDP_NOT_FOUND, 476 data, 477 null); 478 SAMLUtils.sendError(request, response, 479 response.SC_INTERNAL_SERVER_ERROR, 480 "cannotFindIDP", se.getMessage()); 481 throw se; 482 } 483 return idpEntityID; 484 } 485 486 // Retrieves the ArtifactResolutionServiceURL for an IDP. 487 private static String getIDPArtifactResolutionServiceUrl( 488 int endpointIndex, 489 String idpEntityID, 490 IDPSSODescriptorElement idp, 491 HttpServletRequest request, 492 HttpServletResponse response) 493 throws SAML2Exception,IOException 494 { 495 // find the artifact resolution service url 496 List arsList=idp.getArtifactResolutionService(); 497 ArtifactResolutionServiceElement ars = null; 498 String location = null; 499 String defaultLocation = null; 500 String firstLocation = null; 501 int index; 502 boolean isDefault = false; 503 for (int i=0; i<arsList.size(); i++) { 504 ars = (ArtifactResolutionServiceElement)arsList.get(i); 505 location = ars.getLocation(); 506 //String binding = ars.getBinding(); 507 index = ars.getIndex(); 508 isDefault = ars.isIsDefault(); 509 if (index == endpointIndex) { 510 break; 511 } 512 if (isDefault) { 513 defaultLocation = location; 514 } 515 if (i==0) { 516 firstLocation = location; 517 } 518 location = null; 519 } 520 if (location == null || location.length() == 0) { 521 location = defaultLocation; 522 if (location == null || location.length() == 0) { 523 location = firstLocation; 524 if (location == null || location.length() == 0) { 525 SAML2Utils.debug.error("SPACSUtils: Unable to get the " 526 + "location of artifact resolution service for " 527 + idpEntityID); 528 String[] data = {idpEntityID}; 529 LogUtil.error(Level.INFO, 530 LogUtil.ARTIFACT_RESOLUTION_URL_NOT_FOUND, 531 data, 532 null); 533 SAMLUtils.sendError(request, response, 534 response.SC_INTERNAL_SERVER_ERROR, 535 "cannotFindArtifactResolutionUrl", 536 SAML2Utils.bundle.getString( 537 "cannotFindArtifactResolutionUrl")); 538 throw new SAML2Exception( 539 SAML2Utils.bundle.getString( 540 "cannotFindArtifactResolutionUrl")); 541 } 542 } 543 } 544 if (SAML2Utils.debug.messageEnabled()) { 545 SAML2Utils.debug.message("SPACSUtils: IDP artifact resolution " 546 + "service url =" + location); 547 } 548 return location; 549 } 550 551 /** 552 * Obtains <code>SAML Response</code> from <code>SOAPBody</code>. 553 * Used by Artifact profile. 554 */ 555 private static Response getResponseFromSOAP(SOAPMessage resMsg, 556 ArtifactResolve resolve, 557 HttpServletRequest request, 558 HttpServletResponse response, 559 String idpEntityID, 560 IDPSSODescriptorElement idp, 561 String orgName, 562 String hostEntityId, 563 SAML2MetaManager sm) 564 throws SAML2Exception,IOException 565 { 566 String method = "SPACSUtils.getResponseFromSOAP:"; 567 Element resElem = null; 568 try { 569 resElem = SOAPCommunicator.getInstance().getSamlpElement(resMsg, "ArtifactResponse"); 570 } catch (SAML2Exception se) { 571 String[] data = {idpEntityID}; 572 LogUtil.error(Level.INFO, 573 LogUtil.SOAP_ERROR, 574 data, 575 null); 576 SAMLUtils.sendError(request, response, 577 response.SC_INTERNAL_SERVER_ERROR, 578 "soapError", se.getMessage()); 579 throw se; 580 } 581 ArtifactResponse artiResp = null; 582 try { 583 artiResp = ProtocolFactory.getInstance(). 584 createArtifactResponse(resElem); 585 } catch (SAML2Exception se) { 586 if (SAML2Utils.debug.messageEnabled()) { 587 SAML2Utils.debug.message(method + "Couldn't create " 588 + "ArtifactResponse:", se); 589 } 590 String[] data = {idpEntityID}; 591 LogUtil.error(Level.INFO, 592 LogUtil.CANNOT_INSTANTIATE_ARTIFACT_RESPONSE, 593 data, 594 null); 595 SAMLUtils.sendError(request, response, 596 response.SC_INTERNAL_SERVER_ERROR, 597 "failedToCreateArtifactResponse", se.getMessage()); 598 throw se; 599 } 600 601 if (artiResp == null) { 602 String[] data = {idpEntityID}; 603 LogUtil.error(Level.INFO, 604 LogUtil.MISSING_ARTIFACT_RESPONSE, 605 data, 606 null); 607 SAMLUtils.sendError(request, response, 608 response.SC_INTERNAL_SERVER_ERROR, 609 "missingArtifactResponse", 610 SAML2Utils.bundle.getString("missingArtifactResponse")); 611 throw new SAML2Exception( 612 SAML2Utils.bundle.getString("missingArtifactResponse")); 613 } else { 614 if (SAML2Utils.debug.messageEnabled()) { 615 SAML2Utils.debug.message(method + "Received ArtifactResponse:" 616 + artiResp.toXMLString(true, true)); 617 } 618 } 619 620 // verify ArtifactResponse 621 String wantArtiRespSigned = getAttributeValueFromSPSSOConfig( 622 orgName, 623 hostEntityId, 624 sm, 625 SAML2Constants.WANT_ARTIFACT_RESPONSE_SIGNED); 626 if (wantArtiRespSigned != null && wantArtiRespSigned.equals("true")) { 627 Set<X509Certificate> verificationCerts = KeyUtil.getVerificationCerts(idp, idpEntityID, 628 SAML2Constants.IDP_ROLE); 629 if (!artiResp.isSigned() || !artiResp.isSignatureValid(verificationCerts)) { 630 if (SAML2Utils.debug.messageEnabled()) { 631 SAML2Utils.debug.message(method 632 + "ArtifactResponse's signature is invalid."); 633 } 634 String[] data = {idpEntityID}; 635 LogUtil.error(Level.INFO, 636 LogUtil.ARTIFACT_RESPONSE_INVALID_SIGNATURE, 637 data, 638 null); 639 SAMLUtils.sendError(request, response, 640 response.SC_INTERNAL_SERVER_ERROR, "invalidSignature", 641 SAML2Utils.bundle.getString("invalidSignature")); 642 throw new SAML2Exception( 643 SAML2Utils.bundle.getString("invalidSignature")); 644 } 645 } 646 647 String inResponseTo = artiResp.getInResponseTo(); 648 if (inResponseTo == null || !inResponseTo.equals(resolve.getID())) { 649 if (SAML2Utils.debug.messageEnabled()) { 650 SAML2Utils.debug.message(method 651 + "ArtifactResponse's InResponseTo is invalid."); 652 } 653 String[] data = {idpEntityID}; 654 LogUtil.error(Level.INFO, 655 LogUtil.ARTIFACT_RESPONSE_INVALID_INRESPONSETO, 656 data, 657 null); 658 SAMLUtils.sendError(request, response, 659 response.SC_INTERNAL_SERVER_ERROR, "invalidInResponseTo", 660 SAML2Utils.bundle.getString("invalidInResponseTo")); 661 throw new SAML2Exception( 662 SAML2Utils.bundle.getString("invalidInResponseTo")); 663 } 664 665 Issuer idpIssuer = artiResp.getIssuer(); 666 if (idpIssuer == null || !idpIssuer.getValue().equals(idpEntityID)) { 667 if (SAML2Utils.debug.messageEnabled()) { 668 SAML2Utils.debug.message(method 669 + "ArtifactResponse's Issuer is invalid."); 670 } 671 String[] data = {idpEntityID}; 672 LogUtil.error(Level.INFO, 673 LogUtil.ARTIFACT_RESPONSE_INVALID_ISSUER, 674 data, 675 null); 676 SAMLUtils.sendError(request, response, 677 response.SC_INTERNAL_SERVER_ERROR, "invalidIssuer", 678 SAML2Utils.bundle.getString("invalidIssuer")); 679 throw new SAML2Exception( 680 SAML2Utils.bundle.getString("invalidIssuer")); 681 } 682 683 // check time? 684 685 Status status = artiResp.getStatus(); 686 if (status == null || !status.getStatusCode().getValue().equals( 687 SAML2Constants.SUCCESS)) 688 { 689 String statusCode = 690 (status == null)?"":status.getStatusCode().getValue(); 691 if (SAML2Utils.debug.messageEnabled()) { 692 SAML2Utils.debug.message(method 693 + "ArtifactResponse's status code is not success." 694 + statusCode); 695 } 696 String[] data = {idpEntityID, ""}; 697 if (LogUtil.isErrorLoggable(Level.FINE)) { 698 data[1] = statusCode; 699 } 700 LogUtil.error(Level.INFO, 701 LogUtil.ARTIFACT_RESPONSE_INVALID_STATUS_CODE, 702 data, 703 null); 704 SAMLUtils.sendError(request, response, 705 response.SC_INTERNAL_SERVER_ERROR, "invalidStatusCode", 706 SAML2Utils.bundle.getString("invalidStatusCode")); 707 throw new SAML2Exception( 708 SAML2Utils.bundle.getString("invalidStatusCode")); 709 } 710 711 try { 712 return ProtocolFactory.getInstance().createResponse( 713 artiResp.getAny()); 714 } catch (SAML2Exception se) { 715 if (SAML2Utils.debug.messageEnabled()) { 716 SAML2Utils.debug.message(method 717 + "couldn't instantiate Response:", se); 718 } 719 String[] data = {idpEntityID}; 720 LogUtil.error(Level.INFO, 721 LogUtil.CANNOT_INSTANTIATE_RESPONSE_ARTIFACT, 722 data, 723 null); 724 SAMLUtils.sendError(request, response, 725 response.SC_INTERNAL_SERVER_ERROR, 726 "failedToCreateResponse", se.getMessage()); 727 throw se; 728 } 729 } 730 731 /** 732 * Obtains <code>SAML Response</code> from <code>SOAPBody</code>. 733 * Used by ECP profile. 734 */ 735 private static ResponseInfo getResponseFromPostECP( 736 HttpServletRequest request, HttpServletResponse response, 737 String orgName, String hostEntityId, SAML2MetaManager metaManager) 738 throws SAML2Exception,IOException 739 { 740 Message message = null; 741 try { 742 message = new Message(SOAPCommunicator.getInstance().getSOAPMessage(request)); 743 } catch (SOAPException soapex) { 744 String[] data = { hostEntityId } ; 745 LogUtil.error(Level.INFO, 746 LogUtil.CANNOT_INSTANTIATE_SOAP_MESSAGE_ECP, data, null); 747 SAMLUtils.sendError(request, response, 748 response.SC_INTERNAL_SERVER_ERROR, 749 "failedToCreateSOAPMessage", soapex.getMessage()); 750 throw new SAML2Exception(soapex.getMessage()); 751 } catch (SOAPBindingException soapex) { 752 String[] data = { hostEntityId } ; 753 LogUtil.error(Level.INFO, 754 LogUtil.CANNOT_INSTANTIATE_SOAP_MESSAGE_ECP, data, null); 755 SAMLUtils.sendError(request, response, 756 response.SC_INTERNAL_SERVER_ERROR, 757 "failedToCreateSOAPMessage", soapex.getMessage()); 758 throw new SAML2Exception(soapex.getMessage()); 759 } catch(SOAPFaultException sfex) { 760 String[] data = { hostEntityId } ; 761 LogUtil.error(Level.INFO, LogUtil.RECEIVE_SOAP_FAULT_ECP, 762 data, null); 763 String faultString = 764 sfex.getSOAPFaultMessage().getSOAPFault().getFaultString(); 765 SAMLUtils.sendError(request, response, 766 response.SC_INTERNAL_SERVER_ERROR, 767 "failedToCreateSOAPMessage", faultString); 768 throw new SAML2Exception(faultString); 769 } 770 771 List soapHeaders = message.getOtherSOAPHeaders(); 772 ECPRelayState ecpRelayState = null; 773 if ((soapHeaders != null) && (!soapHeaders.isEmpty())) { 774 for(Iterator iter = soapHeaders.iterator(); iter.hasNext();) { 775 Element headerEle = (Element)iter.next(); 776 try { 777 ecpRelayState = 778 ECPFactory.getInstance().createECPRelayState(headerEle); 779 break; 780 } catch (SAML2Exception saml2ex) { 781 // not ECP RelayState 782 } 783 } 784 } 785 String relayState = null; 786 if (ecpRelayState != null) { 787 relayState = ecpRelayState.getValue(); 788 } 789 790 List soapBodies = message.getBodies(); 791 if ((soapBodies == null) || (soapBodies.isEmpty())) { 792 String[] data = { hostEntityId } ; 793 LogUtil.error(Level.INFO, 794 LogUtil.CANNOT_INSTANTIATE_SAML_RESPONSE_FROM_ECP, data, null); 795 SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, 796 "missingSAMLResponse", 797 SAML2Utils.bundle.getString("missingSAMLResponse")); 798 throw new SAML2Exception( 799 SAML2Utils.bundle.getString("missingSAMLResponse")); 800 } 801 802 Element resElem = (Element)soapBodies.get(0); 803 804 Response resp = null; 805 try { 806 resp = ProtocolFactory.getInstance().createResponse(resElem); 807 } catch (SAML2Exception se) { 808 if (SAML2Utils.debug.messageEnabled()) { 809 SAML2Utils.debug.message("SPACSUtils.getResponseFromPostECP:" + 810 "Couldn't create Response:", se); 811 } 812 String[] data = { hostEntityId } ; 813 LogUtil.error(Level.INFO, 814 LogUtil.CANNOT_INSTANTIATE_SAML_RESPONSE_FROM_ECP, data, null); 815 SAMLUtils.sendError(request, response, 816 response.SC_INTERNAL_SERVER_ERROR, 817 "failedToCreateResponse", se.getMessage()); 818 throw se; 819 } 820 821 String idpEntityID = resp.getIssuer().getValue(); 822 IDPSSODescriptorElement idpDesc = null; 823 try { 824 idpDesc = metaManager.getIDPSSODescriptor(orgName, idpEntityID); 825 } catch (SAML2MetaException se) { 826 String[] data = { orgName, idpEntityID }; 827 LogUtil.error(Level.INFO, LogUtil.IDP_META_NOT_FOUND, data, null); 828 SAMLUtils.sendError(request, response, 829 response.SC_INTERNAL_SERVER_ERROR, 830 "failedToGetIDPSSODescriptor", se.getMessage()); 831 throw se; 832 } 833 834 Set<X509Certificate> certificates = KeyUtil.getVerificationCerts(idpDesc, idpEntityID, SAML2Constants.IDP_ROLE); 835 List assertions = resp.getAssertion(); 836 if ((assertions != null) && (!assertions.isEmpty())) { 837 for(Iterator iter = assertions.iterator(); iter.hasNext(); ) { 838 Assertion assertion = (Assertion)iter.next(); 839 if (!assertion.isSigned()) { 840 if (SAML2Utils.debug.messageEnabled()) { 841 SAML2Utils.debug.message( 842 "SPACSUtils.getResponseFromPostECP: " + 843 " Assertion is not signed."); 844 } 845 String[] data = { idpEntityID }; 846 LogUtil.error(Level.INFO, 847 LogUtil.ECP_ASSERTION_NOT_SIGNED, data, null); 848 SAMLUtils.sendError(request, response, 849 response.SC_INTERNAL_SERVER_ERROR, 850 "assertionNotSigned", 851 SAML2Utils.bundle.getString("assertionNotSigned")); 852 throw new SAML2Exception( 853 SAML2Utils.bundle.getString("assertionNotSigned")); 854 } else if (!assertion.isSignatureValid(certificates)) { 855 if (SAML2Utils.debug.messageEnabled()) { 856 SAML2Utils.debug.message( 857 "SPACSUtils.getResponseFromPostECP: " + 858 " Assertion signature is invalid."); 859 } 860 String[] data = { idpEntityID }; 861 LogUtil.error(Level.INFO, 862 LogUtil.ECP_ASSERTION_INVALID_SIGNATURE, data, null); 863 SAMLUtils.sendError(request, response, 864 response.SC_INTERNAL_SERVER_ERROR, 865 "invalidSignature", 866 SAML2Utils.bundle.getString("invalidSignature")); 867 throw new SAML2Exception( 868 SAML2Utils.bundle.getString("invalidSignature")); 869 } 870 } 871 } 872 873 return new ResponseInfo(resp, SAML2Constants.PAOS, relayState); 874 } 875 876 // Obtains SAML Response from POST. 877 private static ResponseInfo getResponseFromPost(HttpServletRequest request, HttpServletResponse response, 878 String orgName, String hostEntityId, SAML2MetaManager metaManager) 879 throws SAML2Exception,IOException { 880 881 String classMethod = "SPACSUtils:getResponseFromPost:"; 882 SAML2Utils.debug.message(classMethod); 883 884 String samlArt = request.getParameter(SAML2Constants.SAML_ART); 885 if (StringUtils.isNotBlank(samlArt)) { 886 return new ResponseInfo(getResponseFromArtifact(samlArt, hostEntityId, request, response, 887 orgName, metaManager), SAML2Constants.HTTP_ARTIFACT, null); 888 } 889 890 String samlResponse = request.getParameter(SAML2Constants.SAML_RESPONSE); 891 if (samlResponse == null) { 892 LogUtil.error(Level.INFO, LogUtil.MISSING_SAML_RESPONSE_FROM_POST, null, null); 893 SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, "missingSAMLResponse", 894 SAML2Utils.bundle.getString("missingSAMLResponse")); 895 throw new SAML2Exception(SAML2Utils.bundle.getString("missingSAMLResponse")); 896 } 897 898 // Get Response back 899 // decode the Response 900 Response resp = null; 901 try { 902 byte[] raw = Base64.decode(samlResponse); 903 if (raw != null) { 904 try (ByteArrayInputStream bis = new ByteArrayInputStream(raw)) { 905 Document doc = XMLUtils.toDOMDocument(bis, SAML2Utils.debug); 906 if (doc != null) { 907 resp = ProtocolFactory.getInstance().createResponse(doc.getDocumentElement()); 908 } 909 } 910 } 911 } catch (SAML2Exception se) { 912 SAML2Utils.debug.error("{} Exception when instantiating SAMLResponse: {}", classMethod, samlResponse, se); 913 SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, "errorObtainResponse", 914 SAML2Utils.bundle.getString("errorObtainResponse")); 915 LogUtil.error(Level.INFO, LogUtil.CANNOT_INSTANTIATE_RESPONSE_POST, null, null); 916 throw new SAML2Exception(SAML2Utils.bundle.getString("errorObtainResponse")); 917 } 918 919 if (resp != null) { 920 String[] data = {""}; 921 if (LogUtil.isAccessLoggable(Level.FINE)) { 922 data[0] = resp.toXMLString(); 923 } 924 LogUtil.access(Level.INFO, LogUtil.GOT_RESPONSE_FROM_POST, data,null); 925 return (new ResponseInfo(resp, SAML2Constants.HTTP_POST, null)); 926 } 927 928 SAML2Utils.debug.message("{} Decoded response is null for SAMLResponse: {}", classMethod, samlResponse); 929 LogUtil.error(Level.INFO, LogUtil.CANNOT_DECODE_RESPONSE, null, null); 930 throw new SAML2Exception(SAML2Utils.bundle.getString("errorDecodeResponse")); 931 } 932 933 /** 934 * Authenticates user with <code>Response</code>. 935 * Auth session upgrade will be called if input session is 936 * not null. 937 * Otherwise, saml2 auth module is called. The name of the auth module 938 * is retrieved from <code>SPSSOConfig</code>. If not found, "SAML2" will 939 * be used. 940 * 941 * @param request HTTP Servlet request 942 * @param response HTTP Servlet response. 943 * @param out the print writer for writing out presentation 944 * @param metaAlias metaAlias for the service provider 945 * @param session input session object. It could be null. 946 * @param respInfo <code>ResponseInfo</code> to be verified. 947 * @param realm realm or organization name of the service provider. 948 * @param hostEntityId hosted service provider Entity ID. 949 * @param metaManager <code>SAML2MetaManager</code> instance for meta operation. 950 * @param auditor a <code>SAML2EventLogger</code> auditor object to hook into 951 * tracking information for the saml request 952 * @return <code>Object</code> which holds result of the session. 953 * @throws SAML2Exception if the processing failed. 954 */ 955 public static Object processResponse( 956 HttpServletRequest request, HttpServletResponse response, PrintWriter out, 957 String metaAlias, Object session, ResponseInfo respInfo, 958 String realm, String hostEntityId, SAML2MetaManager metaManager, SAML2EventLogger auditor 959 ) throws SAML2Exception { 960 961 String classMethod = "SPACSUtils.processResponse: "; 962 if (SAML2Utils.debug.messageEnabled()) { 963 SAML2Utils.debug.message(classMethod + "Response : " + 964 respInfo.getResponse()); 965 } 966 Map smap = null; 967 try { 968 // check Response/Assertion and get back a Map of relevant data 969 smap = SAML2Utils.verifyResponse(request, response, 970 respInfo.getResponse(), realm, hostEntityId, 971 respInfo.getProfileBinding()); 972 } catch (SAML2Exception se) { 973 // invoke SPAdapter for failure 974 invokeSPAdapterForSSOFailure(hostEntityId, realm, 975 request, response, smap, respInfo, 976 SAML2ServiceProviderAdapter.INVALID_RESPONSE, se); 977 throw se; 978 } 979 980 com.sun.identity.saml2.assertion.Subject assertionSubject = 981 (com.sun.identity.saml2.assertion.Subject) 982 smap.get(SAML2Constants.SUBJECT); 983 NameID nameId = assertionSubject.getNameID(); 984 EncryptedID encId = assertionSubject.getEncryptedID(); 985 Assertion authnAssertion = 986 (Assertion) smap.get(SAML2Constants.POST_ASSERTION); 987 String sessionIndex = (String)smap.get(SAML2Constants.SESSION_INDEX); 988 respInfo.setSessionIndex(sessionIndex); 989 Integer authLevel = (Integer) smap.get(SAML2Constants.AUTH_LEVEL); 990 Long maxSessionTime = (Long) smap.get(SAML2Constants.MAX_SESSION_TIME); 991 String inRespToResp = (String) smap.get(SAML2Constants.IN_RESPONSE_TO); 992 List assertions = (List) smap.get(SAML2Constants.ASSERTIONS); 993 994 if (SAML2Utils.debug.messageEnabled()) { 995 SAML2Utils.debug.message(classMethod + "Assertions : " + 996 assertions); 997 } 998 999 SPSSOConfigElement spssoconfig = 1000 metaManager.getSPSSOConfig(realm, hostEntityId); 1001 1002 // get mappers 1003 SPAccountMapper acctMapper = SAML2Utils.getSPAccountMapper(realm, hostEntityId); 1004 SPAttributeMapper attrMapper = SAML2Utils.getSPAttributeMapper(realm, hostEntityId); 1005 1006 boolean needAssertionEncrypted = 1007 Boolean.parseBoolean(SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, 1008 SAML2Constants.WANT_ASSERTION_ENCRYPTED)); 1009 1010 boolean needAttributeEncrypted = getNeedAttributeEncrypted(needAssertionEncrypted, spssoconfig); 1011 boolean needNameIDEncrypted = getNeedNameIDEncrypted(needAssertionEncrypted, spssoconfig); 1012 1013 Set<PrivateKey> decryptionKeys = KeyUtil.getDecryptionKeys(spssoconfig); 1014 if (needNameIDEncrypted && encId == null) { 1015 SAML2Utils.debug.error(classMethod + 1016 "process: NameID was not encrypted."); 1017 SAML2Exception se = new SAML2Exception(SAML2Utils.bundle.getString( 1018 "nameIDNotEncrypted")); 1019 // invoke SPAdapter for failure 1020 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1021 request, response, smap, respInfo, 1022 SAML2ServiceProviderAdapter.INVALID_RESPONSE, se); 1023 throw se; 1024 } 1025 if (encId != null) { 1026 try { 1027 nameId = encId.decrypt(decryptionKeys); 1028 } catch (SAML2Exception se) { 1029 // invoke SPAdapter for failure 1030 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1031 request, response, smap, respInfo, 1032 SAML2ServiceProviderAdapter.INVALID_RESPONSE, se); 1033 throw se; 1034 } 1035 } 1036 respInfo.setNameId(nameId); 1037 1038 SPSSODescriptorElement spDesc = null; 1039 try { 1040 spDesc = metaManager.getSPSSODescriptor(realm, hostEntityId); 1041 } catch (SAML2MetaException ex) { 1042 SAML2Utils.debug.error(classMethod, ex); 1043 } 1044 if (spDesc == null) { 1045 SAML2Exception se = new SAML2Exception(SAML2Utils.bundle.getString( 1046 "metaDataError")); 1047 invokeSPAdapterForSSOFailure(hostEntityId, realm, request, 1048 response, smap, respInfo, 1049 SAML2ServiceProviderAdapter.SSO_FAILED_META_DATA_ERROR, se); 1050 throw se; 1051 } 1052 String nameIDFormat = nameId.getFormat(); 1053 if (nameIDFormat != null) { 1054 List spNameIDFormatList = spDesc.getNameIDFormat(); 1055 1056 if ((spNameIDFormatList != null) && (!spNameIDFormatList.isEmpty()) 1057 && (!spNameIDFormatList.contains(nameIDFormat))) { 1058 1059 Object[] args = { nameIDFormat }; 1060 SAML2Exception se = new SAML2Exception(SAML2Utils.BUNDLE_NAME, 1061 "unsupportedNameIDFormatSP", args); 1062 1063 invokeSPAdapterForSSOFailure(hostEntityId, realm, request, 1064 response, smap, respInfo, 1065 SAML2ServiceProviderAdapter.INVALID_RESPONSE, se); 1066 throw se; 1067 } 1068 } 1069 1070 boolean isTransient = SAML2Constants.NAMEID_TRANSIENT_FORMAT.equals(nameIDFormat); 1071 boolean ignoreProfile = SAML2PluginsUtils.isIgnoredProfile(session, realm); 1072 String existUserName = null; 1073 SessionProvider sessionProvider = null; 1074 try { 1075 sessionProvider = SessionManager.getProvider(); 1076 } catch (SessionException se) { 1077 // invoke SPAdapter for failure 1078 SAML2Exception se2 = new SAML2Exception(se); 1079 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1080 request, response, smap, respInfo, 1081 SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2); 1082 throw se2; 1083 } 1084 if (session != null) { 1085 try { 1086 existUserName = sessionProvider.getPrincipalName(session); 1087 } catch (SessionException se) { 1088 // invoke SPAdapter for failure 1089 SAML2Exception se2 = new SAML2Exception(se); 1090 invokeSPAdapterForSSOFailure(hostEntityId, realm, request, response, smap, respInfo, 1091 SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2); 1092 throw se2; 1093 } 1094 } 1095 1096 String remoteHostId = authnAssertion.getIssuer().getValue(); 1097 String userName = null; 1098 boolean isNewAccountLink = false; 1099 boolean shouldPersistNameID = !isTransient && !ignoreProfile 1100 && acctMapper.shouldPersistNameIDFormat(realm, hostEntityId, remoteHostId, nameIDFormat); 1101 try { 1102 if (shouldPersistNameID) { 1103 if (SAML2Utils.debug.messageEnabled()) { 1104 SAML2Utils.debug.message(classMethod + "querying data store for existing federation links: realm = " 1105 + realm + " hostEntityID = " + hostEntityId + " remoteEntityID = " + remoteHostId); 1106 } 1107 1108 try { 1109 userName = SAML2Utils.getDataStoreProvider().getUserID(realm, SAML2Utils.getNameIDKeyMap( 1110 nameId, hostEntityId, remoteHostId, realm, SAML2Constants.SP_ROLE)); 1111 } catch (DataStoreProviderException dse) { 1112 SAML2Utils.debug.error(classMethod + "DataStoreProviderException whilst retrieving NameID " + 1113 "information", dse); 1114 throw new SAML2Exception(dse.getMessage()); 1115 } 1116 } 1117 if (userName == null) { 1118 userName = acctMapper.getIdentity(authnAssertion, hostEntityId, realm); 1119 isNewAccountLink = true; 1120 } 1121 } catch (SAML2Exception se) { 1122 // invoke SPAdapter for failure 1123 invokeSPAdapterForSSOFailure(hostEntityId, realm, request, response, smap, respInfo, 1124 SAML2ServiceProviderAdapter.SSO_FAILED_NO_USER_MAPPING, se); 1125 throw se; 1126 } 1127 1128 if (userName == null && respInfo.isLocalLogin()) { 1129 // In case we just got authenticated locally, we should accept the freshly authenticated session's principal 1130 // as the username corresponding to the received assertion. 1131 userName = existUserName; 1132 } 1133 if (null != auditor) { 1134 auditor.setUserId(userName); 1135 } 1136 if (SAML2Utils.debug.messageEnabled()) { 1137 SAML2Utils.debug.message( 1138 classMethod + "process: userName =[" + userName + "]"); 1139 } 1140 List attrs = null; 1141 for (Iterator it = assertions.iterator(); it.hasNext(); ) { 1142 Assertion assertion = (Assertion)it.next(); 1143 List origAttrs = getSAMLAttributes(assertion, needAttributeEncrypted, decryptionKeys); 1144 if (origAttrs != null && !origAttrs.isEmpty()) { 1145 if (attrs == null) { 1146 attrs = new ArrayList(); 1147 } 1148 attrs.addAll(origAttrs); 1149 } 1150 } 1151 Map attrMap = null; 1152 if (attrs != null) { 1153 try { 1154 attrMap = attrMapper.getAttributes(attrs, userName, 1155 hostEntityId, remoteHostId, realm); 1156 } catch (SAML2Exception se) { 1157 // invoke SPAdapter for failure 1158 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1159 request, response, smap, respInfo, 1160 SAML2ServiceProviderAdapter.SSO_FAILED_ATTRIBUTE_MAPPING, 1161 se); 1162 throw se; 1163 } 1164 } 1165 if (SAML2Utils.debug.messageEnabled()) { 1166 SAML2Utils.debug.message( 1167 classMethod + "process: remoteHostId = " + remoteHostId); 1168 SAML2Utils.debug.message( 1169 classMethod + "process: attrMap = " + attrMap); 1170 } 1171 respInfo.setAttributeMap(attrMap); 1172 1173 // return error code for local user login 1174 if (StringUtils.isEmpty(userName)) { 1175 // If we couldn't determine the username based on the incoming assertion, then we shouldn't automatically 1176 // map the user to the existing session. 1177 if (session != null) { 1178 try { 1179 sessionProvider.invalidateSession(session, request, response); 1180 } catch (SessionException se) { 1181 SAML2Utils.debug.error("An error occurred while trying to invalidate session", se); 1182 } 1183 } 1184 throw new SAML2Exception(SAML2Utils.bundle.getString("noUserMapping")); 1185 } 1186 1187 boolean writeFedInfo = isNewAccountLink && shouldPersistNameID; 1188 1189 if (SAML2Utils.debug.messageEnabled()) { 1190 SAML2Utils.debug.message( 1191 classMethod + "userName : " + userName); 1192 SAML2Utils.debug.message( 1193 classMethod + "writeFedInfo : " + writeFedInfo); 1194 } 1195 AuthnRequest authnRequest = null; 1196 if (smap != null) { 1197 authnRequest = (AuthnRequest) 1198 smap.get(SAML2Constants.AUTHN_REQUEST); 1199 } 1200 if (inRespToResp != null && inRespToResp.length() != 0) { 1201 SPCache.requestHash.remove(inRespToResp); 1202 } 1203 Map sessionInfoMap = new HashMap(); 1204 sessionInfoMap.put(SessionProvider.REALM, realm); 1205 sessionInfoMap.put(SessionProvider.PRINCIPAL_NAME, userName); 1206 // set client info. always use client IP address to prevent 1207 // reverse host lookup 1208 String clientAddr = ClientUtils.getClientIPAddress(request); 1209 sessionInfoMap.put(SessionProvider.HOST, clientAddr); 1210 sessionInfoMap.put(SessionProvider.HOST_NAME, clientAddr); 1211 sessionInfoMap.put(SessionProvider.AUTH_LEVEL, 1212 String.valueOf(authLevel)); 1213 request.setAttribute(SessionProvider.ATTR_MAP, attrMap); 1214 try { 1215 session = sessionProvider.createSession( 1216 sessionInfoMap, request, response, null); 1217 } catch (SessionException se) { 1218 // invoke SPAdapter for failure 1219 int failureCode = 1220 SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_GENERATION; 1221 int sessCode = se.getErrCode(); 1222 if (sessCode == SessionException.AUTH_USER_INACTIVE) { 1223 failureCode = 1224 SAML2ServiceProviderAdapter.SSO_FAILED_AUTH_USER_INACTIVE; 1225 } else if (sessCode == SessionException.AUTH_USER_LOCKED) { 1226 failureCode = 1227 SAML2ServiceProviderAdapter.SSO_FAILED_AUTH_USER_LOCKED; 1228 } else if (sessCode == SessionException.AUTH_ACCOUNT_EXPIRED) { 1229 failureCode = 1230 SAML2ServiceProviderAdapter.SSO_FAILED_AUTH_ACCOUNT_EXPIRED; 1231 } 1232 if (SAML2Utils.debug.messageEnabled()) { 1233 SAML2Utils.debug.message( 1234 "SPACSUtils.processResponse : error code=" + sessCode, se); 1235 } 1236 SAML2Exception se2 = new SAML2Exception(se); 1237 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1238 request, response, smap, respInfo, failureCode, se2); 1239 throw se2; 1240 } 1241 1242 // set metaAlias 1243 String[] values = { metaAlias }; 1244 try { 1245 setAttrMapInSession(sessionProvider, attrMap, session); 1246 setDiscoBootstrapCredsInSSOToken(sessionProvider, authnAssertion, 1247 session); 1248 sessionProvider.setProperty( 1249 session, SAML2Constants.SP_METAALIAS, values); 1250 } catch (SessionException se) { 1251 // invoke SPAdapter for failure 1252 SAML2Exception se2 = new SAML2Exception(se); 1253 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1254 request, response, smap, respInfo, 1255 SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2); 1256 throw se2; 1257 } 1258 1259 NameIDInfo info = null; 1260 String affiID = nameId.getSPNameQualifier(); 1261 boolean isDualRole = SAML2Utils.isDualRole(hostEntityId, realm); 1262 AffiliationDescriptorType affiDesc = null; 1263 if (affiID != null && !affiID.isEmpty()) { 1264 affiDesc = metaManager.getAffiliationDescriptor(realm, affiID); 1265 } 1266 1267 if (affiDesc != null) { 1268 if (!affiDesc.getAffiliateMember().contains(hostEntityId)) { 1269 throw new SAML2Exception(SAML2Utils.bundle.getString( 1270 "spNotAffiliationMember")); 1271 } 1272 if (isDualRole) { 1273 info = new NameIDInfo(affiID, remoteHostId, nameId, 1274 SAML2Constants.DUAL_ROLE, true); 1275 } else { 1276 info = new NameIDInfo(affiID, remoteHostId, nameId, 1277 SAML2Constants.SP_ROLE, true); 1278 } 1279 } else { 1280 if (isDualRole) { 1281 info = new NameIDInfo(hostEntityId, remoteHostId, nameId, 1282 SAML2Constants.DUAL_ROLE, false); 1283 } else { 1284 info = new NameIDInfo(hostEntityId, remoteHostId, nameId, 1285 SAML2Constants.SP_ROLE, false); 1286 } 1287 } 1288 Map props = new HashMap(); 1289 String nameIDValueString = info.getNameIDValue(); 1290 props.put(LogUtil.NAME_ID, info.getNameIDValue()); 1291 try { 1292 userName = sessionProvider.getPrincipalName(session); 1293 } catch (SessionException se) { 1294 // invoke SPAdapter for failure 1295 SAML2Exception se2 = new SAML2Exception(se); 1296 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1297 request, response, smap, respInfo, 1298 SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2); 1299 throw se2; 1300 } 1301 String[] data1 = {userName, nameIDValueString}; 1302 LogUtil.access(Level.INFO, LogUtil.SUCCESS_FED_SSO, data1, session, 1303 props); 1304 // write fed info into data store 1305 if (writeFedInfo) { 1306 try { 1307 AccountUtils.setAccountFederation(info, userName); 1308 } catch (SAML2Exception se) { 1309 // invoke SPAdapter for failure 1310 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1311 request, response, smap, respInfo, 1312 SAML2ServiceProviderAdapter.FEDERATION_FAILED_WRITING_ACCOUNT_INFO, se); 1313 throw se; 1314 } 1315 String[] data = {userName, ""}; 1316 if (LogUtil.isAccessLoggable(Level.FINE)) { 1317 data[1] = info.toValueString(); 1318 } 1319 LogUtil.access(Level.INFO, 1320 LogUtil.FED_INFO_WRITTEN, 1321 data, 1322 session, 1323 props); 1324 } 1325 String requestID = respInfo.getResponse().getInResponseTo(); 1326 // save info in memory for logout 1327 saveInfoInMemory(sessionProvider, session, sessionIndex, metaAlias, 1328 info, IDPProxyUtil.isIDPProxyEnabled(requestID), isTransient); 1329 1330 // invoke SP Adapter 1331 SAML2ServiceProviderAdapter spAdapter = 1332 SAML2Utils.getSPAdapterClass(hostEntityId, realm); 1333 if (spAdapter != null) { 1334 boolean redirected = spAdapter.postSingleSignOnSuccess( 1335 hostEntityId, realm, request, 1336 response, out, session, authnRequest, respInfo.getResponse(), 1337 respInfo.getProfileBinding(), writeFedInfo); 1338 String[] value = null; 1339 if (redirected) { 1340 value = new String[] {"true"}; 1341 } else { 1342 value = new String[] {"false"}; 1343 } 1344 try { 1345 sessionProvider.setProperty(session, 1346 SAML2Constants.RESPONSE_REDIRECTED, value); 1347 } catch (SessionException ex) { 1348 SAML2Utils.debug.warning("SPSingleLogout.processResp", ex); 1349 } catch (UnsupportedOperationException ex) { 1350 SAML2Utils.debug.warning("SPSingleLogout.processResp", ex); 1351 } 1352 } 1353 1354 String assertionID=authnAssertion.getID(); 1355 if (respInfo.getProfileBinding().equals(SAML2Constants.HTTP_POST)) { 1356 SPCache.assertionByIDCache.put(assertionID, SAML2Constants.ONETIME); 1357 try { 1358 if (SAML2FailoverUtils.isSAML2FailoverEnabled()) { 1359 SAML2FailoverUtils.saveSAML2TokenWithoutSecondaryKey( 1360 assertionID, 1361 SAML2Constants.ONETIME, 1362 ((Long) smap.get(SAML2Constants.NOTONORAFTER)).longValue() / 1000); 1363 } 1364 } catch (SAML2TokenRepositoryException se) { 1365 SAML2Utils.debug.error(classMethod + 1366 "There was a problem saving the assertionID to the SAML2 Token Repository for assertionID:" 1367 + assertionID, se); 1368 } 1369 } 1370 respInfo.setAssertion(authnAssertion); 1371 1372 return session; 1373 } 1374 1375 private static boolean getNeedNameIDEncrypted(boolean needAssertionEncrypted, SPSSOConfigElement spssoconfig) { 1376 if (!needAssertionEncrypted) { 1377 return Boolean.parseBoolean(SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, 1378 SAML2Constants.WANT_NAMEID_ENCRYPTED)); 1379 } 1380 1381 return false; 1382 } 1383 1384 public static boolean getNeedAttributeEncrypted(boolean needAssertionEncrypted, SPSSOConfigElement spssoconfig) { 1385 if (!needAssertionEncrypted) { 1386 return Boolean.parseBoolean(SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, 1387 SAML2Constants.WANT_ATTRIBUTE_ENCRYPTED)); 1388 } 1389 1390 return false; 1391 } 1392 1393 private static void invokeSPAdapterForSSOFailure(String hostEntityId, 1394 String realm, HttpServletRequest request, HttpServletResponse response, 1395 Map smap, ResponseInfo respInfo, int errorCode, 1396 SAML2Exception se) { 1397 SAML2ServiceProviderAdapter spAdapter = null; 1398 try { 1399 spAdapter = SAML2Utils.getSPAdapterClass(hostEntityId, realm); 1400 } catch (SAML2Exception e) { 1401 if (SAML2Utils.debug.messageEnabled()) { 1402 SAML2Utils.debug.message( 1403 "SPACSUtils.invokeSPAdapterForSSOFailure", e); 1404 } 1405 } 1406 if (spAdapter != null) { 1407 AuthnRequest authnRequest = null; 1408 if (smap != null) { 1409 authnRequest = (AuthnRequest) 1410 smap.get(SAML2Constants.AUTHN_REQUEST); 1411 } 1412 boolean redirected = spAdapter.postSingleSignOnFailure( 1413 hostEntityId, realm, request, response, authnRequest, 1414 respInfo.getResponse(), respInfo.getProfileBinding(), 1415 errorCode); 1416 se.setRedirectionDone(redirected); 1417 } 1418 } 1419 1420 public static void saveInfoInMemory(SessionProvider sessionProvider, 1421 Object session, String sessionIndex, String metaAlias, 1422 NameIDInfo info, boolean isIDPProxy, boolean isTransient) 1423 throws SAML2Exception { 1424 1425 String infoKeyString = (new NameIDInfoKey( 1426 info.getNameIDValue(), 1427 info.getHostEntityID(), 1428 info.getRemoteEntityID())).toValueString(); 1429 String infoKeyAttribute = 1430 AccountUtils.getNameIDInfoKeyAttribute(); 1431 String[] fromToken = null; 1432 try { 1433 fromToken = sessionProvider. 1434 getProperty(session, infoKeyAttribute); 1435 if (fromToken == null || fromToken.length == 0 || 1436 fromToken[0] == null || fromToken[0].length() == 0) { 1437 String[] values = { infoKeyString }; 1438 sessionProvider.setProperty( 1439 session, infoKeyAttribute, values); 1440 } else { 1441 if (fromToken[0].indexOf(infoKeyString) == -1) { 1442 String[] values = { fromToken[0] + 1443 SAML2Constants.SECOND_DELIM + 1444 infoKeyString }; 1445 sessionProvider.setProperty( 1446 session, infoKeyAttribute, values); 1447 } 1448 } 1449 if (isTransient) { 1450 String nameIDInfoStr = info.toValueString(); 1451 String infoAttribute = AccountUtils.getNameIDInfoAttribute(); 1452 String[] nameIDInfoStrs = sessionProvider.getProperty(session, 1453 infoAttribute); 1454 if (nameIDInfoStrs == null) { 1455 nameIDInfoStrs = new String[1]; 1456 nameIDInfoStrs[0] = nameIDInfoStr; 1457 } else { 1458 Set nameIDInfoStrSet = new HashSet(); 1459 for(int i=0; i<nameIDInfoStrs.length; i++) { 1460 nameIDInfoStrSet.add(nameIDInfoStrs[i]); 1461 } 1462 nameIDInfoStrSet.add(nameIDInfoStr); 1463 nameIDInfoStrs = (String[])nameIDInfoStrSet.toArray( 1464 new String[nameIDInfoStrSet.size()]); 1465 } 1466 sessionProvider.setProperty(session, infoAttribute, 1467 nameIDInfoStrs); 1468 } 1469 } catch (SessionException sessE) { 1470 throw new SAML2Exception(sessE); 1471 } 1472 String tokenID = sessionProvider.getSessionID(session); 1473 if (!SPCache.isFedlet) { 1474 List fedSessions = (List) SPCache.fedSessionListsByNameIDInfoKey.get(infoKeyString); 1475 if (isIDPProxy) { 1476 IDPSession idpSess = IDPCache.idpSessionsBySessionID.get(tokenID); 1477 if (idpSess == null) { 1478 idpSess = new IDPSession(session); 1479 IDPCache.idpSessionsBySessionID.put(tokenID, idpSess); 1480 } 1481 SAML2Utils.debug.message("Add Session Partner: {}", info.getRemoteEntityID()); 1482 idpSess.addSessionPartner(new SAML2SessionPartner(info.getRemoteEntityID(), true)); 1483 } 1484 1485 if (fedSessions == null) { 1486 synchronized (SPCache.fedSessionListsByNameIDInfoKey) { 1487 fedSessions = (List) 1488 SPCache.fedSessionListsByNameIDInfoKey.get(infoKeyString); 1489 if (fedSessions == null) { 1490 fedSessions = new ArrayList(); 1491 } 1492 } 1493 synchronized (fedSessions) { 1494 fedSessions.add(new SPFedSession(sessionIndex, tokenID, 1495 info, metaAlias)); 1496 SPCache.fedSessionListsByNameIDInfoKey.put( 1497 infoKeyString, fedSessions); 1498 } 1499 if ((agent != null) && agent.isRunning() && (saml2Svc != null)){ 1500 saml2Svc.setFedSessionCount( 1501 (long)SPCache.fedSessionListsByNameIDInfoKey.size()); 1502 } 1503 } else { 1504 synchronized (fedSessions) { 1505 Iterator iter = fedSessions.iterator(); 1506 boolean found = false; 1507 while (iter.hasNext()) { 1508 SPFedSession temp = (SPFedSession) iter.next(); 1509 String idpSessionIndex = null; 1510 if(temp != null) { 1511 idpSessionIndex = temp.idpSessionIndex; 1512 } 1513 if ((idpSessionIndex != null) && 1514 (idpSessionIndex.equals(sessionIndex))) { 1515 temp.spTokenID = tokenID; 1516 temp.info = info; 1517 found = true; 1518 break; 1519 } 1520 } 1521 if (!found) { 1522 fedSessions.add( 1523 new SPFedSession(sessionIndex, tokenID, info, 1524 metaAlias)); 1525 SPCache.fedSessionListsByNameIDInfoKey.put( 1526 infoKeyString, fedSessions); 1527 if ((agent != null) && 1528 agent.isRunning() && 1529 (saml2Svc != null)) 1530 { 1531 saml2Svc.setFedSessionCount( 1532 (long)SPCache.fedSessionListsByNameIDInfoKey. 1533 size()); 1534 } 1535 } 1536 } 1537 } 1538 SPCache.fedSessionListsByNameIDInfoKey.put(infoKeyString, 1539 fedSessions); 1540 if ((agent != null) && agent.isRunning() && (saml2Svc != null)) { 1541 saml2Svc.setFedSessionCount( 1542 (long)SPCache.fedSessionListsByNameIDInfoKey.size()); 1543 } 1544 } 1545 try { 1546 sessionProvider.addListener( 1547 session, new SPSessionListener(infoKeyString, tokenID)); 1548 } catch (SessionException e) { 1549 SAML2Utils.debug.error( 1550 "SPACSUtils.saveInfoInMemory: "+ 1551 "Unable to add session listener."); 1552 } 1553 } 1554 1555 /** Sets the attribute map in the session 1556 * 1557 * @param sessionProvider Session provider 1558 * @param attrMap the Attribute Map 1559 * @param session the valid session object 1560 * @throws com.sun.identity.plugin.session.SessionException 1561 */ 1562 public static void setAttrMapInSession( 1563 SessionProvider sessionProvider, 1564 Map attrMap, Object session) 1565 throws SessionException { 1566 if (attrMap != null && !attrMap.isEmpty()) { 1567 Set entrySet = attrMap.entrySet(); 1568 for(Iterator iter = entrySet.iterator(); iter.hasNext();) { 1569 Map.Entry entry = (Map.Entry)iter.next(); 1570 String attrName = (String)entry.getKey(); 1571 Set attrValues = (Set)entry.getValue(); 1572 if(attrValues != null && !attrValues.isEmpty()) { 1573 sessionProvider.setProperty( 1574 session, attrName, 1575 (String[]) attrValues.toArray( 1576 new String[attrValues.size()])); 1577 if (SAML2Utils.debug.messageEnabled()) { 1578 SAML2Utils.debug.message( 1579 "SPACSUtils.setAttrMapInSessioin: AttrMap:" + 1580 attrName + " , " + attrValues); 1581 } 1582 } 1583 } 1584 } 1585 } 1586 1587 /** Sets Discovery bootstrap credentials in the SSOToken 1588 * 1589 * @param sessionProvider session provider. 1590 * @param assertion assertion. 1591 * @param session the valid session object. 1592 */ 1593 private static void setDiscoBootstrapCredsInSSOToken( 1594 SessionProvider sessionProvider, Assertion assertion, Object session) 1595 throws SessionException { 1596 1597 if (assertion == null) { 1598 return; 1599 } 1600 1601 Set discoBootstrapCreds = null; 1602 Advice advice = assertion.getAdvice(); 1603 if (advice != null) { 1604 List creds = advice.getAdditionalInfo(); 1605 if ((creds != null) && !creds.isEmpty()) { 1606 if (discoBootstrapCreds == null) { 1607 discoBootstrapCreds = new HashSet(); 1608 } 1609 discoBootstrapCreds.addAll(creds); 1610 } 1611 } 1612 1613 if (discoBootstrapCreds != null) { 1614 sessionProvider.setProperty(session, 1615 SAML2Constants.DISCOVERY_BOOTSTRAP_CREDENTIALS, 1616 (String[])discoBootstrapCreds.toArray( 1617 new String[discoBootstrapCreds.size()])); 1618 } 1619 } 1620 1621 /** 1622 * Obtains relay state. Retrieves the relay state from relay state cache. 1623 * If input relay state is null, retrieve it from <code>SPSSOConfig</code>. 1624 * 1625 * @param relayStateID relay state value received from http request. 1626 * @param orgName realm or organization name the service provider resides in 1627 * @param hostEntityId Entity ID of the hosted service provider 1628 * @param sm <code>SAML2MetaManager</code> instance. 1629 * @return final relay state. Or <code>null</code> if the input 1630 * relayStateID is null and no default relay state is configured. 1631 */ 1632 public static String getRelayState( 1633 String relayStateID, 1634 String orgName, 1635 String hostEntityId, 1636 SAML2MetaManager sm 1637 ) { 1638 String relayStateUrl = null; 1639 1640 if ((relayStateID != null) && (relayStateID.trim().length() != 0)) { 1641 CacheObject cache = (CacheObject)SPCache.relayStateHash.remove( 1642 relayStateID); 1643 1644 if (cache != null) { 1645 relayStateUrl = (String)cache.getObject(); 1646 } else if (SAML2FailoverUtils.isSAML2FailoverEnabled()) { 1647 // The key is this way to make it unique compared to when 1648 // the same key is used to store a copy of the AuthnRequestInfo 1649 String key = relayStateID + relayStateID; 1650 try { 1651 // Try and retrieve the value from the SAML2 repository 1652 String relayState = (String) SAML2FailoverUtils.retrieveSAML2Token(key); 1653 if (relayState != null) { 1654 // Get back the relayState 1655 relayStateUrl = relayState; 1656 if (SAML2Utils.debug.messageEnabled()) { 1657 SAML2Utils.debug.message("SPACUtils.getRelayState: relayState" 1658 + " retrieved from SAML2 repository for key: " + key); 1659 } 1660 } 1661 } catch (SAML2TokenRepositoryException se) { 1662 SAML2Utils.debug.error("SPACUtils.getRelayState: Unable to retrieve relayState for key " 1663 + key, se); 1664 } 1665 } else { 1666 if (SAML2Utils.debug.messageEnabled()) { 1667 SAML2Utils.debug.message("SPACUtils.getRelayState: relayState" 1668 + " is null for relayStateID: " + relayStateID + ", SAML2 failover is disabled"); 1669 } 1670 } 1671 1672 if (relayStateUrl == null || relayStateUrl.trim().length() == 0) { 1673 relayStateUrl = relayStateID; 1674 } 1675 } 1676 1677 if (relayStateUrl == null || relayStateUrl.trim().length() == 0) { 1678 relayStateUrl = getAttributeValueFromSPSSOConfig( 1679 orgName, hostEntityId, sm, SAML2Constants.DEFAULT_RELAY_STATE); 1680 } 1681 1682 return relayStateUrl; 1683 } 1684 1685 /** 1686 * Retrieves intermediate redirect url from SP sso config. This url is used 1687 * if you want to goto some place before the final relay state. 1688 * 1689 * @param orgName realm or organization name the service provider resides in 1690 * @param hostEntityId Entity ID of the hosted service provider 1691 * @param sm <code>SAML2MetaManager</code> instance. 1692 * @return intermediate redirect url; or <code>null</code> if the url is 1693 * is not configured or an error occured during the retrieval 1694 * process. 1695 */ 1696 public static String getIntermediateURL(String orgName, 1697 String hostEntityId, 1698 SAML2MetaManager sm) 1699 { 1700 return getAttributeValueFromSPSSOConfig(orgName, hostEntityId, sm, 1701 SAML2Constants.INTERMEDIATE_URL); 1702 } 1703 1704 /** 1705 * Saves response for later retrieval and retrieves local auth url from <code>SPSSOConfig</code>. 1706 * If the url does not exist, generate one from request URI. 1707 * If still cannot get it, (shouldn't happen), get it from {@link SystemConfigurationUtil}. 1708 * 1709 * @param realm Realm or organization name the service provider resides in. 1710 * @param hostEntityId Entity ID of the hosted service provider. 1711 * @param sm <code>SAML2MetaManager</code> instance to perform metadata operations. 1712 * @param respInfo The to be cached <code>ResponseInfo</code>. 1713 * @param requestURI The HTTP request URI. 1714 * @return The local login url. 1715 */ 1716 public static String prepareForLocalLogin(String realm, String hostEntityId, SAML2MetaManager sm, 1717 ResponseInfo respInfo, String requestURI) { 1718 String localLoginUrl = getAttributeValueFromSPSSOConfig(realm, hostEntityId, sm, SAML2Constants.LOCAL_AUTH_URL); 1719 if (StringUtils.isEmpty(localLoginUrl)) { 1720 // get it from request 1721 try { 1722 int index = requestURI.indexOf("Consumer/metaAlias"); 1723 if (index != -1) { 1724 localLoginUrl = requestURI.substring(0, index) + "UI/Login?realm=" + realm; 1725 } 1726 } catch (IndexOutOfBoundsException e) { 1727 localLoginUrl = null; 1728 } 1729 if (StringUtils.isEmpty(localLoginUrl)) { 1730 // shouldn't be here, but in case 1731 localLoginUrl = 1732 SystemConfigurationUtil.getProperty(SAMLConstants.SERVER_PROTOCOL) 1733 + "://" 1734 + SystemConfigurationUtil.getProperty(SAMLConstants.SERVER_HOST) 1735 + SystemConfigurationUtil.getProperty(SAMLConstants.SERVER_PORT) 1736 + "/UI/Login?realm=" 1737 + realm; 1738 } 1739 } 1740 1741 respInfo.setIsLocalLogin(true); 1742 synchronized (SPCache.responseHash) { 1743 SPCache.responseHash.put(respInfo.getResponse().getID(), respInfo); 1744 } 1745 SAML2Utils.debug.message("SPACSUtils:prepareForLocalLogin: localLoginUrl = {}", localLoginUrl); 1746 1747 return localLoginUrl; 1748 } 1749 1750 /** 1751 * Retrieves attribute value for a given attribute name from 1752 * <code>SPSSOConfig</code>. 1753 * @param orgName realm or organization name the service provider resides in 1754 * @param hostEntityId hosted service provider's Entity ID. 1755 * @param sm <code>SAML2MetaManager</code> instance to perform meta 1756 * operations. 1757 * @param attrName name of the attribute whose value ot be retrived. 1758 * @return value of the attribute; or <code>null</code> if the attribute 1759 * if not configured, or an error occured in the process. 1760 */ 1761 private static String getAttributeValueFromSPSSOConfig(String orgName, 1762 String hostEntityId, 1763 SAML2MetaManager sm, 1764 String attrName) 1765 { 1766 String result = null; 1767 try { 1768 SPSSOConfigElement config = sm.getSPSSOConfig(orgName, 1769 hostEntityId); 1770 if (config == null) { 1771 return null; 1772 } 1773 Map attrs = SAML2MetaUtils.getAttributes(config); 1774 List value = (List) attrs.get(attrName); 1775 if (value != null && value.size() != 0) { 1776 result = ((String) value.iterator().next()).trim(); 1777 } 1778 } catch (SAML2MetaException sme) { 1779 if (SAML2Utils.debug.messageEnabled()) { 1780 SAML2Utils.debug.message("SPACSUtils.getAttributeValueFromSPSSO" 1781 + "Config:", sme); 1782 } 1783 result = null; 1784 } 1785 return result; 1786 } 1787 1788 /** 1789 * Gets the attributes from an assert's AttributeStates. 1790 * 1791 * @param assertion The assertion from which to pull the AttributeStates. 1792 * @param needAttributeEncrypted Whether attributes must be encrypted (or else rejected). 1793 * @param privateKeys Private keys used to decrypt those encrypted attributes. 1794 * @return a list of attributes pulled from the provided assertion. 1795 */ 1796 public static List<Attribute> getSAMLAttributes(Assertion assertion, boolean needAttributeEncrypted, 1797 Set<PrivateKey> privateKeys) { 1798 List<Attribute> attrList = null; 1799 if (assertion != null) { 1800 List<AttributeStatement> statements = assertion.getAttributeStatements(); 1801 if (CollectionUtils.isNotEmpty(statements)) { 1802 for (AttributeStatement statement : statements) { 1803 List<Attribute> attributes = statement.getAttribute(); 1804 if (needAttributeEncrypted && attributes != null && !attributes.isEmpty()) { 1805 SAML2Utils.debug.error("Attribute not encrypted."); 1806 return null; 1807 } 1808 if (attributes != null) { 1809 if (attrList == null) { 1810 attrList = new ArrayList<>(); 1811 } 1812 attrList.addAll(attributes); 1813 } 1814 List<EncryptedAttribute> encAttrs = statement.getEncryptedAttribute(); 1815 if (encAttrs != null) { 1816 for (EncryptedAttribute encAttr : encAttrs) { 1817 if (attrList == null) { 1818 attrList = new ArrayList<>(); 1819 } 1820 try { 1821 attrList.add((encAttr).decrypt(privateKeys)); 1822 } catch (SAML2Exception se) { 1823 SAML2Utils.debug.error("Decryption error:", se); 1824 return null; 1825 } 1826 } 1827 } 1828 } 1829 } 1830 } 1831 return attrList; 1832 } 1833 1834 /** 1835 * Processes response from Identity Provider to Fedlet (SP). 1836 * This will do all required protocol processing, include signature, 1837 * issuer and audience validation etc. A map containing processing 1838 * result will be returned. <br> 1839 * Here is a list of keys and values for the returned map: <br> 1840 * SAML2Constants.ATTRIBUTE_MAP -- Attribute map containing all attributes 1841 * passed down from IDP inside the 1842 * Assertion. The value is a 1843 * <code>java.util.Map</code> whose keys 1844 * are attribute names and values are 1845 * <code>java.util.Set</code> of string 1846 * values for the attributes. <br> 1847 * SAML2Constants.RELAY_STATE -- Relay state, value is a string <br> 1848 * SAML2Constants.IDPENTITYID -- IDP entity ID, value is a string<br> 1849 * SAML2Constants.RESPONSE -- Response object, value is an instance of 1850 * com.sun.identity.saml2.protocol.Response 1851 * SAML2Constants.ASSERTION -- Assertion object, value is an instance of 1852 * com.sun.identity.saml2.assertion.Assertion 1853 * SAML2Constants.SUBJECT -- Subject object, value is an instance of 1854 * com.sun.identity.saml2.assertion.Subject 1855 * SAML2Constants.NAMEID -- NameID object, value is an instance of 1856 * com.sun.identity.saml2.assertion.NameID 1857 * 1858 * @param request HTTP Servlet request 1859 * @param response HTTP Servlet response. 1860 * @param out the print writer for writing out presentation 1861 * 1862 * @return <code>Map</code> which holds result of the processing. 1863 * @throws SAML2Exception if the processing failed due to server error. 1864 * @throws IOException if the processing failed due to IO error. 1865 * @throws SessionException if the processing failed due to session error. 1866 * @throws ServletException if the processing failed due to request error. 1867 * 1868 * @supported.api 1869 */ 1870 public static Map processResponseForFedlet (HttpServletRequest request, 1871 HttpServletResponse response, PrintWriter out) throws SAML2Exception, IOException, 1872 SessionException, ServletException { 1873 if (request == null) { 1874 String message = 1875 MessageFormat.format(SAML2SDKUtils.bundle.getString("nullInputMessage"), new String[]{"request"}); 1876 SAML2SDKUtils.debug.error("SPACSUtils.processResponseForFedlet: " + message); 1877 throw new ServletException(message); 1878 } 1879 if (response == null) { 1880 String message = 1881 MessageFormat.format(SAML2SDKUtils.bundle.getString("nullInputMessage"), new String[]{"response"}); 1882 SAML2SDKUtils.debug.error("SPACSUtils.processResponseForFedlet: " + message); 1883 throw new ServletException(message); 1884 } 1885 1886 String requestURL = request.getRequestURL().toString(); 1887 SAML2MetaManager metaManager = new SAML2MetaManager(); 1888 if (metaManager == null) { 1889 throw new SAML2Exception( 1890 SAML2SDKUtils.bundle.getString("errorMetaManager")); 1891 } 1892 String metaAlias = SAML2MetaUtils.getMetaAliasByUri(requestURL); 1893 if ((metaAlias == null) || (metaAlias.length() == 0)) { 1894 // Check in case metaAlias has been supplied as a parameter 1895 metaAlias = request.getParameter(SAML2MetaManager.NAME_META_ALIAS_IN_URI); 1896 if (metaAlias == null || metaAlias.length() == 0) { 1897 // pick the first available one 1898 List spMetaAliases = 1899 metaManager.getAllHostedServiceProviderMetaAliases("/"); 1900 if ((spMetaAliases != null) && !spMetaAliases.isEmpty()) { 1901 // get first one 1902 metaAlias = (String) spMetaAliases.get(0); 1903 } 1904 if ((metaAlias == null) || (metaAlias.length() == 0)) { 1905 throw new ServletException( 1906 SAML2SDKUtils.bundle.getString("nullSPEntityID")); 1907 } 1908 } 1909 } 1910 String hostEntityId = null; 1911 try { 1912 hostEntityId = metaManager.getEntityByMetaAlias(metaAlias); 1913 } catch (SAML2MetaException sme) { 1914 SAML2SDKUtils.debug.error("SPACSUtils.processResponseForFedlet", 1915 sme); 1916 throw new SAML2Exception( 1917 SAML2SDKUtils.bundle.getString("metaDataError")); 1918 } 1919 if (hostEntityId == null) { 1920 // logging? 1921 throw new SAML2Exception( 1922 SAML2SDKUtils.bundle.getString("metaDataError")); 1923 } 1924 // organization is always root org 1925 String orgName = "/"; 1926 String relayState = request.getParameter(SAML2Constants.RELAY_STATE); 1927 SessionProvider sessionProvider = null; 1928 ResponseInfo respInfo = null; 1929 try { 1930 sessionProvider = SessionManager.getProvider(); 1931 } catch (SessionException se) { 1932 SAML2SDKUtils.debug.error("SPACSUtils.processResponseForFedlet", 1933 se); 1934 throw new SAML2Exception(se); 1935 } 1936 respInfo = SPACSUtils.getResponse( 1937 request, response, orgName, hostEntityId, metaManager); 1938 1939 Object newSession = null; 1940 1941 // Throws a SAML2Exception if the response cannot be validated 1942 // or contains a non-Success StatusCode, invoking the SPAdapter SPI 1943 // for taking action on the failed validation. 1944 // The resulting exception has its redirectionDone flag set if 1945 // the SPAdapter issued a HTTP redirect. 1946 newSession = SPACSUtils.processResponse( 1947 request, response, out, metaAlias, null, respInfo, 1948 orgName, hostEntityId, metaManager, null); 1949 1950 SAML2SDKUtils.debug.message("SSO SUCCESS"); 1951 String[] redirected = sessionProvider.getProperty(newSession, 1952 SAML2Constants.RESPONSE_REDIRECTED); 1953 if ((redirected != null) && (redirected.length != 0) && 1954 redirected[0].equals("true")) { 1955 SAML2SDKUtils.debug.message("Already redirected in SPAdapter."); 1956 // response redirected already in SPAdapter 1957 return createMapForFedlet(respInfo, null, hostEntityId); 1958 } 1959 // redirect to relay state 1960 String finalUrl = SPACSUtils.getRelayState( 1961 relayState, orgName, hostEntityId, metaManager); 1962 String realFinalUrl = finalUrl; 1963 if (finalUrl != null && finalUrl.length() != 0) { 1964 try { 1965 realFinalUrl = 1966 sessionProvider.rewriteURL(newSession, finalUrl); 1967 } catch (SessionException se) { 1968 SAML2SDKUtils.debug.message("SPACSUtils.processRespForFedlet", 1969 se); 1970 realFinalUrl = finalUrl; 1971 } 1972 } 1973 String redirectUrl = SPACSUtils.getIntermediateURL( 1974 orgName, hostEntityId, metaManager); 1975 String realRedirectUrl = null; 1976 if (redirectUrl != null && redirectUrl.length() != 0) { 1977 if (realFinalUrl != null && realFinalUrl.length() != 0) { 1978 if (redirectUrl.indexOf("?") != -1) { 1979 redirectUrl += "&goto="; 1980 } else { 1981 redirectUrl += "?goto="; 1982 } 1983 redirectUrl += urlEncodeQueryParameterNameOrValue(realFinalUrl); 1984 try { 1985 realRedirectUrl = sessionProvider.rewriteURL( 1986 newSession, redirectUrl); 1987 } catch (SessionException se) { 1988 SAML2SDKUtils.debug.message( 1989 "SPACSUtils.processRespForFedlet: rewriting failed.", se); 1990 realRedirectUrl = redirectUrl; 1991 } 1992 } else { 1993 realRedirectUrl = redirectUrl; 1994 } 1995 } else { 1996 realRedirectUrl = finalUrl; 1997 } 1998 return createMapForFedlet(respInfo, realRedirectUrl, hostEntityId); 1999 } 2000 2001 private static Map createMapForFedlet( 2002 ResponseInfo respInfo, String relayUrl, String hostedEntityId) { 2003 Map map = new HashMap(); 2004 if (relayUrl != null) { 2005 map.put(SAML2Constants.RELAY_STATE, relayUrl); 2006 } 2007 Response samlResp = respInfo.getResponse(); 2008 map.put(SAML2Constants.RESPONSE, samlResp); 2009 Assertion assertion = respInfo.getAssertion(); 2010 map.put(SAML2Constants.ASSERTION, assertion); 2011 map.put(SAML2Constants.SUBJECT, assertion.getSubject()); 2012 map.put(SAML2Constants.IDPENTITYID, assertion.getIssuer().getValue()); 2013 map.put(SAML2Constants.SPENTITYID, hostedEntityId); 2014 map.put(SAML2Constants.NAMEID, respInfo.getNameId()); 2015 map.put(SAML2Constants.ATTRIBUTE_MAP, respInfo.getAttributeMap()); 2016 map.put(SAML2Constants.SESSION_INDEX, respInfo.getSessionIndex()); 2017 return map; 2018 } 2019 2020 /** 2021 * Returns the username if there was one from the Assertion we were able to map into a local user account. Returns 2022 * null if not. Should only be used from the SP side. Should only be called in conjuncture with the Auth Module. 2023 * In addition, it performs what attribute federation it can. 2024 * 2025 * This method is a picked apart version of the "processResponse" function. 2026 */ 2027 public static String getPrincipalWithoutLogin(Subject assertionSubject, Assertion authnAssertion, String realm, 2028 String spEntityId, SAML2MetaManager metaManager, String idpEntityId, 2029 String storageKey) 2030 throws SAML2Exception { 2031 2032 final EncryptedID encId = assertionSubject.getEncryptedID(); 2033 final SPSSOConfigElement spssoconfig = metaManager.getSPSSOConfig(realm, spEntityId); 2034 final Set<PrivateKey> decryptionKeys = KeyUtil.getDecryptionKeys(spssoconfig); 2035 final SPAccountMapper acctMapper = SAML2Utils.getSPAccountMapper(realm, spEntityId); 2036 2037 boolean needNameIDEncrypted = false; 2038 NameID nameId = assertionSubject.getNameID(); 2039 2040 String assertionEncryptedAttr = 2041 SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, SAML2Constants.WANT_ASSERTION_ENCRYPTED); 2042 if (!Boolean.parseBoolean(assertionEncryptedAttr)) { 2043 String idEncryptedStr = 2044 SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, SAML2Constants.WANT_NAMEID_ENCRYPTED); 2045 needNameIDEncrypted = Boolean.parseBoolean(idEncryptedStr); 2046 } 2047 2048 if (needNameIDEncrypted && encId == null) { 2049 throw new SAML2Exception(SAML2Utils.bundle.getString("nameIDNotEncrypted")); 2050 } 2051 if (encId != null) { 2052 nameId = encId.decrypt(decryptionKeys); 2053 } 2054 2055 SPSSODescriptorElement spDesc = null; 2056 try { 2057 spDesc = metaManager.getSPSSODescriptor(realm, spEntityId); 2058 } catch (SAML2MetaException ex) { 2059 SAML2Utils.debug.error("Unable to read SPSSODescription", ex); 2060 } 2061 2062 if (spDesc == null) { 2063 throw new SAML2Exception(SAML2Utils.bundle.getString("metaDataError")); 2064 } 2065 2066 final String nameIDFormat = nameId.getFormat(); 2067 2068 if (nameIDFormat != null) { 2069 List spNameIDFormatList = spDesc.getNameIDFormat(); 2070 2071 if (CollectionUtils.isNotEmpty(spNameIDFormatList) && !spNameIDFormatList.contains(nameIDFormat)) { 2072 Object[] args = {nameIDFormat}; 2073 throw new SAML2Exception(SAML2Utils.BUNDLE_NAME, "unsupportedNameIDFormatSP", args); 2074 } 2075 } 2076 2077 final boolean isTransient = SAML2Constants.NAMEID_TRANSIENT_FORMAT.equals(nameIDFormat); 2078 final boolean ignoreProfile = SAML2PluginsUtils.isIgnoredProfile(null, realm); 2079 2080 final boolean shouldPersistNameID = !isTransient && !ignoreProfile 2081 && acctMapper.shouldPersistNameIDFormat(realm, spEntityId, idpEntityId, nameIDFormat); 2082 2083 String userName = null; 2084 boolean isNewAccountLink = false; 2085 2086 try { 2087 if (shouldPersistNameID) { 2088 try { 2089 userName = SAML2Utils.getDataStoreProvider().getUserID(realm, SAML2Utils.getNameIDKeyMap( 2090 nameId, spEntityId, idpEntityId, realm, SAML2Constants.SP_ROLE)); 2091 } catch (DataStoreProviderException dse) { 2092 throw new SAML2Exception(dse.getMessage()); 2093 } 2094 } 2095 2096 //if we can't get an already linked account, see if we'll be generating a new one based on federated data 2097 if (userName == null) { 2098 userName = acctMapper.getIdentity(authnAssertion, spEntityId, realm); 2099 isNewAccountLink = true; //we'll use this later to inform us 2100 } 2101 } catch (SAML2Exception se) { 2102 return null; 2103 } 2104 2105 //if we're new and we're persistent, store the federation data in the user pref 2106 if (isNewAccountLink && shouldPersistNameID) { 2107 try { 2108 writeFedData(nameId, spEntityId, realm, metaManager, idpEntityId, userName, storageKey); 2109 } catch (SAML2Exception se) { 2110 return userName; 2111 } 2112 } 2113 2114 return userName; 2115 } 2116 2117 private static void writeFedData(NameID nameId, String spEntityId, String realm, SAML2MetaManager metaManager, 2118 String idpEntityId, String userName, String storageKey) throws SAML2Exception { 2119 final NameIDInfo info; 2120 final String affiID = nameId.getSPNameQualifier(); 2121 boolean isDualRole = SAML2Utils.isDualRole(spEntityId, realm); 2122 AffiliationDescriptorType affiDesc = null; 2123 2124 if (affiID != null && !affiID.isEmpty()) { 2125 affiDesc = metaManager.getAffiliationDescriptor(realm, affiID); 2126 } 2127 2128 if (affiDesc != null) { 2129 if (!affiDesc.getAffiliateMember().contains(spEntityId)) { 2130 throw new SAML2Exception("Unable to locate SP Entity ID in the affiliate descriptor."); 2131 } 2132 if (isDualRole) { 2133 info = new NameIDInfo(affiID, idpEntityId, nameId, SAML2Constants.DUAL_ROLE, true); 2134 } else { 2135 info = new NameIDInfo(affiID, idpEntityId, nameId, SAML2Constants.SP_ROLE, true); 2136 } 2137 } else { 2138 if (isDualRole) { 2139 info = new NameIDInfo(spEntityId, idpEntityId, nameId, SAML2Constants.DUAL_ROLE, false); 2140 } else { 2141 info = new NameIDInfo(spEntityId, idpEntityId, nameId, SAML2Constants.SP_ROLE, false); 2142 } 2143 } 2144 2145 // write fed info into data store 2146 SPCache.fedAccountHash.put(storageKey, "true"); 2147 AccountUtils.setAccountFederation(info, userName); 2148 } 2149 2150 /** 2151 * Gets the attributes for this assertion in a new List. 2152 * @param authnAssertion Assertion from which to reead the attributes. 2153 * @param needAttributeEncrypted Whether the attributes must be encrypted. 2154 * @param decryptionKeys The keys used to decrypt the attributes, if they're encrypted. 2155 * @return a List of the attributes in this assertion. 2156 */ 2157 public static List<Attribute> getAttrs(Assertion authnAssertion, boolean needAttributeEncrypted, 2158 Set<PrivateKey> decryptionKeys) { 2159 final List<Attribute> origAttrs = getSAMLAttributes(authnAssertion, needAttributeEncrypted, decryptionKeys); 2160 2161 List<Attribute> attrs = null; 2162 if (origAttrs != null && !origAttrs.isEmpty()) { 2163 attrs = new ArrayList<>(); 2164 attrs.addAll(origAttrs); 2165 } 2166 2167 return attrs; 2168 } 2169 2170}