/** * 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 * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * 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: IDPSessionListener.java,v 1.10 2009/09/23 22:28:31 bigfatrat Exp $ * * Portions Copyrighted 2014-2015 ForgeRock AS. */ package com.sun.identity.saml2.profile; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.logging.Level; import com.sun.identity.plugin.monitoring.FedMonAgent; import com.sun.identity.plugin.monitoring.FedMonSAML2Svc; import com.sun.identity.plugin.monitoring.MonitorManager; import com.sun.identity.plugin.session.SessionListener; import com.sun.identity.plugin.session.SessionManager; import com.sun.identity.plugin.session.SessionProvider; import com.sun.identity.plugin.session.SessionException; import com.sun.identity.saml2.assertion.NameID; import com.sun.identity.saml2.common.SAML2Constants; import com.sun.identity.saml2.common.SAML2Exception; import com.sun.identity.saml2.common.SAML2FailoverUtils; import com.sun.identity.saml2.common.SAML2Utils; import com.sun.identity.saml2.jaxb.entityconfig.BaseConfigType; import com.sun.identity.saml2.jaxb.entityconfig.SPSSOConfigElement; import com.sun.identity.saml2.jaxb.metadata.EndpointType; import com.sun.identity.saml2.jaxb.metadata.SPSSODescriptorElement; import com.sun.identity.saml2.logging.LogUtil; import com.sun.identity.saml2.meta.SAML2MetaException; import com.sun.identity.saml2.meta.SAML2MetaManager; import com.sun.identity.saml2.meta.SAML2MetaUtils; import com.sun.identity.shared.debug.Debug; import org.forgerock.openam.federation.saml2.SAML2TokenRepositoryException; /** * The class IDPSessionListener implements * SessionListener interface and is used for maintaining the * IDP session cache. */ public class IDPSessionListener implements SessionListener { private static SAML2MetaManager sm = null; private static Debug debug = SAML2Utils.debug; private static FedMonAgent agent; private static FedMonSAML2Svc saml2Svc; static { try { sm = new SAML2MetaManager(); } catch (SAML2MetaException sme) { debug.error("Error retreiving metadata",sme); } agent = MonitorManager.getAgent(); saml2Svc = MonitorManager.getSAML2Svc(); } /** * Constructor of IDPSessionListener. */ public IDPSessionListener() { } /** * Callback for SessionListener. * It is used for cleaning up the IDP session cache. * * @param session The session object */ public void sessionInvalidated(Object session) { String classMethod = "IDPSessionListener.sessionInvalidated: "; HashMap paramsMap = new HashMap(); if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( classMethod + "Entering ..."); } if (session == null) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( classMethod + "Session is null."); } return; } try { SessionProvider sessionProvider = SessionManager.getProvider(); String[] values = sessionProvider.getProperty( session, SAML2Constants.IDP_SESSION_INDEX); if (values == null || values.length == 0) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( classMethod + "No sessionIndex stored in session."); } return; } String sessionIndex = values[0]; if (sessionIndex == null || sessionIndex.length() == 0) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( classMethod + "No sessionIndex stored in session."); } return; } IDPSession idpSession = (IDPSession)IDPCache. idpSessionsByIndices.get(sessionIndex); if (idpSession != null) { paramsMap.put(SAML2Constants.ROLE, SAML2Constants.IDP_ROLE); String metaAlias = idpSession.getMetaAlias(); String realm = SAML2Utils. getRealm(SAML2MetaUtils.getRealmByMetaAlias(metaAlias)); String idpEntityID = sm.getEntityByMetaAlias(metaAlias); try { List list = (List)idpSession.getNameIDandSPpairs(); for (Iterator iter = list.iterator(); iter.hasNext();) { NameIDandSPpair pair = (NameIDandSPpair)iter.next(); String spEntityID = pair.getSPEntityID(); NameID nameID = pair.getNameID(); BaseConfigType idpConfig = sm.getIDPSSOConfig(realm, idpEntityID); if (idpConfig != null) { List idpSessionSyncList = (List) SAML2MetaUtils.getAttributes(idpConfig). get(SAML2Constants.IDP_SESSION_SYNC_ENABLED); if ((idpEntityID != null && spEntityID != null && idpSessionSyncList != null && idpSessionSyncList.size() != 0)) { boolean idpSessionSyncEnabled = ((String)idpSessionSyncList.get(0)). equals(SAML2Constants.TRUE)? true : false; // Initiate IDP SLO on IDP Idle/Max // session timeout only when the Session // Sync flag is enabled if (idpSessionSyncEnabled) { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( classMethod + "IDP Session Synchronization flag " + "is enabled, initiating SLO to SP"); } initiateIDPSingleLogout(sessionIndex, metaAlias, realm, SAML2Constants.SOAP, nameID, spEntityID, paramsMap); } } } else { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( classMethod + "Unable to retrieve the IDP " + "config data, idpConfig is null"); } } } } catch (SAML2MetaException sme) { SAML2Utils.debug.error( "IDPSessionListener.sessionInvalidated:", sme); } catch (SAML2Exception se) { SAML2Utils.debug.error( "IDPSessionListener.sessionInvalidated:", se); } catch (SessionException s) { SAML2Utils.debug.error( "IDPSessionListener.sessionInvalidated:", s); } synchronized(IDPCache.idpSessionsByIndices) { List list = (List)idpSession.getNameIDandSPpairs(); for(Iterator iter = list.iterator(); iter.hasNext();) { NameIDandSPpair pair = (NameIDandSPpair)iter.next(); NameID nameID = pair.getNameID(); if (SAML2Constants.NAMEID_TRANSIENT_FORMAT.equals( nameID.getFormat())) { IDPCache.userIDByTransientNameIDValue.remove( nameID.getValue()); } } } } else { if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message( classMethod + "IDP Session with session index " + sessionIndex + " already removed."); } } IDPCache.idpSessionsByIndices.remove(sessionIndex); IDPCache.authnContextCache.remove(sessionIndex); String sessID = sessionProvider.getSessionID(session); if (IDPCache.idpSessionsBySessionID.get(sessID) != null) { IDPCache.idpSessionsBySessionID.remove(sessID); if ((agent != null) && agent.isRunning() && (saml2Svc != null)){ saml2Svc.setIdpSessionCount( (long)IDPCache.idpSessionsBySessionID.size()); } } if (IDPCache.spSessionPartnerBySessionID.get(sessID) != null) { IDPCache.spSessionPartnerBySessionID.remove(sessID); } // This failing should not cause the whole process to fail try { if (SAML2FailoverUtils.isSAML2FailoverEnabled()) { SAML2FailoverUtils.deleteSAML2Token(sessionIndex); } } catch (SAML2TokenRepositoryException se) { SAML2Utils.debug.error(classMethod + "SAML2 Token Repository error, sessionIndex:" + sessionIndex, se); } if (SAML2Utils.debug.messageEnabled()) { SAML2Utils.debug.message(classMethod + "cleaned up the IDP session cache for a session expiring or being destroyed: sessionIndex=" + sessionIndex); } } catch (SessionException e) { if (SAML2Utils.debug.warningEnabled()) { SAML2Utils.debug.warning( classMethod + "invalid or expired session.", e); } } catch (SAML2MetaException samlme) { if (SAML2Utils.debug.warningEnabled()) { SAML2Utils.debug.warning( classMethod + "unable to retrieve idp entity id.", samlme); } } } /** * Performs an IdP initiated SLO against the remote SP using SOAP binding. * * @param sessionIndex Session Index * @param metaAlias IDP meta alias * @param realm Realm * @param binding Binding used * @param nameID the NameID * @param spEntityID SP Entity ID * @param paramsMap parameters map * @throws SAML2MetaException If there was an error while retrieving the metadata. * @throws SAML2Exception If there was an error while initiating SLO. * @throws SessionException If there was a problem with the session. */ private void initiateIDPSingleLogout(String sessionIndex, String metaAlias, String realm, String binding, NameID nameID, String spEntityID, Map paramsMap) throws SAML2MetaException, SAML2Exception, SessionException { SPSSODescriptorElement spsso = sm.getSPSSODescriptor(realm, spEntityID); if (spsso == null) { String[] data = {spEntityID}; LogUtil.error(Level.INFO, LogUtil.SP_METADATA_ERROR, data, null); throw new SAML2Exception(SAML2Utils.bundle.getString("metaDataError")); } List slosList = spsso.getSingleLogoutService(); String location = LogoutUtil.getSLOServiceLocation(slosList, SAML2Constants.SOAP); if (location == null) { if (debug.messageEnabled()) { debug.message("IDPSessionListener.initiateIDPSingleLogout(): Unable to synchronize sessions with SP \"" + spEntityID + "\" since the SP does not have SOAP SLO endpoint specified in its metadata"); } return; } SPSSOConfigElement spConfig = sm.getSPSSOConfig(realm, spEntityID); LogoutUtil.doLogout(metaAlias, spEntityID, slosList, null, binding, null, sessionIndex, nameID, null, null, paramsMap, spConfig); } }