/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2007 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: FSSingleLogoutHandler.java,v 1.15 2009/11/04 00:06:11 exu Exp $ * * Portions Copyrighted 2013 ForgeRock AS * */ package com.sun.identity.federation.services.logout; import com.sun.identity.multiprotocol.MultiProtocolUtils; import com.sun.identity.plugin.session.SessionException; import java.io.IOException; import java.io.PrintWriter; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.List; import java.util.Vector; import java.util.logging.Level; import com.sun.identity.common.SystemConfigurationUtil; import com.sun.identity.federation.accountmgmt.FSAccountFedInfo; import com.sun.identity.federation.common.IFSConstants; import com.sun.identity.federation.common.LogUtil; import com.sun.identity.federation.common.FSUtils; import com.sun.identity.federation.jaxb.entityconfig.BaseConfigType; import com.sun.identity.federation.key.KeyUtil; import com.sun.identity.federation.message.FSLogoutNotification; import com.sun.identity.federation.message.FSLogoutResponse; import com.sun.identity.federation.message.common.FSMsgException; import com.sun.identity.federation.meta.IDFFMetaException; import com.sun.identity.federation.meta.IDFFMetaManager; import com.sun.identity.federation.meta.IDFFMetaUtils; import com.sun.identity.federation.plugins.FederationSPAdapter; import com.sun.identity.federation.services.FSSessionManager; import com.sun.identity.federation.services.FSSession; import com.sun.identity.federation.services.FSSessionPartner; import com.sun.identity.federation.services.FSSOAPService; import com.sun.identity.federation.services.util.FSServiceUtils; import com.sun.identity.federation.services.util.FSSignatureUtil; import com.sun.identity.liberty.ws.meta.jaxb.ProviderDescriptorType; import com.sun.identity.multiprotocol.SingleLogoutManager; import com.sun.identity.plugin.session.SessionManager; import com.sun.identity.saml.assertion.NameIdentifier; import com.sun.identity.saml.common.SAMLConstants; import com.sun.identity.saml.common.SAMLResponderException; import com.sun.identity.saml.common.SAMLException; import com.sun.identity.saml.protocol.Status; import com.sun.identity.saml.protocol.StatusCode; import com.sun.identity.saml.xmlsig.XMLSignatureManager; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletRequest; import javax.xml.soap.SOAPMessage; import org.w3c.dom.Element; import org.w3c.dom.Document; /** * Work class that handles ID-FF single logout. */ public class FSSingleLogoutHandler { private static final String LOGOUT_JSP = "/saml2/jsp/autologout.jsp"; private static final String WML_LOGOUT_JSP = "/saml2/jsp/autologoutwml.jsp"; private HttpServletResponse response = null; private HttpServletRequest request = null; private String locale = null; private String userID = null; private String sessionIndex = ""; private boolean isWMLAgent = false; private boolean isCurrentProviderIDPRole; private IDFFMetaManager metaManager = null; private ProviderDescriptorType remoteDescriptor = null; private ProviderDescriptorType hostedDescriptor = null; private BaseConfigType hostedConfig = null; private static final char QUESTION_MARK = '?'; private static final char AMPERSAND = '&'; private static String LOGOUT_DONE_URL = null; private static String COMMON_ERROR_URL = null; private String remoteEntityId = ""; private String realm = null; private String hostedEntityId = ""; private String hostedRole = null; private String metaAlias = null; private String relayState = null; private boolean logoutStatus = true; private boolean isHttpRedirect = false; private Object ssoToken = null; private FSLogoutResponse respObj = null; private FSLogoutNotification requestLogout = null; private String singleLogoutProtocol = null; /* * Constructor. */ public FSSingleLogoutHandler() { FSUtils.debug.message("FSSingleLogoutHandler::Constructor"); metaManager = FSUtils.getIDFFMetaManager(); } /** * Sets some commonly used URLs based on hosted provider. */ protected void setLogoutURL() { LOGOUT_DONE_URL = FSServiceUtils.getLogoutDonePageURL( request, hostedConfig, metaAlias); COMMON_ERROR_URL = FSServiceUtils.getErrorPageURL( request, hostedConfig, metaAlias); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("LOGOUT_DONE_URL : " + LOGOUT_DONE_URL + "\nCOMMON_ERROR_URL : " + COMMON_ERROR_URL); } } /** * Sets the value of RelayState attribute. * * @param relayState the value of RelayState attribute. */ public void setRelayState(String relayState) { this.relayState = relayState; } /** * Sets the realm in which the provider resides. * * @param realm the realm in which the provider resides */ public void setRealm(String realm) { this.realm = realm; } /** * Sets the single logout protocol to be used. * @param protocol Single Logout Protocol to be set */ public void setSingleLogoutProtocol(String protocol) { this.singleLogoutProtocol = protocol; } /* * Initiates the logout operation. * @param response HTTP response * @param request HTTP request * @param currentSessionProvider initial provider with whom to broadcast * @param userID who is presently logging out * @param sessionIndex to be sent as part of logout message * @param isWMLAgent determines if response to be sent to WML agent * @param ssoToken session token of the user * @return status of the logout initiation operation. */ public FSLogoutStatus handleSingleLogout( HttpServletResponse response, HttpServletRequest request, FSSessionPartner currentSessionProvider, String userID, String sessionIndex, boolean isWMLAgent, Object ssoToken) { FSUtils.debug.message( "Entered FSSingleLogoutHandler::handleSingleLogout"); // set all varaibles properly this.response = response; this.request = request; locale = FSServiceUtils.getLocale(request); setLogoutURL(); this.userID = userID; this.sessionIndex = sessionIndex; this.isWMLAgent = isWMLAgent; if (currentSessionProvider != null) { isCurrentProviderIDPRole = currentSessionProvider.getIsRoleIDP(); remoteEntityId = currentSessionProvider.getPartner(); setRemoteDescriptor(getRemoteDescriptor(remoteEntityId)); } this.ssoToken = ssoToken; String strProfile = getProfileToCommunicateLogout(); singleLogoutProtocol = strProfile; if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Communicating logout with provider " + remoteEntityId + " using profile " + strProfile); } FSUtils.debug.message("FSSingleLogoutHandler, in case 1"); FSLogoutStatus bLogoutStatus = null; if (strProfile.equals(IFSConstants.LOGOUT_SP_REDIRECT_PROFILE) || strProfile.equals(IFSConstants.LOGOUT_IDP_REDIRECT_PROFILE)) { FSUtils.debug.message("In redirect profile"); try { String[] values = new String[]{"false"}; SessionManager.getProvider().setProperty(ssoToken, IFSConstants.IS_SOAP_PROFILE, values); } catch (UnsupportedOperationException ex) { // ignore } catch (SessionException ex) { // ignore } bLogoutStatus = doHttpRedirect(remoteEntityId); } else if (strProfile.equals(IFSConstants.LOGOUT_IDP_SOAP_PROFILE) || strProfile.equals(IFSConstants.LOGOUT_SP_SOAP_PROFILE)) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("In SOAP profile, current partner IDP? " + isCurrentProviderIDPRole); } try { String[] values = new String[]{"true"}; SessionManager.getProvider().setProperty(ssoToken, IFSConstants.IS_SOAP_PROFILE, values); } catch (UnsupportedOperationException ex) { // ignore } catch (SessionException ex) { // ignore } // This func should take care of initiating next // provider also as it has control bLogoutStatus = doIDPSoapProfile(remoteEntityId); } else if (strProfile.equals(IFSConstants.LOGOUT_IDP_GET_PROFILE) && !isCurrentProviderIDPRole) { FSUtils.debug.message("In GET profile"); // HTTP GET is for IDP only, so always remove session partner FSLogoutUtil.removeCurrentSessionPartner(metaAlias, remoteEntityId, ssoToken, userID); bLogoutStatus = doHttpGet(remoteEntityId); } else { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Single Logout Profile cannot" + " be processed. Verify profile in metadata"); } String[] data = { strProfile }; LogUtil.error(Level.INFO,LogUtil.LOGOUT_PROFILE_NOT_SUPPORTED,data, ssoToken); FSServiceUtils.returnLocallyAfterOperation( response, LOGOUT_DONE_URL,false, IFSConstants.LOGOUT_SUCCESS, IFSConstants.LOGOUT_FAILURE); return new FSLogoutStatus(IFSConstants.SAML_RESPONDER); } if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Logout completed first round with status : " + bLogoutStatus); } // control will come here with error and without going // elsewhere in case of exception if (!bLogoutStatus.getStatus().equalsIgnoreCase( IFSConstants.SAML_SUCCESS)) { FSServiceUtils.returnLocallyAfterOperation( response, LOGOUT_DONE_URL, false, IFSConstants.LOGOUT_SUCCESS, IFSConstants.LOGOUT_FAILURE); } return bLogoutStatus; } /** * Invoked in the case of Single Logout using SOAP profile. * Only in the case of SOAP do we have control to initiate logout for the * next-in-line provider. In the case of HTTP GET/Redirect we send the * message to one provider and lose control. Here in SOAP profile * continueLogout continues the logout process. * @param isSuccess if true, means logout preformed successfully so far; * if false, means logout failed in one or more providers. */ private void continueLogout(boolean isSuccess) { FSUtils.debug.message( "Entered FSSingleLogoutHandler::continueLogout"); if (FSLogoutUtil.liveConnectionsExist(userID, metaAlias)) { FSUtils.debug.message("More liveConnectionsExist"); HashMap providerMap = FSLogoutUtil.getCurrentProvider( userID, metaAlias, ssoToken); if (providerMap != null) { FSSessionPartner currentSessionProvider = (FSSessionPartner)providerMap.get( IFSConstants.PARTNER_SESSION); this.sessionIndex = (String)providerMap.get( IFSConstants.SESSION_INDEX); if (currentSessionProvider != null) { String currentEntityId = currentSessionProvider.getPartner(); isCurrentProviderIDPRole = currentSessionProvider.getIsRoleIDP(); ProviderDescriptorType currentDesc = null; try { if (isCurrentProviderIDPRole) { currentDesc = metaManager.getIDPDescriptor( realm, currentEntityId); } else { currentDesc = metaManager.getSPDescriptor( realm, currentEntityId); } } catch (Exception e) { FSUtils.debug.error( "FSSingleLogoutHandler:cannot get meta:", e); } setRemoteDescriptor(currentDesc); // Clean session Map FSSessionManager sessionManager = FSSessionManager.getInstance(metaAlias); FSSession session = sessionManager.getSession( sessionManager.getSessionList(userID), sessionIndex); if (!supportSOAPProfile(remoteDescriptor)) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "Single Logout Profile cannot" + " be processed. Verify profile in metadata"); } String[] data = { IFSConstants.LOGOUT_IDP_SOAP_PROFILE}; LogUtil.error(Level.INFO, LogUtil.LOGOUT_PROFILE_NOT_SUPPORTED,data,ssoToken); return; } FSUtils.debug.message("FSSLOHandler, SOAP in case 2"); // This func should take care of initiating next // provider also as it has control // remove session partner if status is success or // this is IDP if ((doIDPSoapProfile(currentEntityId)).getStatus(). equalsIgnoreCase(IFSConstants.SAML_SUCCESS) || !isCurrentProviderIDPRole) { FSLogoutUtil.removeCurrentSessionPartner( metaAlias, currentEntityId, ssoToken, userID); FSUtils.debug.message("SOAP partner removed, case 3"); } return; } else { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Reached else part " + " currentSessionProvider "+ "is null. nothing more to broadcast" + "\nNo more providers, destroy user" + "session call destroyPrincipalSession"); } FSLogoutUtil.destroyPrincipalSession( userID, metaAlias, sessionIndex, request, response); if (response != null) { returnAfterCompletion(); } return; } } else { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "GetCurrentProvider returns null HashMap" + " Clean session and return" + "\nNo live connections, destroy user" + " session call destroyPrincipalSession"); } FSLogoutUtil.destroyPrincipalSession( userID, metaAlias, sessionIndex, request, response); if (response != null) { returnAfterCompletion(); } return; } } else { FSUtils.debug.message("Reached else part in continuelogout"); // destroy session when there is no failed logout or this is IDP // for SP does not logout local session in case IDP logout failed. if (isSuccess || !isCurrentProviderIDPRole) { FSUtils.debug.message("No live connections, destroy session"); FSLogoutUtil.destroyPrincipalSession( userID, metaAlias, sessionIndex, request, response); } // Call SP Adapter postSingleLogoutSuccess for SP/SOAP callPostSingleLogoutSuccess( respObj, IFSConstants.LOGOUT_SP_SOAP_PROFILE); if (response != null) { returnAfterCompletion(); } return; } } /** * Performs the logout notification in the case of HTTP Redirect profile. * @param entityId the remote provider to whom logout message needs to * be sent * @return logout status */ private FSLogoutStatus doHttpRedirect(String entityId) { try { FSUtils.debug.message("In HTTP Redirect profile"); isHttpRedirect = true; FSSessionManager sMgr = FSSessionManager.getInstance(metaAlias); if (ssoToken == null) { try { //this is HTTP based protocol, get from HTTP servlet request ssoToken = SessionManager.getProvider().getSession(request); } catch (SessionException ex) { FSUtils.debug.error( "FSSLOHandler.doHttpRedirect: null ssoToken:", ex); } } FSSession session = sMgr.getSession(ssoToken); FSAccountFedInfo acctObj = null; if (session!=null) { acctObj = session.getAccountFedInfo(); } if (acctObj == null && session != null && !session.getOneTime()) { acctObj = FSLogoutUtil.getCurrentWorkingAccount( userID, entityId, metaAlias); } if (acctObj == null) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSingleLogoutHandler.doHttp" + "Redirect: Account might have been terminated."); } return new FSLogoutStatus(IFSConstants.SAML_SUCCESS); } FSLogoutNotification reqLogout = createSingleLogoutRequest(acctObj, sessionIndex); if (this.relayState != null) { reqLogout.setRelayState(this.relayState); } if (reqLogout == null) { FSUtils.debug.message("Logout Request is null"); return new FSLogoutStatus(IFSConstants.SAML_REQUESTER); } reqLogout.setMinorVersion(getMinorVersion(remoteDescriptor)); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSingleLogoutHandler::doHttpRedirect " + remoteDescriptor.getSingleLogoutServiceURL() + "\nLogout request: " + reqLogout.toXMLString()); } String urlEncodedRequest = reqLogout.toURLEncodedQueryString(); // Sign the request querystring if (FSServiceUtils.isSigningOn()) { String certAlias = IDFFMetaUtils.getFirstAttributeValueFromConfig( hostedConfig, IFSConstants.SIGNING_CERT_ALIAS); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "Retrieving self certalias : " + certAlias); } if (certAlias == null || certAlias.length() == 0) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSingleLogoutHandler::" + " doHttpRedirect: couldn't obtain " + "this site's cert alias."); } return new FSLogoutStatus(IFSConstants.SAML_RESPONDER); } urlEncodedRequest = FSSignatureUtil.signAndReturnQueryString( urlEncodedRequest, certAlias); } StringBuffer redirectURL = new StringBuffer(); String retURL = remoteDescriptor.getSingleLogoutServiceURL(); FSUtils.debug.message("Encoded Redirect URL " + urlEncodedRequest); redirectURL.append(retURL); if (retURL.indexOf(QUESTION_MARK) == -1) { redirectURL.append(QUESTION_MARK); } else { redirectURL.append(AMPERSAND); } redirectURL.append(urlEncodedRequest); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSingleLogoutHandler::doHttpRedirect" + " URL is " + redirectURL.toString()); } response.sendRedirect(redirectURL.toString()); return new FSLogoutStatus(IFSConstants.SAML_SUCCESS); }catch(FSMsgException e){ FSUtils.debug.error("FSSingleLogoutHandler::" + " doHttpRedirect FSMsgException:", e); }catch(IOException e){ FSUtils.debug.error("FSSingleLogoutHandler::" + "doHttpRedirect IOException:", e); } return new FSLogoutStatus(IFSConstants.SAML_RESPONDER); } /** * Invoked to either send back control to remote provider if logout message * was received from one or * to show the local logout status page to the user. */ protected void returnAfterCompletion() { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Entered FSSingleLogoutHandler::returnAC: " + "PROTOCOL=" + this.singleLogoutProtocol + ", relayState=" + this.relayState); } try { String returnProviderId = ""; String relayState = ""; String logoutStatusString = ""; String inResponseTo = ""; FSReturnSessionManager mngInst = FSReturnSessionManager.getInstance(metaAlias); HashMap providerMap = new HashMap(); if (mngInst != null) { providerMap = mngInst.getUserProviderInfo(userID); } if (providerMap != null) { returnProviderId = (String) providerMap.get(IFSConstants.PROVIDER); relayState = (String) providerMap.get(IFSConstants.LOGOUT_RELAY_STATE); logoutStatusString = (String) providerMap.get(IFSConstants.LOGOUT_STATUS); if (logoutStatusString == null || logoutStatusString.length() == 0) { logoutStatusString = IFSConstants.SAML_SUCCESS; } inResponseTo = (String) providerMap.get(IFSConstants.RESPONSE_TO); mngInst.removeUserProviderInfo(userID); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Deleted " + returnProviderId + " from return list"); } ProviderDescriptorType descriptor = null; if (hostedRole.equalsIgnoreCase(IFSConstants.IDP)) { descriptor = metaManager.getSPDescriptor( realm, returnProviderId); } else { descriptor = metaManager.getIDPDescriptor( realm, returnProviderId); } String retURL = descriptor.getSingleLogoutServiceReturnURL(); if (retURL != null) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "Getting provider " + returnProviderId + " IDP Return URL = " + retURL); } FSLogoutResponse responseLogout = new FSLogoutResponse(); responseLogout.setResponseTo(inResponseTo); responseLogout.setRelayState(relayState); responseLogout.setProviderId(hostedEntityId); responseLogout.setStatus(logoutStatusString); responseLogout.setID(IFSConstants.LOGOUTID); responseLogout.setMinorVersion( getMinorVersion(descriptor)); responseLogout.setResponseID(FSUtils.generateID()); // Call SP Adapter postSingleLogoutSuccess for SP/HTTP callPostSingleLogoutSuccess(responseLogout, IFSConstants.LOGOUT_IDP_REDIRECT_PROFILE); // call multi-federation protocol processing if (MultiProtocolUtils.isMultipleProtocolSession(request, SingleLogoutManager.IDFF) && hostedRole.equalsIgnoreCase(IFSConstants.IDP) && !MultiProtocolUtils.isMultiProtocolRelayState( relayState)) { int retStatus = handleMultiProtocolLogout(false, responseLogout.toXMLString(true, true), returnProviderId); if (retStatus == SingleLogoutManager.LOGOUT_REDIRECTED_STATUS) { return; } else { if ((retStatus == SingleLogoutManager.LOGOUT_FAILED_STATUS) || (retStatus == SingleLogoutManager.LOGOUT_PARTIAL_STATUS)){ responseLogout.setStatus( IFSConstants.SAML_RESPONDER); } } } String urlEncodedResponse = responseLogout.toURLEncodedQueryString(); // Sign the request querystring if (FSServiceUtils.isSigningOn()) { String certAlias = IDFFMetaUtils.getFirstAttributeValueFromConfig( hostedConfig, IFSConstants.SIGNING_CERT_ALIAS); if (certAlias == null || certAlias.length() == 0) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "FSBrowserArtifactConsumerHandler:: " + "signSAMLRequest:" + "couldn't obtain this site's cert alias."); } throw new SAMLResponderException( FSUtils.bundle.getString( IFSConstants.NO_CERT_ALIAS)); } urlEncodedResponse = FSSignatureUtil.signAndReturnQueryString( urlEncodedResponse, certAlias); } StringBuffer redirectURL = new StringBuffer(); redirectURL.append(retURL); if (retURL.indexOf(IFSConstants.QUESTION_MARK) == -1) { redirectURL.append(IFSConstants.QUESTION_MARK); } else { redirectURL.append(IFSConstants.AMPERSAND); } redirectURL.append(urlEncodedResponse); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Response to be sent : " + redirectURL.toString()); } String[] data = { userID }; LogUtil.access(Level.INFO,LogUtil.LOGOUT_SUCCESS,data); response.sendRedirect(redirectURL.toString()); return; } } else { FSUtils.debug.message( "no source provider. return to local status page"); // handle muliple federation protocol for IDP initiated SOAP // binding case, no need to redirect to default URL, // just return so the LogoutResponse is send back to // Multiple protocol single logout handler if ((this.singleLogoutProtocol != null) && this.singleLogoutProtocol.equals( IFSConstants.LOGOUT_IDP_SOAP_PROFILE) && (this.relayState != null) && MultiProtocolUtils.isMultiProtocolRelayState( this.relayState)) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSingleLogoutHandler::returnAC:" + " this is multiProto for IDP initiated SOAP"); } return; } // call multi-federation protocol processing if (MultiProtocolUtils.isMultipleProtocolSession(request, SingleLogoutManager.IDFF) && hostedRole.equalsIgnoreCase(IFSConstants.IDP) && !MultiProtocolUtils.isMultiProtocolRelayState(relayState)) { boolean isSOAPInitiated = false; if ((singleLogoutProtocol.equals( IFSConstants.LOGOUT_IDP_SOAP_PROFILE)) || (singleLogoutProtocol.equals( IFSConstants.LOGOUT_SP_SOAP_PROFILE))) { isSOAPInitiated = true; } int retStatus = handleMultiProtocolLogout(isSOAPInitiated, null, remoteEntityId); if (retStatus == SingleLogoutManager.LOGOUT_REDIRECTED_STATUS) { return; } else { if ((retStatus == SingleLogoutManager.LOGOUT_FAILED_STATUS) || (retStatus == SingleLogoutManager.LOGOUT_PARTIAL_STATUS)) { logoutStatus = false; } } } if (logoutStatus) { FSServiceUtils.returnLocallyAfterOperation( response, LOGOUT_DONE_URL, true, IFSConstants.LOGOUT_SUCCESS, IFSConstants.LOGOUT_FAILURE); } return; } } catch (IDFFMetaException e){ if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Unable to get LRURL. " + "No location to redirect. processing completed"); } String[] data = { FSUtils.bundle.getString(IFSConstants.LOGOUT_REDIRECT_FAILED) }; LogUtil.error(Level.INFO,LogUtil.LOGOUT_REDIRECT_FAILED,data, ssoToken); } catch (Exception ex){ String[] data = { FSUtils.bundle.getString(IFSConstants.LOGOUT_REDIRECT_FAILED) }; LogUtil.error(Level.INFO,LogUtil.LOGOUT_REDIRECT_FAILED,data, ssoToken); } } /** * Invoked when logout needs to done using the HTTP GET profile. * @param providerId the first provider whose preferred profile is HTTP GET * @return FSLogoutStatus */ private FSLogoutStatus doHttpGet(String providerId) { FSUtils.debug.message("doHttpGet - Entered"); if (isWMLAgent) { return doWMLGet(providerId); } else { return doHTMLGet(providerId); } } /** * Performs the HTTP GET related operations when the user agent is * WML based. * @param providerId the first provider whose preferred profile is HTTP GET */ private FSLogoutStatus doWMLGet(String providerId) { FSUtils.debug.message("In WML based response"); StringBuffer destination = new StringBuffer(); destination.append(hostedDescriptor.getSingleLogoutServiceURL()); if ((destination.toString()).indexOf(QUESTION_MARK) == -1) { destination.append(QUESTION_MARK); } else { destination.append(AMPERSAND); } destination.append("logoutSource=logoutGet"); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "Submit action : " + destination.toString()); } // DO WML response FSUtils.debug.message("Calling getLogoutGETProviders"); HashMap providerMap = FSLogoutUtil.getLogoutGETProviders( userID, providerId, sessionIndex, realm, metaAlias); Vector providerGetList = (Vector)providerMap.get("Provider"); FSUtils.debug.message("Calling cleanSessionMapProviders"); FSLogoutUtil.cleanSessionMapProviders(userID, providerGetList, metaAlias); FSUtils.debug.message("Calling getMultiLogoutRequest"); String multiLogoutRequest = getMultiLogoutRequest(providerMap); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "Image Statements : " + multiLogoutRequest); } request.setAttribute("DESTINATION_URL", destination.toString()); request.setAttribute("MULTI_LOGOUT_REQUEST", multiLogoutRequest); try { request.getRequestDispatcher(WML_LOGOUT_JSP).forward(request, response); } catch (ServletException sE) { FSUtils.debug.error("Error in performing HTTP GET for WML agent:", sE); return new FSLogoutStatus(IFSConstants.SAML_RESPONDER); } catch (IOException ioE) { FSUtils.debug.error("Error in performing HTTP GET for WML agent:", ioE); return new FSLogoutStatus(IFSConstants.SAML_RESPONDER); } return new FSLogoutStatus(IFSConstants.SAML_SUCCESS); } /** * Performs the HTTP GET related operations when the * user agent is non WML based. * @param providerId the first provider whose preferred profile is HTTP GET */ private FSLogoutStatus doHTMLGet(String providerId) { // DO Normal HTML response FSUtils.debug.message("In HTML based response"); StringBuffer destination = new StringBuffer(); destination.append(hostedDescriptor.getSingleLogoutServiceURL()); if ((destination.toString()).indexOf(QUESTION_MARK) == -1) { destination.append(QUESTION_MARK); } else { destination.append(AMPERSAND); } destination.append("logoutSource=logoutGet"); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Submit action : " + destination.toString()); } FSUtils.debug.message("Calling getLogoutGETProviders"); HashMap providerMap = FSLogoutUtil.getLogoutGETProviders( userID, providerId, sessionIndex, realm, metaAlias); Vector providerGetList = (Vector)providerMap.get(IFSConstants.PROVIDER); FSUtils.debug.message("Calling cleanSessionMapProviders"); FSLogoutUtil.cleanSessionMapProviders( userID, providerGetList, metaAlias); FSUtils.debug.message("Calling getMultiLogoutRequest"); String multiLogoutRequest = getMultiLogoutRequest(providerMap); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Image Statements : " + multiLogoutRequest); } request.setAttribute("DESTINATION_URL", destination.toString()); request.setAttribute("MULTI_LOGOUT_REQUEST", multiLogoutRequest); try { request.getRequestDispatcher(LOGOUT_JSP).forward(request, response); } catch (ServletException sE) { FSUtils.debug.error("Error in performing HTTP GET for regular agent", sE); return new FSLogoutStatus(IFSConstants.SAML_RESPONDER); } catch (IOException ioE) { FSUtils.debug.error("Error in performing HTTP GET for regular agent", ioE); return new FSLogoutStatus(IFSConstants.SAML_RESPONDER); } return new FSLogoutStatus(IFSConstants.SAML_SUCCESS); } /** * Prepares the IMG tags that correspond to Single logout requests that * will all be shown in a single page when HTTP GET profile is used. * @param providerMap contains information about all the providers * for whom GET is the logout profile * @return String that has the IMG tags for each provider to be notified */ private String getMultiLogoutRequest(HashMap providerMap) { try { Vector providerList = (Vector)providerMap.get(IFSConstants.PROVIDER); HashMap sessionList = (HashMap)providerMap.get(IFSConstants.SESSION_INDEX); StringBuffer imgString = new StringBuffer(); if (providerList != null) { for (int i = 0; i < providerList.size(); i++) { String providerId = (String)providerList.elementAt(i); FSAccountFedInfo currentAccount = FSLogoutUtil.getCurrentWorkingAccount( userID, providerId, metaAlias); FSLogoutNotification reqLogout = createSingleLogoutRequest(currentAccount, (String) sessionList.get(providerId)); ProviderDescriptorType descriptor = metaManager.getSPDescriptor(realm, providerId); reqLogout.setMinorVersion(getMinorVersion(descriptor)); String urlEncodedRequest = reqLogout.toURLEncodedQueryString(); // Sign the request querystring String certAlias = IDFFMetaUtils.getFirstAttributeValueFromConfig( hostedConfig, IFSConstants.SIGNING_CERT_ALIAS); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("certalias : " + certAlias); } if (certAlias == null || certAlias.length() == 0) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSingleLogoutHandler" + " getMultiLogoutRequest: couldn't obtain " + "this site's cert alias."); } continue; } urlEncodedRequest = FSSignatureUtil.signAndReturnQueryString( urlEncodedRequest, certAlias); StringBuffer redirectURL = new StringBuffer(); String retURL = descriptor.getSingleLogoutServiceURL(); redirectURL.append(retURL); if (retURL.indexOf(QUESTION_MARK) == -1) { redirectURL.append(QUESTION_MARK); } else { redirectURL.append(AMPERSAND); } redirectURL.append(urlEncodedRequest); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSingleLogoutHandler::" + "doHttpRedirect URL is " + redirectURL.toString()); } imgString.append(""); } return imgString.toString(); } } catch(FSMsgException e){ FSUtils.debug.error("FSSingleLogoutHandler::getMultiLogoutRequest" + " FSMsgException", e); } catch (IDFFMetaException e){ FSUtils.debug.error("FSSingleLogoutHandler::getMultiLogoutRequest" + " IDFFMetaException", e); } FSUtils.debug.error("Returning null from getMultiLogoutRequest"); return null; } /** * Initiates SOAP profile logout. It iterates through all the providers in * a loop. * @param providerId the first provider with SOAP as logout profile. */ private FSLogoutStatus doIDPSoapProfile(String providerId) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSLOHandler.doIDPSoapProfile : providerId=" + providerId); } FSLogoutStatus bSoapStatus = doSoapProfile(providerId); if (bSoapStatus.getStatus().equalsIgnoreCase(IFSConstants.SAML_SUCCESS)) { FSUtils.debug.message( "SOAP first round went fine. Calling continue logout"); // remove current session partner in case of success FSLogoutUtil.removeCurrentSessionPartner(metaAlias, providerId, ssoToken, userID); FSUtils.debug.message("SOAP partner removed in case of success"); } else { FSUtils.debug.message("SOAP first round false. No continue logout"); // remove session partner if this is IDP if (!isCurrentProviderIDPRole) { FSLogoutUtil.removeCurrentSessionPartner(metaAlias, providerId, ssoToken, userID); } logoutStatus = false; } if (!isHttpRedirect && (logoutStatus || !isCurrentProviderIDPRole)) { continueLogout(logoutStatus); } if (!isHttpRedirect) { FSUtils.debug.message("FSSLOHandler.doIDPSoapProfile: call MP/SOAP"); try { // call Multi-Federation protocol single logout if ((SessionManager.getProvider().isValid(ssoToken)) && (MultiProtocolUtils.isMultipleProtocolSession(ssoToken, SingleLogoutManager.IDFF))) { int retStatus = handleMultiProtocolLogout(true, null, remoteEntityId); logoutStatus = updateLogoutStatus(logoutStatus, retStatus); } } catch (SessionException ex) { //ignore; FSUtils.debug.message("FSSLOHandler.doIDPSoapProfile2", ex); } } if (!logoutStatus) { return new FSLogoutStatus(IFSConstants.SAML_RESPONDER); } else { // redirect in case of SOAP and successful logout if (response != null && !isHttpRedirect) { returnAfterCompletion(); } return bSoapStatus; } } private boolean updateLogoutStatus(boolean logoutStatus, int retStatus) { boolean status = logoutStatus; switch (retStatus) { case SingleLogoutManager.LOGOUT_FAILED_STATUS: status = false; break; case SingleLogoutManager.LOGOUT_PARTIAL_STATUS: status = false; break; default: break; } return status; } /** * Initiates SOAP proifle logout. * @param providerId the first provider with SOAP as logout profile */ private FSLogoutStatus doSoapProfile(String providerId) { FSUtils.debug.message("Entered IDP's doSoapProfile"); try{ FSSessionManager sMgr = FSSessionManager.getInstance(metaAlias); FSSession session = sMgr.getSession(ssoToken); FSAccountFedInfo currentAccount = null; if (session!=null) { currentAccount = session.getAccountFedInfo(); } if (currentAccount == null && !session.getOneTime()) { currentAccount = FSLogoutUtil.getCurrentWorkingAccount( userID, providerId, metaAlias); } if(currentAccount == null) { if(FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSingleLogoutHandler. User's " + "account may have been terminated."); } return new FSLogoutStatus(IFSConstants.SAML_SUCCESS); } FSLogoutNotification reqLogout = createSingleLogoutRequest(currentAccount, sessionIndex); reqLogout.setMinorVersion(getMinorVersion(remoteDescriptor)); if (reqLogout != null) { FSSOAPService instSOAP = FSSOAPService.getInstance(); if (instSOAP != null){ FSUtils.debug.message( "Signing suceeded. To call bindLogoutRequest"); reqLogout.setID(IFSConstants.LOGOUTID); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("logout request before sign: " + reqLogout.toXMLString(true,true)); } SOAPMessage msgLogout = instSOAP.bind( reqLogout.toXMLString(true, true)); if (msgLogout != null) { SOAPMessage retSOAPMessage = null; try{ if(FSServiceUtils.isSigningOn()) { int minorVersion = reqLogout.getMinorVersion(); switch (minorVersion) { case IFSConstants.FF_11_PROTOCOL_MINOR_VERSION: msgLogout = signLogoutRequest( msgLogout, IFSConstants.ID, reqLogout.getID()); break; case IFSConstants.FF_12_PROTOCOL_MINOR_VERSION: msgLogout = signLogoutRequest( msgLogout, IFSConstants.REQUEST_ID, reqLogout.getRequestID()); break; default: FSUtils.debug.message( "invalid minor version."); break; } } retSOAPMessage = instSOAP.sendMessage( msgLogout, remoteDescriptor.getSoapEndpoint()); } catch(Exception e){ FSUtils.debug.error( "FSSOAPException in doSOAPProfile" + " Cannot send request", e); return new FSLogoutStatus( IFSConstants.SAML_RESPONDER); } if(retSOAPMessage != null) { Element elt = instSOAP.parseSOAPMessage(retSOAPMessage); if (FSServiceUtils.isSigningOn()) { if (!verifyResponseSignature(retSOAPMessage)){ if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Response " + "signature verification failed"); } FSServiceUtils.returnLocallyAfterOperation( response, LOGOUT_DONE_URL, false, IFSConstants.LOGOUT_SUCCESS, IFSConstants.LOGOUT_FAILURE); return new FSLogoutStatus( IFSConstants.SAML_REQUESTER); } } this.requestLogout = reqLogout; respObj = new FSLogoutResponse(elt); // Call SP Adapter preSingleLogout for SP/SOAP if (hostedRole != null && hostedRole.equalsIgnoreCase(IFSConstants.SP)) { FederationSPAdapter spAdapter = FSServiceUtils.getSPAdapter( hostedEntityId, hostedConfig); if (spAdapter != null) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSLOHandler." + "preSingleLogoutProcess, SP/SOAP"); } try { spAdapter.preSingleLogoutProcess( hostedEntityId, request, response, userID, reqLogout, respObj, IFSConstants. LOGOUT_SP_SOAP_PROFILE); } catch (Exception e) { // ignore adapter error FSUtils.debug.error("spAdapter." + "preSingleLogoutProcess, SP/SOAP:", e); } } } Status status = respObj.getStatus(); StatusCode statusCode = status.getStatusCode(); StatusCode secondLevelStatus = statusCode.getStatusCode(); String statusString = statusCode.getValue(); if (statusString.equalsIgnoreCase( IFSConstants.SAML_SUCCESS)) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "FSSingleLogoutHandler: " + " doSoapProfile returning success"); } return new FSLogoutStatus( IFSConstants.SAML_SUCCESS); } else { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "FSSingleLogoutHandler: " + "SOAP Profile failure " + statusString); } return new FSLogoutStatus(statusString); } } } } if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Unable to bindLogoutRequest." + "Current Provider cannot be processed"); } } else { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Unable to create logout request" + " Current Provider cannot be processed"); } } } catch (Exception e){ FSUtils.debug.error("In IOException of doSOAPProfile : " ,e); } return new FSLogoutStatus(IFSConstants.SAML_RESPONDER); } public FSLogoutStatus doIDPProxySoapProfile( HttpServletRequest request, HttpServletResponse response, FSSessionPartner currentSessionProvider, String userID, String sessionIndex, Object ssoToken) { this.request = request; this.response = response; this.userID = userID; this.ssoToken = ssoToken; this.sessionIndex = sessionIndex; isCurrentProviderIDPRole = true; this.remoteEntityId = currentSessionProvider.getPartner(); setRemoteDescriptor(getRemoteDescriptor(remoteEntityId)); FSLogoutStatus retStatus = doSoapProfile(remoteEntityId); if (retStatus.getStatus().equalsIgnoreCase(IFSConstants.SAML_SUCCESS)) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "FSSingleLogoutHandler.doIDPProxySoapProfile: " + "single logout from " + remoteEntityId); } // remove current session partner FSLogoutUtil.removeCurrentSessionPartner( metaAlias, remoteEntityId, ssoToken, userID); callPostSingleLogoutSuccess(respObj, IFSConstants.LOGOUT_IDP_SOAP_PROFILE); } return retStatus; } /** * Creates the logoutNotification message for a provider. * @param acctInfo the curerent user-provider information * @param sessionIndex to be sent as part of lgout request * @return the logout request */ private FSLogoutNotification createSingleLogoutRequest( FSAccountFedInfo acctInfo, String sessionIndex) { FSUtils.debug.message( "Entered FSSingleLogoutHandler::createSingleLogoutRequest"); FSLogoutNotification reqName = new FSLogoutNotification(); if (reqName != null){ NameIdentifier nameIdentifier = acctInfo.getRemoteNameIdentifier(); if (nameIdentifier == null) { nameIdentifier = acctInfo.getLocalNameIdentifier(); } if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Hosted Provider Id : " + hostedEntityId); } reqName.setProviderId(hostedEntityId); reqName.setNameIdentifier(nameIdentifier); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Session index is " + sessionIndex); } if (sessionIndex != null) { reqName.setSessionIndex(sessionIndex); } return reqName; } return null; } /** * Sets the hosted provider details. * @param hostedProviderDesc the descriptor of the hosted provider handling * logout */ public void setHostedDescriptor( ProviderDescriptorType hostedProviderDesc) { this.hostedDescriptor = hostedProviderDesc; } /** * Sets the hosted provider's extended meta config. * @param hostedConfig hosted provider's extended meta config */ public void setHostedDescriptorConfig(BaseConfigType hostedConfig) { this.hostedConfig = hostedConfig; } /** * Sets hosted provider's entity id. * @param hostedEntityId hosted provider's entity id. */ public void setHostedEntityId(String hostedEntityId) { this.hostedEntityId = hostedEntityId; } /** * Sets hosted provider's role. * @param hostedRole hosted provider's role */ public void setHostedProviderRole(String hostedRole) { this.hostedRole = hostedRole; } /** * Sets hosted provider's meta alias. * @param metaAlias hosted provider's meta alias. */ public void setMetaAlias(String metaAlias) { this.metaAlias = metaAlias; } /** * Sets the remote provider descriptor. * @param remoteDesc Remote Provider Descriptor. */ public void setRemoteDescriptor(ProviderDescriptorType remoteDesc) { this.remoteDescriptor = remoteDesc; } /** * Gets the remote provider descriptor. * @return remote provider descriptor */ protected ProviderDescriptorType getRemoteDescriptor(String remoteEntityId){ if (remoteEntityId == null || remoteEntityId.length() == 0 || metaManager == null) { return null; } FSUtils.debug.message( "FSSingleLogoutHandler :: getRemoteDescriptor..."); ProviderDescriptorType providerDesc = null; try { if (isCurrentProviderIDPRole) { providerDesc = metaManager.getIDPDescriptor( realm, remoteEntityId); } else { providerDesc = metaManager.getSPDescriptor( realm, remoteEntityId); } } catch(IDFFMetaException e){ FSUtils.debug.error("FSSingleLogoutHandler::" + " getRemoteDescriptor failed:", e); } return providerDesc; } /** * Determines the profile to be used to communicate logout. * @return String the liberty defined logout profile */ protected String getProfileToCommunicateLogout() { FSUtils.debug.message( "FSSingleLogoutHandler :: getProfileToCommunicateLogout..."); if (singleLogoutProtocol != null) { return singleLogoutProtocol; } String retProfileType = ""; if (metaManager != null) { ProviderDescriptorType descriptor = remoteDescriptor; if (isCurrentProviderIDPRole) { FSUtils.debug.message("Local provider is SP"); descriptor = hostedDescriptor; } else { FSUtils.debug.message("Local provider is IDP"); } List profiles = descriptor.getSingleLogoutProtocolProfile(); if (profiles != null && !profiles.isEmpty()) { retProfileType = (String) profiles.iterator().next(); } } return retProfileType; } /** * Processes the logout request received through http. * @param response the HttpServletResponse object * @param reqLogout the logout request * @param currentSessionProvider initial provider with whom to broadcast * @param userID who is presently logging out * @param ssoToken user session * @param sourceEntityId source provider's entity id * @param sessionIndex to be sent as part of logout message * @param isWMLAgent determines if response to be sent to AML agent * @param relayState received with the logout request * @param isSourceIDP whether source provider is an IDP or not * @return logout status */ public FSLogoutStatus processHttpSingleLogoutRequest( HttpServletResponse response, HttpServletRequest request, FSLogoutNotification reqLogout, FSSessionPartner currentSessionProvider, String userID, Object ssoToken, String sourceEntityId, String sessionIndex, boolean isWMLAgent, String relayState, String isSourceIDP) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Entered FSSingleLogoutHandler::" + " processHttpSingleLogoutRequest - HTTP"); } this.response = response; this.request = request; this.requestLogout = reqLogout; locale = FSServiceUtils.getLocale(request); setLogoutURL(); if (currentSessionProvider != null) { isCurrentProviderIDPRole = currentSessionProvider.getIsRoleIDP(); remoteEntityId = currentSessionProvider.getPartner(); setRemoteDescriptor(getRemoteDescriptor(remoteEntityId)); } this.userID = userID; this.ssoToken = ssoToken; this.sessionIndex = sessionIndex; this.isWMLAgent = isWMLAgent; if (reqLogout != null) { FSUtils.debug.message("FSLogoutNotification formed really well"); FSReturnSessionManager localManager = FSReturnSessionManager.getInstance(metaAlias); if (localManager != null) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Added " + sourceEntityId + " top return list"); } localManager.setUserProviderInfo( userID, sourceEntityId, isSourceIDP, relayState, reqLogout.getRequestID()); } else { FSUtils.debug.message("Cannot get FSReturnSessionManager"); } FSSessionManager sessionManager = FSSessionManager.getInstance(metaAlias); FSSession session = sessionManager.getSession( sessionManager.getSessionList(userID), sessionIndex); if (currentSessionProvider == null) { FSUtils.debug.message( "currentSessionProvider is null. destroy and return"); FSLogoutUtil.destroyPrincipalSession( userID, metaAlias, reqLogout.getSessionIndex(), request, response); returnAfterCompletion(); return new FSLogoutStatus(IFSConstants.SAML_SUCCESS); } else { String currentEntityId = currentSessionProvider.getPartner(); isCurrentProviderIDPRole = currentSessionProvider.getIsRoleIDP(); FSUtils.debug.message("FSSLOHandler, in case 3"); FSLogoutUtil.cleanSessionMapPartnerList( userID, currentEntityId, metaAlias, session); FSLogoutStatus bLogoutStatus = null; List profiles = remoteDescriptor.getSingleLogoutProtocolProfile(); if (profiles != null && (profiles.contains(IFSConstants.LOGOUT_SP_REDIRECT_PROFILE) || profiles.contains( IFSConstants.LOGOUT_IDP_REDIRECT_PROFILE))) { FSUtils.debug.message("In redirect profile"); bLogoutStatus = doHttpRedirect( currentEntityId); } else if (profiles != null && profiles.contains(IFSConstants.LOGOUT_IDP_GET_PROFILE) && !isCurrentProviderIDPRole) { FSUtils.debug.message("In GET profile"); bLogoutStatus = doHttpGet(currentEntityId); } else { FSUtils.debug.error("Provider " + currentEntityId + "doesn't support HTTP profile."); returnAfterCompletion(); bLogoutStatus = new FSLogoutStatus( IFSConstants.SAML_RESPONDER); } if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Logout completed first round" + " with status : " + bLogoutStatus); } return bLogoutStatus; } } else { String[] data = { userID }; LogUtil.error(Level.INFO,LogUtil.LOGOUT_FAILED_REQUEST_IMPROPER, data, ssoToken); FSUtils.debug.message( "Request not proper. Cannot proceed with single logout"); returnAfterCompletion(); return new FSLogoutStatus(IFSConstants.SAML_REQUESTER); } } /** * Processes the logout request received from soap profile. * @param reqLogout the logout request * @param currentSessionProvider initial provider with whom to broadcast * @param userID who is presently logging out * @param sourceEntityId remote provider id * @param sessionIndex to be sent as part of logout message * @param isWMLAgent determines if response to be sent to AML agent * @param isSourceIDP determines the role of the provider * @return logout status */ protected FSLogoutStatus processSingleLogoutRequest( FSLogoutNotification reqLogout, FSSessionPartner currentSessionProvider, String userID, String sourceEntityId, String sessionIndex, boolean isWMLAgent, String isSourceIDP) { FSUtils.debug.message( "Entered FSSingleLogoutHandler::processSingleLogoutRequest - SOAP"); if (currentSessionProvider != null) { isCurrentProviderIDPRole = currentSessionProvider.getIsRoleIDP(); remoteEntityId = currentSessionProvider.getPartner(); setRemoteDescriptor(getRemoteDescriptor(remoteEntityId)); } this.requestLogout = reqLogout; this.userID = userID; this.sessionIndex = sessionIndex; this.isWMLAgent = isWMLAgent; if (reqLogout != null) { FSUtils.debug.message("FSLogoutNotification formed really well"); if (currentSessionProvider == null) { FSUtils.debug.message( "currentSessionProvider is null. destroy and return"); // get ssoToken corresponding to the session index Vector sessionObjList = FSLogoutUtil.getSessionObjectList( userID, metaAlias, sessionIndex); if ((sessionObjList != null) && !sessionObjList.isEmpty()) { String sessid = ((FSSession) sessionObjList.get(0)).getSessionID(); try { ssoToken = SessionManager.getProvider().getSession(sessid); } catch (SessionException ex) { // ignore; } } // handle idp proxy case FSLogoutStatus proxyStatus = handleIDPProxyLogout(sourceEntityId); if (proxyStatus != null && !proxyStatus.getStatus().equalsIgnoreCase( IFSConstants.SAML_SUCCESS)) { logoutStatus = false; } FSLogoutUtil.destroyPrincipalSession( userID, metaAlias, reqLogout.getSessionIndex(), request, response); // call multi-federation protocol processing int retStatus = handleMultiProtocolLogout(true,null, sourceEntityId); if ((retStatus == SingleLogoutManager.LOGOUT_FAILED_STATUS) || (retStatus == SingleLogoutManager.LOGOUT_PARTIAL_STATUS)) { return new FSLogoutStatus(IFSConstants.LOGOUT_FAILURE); } else { return new FSLogoutStatus(IFSConstants.SAML_SUCCESS); } } else { // get ssoToken corresponding to the session index Vector sessionObjList = FSLogoutUtil.getSessionObjectList( userID, metaAlias, sessionIndex); if ((sessionObjList != null) && !sessionObjList.isEmpty()) { String sessid = ((FSSession) sessionObjList.get(0)).getSessionID(); try { ssoToken = SessionManager.getProvider().getSession(sessid); } catch (SessionException ex) { // ignore; } } // handle idp proxy case. FSLogoutStatus proxyStatus = handleIDPProxyLogout(sourceEntityId); // Check if any of the connections use HTTP GET/Redirect String currentEntityId = currentSessionProvider.getPartner(); isCurrentProviderIDPRole = currentSessionProvider.getIsRoleIDP(); if (!supportSOAPProfile(remoteDescriptor)) { return new FSLogoutStatus(IFSConstants.SAML_UNSUPPORTED); } FSSessionManager sessionManager = FSSessionManager.getInstance(metaAlias); FSSession session = sessionManager.getSession( sessionManager.getSessionList(userID), sessionIndex); FSUtils.debug.message("FSSLOHandler, process logout case 4"); FSLogoutUtil.cleanSessionMapPartnerList( userID, currentEntityId, metaAlias, session); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Communicate with provider " + currentEntityId + " using soap profile."); } // In the middle of a SOAP call you can only use // SOAP profile FSUtils.debug.message("In SOAP profile"); // This func should take care of initiating // next provider also as it has control FSLogoutStatus bLogoutStatus = doIDPSoapProfile(currentEntityId); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Logout completed first round " + "with status : " + bLogoutStatus); } if (bLogoutStatus.getStatus().equalsIgnoreCase( IFSConstants.SAML_SUCCESS) && (proxyStatus != null) && !proxyStatus.getStatus().equalsIgnoreCase( IFSConstants.SAML_SUCCESS)) { bLogoutStatus = proxyStatus; } return bLogoutStatus; } } else { String[] data = { userID }; LogUtil.error(Level.INFO,LogUtil.LOGOUT_FAILED_REQUEST_IMPROPER, data); if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Request not proper " + "Cannot proceed federation termination"); } return new FSLogoutStatus(IFSConstants.SAML_REQUESTER); } } /** * Determines if any of the providers with whom we have * liveConnections uses either HTTP GET or Redirect profiles. * @return true if at least one provider uses http redirect * or get profile; false otherwise. */ private boolean supportSOAPProfile(ProviderDescriptorType currentDesc) { FSUtils.debug.message( "Entered FSSingleLogoutHandler::supportSOAPProfile"); if (currentDesc == null) { return false; } List profiles = currentDesc.getSingleLogoutProtocolProfile(); if (profiles != null && (profiles.contains(IFSConstants.LOGOUT_IDP_SOAP_PROFILE) || profiles.contains(IFSConstants.LOGOUT_SP_SOAP_PROFILE))) { return true; } return false; } /** * Signs the logout request before sending it to the remote provider. * @param msg logout request message to be sent to remote provider * @param idAttrName id attribute name * @param id id attribute value * @return the signed logout request * @exception SAMLException, FSMsgException if an error occurred during * the process */ private SOAPMessage signLogoutRequest( SOAPMessage msg, String idAttrName, String id) throws SAMLException, FSMsgException { FSUtils.debug.message( "Entered FSSingleLogoutHandler::signLogoutRequest"); String certAlias = IDFFMetaUtils.getFirstAttributeValueFromConfig( hostedConfig, IFSConstants.SIGNING_CERT_ALIAS); if (certAlias == null || certAlias.length() == 0) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSingleLogoutHandler::" + " signLogoutRequest: couldn't obtain " + "this site's cert alias."); } throw new SAMLResponderException( FSUtils.bundle.getString(IFSConstants.NO_CERT_ALIAS)); } if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSingleLogoutHandler::signLogoutRequest" + " Provider's certAlias is found: " + certAlias); } XMLSignatureManager manager = XMLSignatureManager.getInstance(); Document doc = (Document)FSServiceUtils.createSOAPDOM(msg); String xpath = "//*[local-name()=\'ProviderID\']"; manager.signXML(doc, certAlias, SystemConfigurationUtil.getProperty( SAMLConstants.XMLSIG_ALGORITHM), idAttrName, id, false, xpath); return FSServiceUtils.convertDOMToSOAP(doc); } private boolean verifyResponseSignature(SOAPMessage msg){ FSUtils.debug.message( "Entered FSLogoutResponse::verifyResponseSignature"); try { X509Certificate cert = KeyUtil.getVerificationCert( remoteDescriptor, remoteEntityId, !hostedRole.equalsIgnoreCase(IFSConstants.IDP)); if (cert == null) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("Logout.verifyResponseSignature" + "couldn't obtain this site's cert."); } throw new SAMLResponderException( FSUtils.bundle.getString(IFSConstants.NO_CERT)); } XMLSignatureManager manager = XMLSignatureManager.getInstance(); Document doc = (Document)FSServiceUtils.createSOAPDOM(msg); return manager.verifyXMLSignature(doc, cert); } catch (SAMLException e){ FSUtils.debug.error("Error in verifying response:", e); return false; } } private int getMinorVersion(ProviderDescriptorType descriptor) { try { if (descriptor != null) { return FSServiceUtils.getMinorVersion( descriptor.getProtocolSupportEnumeration()); } } catch (Exception e) { FSUtils.debug.error("FSSingleLogoutHandler.getMinorVersion:" + "Error in getting in minor ver.", e); } return IFSConstants.FF_11_PROTOCOL_MINOR_VERSION; } private void callPostSingleLogoutSuccess(FSLogoutResponse responseLogout, String sloProfile) { // Call SP Adapter postSingleLogoutSuccess if (hostedRole != null && hostedRole.equalsIgnoreCase(IFSConstants.SP)) { FederationSPAdapter spAdapter = FSServiceUtils.getSPAdapter(hostedEntityId, hostedConfig); if (spAdapter != null) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message( "FSSingleLogoutHandler, call postSingleLogoutSuccess"); } try { spAdapter.postSingleLogoutSuccess( hostedEntityId, request, response, userID, requestLogout, responseLogout, sloProfile); } catch (Exception e) { // ignore adapter exception FSUtils.debug.error("postSingleLogoutSuccess." + sloProfile, e); } } } } private FSLogoutStatus handleIDPProxyLogout(String sourceEntityId) { FSLogoutStatus retStatus = null; FSUtils.debug.message("FSSingleLogoutHandler.handleIDPProxyLogout."); // get sp metaAlias if any String proxySPAlias = null; boolean isProxy = false; BaseConfigType proxySPConfig = null; ProviderDescriptorType proxySPDescriptor = null; if (hostedRole == IFSConstants.IDP) { // see if there is a hosted SP with the same hostedEntityId proxySPAlias = IDFFMetaUtils.getMetaAlias( realm, hostedEntityId, IFSConstants.SP, null); if (proxySPAlias != null) { // check to see if original SP is idp proxy enabled if (metaManager != null) { try { BaseConfigType sourceSPConfig = metaManager.getSPDescriptorConfig( realm, sourceEntityId); String enabledString = IDFFMetaUtils.getFirstAttributeValueFromConfig( sourceSPConfig, IFSConstants.ENABLE_IDP_PROXY); if (enabledString != null && enabledString.equalsIgnoreCase("true")) { isProxy = true; } } catch (IDFFMetaException ie) { // Shouldn't be here isProxy = false; } } } } if (isProxy) { FSUtils.debug.message( "FSSingleLogoutHandler.handleIDPProxyLogout:isProxy is true."); // see if there is any session with that proxySPAlias try { FSSessionManager sessionMgr = FSSessionManager.getInstance(proxySPAlias); FSSession session = sessionMgr.getSession(ssoToken); if (session != null) { List partners = session.getSessionPartners(); if (partners != null && !partners.isEmpty()) { FSSingleLogoutHandler handler = new FSSingleLogoutHandler(); proxySPConfig = metaManager.getSPDescriptorConfig( realm, hostedEntityId); proxySPDescriptor = metaManager.getSPDescriptor( realm, hostedEntityId); handler.setHostedDescriptor(proxySPDescriptor); handler.setHostedDescriptorConfig(proxySPConfig); handler.setRealm(realm); handler.setHostedEntityId(hostedEntityId); handler.setHostedProviderRole(IFSConstants.SP); handler.setMetaAlias(proxySPAlias); Iterator iter = partners.iterator(); retStatus = new FSLogoutStatus( IFSConstants.SAML_SUCCESS); // most of the time it will have only one idp partner while (iter.hasNext()) { FSSessionPartner sessionPartner = (FSSessionPartner)iter.next(); String curEntityId = sessionPartner.getPartner(); if (curEntityId.equals(sourceEntityId) || !sessionPartner.getIsRoleIDP()) { continue; } FSLogoutStatus curStatus = handler.doIDPProxySoapProfile(request, response, sessionPartner, userID, session.getSessionIndex(), ssoToken); if (!curStatus.getStatus().equalsIgnoreCase( IFSConstants.SAML_SUCCESS)) { retStatus = curStatus; } } } } } catch(Exception e) { FSUtils.debug.error("FSSingleLogoutHandler.handleIDPProxy:",e); retStatus = new FSLogoutStatus(IFSConstants.SAML_RESPONDER); } } return retStatus; } private int handleMultiProtocolLogout(boolean isSOAPInited, String responseXML, String remoteSPId) { if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSLOHandler.handleMultiProtocolLogout: " + "isSOAP initiated = " + isSOAPInited + ", response XML=" + responseXML); } if (ssoToken == null) { try { // this is HTTP based protocol, get from HTTP servlet request ssoToken = SessionManager.getProvider().getSession(request); } catch (SessionException ex) { FSUtils.debug.message("FSSLOHandler.handleMPLogout: null", ex); return SingleLogoutManager.LOGOUT_NO_ACTION_STATUS; } } try { if (!SessionManager.getProvider().isValid(ssoToken)) { return SingleLogoutManager.LOGOUT_NO_ACTION_STATUS; } } catch (SessionException ex) { FSUtils.debug.message("FSSLOHandler.handleMPLogout: invalid", ex); return SingleLogoutManager.LOGOUT_NO_ACTION_STATUS; } Set set = new HashSet(); set.add(ssoToken); int currentStatus = (logoutStatus) ? SingleLogoutManager.LOGOUT_SUCCEEDED_STATUS : SingleLogoutManager.LOGOUT_FAILED_STATUS; int retStatus = SingleLogoutManager.LOGOUT_SUCCEEDED_STATUS; try { String requestXML = (requestLogout == null) ? null : requestLogout.toXMLString(true, true); String finalRelayState = relayState; if ((finalRelayState == null) || (finalRelayState.length() == 0)) { finalRelayState = LOGOUT_DONE_URL; } retStatus = SingleLogoutManager.getInstance(). doIDPSingleLogout(set, userID, request, response, isSOAPInited, FSLogoutUtil.isIDPInitiatedProfile(singleLogoutProtocol), SingleLogoutManager.IDFF, realm, hostedEntityId, remoteSPId, finalRelayState, requestXML, responseXML, currentStatus); } catch (Exception e) { FSUtils.debug.error("FSSLOHandler.doIDPProfile: MP/SOAP", e); retStatus = SingleLogoutManager.LOGOUT_FAILED_STATUS; } if (FSUtils.debug.messageEnabled()) { FSUtils.debug.message("FSSLOHandler.handleMultiProtocolLogout: " + "return status = " + retStatus); } return retStatus; } }