Skip to content

Commit

Permalink
fix: SAML2 Logout not workibg - EXO-70293
Browse files Browse the repository at this point in the history
Before this fix, SAML2 logout not working as idp redirection on logout was done on the same url as login
This commit allow to configure a logout url in picketlink-sp.xml, and redirect on it during logout process

Resolves meeds-io/meeds-1771
  • Loading branch information
rdenarie committed Mar 18, 2024
1 parent 645d663 commit 4eaa53f
Showing 1 changed file with 188 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,27 @@

package org.gatein.sso.agent.saml;

import jakarta.servlet.http.HttpSession;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.gatein.wci.ServletContainerFactory;
import org.picketlink.common.constants.GeneralConstants;
import org.picketlink.common.constants.JBossSAMLURIConstants;
import org.picketlink.common.exceptions.ConfigurationException;
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.saml.v2.common.IDGenerator;
import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerRequest;
import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerResponse;
import org.picketlink.identity.federation.core.saml.v2.util.XMLTimeUtil;
import org.picketlink.identity.federation.saml.v2.SAML2Object;
import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
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.handlers.saml2.SAML2LogOutHandler;

Expand All @@ -39,6 +52,9 @@
import org.picketlink.common.exceptions.ProcessingException;
import jakarta.servlet.http.Cookie;

import java.net.URI;
import java.security.Principal;

/**
* Extension of {@link SAML2LogOutHandler} because we need to enforce WCI (crossContext) logout in portal environment.
*
Expand All @@ -50,6 +66,8 @@ public class PortalSAML2LogOutHandler extends SAML2LogOutHandler

private static final String OAUTH_COOKIE_NAME = "oauth_rememberme";

private final SPLogOutHandler sp = new SPLogOutHandler();

private static Log log = ExoLogger.getLogger(PortalSAML2LogOutHandler.class);

@Override
Expand Down Expand Up @@ -101,6 +119,20 @@ public void handleStatusResponseType(SAML2HandlerRequest request, SAML2HandlerRe

}


public void generateSAMLRequest(SAML2HandlerRequest request, SAML2HandlerResponse response) throws ProcessingException {
if (request.getTypeOfRequestToBeGenerated() == null) {
return;
}
if (SAML2HandlerRequest.GENERATE_REQUEST_TYPE.LOGOUT != request.getTypeOfRequestToBeGenerated())
return;

if (getType() == HANDLER_TYPE.IDP) {
super.generateSAMLRequest(request, response);
} else {
sp.generateSAMLRequest(request, response);
}
}
/**
* Performs portal logout by calling WCI logout.
*
Expand Down Expand Up @@ -133,5 +165,161 @@ protected void portalLogout(HttpServletRequest request, HttpServletResponse resp
oauthCookie.setMaxAge(0);
response.addCookie(oauthCookie);
}


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 = getUserPrincipal(httpRequest);
if (userPrincipal == null) {
return;
}
try {
LogoutRequestType lot = samlRequest.createLogoutRequest(request.getIssuer().getValue());

NameIDType nameID = new NameIDType();
nameID.setValue(userPrincipal.getName());
lot.setNameID(nameID);

SPType spConfiguration = getSPConfiguration();
String logoutUrl = spConfiguration.getLogoutUrl();

if (logoutUrl == null) {
logoutUrl = getIdentityURL(request);
}

lot.setDestination(URI.create(logoutUrl));
response.setDestination(logoutUrl);
response.setResultingDocument(samlRequest.convert(lot));
response.setSendRequest(true);
} catch (Exception e) {
throw logger.processingError(e);
}
}

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);

String relayState = servletRequest.getParameter("RelayState");

session.invalidate(); // Invalidate the current session at the SP

// 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_RESPONDER.get()));

// 2nd level status code
StatusCodeType status2ndLevel = new StatusCodeType();
status2ndLevel.setValue(URI.create(JBossSAMLURIConstants.STATUS_SUCCESS.get()));
statusCodeType.setStatusCode(status2ndLevel);

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);
}

response.setRelayState(relayState);
response.setDestination(logOutRequest.getIssuer().getValue());
response.setSendRequest(false);
}
}

private SPType getSPConfiguration() {
return (SPType) getProviderconfig();
}

Principal getUserPrincipal(HttpServletRequest request) {
HttpSession session = request.getSession();
Principal userPrincipal = request.getUserPrincipal();
if (userPrincipal == null) {
userPrincipal = (Principal) session.getAttribute(GeneralConstants.PRINCIPAL_ID);
}

return userPrincipal;
}

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();
}

}

0 comments on commit 4eaa53f

Please sign in to comment.