package org.jboss.seam.security.external.saml.idp; import java.util.List; import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jboss.seam.security.external.InvalidRequestException; import org.jboss.seam.security.external.ResponseHandler; import org.jboss.seam.security.external.SamlNameIdImpl; import org.jboss.seam.security.external.dialogues.DialogueBean; import org.jboss.seam.security.external.dialogues.api.DialogueManager; import org.jboss.seam.security.external.jaxb.samlv2.assertion.NameIDType; import org.jboss.seam.security.external.jaxb.samlv2.protocol.LogoutRequestType; import org.jboss.seam.security.external.jaxb.samlv2.protocol.RequestAbstractType; import org.jboss.seam.security.external.jaxb.samlv2.protocol.StatusResponseType; import org.jboss.seam.security.external.saml.SamlConstants; import org.jboss.seam.security.external.saml.SamlDialogue; import org.jboss.seam.security.external.saml.SamlMessageFactory; import org.jboss.seam.security.external.saml.SamlMessageSender; import org.jboss.seam.security.external.saml.SamlProfile; import org.jboss.seam.security.external.saml.api.SamlIdpSession; import org.jboss.seam.security.external.saml.api.SamlNameId; import org.jboss.seam.security.external.saml.api.SamlPrincipal; import org.jboss.seam.security.external.spi.SamlIdentityProviderSpi; /** * @author Marcel Kolsteren */ public class SamlIdpSingleLogoutService { @Inject private SamlMessageFactory samlMessageFactory; @Inject private SamlMessageSender samlMessageSender; @Inject private SamlIdpSessions samlIdpSessions; @Inject private Instance<SamlIdentityProviderSpi> samlIdentityProviderSpi; @Inject private Instance<DialogueBean> dialogue; @Inject private Instance<SamlDialogue> samlDialogue; @Inject private Instance<SamlIdpIncomingLogoutDialogue> samlIdpIncomingLogoutDialogue; @Inject private Instance<SamlIdpOutgoingLogoutDialogue> samlIdpOutgoingLogoutDialogue; @Inject private DialogueManager dialogueManager; @Inject private ResponseHandler responseHandler; public void processSPRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse, RequestAbstractType request) throws InvalidRequestException { if (!(request instanceof LogoutRequestType)) { throw new InvalidRequestException("Request should be a single logout request."); } LogoutRequestType logoutRequest = (LogoutRequestType) request; NameIDType nameIdJaxb = logoutRequest.getNameID(); SamlNameId samlNameId = new SamlNameIdImpl(nameIdJaxb.getValue(), nameIdJaxb.getFormat(), nameIdJaxb.getNameQualifier()); samlIdpIncomingLogoutDialogue.get().setNameId(samlNameId); samlIdpIncomingLogoutDialogue.get().setSessionIndexes(logoutRequest.getSessionIndex()); removeNextSessionParticipant(httpResponse); } public void handleIDPInitiatedSingleLogout(SamlPrincipal principal, List<String> indexes, HttpServletResponse response) { samlIdpIncomingLogoutDialogue.get().setNameId(principal.getNameId()); samlIdpIncomingLogoutDialogue.get().setSessionIndexes(indexes); removeNextSessionParticipant(response); } private void removeNextSessionParticipant(HttpServletResponse response) { SamlNameId samlNameId = samlIdpIncomingLogoutDialogue.get().getNameId(); List<String> sessionIndexes = samlIdpIncomingLogoutDialogue.get().getSessionIndexes(); boolean readyForNow = false; while (!readyForNow) { // Find the next session that matches with the removal criteria and // that has not been removed yet. SamlIdpSession sessionToRemove = null; for (SamlIdpSession session : samlIdpSessions.getSessions()) { if (session.getPrincipal().getNameId().equals(samlNameId)) { if (sessionIndexes == null || sessionIndexes.size() == 0 || sessionIndexes.contains(((SamlIdpSessionImpl) session).getSessionIndex())) { sessionToRemove = session; break; } } } if (sessionToRemove != null) { if (sessionToRemove.getServiceProviders().size() != 0) { // For the session that is about to be removed, get the first // service provider that participates in the session. Remove it // from the session. SamlExternalServiceProvider sp = sessionToRemove.getServiceProviders().iterator().next(); sessionToRemove.getServiceProviders().remove(sp); // If the session participant is not the party that initiated the // single logout, and it has a single logout service, send a // single logout request. Otherwise, move on to the next session // participant (if available) or to the next session. if (sp != null && !sp.equals(samlDialogue.get().getExternalProvider()) && sp.getService(SamlProfile.SINGLE_LOGOUT) != null) { String incomingDialogueId = dialogue.get().getId(); dialogueManager.detachDialogue(); dialogueManager.beginDialogue(); samlIdpOutgoingLogoutDialogue.get().setIncomingDialogueId(incomingDialogueId); sendSingleLogoutRequestToSP(sessionToRemove, sp, response); readyForNow = true; } } else { // Session has no participating service providers (any more). // Remove the session. samlIdpSessions.removeSession((SamlIdpSessionImpl) sessionToRemove); if (samlDialogue.get().getExternalProvider() != null) { samlIdentityProviderSpi.get().loggedOut(sessionToRemove); } } } else { finishSingleLogoutProcess(response); readyForNow = true; } } } private void finishSingleLogoutProcess(HttpServletResponse response) { boolean failed = samlIdpIncomingLogoutDialogue.get().isFailed(); if (samlDialogue.get().getExternalProvider() != null) { StatusResponseType statusResponse = samlMessageFactory.createStatusResponse(failed ? SamlConstants.STATUS_RESPONDER : SamlConstants.STATUS_SUCCESS, null); samlMessageSender.sendResponse(samlDialogue.get().getExternalProvider(), statusResponse, SamlProfile.SINGLE_LOGOUT, response); } else { if (failed) { samlIdentityProviderSpi.get().globalLogoutFailed(responseHandler.createResponseHolder(response)); } else { samlIdentityProviderSpi.get().globalLogoutSucceeded(responseHandler.createResponseHolder(response)); } } dialogue.get().setFinished(true); } public void processSPResponse(HttpServletRequest httpRequest, HttpServletResponse httpResponse, StatusResponseType statusResponse) { // End the outgoing samlDialogue and re-attach to the incoming // samlDialogue String incomingDialogueId = samlIdpOutgoingLogoutDialogue.get().getIncomingDialogueId(); dialogueManager.endDialogue(); dialogueManager.attachDialogue(incomingDialogueId); if (statusResponse.getStatus() != null && !statusResponse.getStatus().getStatusCode().getValue().equals(SamlConstants.STATUS_SUCCESS)) { samlIdpIncomingLogoutDialogue.get().setFailed(true); } removeNextSessionParticipant(httpResponse); } public void sendSingleLogoutRequestToSP(SamlIdpSession session, SamlExternalServiceProvider sp, HttpServletResponse response) { LogoutRequestType logoutRequest; logoutRequest = samlMessageFactory.createLogoutRequest(session.getPrincipal().getNameId(), ((SamlIdpSessionImpl) session).getSessionIndex()); samlDialogue.get().setExternalProvider(sp); samlMessageSender.sendRequest(sp, SamlProfile.SINGLE_LOGOUT, logoutRequest, response); } }