001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2014 ForgeRock AS. 015 */ 016package org.forgerock.openig.handler.saml; 017 018import static java.lang.String.*; 019 020import java.io.File; 021import java.io.IOException; 022import java.util.ArrayList; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Properties; 029 030import javax.servlet.ServletException; 031import javax.servlet.http.HttpServletRequest; 032import javax.servlet.http.HttpServletResponse; 033 034import org.forgerock.json.fluent.JsonValue; 035import org.forgerock.openig.config.Environment; 036import org.forgerock.openig.handler.GenericHandler; 037import org.forgerock.openig.handler.HandlerException; 038import org.forgerock.openig.header.LocationHeader; 039import org.forgerock.openig.heap.GenericHeaplet; 040import org.forgerock.openig.heap.HeapException; 041import org.forgerock.openig.http.Exchange; 042import org.forgerock.openig.http.Form; 043import org.forgerock.openig.http.Request; 044import org.forgerock.openig.http.Response; 045import org.forgerock.openig.http.Session; 046 047import com.sun.identity.common.ShutdownManager; 048import com.sun.identity.plugin.session.SessionException; 049import com.sun.identity.saml2.assertion.Assertion; 050import com.sun.identity.saml2.assertion.AuthnStatement; 051import com.sun.identity.saml2.assertion.Subject; 052import com.sun.identity.saml2.common.SAML2Constants; 053import com.sun.identity.saml2.common.SAML2Exception; 054import com.sun.identity.saml2.common.SAML2Utils; 055import com.sun.identity.saml2.jaxb.entityconfig.SPSSOConfigElement; 056import com.sun.identity.saml2.meta.SAML2MetaManager; 057import com.sun.identity.saml2.profile.CacheObject; 058import com.sun.identity.saml2.profile.LogoutUtil; 059import com.sun.identity.saml2.profile.SPACSUtils; 060import com.sun.identity.saml2.profile.SPCache; 061import com.sun.identity.saml2.profile.SPSSOFederate; 062import com.sun.identity.saml2.profile.SPSingleLogout; 063import com.sun.identity.saml2.servlet.SPSingleLogoutServiceSOAP; 064 065/** 066 * The SAML federation handler. 067 */ 068public class SamlFederationHandler extends GenericHandler { 069 070 /** Default Realm is always / in this case. */ 071 private static final String DEFAULT_REALM = "/"; 072 073 /** The attribute mapping. */ 074 private final Map<String, String> attributeMapping; 075 076 /** The value contained in the assertion subject is set as the value of the attribute subjectName in the session. */ 077 private final String subjectMapping; 078 079 /** The delimiter to use when there are multiple contexts in the assertion. */ 080 private final String authnContextDelimiter; 081 082 /** The name to use when placing context values into the session. */ 083 private final String authnContext; 084 085 private final String sessionIndexMapping; 086 087 private final String redirectURI; 088 089 private final String logoutURI; 090 091 private final String assertionConsumerEndpoint; 092 093 private final String sPinitiatedSSOEndpoint; 094 095 private final String singleLogoutEndpoint; 096 097 /** IDP Single Logout SOAP Endpoint. */ 098 private final String singleLogoutEndpointSoap; 099 100 /** SP Single Logout Endpoint. */ 101 private final String sPinitiatedSLOEndpoint; 102 103 /** 104 * Constructs a federation handler according to the specified parameters. 105 * 106 * @param attributeMapping 107 * The attribute mapping. 108 * @param subjectMapping 109 * The value contained in the assertion subject is set as the value of the attribute subjectName in the 110 * session. 111 * @param authnContextDelimiter 112 * The delimiter to use when there are multiple contexts in the assertion. 113 * @param authnContext 114 * The name to use when placing context values into the session. 115 * @param sessionIndexMapping 116 * The IDP's sessionIndex for the user is sent in the assertion. 117 * @param redirectURI 118 * The redirectURI should be set to the page the Form-Filter recognizes as the login page for the target 119 * application. 120 * @param logoutURI 121 * The logoutURI should be set to the URI which logs the user out of the target application. 122 * @param assertionConsumerEndpoint 123 * The assertionMapping defines how to transform the attributes from the incoming assertion to attribute 124 * value pairs in the session. 125 * @param sPinitiatedSSOEndpoint 126 * The default value is SPInitiatedSSO. 127 * @param singleLogoutEndpoint 128 * The default value of fedletSLO is the same as the Fedlet. 129 * @param singleLogoutEndpointSoap 130 * IDP Single Logout SOAP Endpoint. 131 * @param sPinitiatedSLOEndpoint 132 * SP Single Logout Endpoint. 133 */ 134 SamlFederationHandler(Map<String, String> attributeMapping, 135 String subjectMapping, 136 String authnContextDelimiter, 137 String authnContext, 138 String sessionIndexMapping, 139 String redirectURI, 140 String logoutURI, 141 String assertionConsumerEndpoint, 142 String sPinitiatedSSOEndpoint, 143 String singleLogoutEndpoint, 144 String singleLogoutEndpointSoap, 145 String sPinitiatedSLOEndpoint) { 146 this.attributeMapping = Collections.unmodifiableMap(attributeMapping); 147 this.subjectMapping = subjectMapping; 148 this.authnContextDelimiter = authnContextDelimiter; 149 this.authnContext = authnContext; 150 this.sessionIndexMapping = sessionIndexMapping; 151 this.redirectURI = redirectURI; 152 this.logoutURI = logoutURI; 153 this.assertionConsumerEndpoint = assertionConsumerEndpoint; 154 this.sPinitiatedSSOEndpoint = sPinitiatedSSOEndpoint; 155 this.singleLogoutEndpoint = singleLogoutEndpoint; 156 this.singleLogoutEndpointSoap = singleLogoutEndpointSoap; 157 this.sPinitiatedSLOEndpoint = sPinitiatedSLOEndpoint; 158 } 159 160 @Override 161 public void handle(Exchange exchange) throws HandlerException, IOException { 162 HttpServletRequest request = adaptRequest(exchange); 163 HttpServletResponse response = adaptResponse(exchange); 164 try { 165 String path = exchange.request.getUri().getPath(); 166 if (path.indexOf(assertionConsumerEndpoint) > 0) { 167 serviceAssertionConsumer(exchange, request, response); 168 } else if (path.indexOf(sPinitiatedSSOEndpoint) > 0) { 169 serviceSPInitiatedSSO(exchange, request, response); 170 } else if (path.indexOf(sPinitiatedSLOEndpoint) > 0) { 171 serviceSPInitiatedSLO(exchange, request, response); 172 } else if (path.indexOf(singleLogoutEndpointSoap) > 0) { 173 serviceIDPInitiatedSLOSOAP(request, response); 174 } else if (path.indexOf(singleLogoutEndpoint) > 0) { 175 serviceIDPInitiatedSLO(exchange, request, response); 176 } else { 177 logger.warning(format("FederationServlet warning: URI not in service %s", exchange.request.getUri())); 178 } 179 } catch (ServletException se) { 180 errorResponse(exchange, response, se.getMessage()); 181 } catch (SAML2Exception sme) { 182 errorResponse(exchange, response, sme.getMessage()); 183 } catch (SessionException se) { 184 errorResponse(exchange, response, se.getMessage()); 185 } 186 } 187 188 /** 189 * Whether IDP or SP initiated, the final request ends up here. 190 * <p> 191 * The assertion is validated, attributes are retrieved from and set in the HttpSession where downstream filters 192 * can access them and pass them on to the target application. 193 */ 194 private void serviceAssertionConsumer(Exchange exchange, 195 HttpServletRequest request, 196 HttpServletResponse response) throws IOException, 197 ServletException, 198 SAML2Exception, 199 SessionException { 200 Map<?, ?> map = SPACSUtils.processResponseForFedlet(request, response); 201 addAttributesToSession(exchange.session, map); 202 /* 203 * Redirect back to the original target application's login page and let the filters take over. If the relayURI 204 * is set in the assertion we must use that, otherwise we will use the configured value, which should be the 205 * login page for the target application. 206 */ 207 String relayURI = (String) map.get(SAML2Constants.RELAY_STATE); 208 String uri = isRelayURIProvided(relayURI) ? relayURI : redirectURI; 209 sendRedirect(exchange, uri); 210 } 211 212 private boolean isRelayURIProvided(String relayURI) { 213 return relayURI != null && !relayURI.isEmpty(); 214 } 215 216 /** 217 * Store attribute value pairs in the session based on the assertionMapping found in config file. 218 * <p> 219 * The intent is to have a filter use one of these attributes as the subject and possibly the password. The 220 * presence of these attributes in the Session implies the assertion has been processed and validated. 221 * 222 * @param session 223 * exchange's {@link Session} 224 * @param assertion 225 * SAML assertion content 226 */ 227 private void addAttributesToSession(final Session session, Map<?, ?> assertion) { 228 Map<?, ?> attributeStatement = (Map<?, ?>) assertion.get(SAML2Constants.ATTRIBUTE_MAP); 229 if (attributeStatement != null) { 230 for (String key : attributeMapping.keySet()) { 231 HashSet<?> t = (HashSet<?>) attributeStatement.get(attributeMapping.get(key)); 232 if (t != null) { 233 String sessionValue = (String) t.iterator().next(); 234 session.put(key, sessionValue); 235 } else { 236 logger.warning(format("FederationServlet: Warning no assertion attribute found for : %s", 237 attributeMapping.get(key))); 238 } 239 } 240 } else { 241 logger.warning("FederationServlet attribute statement was not present in assertion"); 242 } 243 if (subjectMapping != null) { 244 String subjectValue = ((Subject) assertion.get(SAML2Constants.SUBJECT)).getNameID().getValue(); 245 session.put(subjectMapping, subjectValue); 246 logger.debug(format("FederationServlet adding subject to session: %s = %s", subjectMapping, 247 subjectValue)); 248 } 249 250 if (sessionIndexMapping != null) { 251 String sessionIndexValue = (String) assertion.get(SAML2Constants.SESSION_INDEX); 252 session.put(sessionIndexMapping, sessionIndexValue); 253 logger.debug(format("FederationServlet adding session index: %s = %s", sessionIndexMapping, 254 sessionIndexValue)); 255 } 256 257 if (authnContext != null) { 258 @SuppressWarnings("unchecked") 259 List<AuthnStatement> authnStatements = ((Assertion) assertion.get(SAML2Constants.ASSERTION)) 260 .getAuthnStatements(); 261 StringBuilder authnContextValues = new StringBuilder(); 262 for (AuthnStatement authnStatement : authnStatements) { 263 String authnContextValue = authnStatement.getAuthnContext().getAuthnContextClassRef(); 264 if (authnContextValue != null && !authnContextValue.isEmpty()) { 265 authnContextValues.append(authnContextValue); 266 authnContextValues.append(authnContextDelimiter); 267 } 268 } 269 if (authnContextValues.length() > 0) { 270 // remove the last delimiter as it is redundant 271 authnContextValues.deleteCharAt(authnContextValues.length() - 1); 272 session.put(authnContext, authnContextValues.toString()); 273 logger.debug(format("FederationServlet adding authentication contexts to session: %s = %s", 274 authnContext, authnContextValues)); 275 } 276 } 277 } 278 279 @SuppressWarnings("unchecked") 280 private static void serviceSPInitiatedSSO(Exchange exchange, 281 HttpServletRequest request, 282 HttpServletResponse response) throws SAML2Exception { 283 Form form = exchange.request.getForm(); 284 String metaAlias = form.getFirst(SAML2Constants.METAALIAS); 285 if (metaAlias == null || metaAlias.length() == 0) { 286 SAML2MetaManager manager = new SAML2MetaManager(); 287 List<String> spMetaAliases = 288 manager.getAllHostedServiceProviderMetaAliases(DEFAULT_REALM); 289 if (spMetaAliases != null && !spMetaAliases.isEmpty()) { 290 metaAlias = spMetaAliases.get(0); 291 } 292 } 293 String idpEntityID = form.getFirst(SAML2Constants.IDPENTITYID); 294 Map<String, List<?>> paramsMap = SAML2Utils.getParamsMap(request); 295 List<String> list = new ArrayList<String>(); 296 list.add(SAML2Constants.NAMEID_TRANSIENT_FORMAT); 297 298 // next line testing to see if we can change the name format 299 paramsMap.put(SAML2Constants.NAMEID_POLICY_FORMAT, list); 300 301 // TODO: add option to specify artifact 302 if (paramsMap.get(SAML2Constants.BINDING) == null) { 303 // use POST binding 304 list = new ArrayList<String>(); 305 list.add(SAML2Constants.HTTP_POST); 306 paramsMap.put(SAML2Constants.BINDING, list); 307 } 308 if (idpEntityID == null || idpEntityID.length() == 0) { 309 SAML2MetaManager manager = new SAML2MetaManager(); 310 List<String> idpEntities = manager.getAllRemoteIdentityProviderEntities(DEFAULT_REALM); 311 if (idpEntities != null && !idpEntities.isEmpty()) { 312 idpEntityID = idpEntities.get(0); 313 } 314 } 315 if (metaAlias == null || idpEntityID == null) { 316 throw new SAML2Exception("No metadata for SP or IDP"); 317 } 318 SPSSOFederate.initiateAuthnRequest(request, response, metaAlias, idpEntityID, paramsMap); 319 } 320 321 /** 322 * This implementation is based on the {@literal spSingleLogoutInit.jsp} from OpenAM. 323 * Expects to find the {@literal NameID} and {@literal SessionIndex} in the session, these are stored during the 324 * IDP login process. 325 * <p> 326 * Optional request parameters are: 327 * <ul> 328 * <li>{@literal RelayState} - the target URL on successful Single Logout.</li> 329 * <li>{@literal spEntityID} - SP entity ID. When it is missing, first SP from metadata is used.</li> 330 * <li>{@literal idpEntityID} - IDP entity ID. When it is missing, first IDP from metadata is used.</li> 331 * <li>{@literal binding} - binding used for this request and when not set it will use the default binding of 332 * the IDP.</li> 333 * </ul> 334 */ 335 @SuppressWarnings("unchecked") 336 private void serviceSPInitiatedSLO(Exchange exchange, HttpServletRequest request, HttpServletResponse response) 337 throws IOException, SAML2Exception { 338 logger.debug("FederationServlet.serviceSPInitiatedSLO entering"); 339 340 Session session = exchange.session; 341 // Retrieve these values from the session, if they do not exist then the session has expired 342 // or this is being called before we have authenticated to the IDP 343 String nameID = (String) session.get(subjectMapping); 344 String sessionIndex = (String) session.get(sessionIndexMapping); 345 if (nameID == null || nameID.isEmpty() || sessionIndex == null || sessionIndex.isEmpty()) { 346 throw new SAML2Exception(SAML2Utils.bundle.getString("nullNameID")); 347 } 348 349 Form form = exchange.request.getForm(); 350 SAML2MetaManager manager = new SAML2MetaManager(); 351 String relayState = form.getFirst(SAML2Constants.RELAY_STATE); 352 String spEntityID = form.getFirst(SAML2Constants.SPENTITYID); 353 String binding = form.getFirst(SAML2Constants.BINDING); 354 String idpEntityID = form.getFirst(SAML2Constants.IDPENTITYID); 355 356 // If the idpEntityID has not been specified then read it from the IDP metadata. 357 if (idpEntityID == null || idpEntityID.isEmpty()) { 358 List<String> idpEntities = manager.getAllRemoteIdentityProviderEntities(DEFAULT_REALM); 359 if (idpEntities != null && !idpEntities.isEmpty()) { 360 // Just take the first one since only one is supported 361 idpEntityID = idpEntities.get(0); 362 } 363 } 364 logger.debug(format("FederationServlet.serviceSPInitiatedSLO idpEntityID: %s", idpEntityID)); 365 366 367 String metaAlias = null; 368 // If the spEntityID has not been specified then read it from the SP metadata. 369 if (spEntityID == null || spEntityID.isEmpty()) { 370 List<String> spMetaAliases = manager.getAllHostedServiceProviderMetaAliases(DEFAULT_REALM); 371 if (spMetaAliases != null && !spMetaAliases.isEmpty()) { 372 // Just take the first one since only one is supported 373 metaAlias = spMetaAliases.get(0); 374 spEntityID = manager.getEntityByMetaAlias(metaAlias); 375 } 376 } else { 377 SPSSOConfigElement spConfig = manager.getSPSSOConfig(DEFAULT_REALM, spEntityID); 378 if (spConfig != null) { 379 metaAlias = spConfig.getMetaAlias(); 380 } 381 } 382 logger.debug(format("FederationServlet.serviceSPInitiatedSLO metaAlias: %s", metaAlias)); 383 logger.debug(format("FederationServlet.serviceSPInitiatedSLO spEntityID: %s", spEntityID)); 384 385 386 if (metaAlias == null || idpEntityID == null) { 387 throw new SAML2Exception("No metadata for SP or IDP"); 388 } 389 390 // If the binding has not been specified then look up the IDP's default binding. 391 if (binding == null) { 392 binding = LogoutUtil.getSLOBindingInfo(request, metaAlias, SAML2Constants.SP_ROLE, idpEntityID); 393 } 394 395 if (!SAML2Utils.isSPProfileBindingSupported(DEFAULT_REALM, spEntityID, SAML2Constants.SLO_SERVICE, binding)) { 396 logger.error(format("FederationServlet.serviceSPInitiatedSLO unsupported binding: %s", binding)); 397 throw new SAML2Exception(SAML2Utils.bundle.getString("unsupportedBinding")); 398 } 399 400 logger.debug(format("FederationServlet.serviceSPInitiatedSLO binding: %s", binding)); 401 402 HashMap<String, String> paramsMap = new HashMap<String, String>(7); 403 paramsMap.put(SAML2Constants.INFO_KEY, spEntityID + "|" + idpEntityID + "|" + nameID); 404 paramsMap.put(SAML2Constants.SESSION_INDEX, sessionIndex); 405 paramsMap.put(SAML2Constants.METAALIAS, metaAlias); 406 paramsMap.put(SAML2Constants.IDPENTITYID, idpEntityID); 407 paramsMap.put(SAML2Constants.ROLE, SAML2Constants.SP_ROLE); 408 paramsMap.put(SAML2Constants.BINDING, binding); 409 410 // If the relayState has not been specified try to use the SP default otherwise set it to the logoutURI if set. 411 if (relayState == null || relayState.isEmpty()) { 412 relayState = SAML2Utils.getAttributeValueFromSSOConfig(DEFAULT_REALM, spEntityID, SAML2Constants.SP_ROLE, 413 SAML2Constants.DEFAULT_RELAY_STATE); 414 if (relayState == null || (relayState.isEmpty() && logoutURI != null && !logoutURI.isEmpty())) { 415 relayState = logoutURI; 416 } 417 } 418 if (relayState != null && !relayState.isEmpty()) { 419 paramsMap.put(SAML2Constants.RELAY_STATE, relayState); 420 } 421 422 logger.debug(format("FederationServlet.serviceSPInitiatedSLO relayState: %s", relayState)); 423 424 SPSingleLogout.initiateLogoutRequest(request, response, binding, paramsMap); 425 426 if (SAML2Constants.SOAP.equalsIgnoreCase(binding)) { 427 sendRedirect(exchange, relayState); 428 } 429 } 430 431 private void serviceIDPInitiatedSLO(final Exchange exchange, 432 HttpServletRequest request, 433 HttpServletResponse response) 434 throws SAML2Exception, SessionException, IOException { 435 logger.debug("FederationServlet.serviceIDPInitiatedSLO entering"); 436 437 String relayState = getLogoutRelayState(exchange.request); 438 logger.debug(format("FederationServlet.serviceIDPInitiatedSLO relayState : %s", relayState)); 439 440 Form form = exchange.request.getForm(); 441 // Check if this is a request as part of an IDP initiated SLO 442 String samlRequest = form.getFirst(SAML2Constants.SAML_REQUEST); 443 if (samlRequest != null) { 444 logger.debug("FederationServlet.serviceIDPInitiatedSLO processing IDP request"); 445 SPSingleLogout.processLogoutRequest(request, response, samlRequest, relayState); 446 } else { 447 // Otherwise it might be a response from the IDP as part of a SP initiated SLO 448 String samlResponse = form.getFirst(SAML2Constants.SAML_RESPONSE); 449 if (samlResponse != null) { 450 logger.debug("FederationServlet.serviceIDPInitiatedSLO processing IDP response"); 451 SPSingleLogout.processLogoutResponse(request, response, samlResponse, relayState); 452 if (relayState != null) { 453 sendRedirect(exchange, relayState); 454 } 455 } 456 } 457 458 cleanSession(exchange.session); 459 } 460 461 private void serviceIDPInitiatedSLOSOAP(HttpServletRequest request, HttpServletResponse response) 462 throws IOException, ServletException { 463 464 logger.debug("FederationServlet.serviceIDPInitiatedSLOSOAP entering"); 465 466 SPSingleLogoutServiceSOAP spSingleLogoutServiceSOAP = new SPSingleLogoutServiceSOAP(); 467 spSingleLogoutServiceSOAP.doPost(request, response); 468 } 469 470 private String getLogoutRelayState(Request request) { 471 472 Form form = request.getForm(); 473 String relayState = form.getFirst(SAML2Constants.RELAY_STATE); 474 if (relayState != null) { 475 // Check the SP cache for the actual relayState value as the relayState is 476 // often passed as an ID to the IDP as part of the SP initiated SLO. Based on 477 // code from spSingleLogoutPOST.jsp in OpenAM 478 CacheObject tmpRs = (CacheObject) SPCache.relayStateHash.remove(relayState); 479 if (tmpRs != null) { 480 relayState = (String) tmpRs.getObject(); 481 } 482 } 483 484 if (relayState == null || (relayState.isEmpty() && logoutURI != null && !logoutURI.isEmpty())) { 485 relayState = logoutURI; 486 } 487 488 return relayState; 489 } 490 491 /** Clean the session at the end of the SLO process. */ 492 private void cleanSession(final Session session) { 493 logger.debug("End of SLO - Processing to session cleanup"); 494 session.remove(subjectMapping); 495 session.remove(sessionIndexMapping); 496 session.remove(authnContext); 497 498 if (attributeMapping != null) { 499 for (final String key : attributeMapping.keySet()) { 500 session.remove(key); 501 } 502 } 503 } 504 505 private void errorResponse(Exchange exchange, HttpServletResponse response, String message) throws IOException { 506 final String msg = format("SSO Failed: %s", message); 507 if (!response.isCommitted()) { 508 sendError(exchange, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg); 509 } 510 logger.error(msg); 511 } 512 513 private static void sendError(Exchange exchange, int status, String message) { 514 exchange.response = new Response(); 515 exchange.response.setStatus(status); 516 exchange.response.getEntity().setString(message); 517 } 518 519 private static void sendRedirect(Exchange exchange, String redirectUri) { 520 exchange.response = new Response(); 521 // Redirect with a 302 (Found) status code 522 exchange.response.setStatus(HttpServletResponse.SC_FOUND); 523 // Web container was rebasing location header against server URL 524 // Not useful if relayState is already (and always) an absolute URL 525 LocationHeader header = new LocationHeader(redirectUri); 526 header.toMessage(exchange.response); 527 } 528 529 private static HttpServletResponse adaptResponse(Exchange exchange) { 530 return (HttpServletResponse) exchange.get(HttpServletResponse.class.getName()); 531 } 532 533 private static HttpServletRequest adaptRequest(Exchange exchange) { 534 HttpServletRequest request = (HttpServletRequest) exchange.get(HttpServletRequest.class.getName()); 535 return new RequestAdapter(request, exchange); 536 } 537 538 /** 539 * Reads the actual federation servlet from the JSON configuration file. 540 */ 541 public static class Heaplet extends GenericHeaplet { 542 @Override 543 public Object create() throws HeapException { 544 final Map<String, String> attributeMapping = new HashMap<String, String>(); 545 JsonValue mappings = config.get("assertionMapping").expect(Map.class); 546 if (mappings != null) { 547 for (String key : mappings.keys()) { 548 attributeMapping.put(key, mappings.get(key).asString()); 549 } 550 } 551 final String authnContextDelimiter = config.get("authnContextDelimiter").defaultTo("|").asString(); 552 final String authnContext = config.get("authnContext").asString(); 553 final String redirectURI = config.get("redirectURI").asString(); 554 final String logoutURI = config.get("logoutURI").asString(); 555 // Give subjectMapping and sessionIndexMapping a default value as they are needed when doing SP initiated 556 // SLO 557 final String subjectMapping = config.get("subjectMapping").defaultTo("subjectMapping").asString(); 558 final String sessionIndexMapping = config.get("sessionIndexMapping").defaultTo("sessionIndexMapping") 559 .asString(); 560 final String assertionConsumerEndpoint = config.get("assertionConsumerEndpoint") 561 .defaultTo("fedletapplication").asString(); 562 final String sPinitiatedSSOEndpoint = config.get("SPinitiatedSSOEndpoint").defaultTo("SPInitiatedSSO") 563 .asString(); 564 final String singleLogoutEndpoint = config.get("singleLogoutEndpoint").defaultTo("fedletSloRedirect") 565 .asString(); 566 final String singleLogoutEndpointSoap = config.get("singleLogoutEndpointSoap").defaultTo("fedletSloSoap") 567 .asString(); 568 final String sPinitiatedSLOEndpoint = config.get("SPinitiatedSLOEndpoint").defaultTo("SPInitiatedSLO") 569 .asString(); 570 /* 571 * Get the gateway configuration directory and set it as a system property to override the default openFed 572 * location. Federation config files will reside in the SAML directory. 573 */ 574 Environment environment = heap.get(Environment.ENVIRONMENT_HEAP_KEY, Environment.class); 575 String samlDirectory = new File(environment.getBaseDirectory(), "SAML").getPath(); 576 logger.info(format("FederationServlet init directory: %s", samlDirectory)); 577 Properties p = System.getProperties(); 578 p.setProperty("com.sun.identity.fedlet.home", samlDirectory); 579 System.setProperties(p); 580 581 return new SamlFederationHandler(attributeMapping, 582 subjectMapping, 583 authnContextDelimiter, 584 authnContext, 585 sessionIndexMapping, 586 redirectURI, 587 logoutURI, 588 assertionConsumerEndpoint, 589 sPinitiatedSSOEndpoint, 590 singleLogoutEndpoint, 591 singleLogoutEndpointSoap, 592 sPinitiatedSLOEndpoint); 593 } 594 595 @Override 596 public void destroy() { 597 // Automatically shutdown the fedlet 598 ShutdownManager manager = ShutdownManager.getInstance(); 599 if (manager.acquireValidLock()) { 600 try { 601 manager.shutdown(); 602 } finally { 603 manager.releaseLockAndNotify(); 604 } 605 } 606 super.destroy(); 607 } 608 } 609}