/**
* 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
* 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
*
*/
/**
* Work class that handles <code>ID-FF</code> single logout.
*/
public class FSSingleLogoutHandler {
private boolean isWMLAgent = false;
private boolean isCurrentProviderIDPRole;
private boolean logoutStatus = true;
private boolean isHttpRedirect = false;
/*
* Constructor.
*/
public FSSingleLogoutHandler() {
}
/**
* Sets some commonly used URLs based on hosted provider.
*/
protected void setLogoutURL() {
"\nCOMMON_ERROR_URL : " + COMMON_ERROR_URL);
}
}
/**
* Sets the value of <code>RelayState</code> attribute.
*
* @param relayState the value of <code>RelayState</code> attribute.
*/
this.relayState = relayState;
}
/**
* Sets the realm in which the provider resides.
*
* @param realm the realm in which the provider resides
*/
}
/**
* Sets the single logout protocol to be used.
* @param protocol Single Logout Protocol to be set
*/
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.
*/
boolean isWMLAgent,
{
"Entered FSSingleLogoutHandler::handleSingleLogout");
// set all varaibles properly
setLogoutURL();
this.sessionIndex = sessionIndex;
this.isWMLAgent = isWMLAgent;
if (currentSessionProvider != null) {
}
}
{
try {
} catch (UnsupportedOperationException ex) {
// ignore
} catch (SessionException ex) {
// ignore
}
{
}
try {
} catch (UnsupportedOperationException ex) {
// ignore
} catch (SessionException ex) {
// ignore
}
// This func should take care of initiating next
// provider also as it has control
{
// HTTP GET is for IDP only, so always remove session partner
} else {
" be processed. Verify profile in metadata");
}
ssoToken);
response, LOGOUT_DONE_URL,false,
}
+ bLogoutStatus);
}
// control will come here with error and without going
// elsewhere in case of exception
{
response, LOGOUT_DONE_URL, false,
}
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
* message to one provider and lose control. Here in SOAP profile
* <code>continueLogout</code> continues the logout process.
* @param isSuccess if true, means logout preformed successfully so far;
* if false, means logout failed in one or more providers.
*/
"Entered FSSingleLogoutHandler::continueLogout");
{
if (providerMap != null) {
if (currentSessionProvider != null) {
try {
if (isCurrentProviderIDPRole) {
} else {
}
} catch (Exception e) {
"FSSingleLogoutHandler:cannot get meta:", e);
}
// Clean session Map
if (!supportSOAPProfile(remoteDescriptor)) {
"Single Logout Profile cannot" +
" be processed. Verify profile in metadata");
}
return;
}
// 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
{
}
return;
} else {
" currentSessionProvider "+
"is null. nothing more to broadcast" +
"\nNo more providers, destroy user" +
"session call destroyPrincipalSession");
}
response);
}
return;
}
} else {
"GetCurrentProvider returns null HashMap" +
" Clean session and return" +
"\nNo live connections, destroy user" +
" session call destroyPrincipalSession");
}
}
return;
}
} else {
// 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) {
}
}
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
*/
try {
isHttpRedirect = true;
try {
//this is HTTP based protocol, get from HTTP servlet request
} catch (SessionException ex) {
"FSSLOHandler.doHttpRedirect: null ssoToken:", ex);
}
}
}
}
"Redirect: Account might have been terminated.");
}
}
if (this.relayState != null) {
}
}
}
// Sign the request querystring
if (FSServiceUtils.isSigningOn()) {
"Retrieving self certalias : " + certAlias);
}
" doHttpRedirect: couldn't obtain " +
"this site's cert alias.");
}
}
}
} else {
}
}
}catch(FSMsgException e){
" doHttpRedirect FSMsgException:", e);
}catch(IOException e){
"doHttpRedirect IOException:", e);
}
}
/**
* 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() {
+ "PROTOCOL=" + this.singleLogoutProtocol
+ ", relayState=" + this.relayState);
}
try {
}
if (providerMap != null) {
if (logoutStatusString == null ||
}
" from return list");
}
} else {
}
"Getting provider " +
returnProviderId + " IDP Return URL = " +
retURL);
}
// call multi-federation protocol processing
relayState)) {
int retStatus = handleMultiProtocolLogout(false,
responseLogout.toXMLString(true, true),
if (retStatus ==
return;
} else {
if ((retStatus ==
(retStatus ==
}
}
}
// Sign the request querystring
if (FSServiceUtils.isSigningOn()) {
"FSBrowserArtifactConsumerHandler:: " +
"signSAMLRequest:" +
"couldn't obtain this site's cert alias.");
}
throw new SAMLResponderException(
}
}
} else {
}
redirectURL.toString());
}
return;
}
} else {
"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(
(this.relayState != null) &&
this.relayState)) {
+ " this is multiProto for IDP initiated SOAP");
}
return;
}
// call multi-federation protocol processing
boolean isSOAPInitiated = false;
if ((singleLogoutProtocol.equals(
isSOAPInitiated = true;
}
if (retStatus ==
return;
} else {
if ((retStatus ==
(retStatus ==
logoutStatus = false;
}
}
}
if (logoutStatus) {
response, LOGOUT_DONE_URL, true,
}
return;
}
} catch (IDFFMetaException e){
"No location to redirect. processing completed");
}
ssoToken);
ssoToken);
}
}
/**
* Invoked when logout needs to done using the HTTP GET profile.
* @param providerId the first provider whose preferred profile is HTTP GET
* @return <code>FSLogoutStatus</code>
*/
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
*/
} else {
}
}
// DO WML response
"Image Statements : " + multiLogoutRequest);
}
try {
} catch (ServletException sE) {
} catch (IOException ioE) {
}
}
/**
* 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
*/
// DO Normal HTML response
} else {
}
destination.toString());
}
}
try {
} catch (ServletException sE) {
} catch (IOException ioE) {
}
}
/**
* 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
*/
try {
if (providerList != null) {
// Sign the request querystring
}
" getMultiLogoutRequest: couldn't obtain "
+ "this site's cert alias.");
}
continue;
}
} else {
}
}
}
}
} catch(FSMsgException e){
" FSMsgException", e);
} catch (IDFFMetaException e){
" IDFFMetaException", e);
}
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.
*/
+ providerId);
}
{
"SOAP first round went fine. Calling continue logout");
// remove current session partner in case of success
} else {
// remove session partner if this is IDP
if (!isCurrentProviderIDPRole) {
}
logoutStatus = false;
}
}
if (!isHttpRedirect) {
try {
// call Multi-Federation protocol single logout
SingleLogoutManager.IDFF))) {
}
} catch (SessionException ex) {
//ignore;
}
}
if (!logoutStatus) {
} else {
// redirect in case of SOAP and successful logout
}
return bSoapStatus;
}
}
boolean status = logoutStatus;
switch (retStatus) {
status = false;
break;
status = false;
break;
default:
break;
}
return status;
}
/**
* Initiates SOAP proifle logout.
* @param providerId the first provider with SOAP as logout profile
*/
try{
}
}
if(currentAccount == null) {
"account may have been terminated.");
}
}
"Signing suceeded. To call bindLogoutRequest");
+ reqLogout.toXMLString(true,true));
}
reqLogout.toXMLString(true, true));
try{
if(FSServiceUtils.isSigningOn()) {
switch (minorVersion) {
break;
break;
default:
"invalid minor version.");
break;
}
}
} catch(Exception e){
"FSSOAPException in doSOAPProfile" +
" Cannot send request", e);
return new FSLogoutStatus(
}
if(retSOAPMessage != null) {
if (FSServiceUtils.isSigningOn()) {
"signature verification failed");
}
response, LOGOUT_DONE_URL, false,
return new FSLogoutStatus(
}
}
this.requestLogout = reqLogout;
if (hostedRole != null &&
{
}
try {
} catch (Exception e) {
// ignore adapter error
}
}
}
{
"FSSingleLogoutHandler: " +
" doSoapProfile returning success");
}
return new FSLogoutStatus(
} else {
"FSSingleLogoutHandler: " +
"SOAP Profile failure " + statusString);
}
return new FSLogoutStatus(statusString);
}
}
}
}
"Current Provider cannot be processed");
}
} else {
" Current Provider cannot be processed");
}
}
} catch (Exception e){
}
}
{
this.sessionIndex = sessionIndex;
isCurrentProviderIDPRole = true;
"FSSingleLogoutHandler.doIDPProxySoapProfile: " +
"single logout from " + remoteEntityId);
}
// remove current session partner
}
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
*/
{
"Entered FSSingleLogoutHandler::createSingleLogoutRequest");
if (nameIdentifier == null) {
}
}
}
if (sessionIndex != null) {
}
return reqName;
}
return null;
}
/**
* Sets the hosted provider details.
* @param hostedProviderDesc the descriptor of the hosted provider handling
* logout
*/
public void setHostedDescriptor(
{
this.hostedDescriptor = hostedProviderDesc;
}
/**
* Sets the hosted provider's extended meta config.
* @param hostedConfig hosted provider's extended meta config
*/
this.hostedConfig = hostedConfig;
}
/**
* Sets hosted provider's entity id.
* @param hostedEntityId hosted provider's entity id.
*/
this.hostedEntityId = hostedEntityId;
}
/**
* Sets hosted provider's role.
* @param hostedRole hosted provider's role
*/
this.hostedRole = hostedRole;
}
/**
* Sets hosted provider's meta alias.
* @param metaAlias hosted provider's meta alias.
*/
}
/**
* Sets the remote provider descriptor.
* @param remoteDesc Remote Provider Descriptor.
*/
this.remoteDescriptor = remoteDesc;
}
/**
* Gets the remote provider descriptor.
* @return remote provider descriptor
*/
metaManager == null)
{
return null;
}
"FSSingleLogoutHandler :: getRemoteDescriptor...");
try {
if (isCurrentProviderIDPRole) {
} else {
}
} catch(IDFFMetaException e){
" getRemoteDescriptor failed:", e);
}
return providerDesc;
}
/**
* Determines the profile to be used to communicate logout.
* @return String the liberty defined logout profile
*/
"FSSingleLogoutHandler :: getProfileToCommunicateLogout...");
if (singleLogoutProtocol != null) {
return singleLogoutProtocol;
}
if (metaManager != null) {
if (isCurrentProviderIDPRole) {
} else {
}
}
}
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
*/
boolean isWMLAgent,
{
" processHttpSingleLogoutRequest - HTTP");
}
this.requestLogout = reqLogout;
setLogoutURL();
if (currentSessionProvider != null) {
}
this.sessionIndex = sessionIndex;
this.isWMLAgent = isWMLAgent;
if (localManager != null) {
" top return list");
}
} else {
}
if (currentSessionProvider == null) {
"currentSessionProvider is null. destroy and return");
response);
} else {
||
{
{
} else {
"doesn't support HTTP profile.");
bLogoutStatus = new FSLogoutStatus(
}
" with status : " + bLogoutStatus);
}
return bLogoutStatus;
}
} else {
"Request not proper. Cannot proceed with single logout");
}
}
/**
* 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
*/
boolean isWMLAgent,
{
"Entered FSSingleLogoutHandler::processSingleLogoutRequest - SOAP");
if (currentSessionProvider != null) {
}
this.requestLogout = reqLogout;
this.sessionIndex = sessionIndex;
this.isWMLAgent = isWMLAgent;
if (currentSessionProvider == null) {
"currentSessionProvider is null. destroy and return");
// get ssoToken corresponding to the session index
try {
ssoToken =
} catch (SessionException ex) {
// ignore;
}
}
// handle idp proxy case
if (proxyStatus != null &&
{
logoutStatus = false;
}
response);
// call multi-federation protocol processing
} else {
}
} else {
// get ssoToken corresponding to the session index
try {
ssoToken =
} catch (SessionException ex) {
// ignore;
}
}
// handle idp proxy case.
if (!supportSOAPProfile(remoteDescriptor)) {
}
currentEntityId + " using soap profile.");
}
// In the middle of a SOAP call you can only use
// SOAP profile
// This func should take care of initiating
// next provider also as it has control
"with status : " + bLogoutStatus);
}
(proxyStatus != null) &&
{
}
return bLogoutStatus;
}
} else {
data);
"Cannot proceed federation termination");
}
}
}
/**
* Determines if any of the providers with whom we have
* liveConnections uses either HTTP GET or Redirect profiles.
* @return <code>true</code> if at least one provider uses http redirect
* or get profile; <code>false</code> otherwise.
*/
{
"Entered FSSingleLogoutHandler::supportSOAPProfile");
if (currentDesc == null) {
return false;
}
{
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
*/
throws SAMLException, FSMsgException
{
"Entered FSSingleLogoutHandler::signLogoutRequest");
" signLogoutRequest: couldn't obtain "
+ "this site's cert alias.");
}
throw new SAMLResponderException(
}
" Provider's certAlias is found: " + certAlias);
}
id,
false,
xpath);
}
"Entered FSLogoutResponse::verifyResponseSignature");
try {
"couldn't obtain this site's cert.");
}
throw new SAMLResponderException(
}
} catch (SAMLException e){
return false;
}
}
try {
if (descriptor != null) {
return FSServiceUtils.getMinorVersion(
}
} catch (Exception e) {
"Error in getting in minor ver.", e);
}
}
{
// Call SP Adapter postSingleLogoutSuccess
if (hostedRole != null &&
{
"FSSingleLogoutHandler, call postSingleLogoutSuccess");
}
try {
} catch (Exception e) {
// ignore adapter exception
sloProfile, e);
}
}
}
}
{
// get sp metaAlias if any
boolean isProxy = false;
// see if there is a hosted SP with the same hostedEntityId
if (proxySPAlias != null) {
// check to see if original SP is idp proxy enabled
if (metaManager != null) {
try {
if (enabledString != null &&
{
isProxy = true;
}
} catch (IDFFMetaException ie) {
// Shouldn't be here
isProxy = false;
}
}
}
}
if (isProxy) {
"FSSingleLogoutHandler.handleIDPProxyLogout:isProxy is true.");
// see if there is any session with that proxySPAlias
try {
new FSSingleLogoutHandler();
retStatus = new FSLogoutStatus(
// most of the time it will have only one idp partner
{
continue;
}
{
}
}
}
}
} catch(Exception e) {
}
}
return retStatus;
}
+ responseXML);
}
try {
// this is HTTP based protocol, get from HTTP servlet request
} catch (SessionException ex) {
}
}
try {
}
} catch (SessionException ex) {
}
int currentStatus = (logoutStatus) ?
try {
}
} catch (Exception e) {
}
+ "return status = " + retStatus);
}
return retStatus;
}
}