package org.atricore.idbus.capabilities.sso.main.idp; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.atricore.idbus.capabilities.sso.main.SSOException; import org.atricore.idbus.capabilities.sso.support.binding.SSOBinding; import org.atricore.idbus.capabilities.sso.support.metadata.SSOService; import org.atricore.idbus.common.sso._1_0.protocol.IDPInitiatedLogoutRequestType; import org.atricore.idbus.common.sso._1_0.protocol.SSOResponseType; import org.atricore.idbus.kernel.main.federation.metadata.EndpointDescriptor; import org.atricore.idbus.kernel.main.federation.metadata.EndpointDescriptorImpl; import org.atricore.idbus.kernel.main.mediation.Channel; import org.atricore.idbus.kernel.main.mediation.IdentityMediationException; import org.atricore.idbus.kernel.main.mediation.IdentityMediator; import org.atricore.idbus.kernel.main.mediation.endpoint.IdentityMediationEndpoint; import org.atricore.idbus.kernel.main.mediation.provider.IdentityProvider; import org.atricore.idbus.kernel.main.mediation.state.LocalState; import org.atricore.idbus.kernel.main.mediation.state.ProviderStateContext; import org.atricore.idbus.kernel.main.session.BaseSession; import org.atricore.idbus.kernel.main.session.SSOSession; import org.atricore.idbus.kernel.main.session.SSOSessionEventListener; import org.atricore.idbus.kernel.main.util.UUIDGenerator; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * @author <a href="mailto:sgonzalez@atricore.org">Sebastian Gonzalez Oyuela</a> * @version $Id$ */ public class IdPSessionEventListener implements SSOSessionEventListener, ApplicationContextAware { private static final Log logger = LogFactory.getLog(IdPSessionEventListener.class); private ApplicationContext applicationContext; private IdentityProvider identityProvider; private UUIDGenerator uuidGenerator = new UUIDGenerator(); public void handleEvent(String type, SSOSession session, Object data) { if (type.equals(BaseSession.SESSION_DESTROYED_EVENT)) { if (logger.isDebugEnabled()) logger.debug("Received SSO Session event 'DESTROYED' for session " + session.getId()); invalidateSession(session.getId()); } } public ApplicationContext getApplicationContext() { return applicationContext; } public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public IdentityProvider getIdentityProvider() { return identityProvider; } public void setIdentityProvider(IdentityProvider identityProvider) { this.identityProvider = identityProvider; } // TODO : Duplicated with MBean, se if we can send a direct message through camel instead of using soap. protected boolean invalidateSession(String sessionId) { try { if (logger.isTraceEnabled()) logger.trace("Invalidating SSO Session from IDP Session Listener. Session ID:" + sessionId); ProviderStateContext ctx = new ProviderStateContext(identityProvider, applicationContext.getClassLoader()); LocalState state = ctx.retrieve(IdentityProviderConstants.SEC_CTX_SSOSESSION_KEY, sessionId); if (state == null) { if (logger.isDebugEnabled()) logger.debug("No security context found for SSO Session " + sessionId); return true; } IdPSecurityContext secCtx = (IdPSecurityContext) state.getValue(identityProvider.getName().toUpperCase() + "_SECURITY_CTX"); if (secCtx == null) { if (logger.isDebugEnabled()) logger.debug("IdP Security Context not found for SSO Session ID: " + sessionId); return false; } // Trigger IdP Initiated SLO for given session: triggerIdPInitiatedSLO(secCtx); if (logger.isDebugEnabled()) logger.debug("SSO Session invalidated from IDP Session Listener: " + sessionId); return true; } catch (Exception e) { logger.error("Cannot invalidate SSO Session from IDP Session Listener: " + e.getMessage(), e); } return false; } protected void triggerIdPInitiatedSLO(IdPSecurityContext secCtx) throws SSOException, IdentityMediationException { if (logger.isTraceEnabled()) logger.trace("Triggering IDP Initiated SLO from IDP Session Listener for Security Context " + secCtx); EndpointDescriptor ed = resolveIdpSloEndpoint(identityProvider); if (logger.isDebugEnabled()) logger.debug("Using IDP Initiated SLO endpoint " + ed); IDPInitiatedLogoutRequestType sloRequest = new IDPInitiatedLogoutRequestType(); sloRequest.setID(uuidGenerator.generateId()); sloRequest.setSsoSessionId(secCtx.getSessionIndex()); if (logger.isTraceEnabled()) logger.trace("Sending SLO Request " + sloRequest.getID() + " to IDP " + identityProvider.getName() + " using endpoint " + ed.getLocation()); IdentityMediator mediator = identityProvider.getChannel().getIdentityMediator(); // Response from SP SSOResponseType sloResponse = (SSOResponseType) mediator.sendMessage(sloRequest, ed, identityProvider.getChannel()); if (logger.isTraceEnabled()) logger.trace("Recevied SLO Response " + sloResponse.getID() + " from IDP " + identityProvider.getName() + " using endpoint " + ed.getLocation()); } protected EndpointDescriptor resolveIdpSloEndpoint(IdentityProvider idp) throws SSOException { // User default channel to signal SLO Channel defaultChannel = idp.getChannel(); // Look for local SLO endpoint, it will also receive SLO IDP Initiated requests IdentityMediationEndpoint e = null; for (IdentityMediationEndpoint endpoint : defaultChannel.getEndpoints()) { if (endpoint.getType().equals(SSOService.SingleLogoutService.toString())) { if (endpoint.getBinding().equals(SSOBinding.SSO_LOCAL.getValue())) { // We need to build an endpoint descriptor descriptor now ... String location = endpoint.getLocation().startsWith("/") ? defaultChannel.getLocation() + endpoint.getLocation() : endpoint.getLocation(); return new EndpointDescriptorImpl(idp.getName() + "-sso-slo-local", SSOService.SingleLogoutService.toString(), SSOBinding.SSO_LOCAL.toString(), location, null); } else if (endpoint.getBinding().equals(SSOBinding.SSO_SOAP.getValue())) { e = endpoint; } } } if (e != null) { String location = e.getLocation().startsWith("/") ? defaultChannel.getLocation() + e.getLocation() : e.getLocation(); return new EndpointDescriptorImpl(idp.getName() + "-sso-slo-soap", SSOService.SingleLogoutService.toString(), e.getBinding(), location, null); } throw new SSOException("No IDP SLO endpoint using LOCAL/SOAP binding found!"); } }