SPSingleLogout.java revision e07ee287a1c75dec6be37e5cbb79d48788ebdc97
/**
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2006 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: SPSingleLogout.java,v 1.29 2009/11/24 21:53:28 madan_ranganath Exp $
*
* Portions Copyrighted 2013-2014 ForgeRock AS.
*/
/**
* This class reads the required data from HttpServletRequest and
* initiates the <code>LogoutRequest</code> from SP to IDP.
*/
public class SPSingleLogout {
static final Status SUCCESS_STATUS =
static final Status PARTIAL_LOGOUT_STATUS =
private static FedMonAgent agent;
private static FedMonSAML2Svc saml2Svc;
static {
try {
sm = new SAML2MetaManager();
} catch (SAML2MetaException sme) {
}
try {
} catch (SessionException se) {
}
}
/**
* Parses the request parameters and initiates the Logout
* Request to be sent to the IDP.
*
* @param request the HttpServletRequest.
* @param response the HttpServletResponse.
* @param binding binding used for this request.
* @param paramsMap Map of all other parameters.
* Following parameters names with their respective
* String values are allowed in this paramsMap.
* "RelayState" - the target URL on successful Single Logout
* "Destination" - A URI Reference indicating the address to
* which the request has been sent.
* "Consent" - Specifies a URI a SAML defined identifier
* known as Consent Identifiers.
* "Extension" - Specifies a list of Extensions as list of
* String objects.
* @throws SAML2Exception if error initiating request to IDP.
*/
public static void initiateLogoutRequest(
throws SAML2Exception {
}
/**
* Parses the request parameters and initiates the Logout
* Request to be sent to the IDP.
*
* @param request the HttpServletRequest.
* @param response the HttpServletResponse.
* @param binding binding used for this request.
* @param paramsMap Map of all other parameters.
* Following parameters names with their respective
* String values are allowed in this paramsMap.
* "RelayState" - the target URL on successful Single Logout
* "Destination" - A URI Reference indicating the address to
* which the request has been sent.
* "Consent" - Specifies a URI a SAML defined identifier
* known as Consent Identifiers.
* "Extension" - Specifies a list of Extensions as list of
* String objects.
* @param origLogoutRequest original LogoutRequest
* @param msg SOAPMessage
* @param newSession Session object for IDP Proxy
* @throws SAML2Exception if error initiating request to IDP.
*/
public static void initiateLogoutRequest(
throws SAML2Exception {
if (debug.messageEnabled()) {
}
try {
if (newSession != null) {
} else {
}
throw new SAML2Exception(
}
}
}
} else {
// get first one
}
}
}
throw new SAML2Exception(
}
if (spEntityID == null) {
throw new SAML2Exception(
}
// clean up session index
} else {
try {
}
} catch (SessionException se) {
"session.", se);
throw new SAML2Exception(
}
}
if (debug.messageEnabled()) {
}
// get SPSSODescriptor
null);
throw new SAML2Exception(
}
}
// Validate the RelayState URL.
if (infoKeyString == null) {
// termination case, do local logout only and send to
// relay state if any
try {
} catch (IOException e) {
+ "Error in send redirect to " + relayState, e);
}
} else {
"saml2/jsp/default.jsp?message=spSloSuccess");
try {
} catch (IOException e) {
+ "Error in forwarding to default.jsp", e);
} catch (ServletException e) {
+ "Error in forwarding to default.jsp", e);
}
}
return;
}
while (st.hasMoreTokens()) {
//only try to perform the logout for the SP entity who is currently assigned to the session, this is
//to cover the case when there are multiple hosted SPs authenticating against the same IdP. In this
//scenario the sp metaalias will always be the SP who authenticated last, so we must ensure that we
//send out the LogoutRequest to the single IdP correctly. Once that's done the IdP will send the
//logout request to the other SP instance, invalidating the session for both SPs.
requestID = prepareForLogout(realm, tokenID, metaAlias, extensionsList, binding, relayState, request,
}
}
// IDP Proxy
}
// local log out for SOAP. For HTTP case, session will be destroyed
// when SAML Response reached the SP side.
}
} catch (SAML2MetaException sme) {
throw new SAML2Exception(
} catch (SessionException ssoe) {
throw new SAML2Exception(
}
}
} else {
synchronized (list) {
{
(long)SPCache.
}
}
break;
}
fedSession = null;
}
}
}
if (fedSession == null) {
// just do local logout
if (debug.messageEnabled()) {
"No session partner, just do local logout.");
}
return null;
}
}
// get IDPSSODescriptor
null);
throw new SAML2Exception(
}
null);
throw new SAML2Exception(
}
// get IDP entity config in case of SOAP, for basic auth info
);
}
if (debug.messageEnabled()) {
"\nSPSLO.requestIDStr = " + requestIDStr +
"\nbinding = " + binding);
}
(origLogoutRequest != null)) {
}
return requestIDStr;
}
/**
* Gets and processes the Single <code>LogoutResponse</code> from IDP,
* destroys the local session, checks response's issuer
* and inResponseTo.
*
* @param request the HttpServletRequest.
* @param response the HttpServletResponse.
* @param samlResponse <code>LogoutResponse</code> in the
* XML string format.
* @param relayState the target URL on successful
* <code>LogoutResponse</code>.
* @throws SAML2Exception if error processing
* <code>LogoutResponse</code>.
* @throws SessionException if error processing
* <code>LogoutResponse</code>.
*/
public static Map processLogoutResponse(
if (debug.messageEnabled()) {
}
}
{
// get first one
}
}
throw new SAML2Exception(
}
{
throw new SAML2Exception(
}
// Validate the RelayState URL.
response);
if (decodedStr == null) {
"nullDecodedStrFromSamlResponse"));
}
}
if (debug.messageEnabled()) {
"is null");
}
return null;
}
// invoke SPAdapter preSingleLogoutProcess
}
boolean needToVerify =
if (debug.messageEnabled()) {
}
if (needToVerify) {
boolean valid = false;
} else {
}
if (!valid) {
"Invalid signature in SLO Response.");
"invalidSignInResponse"));
}
loc)) {
throw new SAML2Exception(
}
}
if (inResponseTo == null ||
if (debug.messageEnabled()) {
"LogoutResponse inResponseTo is null");
}
throw new SAML2Exception(
"nullInResponseToFromSamlResponse"));
}
if (debug.messageEnabled()) {
"LogoutResponse inResponseTo matches "+
"LogoutRequest ID.");
}
} else {
if (debug.messageEnabled()) {
"LogoutResponse inResponseTo does not match " +
"LogoutRequest ID.");
}
throw new SAML2Exception(
"LogoutRequestIDandInResponseToDoNotMatch"));
}
// destroy session
try {
}
} catch (SessionException se) {
debug.message("SPSingleLogout.processLogoutResponse() : Unable to invalidate session: " + se.getMessage());
}
// invoke SPAdapter postSingleLogoutSucces
} else {
}
} else {
// obtain fedlet adapter
if (fedletAdapter != null) {
} else {
}
}
}
return infoMap;
}
try {
} catch (SAML2Exception e) {
"SPACSUtils.invokeSPAdapterForPreSLOProcess", e);
}
}
try {
}
} catch (SessionException ex) {
"SPACSUtils.invokeSPAdapterForPreSLOProcess2", ex);
}
}
}
}
return userID;
}
try {
} catch (SAML2Exception e) {
"SPACSUtils.invokeSPAdapterForPostSLOProcess", e);
}
}
}
}
/**
* Gets and processes the Single <code>LogoutRequest</code> from IDP.
*
* @param request the HttpServletRequest.
* @param response the HttpServletResponse.
* @param out the print writer for writing out presentation
* @param samlRequest <code>LogoutRequest</code> in the
* XML string format.
* @param relayState the target URL on successful
* <code>LogoutRequest</code>.
* @throws SAML2Exception if error processing
* <code>LogoutRequest</code>.
* @throws SessionException if error processing
* <code>LogoutRequest</code>.
*/
public static void processLogoutRequest(
if (debug.messageEnabled()) {
}
}
request.getRequestURI()) ;
{
// get first one
}
throw new SAML2Exception(
}
}
{
throw new SAML2Exception(
}
response);
if (decodedStr == null) {
"nullDecodedStrFromSamlRequest"));
}
}
if (debug.messageEnabled()) {
"is null");
}
return;
}
// invoke SPAdapter preSingleLogoutProcess : IDP initiated HTTP
//String userId = preSingleLogoutProcess(spEntityID, realm, request,
// response, null, logoutReq, null, SAML2Constants.HTTP_REDIRECT);
boolean needToVerify =
if (debug.messageEnabled()) {
}
if (needToVerify == true) {
boolean valid = false;
} else {
}
if (!valid) {
"Invalid signature in SLO Request.");
"invalidSignInRequest"));
}
loc)) {
throw new SAML2Exception(
}
}
// get IDPSSODescriptor
null);
throw new SAML2Exception(
}
null);
throw new SAML2Exception(
}
"Unable to find the IDP's single logout "+
"response service with the HTTP-Redirect binding");
throw new SAML2Exception(
"sloResponseServiceLocationNotfound"));
} else {
if (debug.messageEnabled()) {
"SP's single logout response service location = "+
location);
}
}
} else {
if (debug.messageEnabled()) {
"IDP's single logout response service location = "+
location);
}
}
//IDP Proxy Case
location));
} else {
location));
}
}
/**
* Gets and processes the Single <code>LogoutRequest</code> from IDP
* and return <code>LogoutResponse</code>.
*
* @param logoutReq <code>LogoutRequest</code> from IDP
* @param spEntityID name of host entity ID.
* @param realm name of host entity.
* @param request HTTP servlet request.
* @param response HTTP servlet response.
* @param isLBReq true if the request is for load balancing.
* @param binding value of <code>SAML2Constants.HTTP_REDIRECT</code> or
* <code>SAML2Constants.SOAP</code>.
* @param isVerified true if the request is verified already.
* @return LogoutResponse the target URL on successful
* <code>LogoutRequest</code>.
*/
public static LogoutResponse processLogoutRequest(
}
/**
* Gets and processes the Single <code>LogoutRequest</code> from IDP
* and return <code>LogoutResponse</code>.
*
* @param logoutReq <code>LogoutRequest</code> from IDP
* @param spEntityID name of host entity ID.
* @param realm name of host entity.
* @param request HTTP servlet request.
* @param response HTTP servlet response.
* @param isLBReq true if the request is for load balancing.
* @param binding value of <code>SAML2Constants.HTTP_REDIRECT</code> or
* <code>SAML2Constants.SOAP</code>.
* @param isVerified true if the request is verified already.
* @return LogoutResponse the target URL on successful
* <code>LogoutRequest</code>.
*/
public static LogoutResponse processLogoutRequest(
boolean isVerified) {
try {
do {
// TODO: check the NotOnOrAfter attribute of LogoutRequest
// get SessionIndex and NameID form LogoutRequest
int numSI = 0;
if (debug.messageEnabled()) {
"Number of session indices in the logout request is "
+ numSI);
}
}
"LogoutRequest does not contain Name ID");
getString("missing_name_identifier"));
break;
}
if (debug.messageEnabled()) {
}
// verify request
if(!isVerified &&
{
throw new SAML2Exception(
}
// obtain fedlet adapter
boolean result = false;
if (fedletAdapter != null) {
// call adapter to do real logout
}
if (result) {
} else {
}
break;
}
.get(infoKeyString);
if (debug.messageEnabled()) {
}
.get(infoKeyString);
}
}
boolean foundPeer = false;
if (isLBReq) {
}
if (debug.messageEnabled()) {
", foundPeer = " + foundPeer);
}
if (foundPeer) {
boolean peerError = false;
!isNameNotFound(logoutRes)) {
if (numSI > 0) {
siList =
peerError = false;
break;
}
}
} else {
peerError = true;
}
}
}
if (peerError ||
} else {
}
} else {
.getString("invalid_name_identifier"));
}
break;
} else {
// find the session, do signature validation
if(!isVerified &&
throw new SAML2Exception(
}
// invoke SPAdapter for preSingleLogoutProcess
try {
"processLogoutRequest, user = " + userId);
}
} catch (SessionException ex) {
"processLogoutRequest", ex);
}
}
}
// get application logout URL
if (debug.messageEnabled()) {
"external app logout URL= " + appLogoutURL);
}
if (numSI == 0) {
// logout all fed sessions for this user
// between this SP and the IDP
synchronized (list) {
{
(long)SPCache.
}
}
}
try {
} catch (SessionException se) {
+ "Could not create session from token ID = " +
tokenID);
continue;
}
if (debug.messageEnabled()) {
+ "destroy token " + tokenID);
}
// handle external application logout if configured
if ((appLogoutURL != null) &&
}
if (destroySession) {
response);
}
}
if (foundPeer) {
boolean peerError = false;
isNameNotFound(logoutRes))) {
peerError = true;
}
}
if (peerError) {
} else {
}
}
} else {
// logout only those fed sessions specified
// in logout request session list
for (int i = 0; i < numSI; i++) {
synchronized (list) {
if (sessionIndex
if (debug.messageEnabled()) {
}
{
(long)SPCache.
size());
}
break;
}
}
}
if (tokenIDToBeDestroyed != null) {
try {
if (debug.messageEnabled()) {
+ "destroy token (2) "
}
// handle external application logout
if ((appLogoutURL != null) &&
}
if (destroySession) {
}
} catch (SessionException se) {
"session from token ID = " +
}
} else {
}
}
if (isLBReq) {
boolean peerError = false;
!isNameNotFound(logoutRes)) {
} else {
peerError = true;
}
}
if (debug.messageEnabled()) {
+ "siNotFound = "
+ siNotFound);
}
if (siNotFound == null ||
siNotFound.isEmpty()) {
peerError = false;
break;
}
}
if (peerError ||
} else {
}
} else {
}
} else {
if (siNotFound.isEmpty()) {
} else {
}
}
}
} while (false);
} catch (SessionException se) {
status =
} catch (SAML2Exception e) {
"failed to create response", e);
e.toString());
}
// create LogoutResponse
if (spEntityID == null) {
}
if (isSuccess(logResponse)) {
// invoke SPAdapter for postSingleLogoutSuccess
}
return logResponse;
}
}
statusMessage != null &&
}
try {
src.getDestination()));
} catch(SAML2Exception ex) {
}
return dest;
}
private static String getSLOResponseLocationOrLocation(
}
}
}
return location;
}
return null;
}
if (queryString == null) {
} else {
}
}
}