/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.picketlink.identity.federation.web.handlers.saml2; import org.jboss.security.audit.AuditLevel; import org.picketlink.common.constants.GeneralConstants; import org.picketlink.common.constants.JBossSAMLURIConstants; import org.picketlink.common.exceptions.ConfigurationException; import org.picketlink.common.exceptions.ParsingException; import org.picketlink.common.exceptions.ProcessingException; import org.picketlink.common.util.StringUtil; import org.picketlink.config.federation.SPType; import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request; import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response; import org.picketlink.identity.federation.core.audit.PicketLinkAuditEvent; import org.picketlink.identity.federation.core.audit.PicketLinkAuditEventType; import org.picketlink.identity.federation.core.audit.PicketLinkAuditHelper; import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator; import org.picketlink.identity.federation.core.saml.v2.common.SAMLProtocolContext; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerRequest; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerRequest.GENERATE_REQUEST_TYPE; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerResponse; import org.picketlink.identity.federation.core.saml.v2.util.DocumentUtil; import org.picketlink.identity.federation.core.saml.v2.util.XMLTimeUtil; import org.picketlink.identity.federation.core.sts.PicketLinkCoreSTS; import org.picketlink.identity.federation.core.wstrust.plugins.saml.SAMLUtil; import org.picketlink.identity.federation.saml.v2.SAML2Object; import org.picketlink.identity.federation.saml.v2.assertion.AssertionType; import org.picketlink.identity.federation.saml.v2.assertion.AuthnStatementType; import org.picketlink.identity.federation.saml.v2.assertion.NameIDType; import org.picketlink.identity.federation.saml.v2.assertion.StatementAbstractType; import org.picketlink.identity.federation.saml.v2.protocol.LogoutRequestType; import org.picketlink.identity.federation.saml.v2.protocol.ResponseType; import org.picketlink.identity.federation.saml.v2.protocol.StatusCodeType; import org.picketlink.identity.federation.saml.v2.protocol.StatusResponseType; import org.picketlink.identity.federation.saml.v2.protocol.StatusType; import org.picketlink.identity.federation.web.core.HTTPContext; import org.picketlink.identity.federation.web.core.IdentityServer; import org.picketlink.identity.federation.web.core.SessionManager; import org.picketlink.identity.federation.web.util.PostBindingUtil; import org.picketlink.identity.federation.web.util.RedirectBindingUtil; import org.w3c.dom.Document; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.xml.parsers.ParserConfigurationException; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.net.URLEncoder; import java.security.Principal; import java.util.Map; import java.util.Set; /** * SAML2 LogOut Profile * * @author Anil.Saldhana@redhat.com * @since Sep 17, 2009 */ public class SAML2LogOutHandler extends BaseSAML2Handler { public static final String BACK_CHANNEL_LOGOUT = "BACK_CHANNEL_LOGOUT"; private final IDPLogOutHandler idp = new IDPLogOutHandler(); private final SPLogOutHandler sp = new SPLogOutHandler(); /** * @see SAML2Handler#generateSAMLRequest(SAML2HandlerRequest, SAML2HandlerResponse) */ public void generateSAMLRequest(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException { if (request.getTypeOfRequestToBeGenerated() == null) { return; } if (GENERATE_REQUEST_TYPE.LOGOUT != request.getTypeOfRequestToBeGenerated()) return; if (getType() == HANDLER_TYPE.IDP) { idp.generateSAMLRequest(request, response); } else { sp.generateSAMLRequest(request, response); } } /** * @see SAML2Handler#handleRequestType(RequestAbstractType) */ public void handleRequestType(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException { if (request.getSAML2Object() instanceof LogoutRequestType == false) return; if (getType() == HANDLER_TYPE.IDP) { idp.handleRequestType(request, response); } else { sp.handleRequestType(request, response); } } /** * @see SAML2Handler#handleStatusResponseType(StatusResponseType, Document resultingDocument) */ public void handleStatusResponseType(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException { // we do not handle any ResponseType (authentication etc) if (request.getSAML2Object() instanceof ResponseType) return; if (request.getSAML2Object() instanceof StatusResponseType == false) return; if (getType() == HANDLER_TYPE.IDP) { idp.handleStatusResponseType(request, response); } else { sp.handleStatusResponseType(request, response); } } public static boolean isBackChannelLogoutRequest(SAML2HandlerRequest request) { HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest httpServletRequest = httpContext.getRequest(); return httpServletRequest.getParameter(BACK_CHANNEL_LOGOUT) != null; } private class IDPLogOutHandler { public void generateSAMLRequest(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException { } public void handleStatusResponseType(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException { // we got a logout response from a SP SAML2Object samlObject = request.getSAML2Object(); StatusResponseType statusResponseType = (StatusResponseType) samlObject; checkDestination(statusResponseType.getDestination(), getProviderconfig().getIdentityURL()); String statusIssuer = statusResponseType.getIssuer().getValue(); if (statusIssuer.equals(getProviderconfig().getIdentityURL())) { response.setDestination(getProviderconfig().getIdentityURL()); return; } HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest httpRequest = httpContext.getRequest(); HttpSession httpSession = httpRequest.getSession(false); String relayState = request.getRelayState(); String decodedRelayState = relayState; try { decodedRelayState = RedirectBindingUtil.urlDecode(relayState); } catch (IOException ignore) { decodedRelayState = relayState; } ServletContext servletCtx = httpContext.getServletContext(); IdentityServer server = (IdentityServer) servletCtx.getAttribute("IDENTITY_SERVER"); if (server == null) throw logger.samlHandlerIdentityServerNotFoundError(); String sessionID = httpSession.getId(); server.stack().deRegisterTransitParticipant(sessionID, statusIssuer); String nextParticipant = this.getParticipant(server, sessionID, decodedRelayState); if (nextParticipant == null || nextParticipant.equals(decodedRelayState)) { // we are done with logout - First ask STS to cancel the token AssertionType assertion = (AssertionType) httpSession.getAttribute(GeneralConstants.ASSERTION); if (assertion != null) { PicketLinkCoreSTS sts = PicketLinkCoreSTS.instance(); SAMLProtocolContext samlProtocolContext = new SAMLProtocolContext(); samlProtocolContext.setIssuedAssertion(assertion); sts.cancelToken(samlProtocolContext); httpSession.removeAttribute(GeneralConstants.ASSERTION); } // TODO: check the in transit map for partial logouts try { generateSuccessStatusResponseType(statusResponseType.getInResponseTo(), request, response, relayState); boolean isPost = isPostBindingForResponse(server, relayState, request); response.setPostBindingForResponse(isPost); } catch (Exception e) { throw logger.processingError(e); } Map<String, Object> requestOptions = request.getOptions(); PicketLinkAuditHelper auditHelper = (PicketLinkAuditHelper) requestOptions.get(GeneralConstants.AUDIT_HELPER); if (auditHelper != null) { PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent(AuditLevel.INFO); auditEvent.setWhoIsAuditing((String) requestOptions.get(GeneralConstants.CONTEXT_PATH)); auditEvent.setType(PicketLinkAuditEventType.INVALIDATE_HTTP_SESSION); auditEvent.setHttpSessionID(httpSession.getId()); auditHelper.audit(auditEvent); } httpSession.invalidate(); // We are done with the logout interaction } else { // Put the participant in transit mode server.stack().registerTransitParticipant(sessionID, nextParticipant); boolean isPost = isPostBindingForResponse(server, nextParticipant, request); response.setPostBindingForResponse(isPost); // send logout request to participant with relaystate to orig response.setRelayState(relayState); response.setDestination(nextParticipant); SAML2Request saml2Request = new SAML2Request(); try { LogoutRequestType lort = saml2Request.createLogoutRequest(request.getIssuer().getValue()); // set NameID as per the SAML2 spec Principal userPrincipal = httpRequest.getUserPrincipal(); if (userPrincipal == null) { throw logger.samlHandlerPrincipalNotFoundError(); } NameIDType nameID = new NameIDType(); nameID.setValue(userPrincipal.getName()); //Deal with NameID Format String nameIDFormat = (String) handlerConfig.getParameter(GeneralConstants.NAMEID_FORMAT); if(StringUtil.isNullOrEmpty(nameIDFormat)){ nameIDFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get(); } nameID.setFormat(URI.create(nameIDFormat)); lort.setNameID(nameID); // set destination as per the SAML2 spec lort.setDestination(URI.create(nextParticipant)); response.setResultingDocument(saml2Request.convert(lort)); response.setSendRequest(true); } catch (Exception e) { throw logger.processingError(e); } } } public void handleRequestType(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException { HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest httpServletRequest = httpContext.getRequest(); HttpSession session = httpServletRequest.getSession(false); String relayState = httpContext.getRequest().getParameter(GeneralConstants.RELAY_STATE); LogoutRequestType logOutRequest = (LogoutRequestType) request.getSAML2Object(); checkDestination(logOutRequest.getDestination(), getProviderconfig().getIdentityURL()); String issuer = logOutRequest.getIssuer().getValue(); try { String originalIssuer = (relayState == null) ? issuer : relayState; if (getIdentityServer(session.getServletContext()) == null) throw logger.samlHandlerIdentityServerNotFoundError(); if (isBackChannelLogout()) { performBackChannelLogout(request, originalIssuer); } else { performFrontChannelLogout(request, response, logOutRequest, originalIssuer, relayState); } } catch (ParserConfigurationException pe) { throw logger.processingError(pe); } catch (ConfigurationException pe) { throw logger.processingError(pe); } catch (ParsingException e) { throw logger.processingError(e); } return; } private void performFrontChannelLogout(SAML2HandlerRequest request, SAML2HandlerResponse response, LogoutRequestType logOutRequest, String originalIssuer, String relayState) throws ConfigurationException, ParserConfigurationException, ProcessingException, ParsingException { HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest httpServletRequest = httpContext.getRequest(); HttpSession session = httpServletRequest.getSession(false); String sessionID = session.getId(); ServletContext servletCtx = httpContext.getServletContext(); IdentityServer server = getIdentityServer(servletCtx); String participant = this.getParticipant(server, sessionID, originalIssuer); boolean isIssuer = participant.equals(originalIssuer); if (participant == null || isIssuer) { // All log out is done session.invalidate(); server.stack().pop(sessionID); if (participant.equals(getProviderconfig().getIdentityURL())) { response.setDestination(getProviderconfig().getIdentityURL()); } else { generateSuccessStatusResponseType(logOutRequest.getID(), request, response, originalIssuer); boolean isPost = isPostBindingForResponse(server, participant, request); response.setPostBindingForResponse(isPost); } } else { // Put the participant in transit mode server.stack().registerTransitParticipant(sessionID, participant); // send logout request to participant with relaystate to orig response.setRelayState(originalIssuer); response.setDestination(participant); boolean isPost = isPostBindingForResponse(server, participant, request); response.setPostBindingForResponse(isPost); LogoutRequestType lort = createLogoutRequest(request, participant); response.setResultingDocument(new SAML2Request().convert(lort)); response.setSendRequest(true); } } private void performBackChannelLogout(SAML2HandlerRequest request, String originalIssuer) throws ProcessingException { String partitipant = originalIssuer; HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest httpServletRequest = httpContext.getRequest(); HttpSession session = httpServletRequest.getSession(false); String sessionID = session.getId(); ServletContext servletCtx = httpContext.getServletContext(); IdentityServer server = getIdentityServer(servletCtx); while (partitipant != null) { HttpURLConnection urlConnection = null; try { URL participantURL = new URL(partitipant); urlConnection = (HttpURLConnection) participantURL.openConnection(); if (participantURL.getProtocol().contains("https")) { try { HttpsURLConnection https = (HttpsURLConnection) urlConnection; SSLContext sslContext = SSLContext.getInstance( "TLS"); sslContext.init(null, new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) {} public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) {} } }, null); https.setSSLSocketFactory(sslContext.getSocketFactory()); https.setDefaultHostnameVerifier( new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }); } catch( Exception e) { throw new ProcessingException("Error while preparing HTTPS connection during back channel logout.", e); } } urlConnection.setRequestMethod("POST"); urlConnection.setDoOutput(true); urlConnection.setAllowUserInteraction(false); urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); LogoutRequestType logoutRequest = createLogoutRequest(request, partitipant); Document logoutRequestDocument = new SAML2Request().convert(logoutRequest); byte[] responseBytes = DocumentUtil.getDocumentAsString(logoutRequestDocument).getBytes("UTF-8"); String samlResponse = PostBindingUtil.base64Encode(new String(responseBytes)); StringBuilder parameterBuilder = new StringBuilder(); parameterBuilder .append(GeneralConstants.SAML_REQUEST_KEY) .append("=") .append(URLEncoder.encode(samlResponse, "UTF-8")) .append("&") .append(BACK_CHANNEL_LOGOUT) .append("=") .append(BACK_CHANNEL_LOGOUT); urlConnection.setRequestProperty("Content-Length", Integer.toString(parameterBuilder.length())); if (logger.isDebugEnabled()) { logger.debug("Sending back channel logout request to [" + partitipant + "]. Logout request is [ " + DocumentUtil.asString(logoutRequestDocument) + "]."); } DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream()); wr.writeBytes(parameterBuilder.toString()); wr.flush(); wr.close(); InputStream inputStream = urlConnection.getInputStream(); inputStream.close(); } catch (Exception ignore) { logger.warn("Could not perform global logout from service provider [" + partitipant + "]."); logger.samlLogoutError(ignore); } finally { if (urlConnection != null) { urlConnection.disconnect(); } } partitipant = server.stack().pop(sessionID); } session.invalidate(); try { HttpServletResponse httpServletResponse = httpContext.getResponse(); httpServletResponse.sendRedirect(getProviderconfig().getIdentityURL()); } catch (IOException e) { throw new ProcessingException("Could not redirect to IdP after a successful logout.", e); } } private IdentityServer getIdentityServer(ServletContext servletCtx) { return (IdentityServer) servletCtx.getAttribute(GeneralConstants.IDENTITY_SERVER); } private LogoutRequestType createLogoutRequest(SAML2HandlerRequest request, String participant) throws ConfigurationException, ProcessingException { HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest httpServletRequest = httpContext.getRequest(); LogoutRequestType lort = new SAML2Request().createLogoutRequest(request.getIssuer().getValue()); Principal userPrincipal = httpServletRequest.getUserPrincipal(); if (userPrincipal == null) { throw logger.samlHandlerPrincipalNotFoundError(); } NameIDType nameID = new NameIDType(); nameID.setValue(userPrincipal.getName()); //Deal with NameID Format String nameIDFormat = (String) handlerConfig.getParameter(GeneralConstants.NAMEID_FORMAT); if (StringUtil.isNullOrEmpty(nameIDFormat)) { nameIDFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get(); } nameID.setFormat(URI.create(nameIDFormat)); lort.setNameID(nameID); long assertionValidity = PicketLinkCoreSTS.instance().getConfiguration().getIssuedTokenTimeout(); lort.setNotOnOrAfter(XMLTimeUtil.add(lort.getIssueInstant(), assertionValidity)); lort.setDestination(URI.create(participant)); return lort; } private boolean isBackChannelLogout() { return handlerConfig.getParameter(BACK_CHANNEL_LOGOUT) != null ? Boolean.valueOf(handlerConfig.getParameter(BACK_CHANNEL_LOGOUT).toString()) : false; } private void generateSuccessStatusResponseType(String logOutRequestID, SAML2HandlerRequest request, SAML2HandlerResponse response, String originalIssuer) throws ConfigurationException, ParserConfigurationException, ProcessingException { logger.trace("Generating Success Status Response for " + originalIssuer); StatusResponseType statusResponse = new StatusResponseType(IDGenerator.create("ID_"), XMLTimeUtil.getIssueInstant()); // Status StatusType statusType = new StatusType(); StatusCodeType statusCodeType = new StatusCodeType(); statusCodeType.setValue(URI.create(JBossSAMLURIConstants.STATUS_SUCCESS.get())); statusType.setStatusCode(statusCodeType); statusResponse.setStatus(statusType); statusResponse.setInResponseTo(logOutRequestID); statusResponse.setIssuer(request.getIssuer()); statusResponse.setDestination(originalIssuer); try { SAML2Response saml2Response = new SAML2Response(); response.setResultingDocument(saml2Response.convert(statusResponse)); } catch (ParsingException je) { throw logger.processingError(je); } response.setDestination(originalIssuer); } private String getParticipant(IdentityServer server, String sessionID, String originalRequestor) { int participants = server.stack().getParticipants(sessionID); String participant = originalRequestor; // Get a participant who is not equal to the original issuer of the logout request if (participants > 0) { do { participant = server.stack().pop(sessionID); --participants; } while (participants > 0 && participant.equals(originalRequestor)); } return participant; } private boolean isPostBindingForResponse(IdentityServer server, String participant, SAML2HandlerRequest request) { Boolean isPostParticipant = server.stack().getBinding(participant); if (isPostParticipant == null) isPostParticipant = Boolean.TRUE; Boolean isStrictPostBindingForResponse = (Boolean) request.getOptions().get( GeneralConstants.SAML_IDP_STRICT_POST_BINDING); if (isStrictPostBindingForResponse == null) isStrictPostBindingForResponse = Boolean.FALSE; return isPostParticipant || isStrictPostBindingForResponse; } } private class SPLogOutHandler { public void generateSAMLRequest(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException { // Generate the LogOut Request SAML2Request samlRequest = new SAML2Request(); HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest httpRequest = httpContext.getRequest(); Principal userPrincipal = (Principal) httpRequest.getSession().getAttribute(GeneralConstants.PRINCIPAL_ID); if (userPrincipal == null) { userPrincipal = httpRequest.getUserPrincipal(); } if (userPrincipal == null) { return; } try { LogoutRequestType lot = samlRequest.createLogoutRequest(request.getIssuer().getValue()); NameIDType nameID = new NameIDType(); nameID.setValue(userPrincipal.getName()); //Deal with NameID Format String nameIDFormat = (String) handlerConfig.getParameter(GeneralConstants.NAMEID_FORMAT); if(StringUtil.isNullOrEmpty(nameIDFormat)){ nameIDFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get(); } nameID.setFormat(URI.create(nameIDFormat)); lot.setNameID(nameID); SPType spConfiguration = getSPConfiguration(); String logoutUrl = spConfiguration.getLogoutUrl(); if (logoutUrl == null) { logoutUrl = getIdentityURL(request); } lot.setDestination(URI.create(logoutUrl)); populateSessionIndex(httpRequest, lot); response.setResultingDocument(samlRequest.convert(lot)); response.setSendRequest(true); } catch (Exception e) { throw logger.processingError(e); } } private void populateSessionIndex(HttpServletRequest httpRequest, LogoutRequestType lot) throws ProcessingException, ConfigurationException, ParsingException { Document currentAssertion = (Document) httpRequest.getSession().getAttribute(GeneralConstants.ASSERTION_SESSION_ATTRIBUTE_NAME); if (currentAssertion != null) { AssertionType assertionType = SAMLUtil.fromElement(currentAssertion.getDocumentElement()); Set<StatementAbstractType> statements = assertionType.getStatements(); for (StatementAbstractType statementAbstractType : statements) { if (AuthnStatementType.class.isInstance(statementAbstractType)) { AuthnStatementType authnStatement = (AuthnStatementType) statementAbstractType; String sessionIndex = authnStatement.getSessionIndex(); if (sessionIndex != null) { lot.addSessionIndex(sessionIndex); } break; } } } } public void handleStatusResponseType(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException { // Handler a log out response from IDP StatusResponseType statusResponseType = (StatusResponseType) request.getSAML2Object(); checkDestination(statusResponseType.getDestination(), getSPConfiguration().getServiceURL()); HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest servletRequest = httpContext.getRequest(); HttpSession session = servletRequest.getSession(false); // TODO: Deal with partial logout report StatusType statusType = statusResponseType.getStatus(); StatusCodeType statusCode = statusType.getStatusCode(); URI statusCodeValueURI = statusCode.getValue(); boolean success = false; if (statusCodeValueURI != null) { String statusCodeValue = statusCodeValueURI.toString(); if (JBossSAMLURIConstants.STATUS_SUCCESS.get().equals(statusCodeValue)) { success = true; session.invalidate(); } } } public void handleRequestType(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException { SAML2Object samlObject = request.getSAML2Object(); if (samlObject instanceof LogoutRequestType == false) return; //get the configuration to handle a logout request from idp and set the correct response location SPType spConfiguration = getSPConfiguration(); LogoutRequestType logOutRequest = (LogoutRequestType) samlObject; checkDestination(logOutRequest.getDestination(), spConfiguration.getServiceURL()); HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest servletRequest = httpContext.getRequest(); HttpSession session = servletRequest.getSession(false); SessionManager sessionManager = SessionManager.get(session.getServletContext()); final String pricipalName = logOutRequest.getNameID().getValue(); sessionManager.invalidate(new Principal() { @Override public String getName() { return pricipalName; } }); // Generate a Logout Response StatusResponseType statusResponse = null; try { statusResponse = new StatusResponseType(IDGenerator.create("ID_"), XMLTimeUtil.getIssueInstant()); } catch (ConfigurationException e) { throw logger.processingError(e); } // Status StatusType statusType = new StatusType(); StatusCodeType statusCodeType = new StatusCodeType(); statusCodeType.setValue(URI.create(JBossSAMLURIConstants.STATUS_SUCCESS.get())); statusType.setStatusCode(statusCodeType); statusResponse.setStatus(statusType); statusResponse.setInResponseTo(logOutRequest.getID()); statusResponse.setIssuer(request.getIssuer()); String logoutResponseLocation = spConfiguration.getLogoutResponseLocation(); if (logoutResponseLocation == null) { response.setDestination(logOutRequest.getIssuer().getValue()); } else { response.setDestination(logoutResponseLocation); } statusResponse.setDestination(response.getDestination()); SAML2Response saml2Response = new SAML2Response(); try { response.setResultingDocument(saml2Response.convert(statusResponse)); } catch (Exception je) { throw logger.processingError(je); } String relayState = servletRequest.getParameter("RelayState"); response.setRelayState(relayState); response.setSendRequest(false); } } private String getIdentityURL(SAML2HandlerRequest request) { SPType spConfiguration = getSPConfiguration(); HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest httpServletRequest = httpContext.getRequest(); String desiredIdP = (String) httpServletRequest.getAttribute(org.picketlink.identity.federation.web.constants.GeneralConstants.DESIRED_IDP); if (desiredIdP != null) { return desiredIdP; } return spConfiguration.getIdentityURL(); } private SPType getSPConfiguration() { return (SPType) getProviderconfig(); } }