All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencms.main.CmsSessionManager Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 18.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH & Co. KG, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.main;

import org.opencms.configuration.CmsSystemConfiguration.UserSessionMode;
import org.opencms.db.CmsUserSettings;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsProject;
import org.opencms.file.CmsRequestContext;
import org.opencms.file.CmsUser;
import org.opencms.security.CmsCustomLoginException;
import org.opencms.security.CmsRole;
import org.opencms.security.CmsSecurityException;
import org.opencms.ui.login.CmsLoginHelper;
import org.opencms.util.CmsRequestUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import org.opencms.workplace.CmsWorkplaceManager;
import org.opencms.workplace.tools.CmsToolManager;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;

import org.apache.commons.collections.Buffer;
import org.apache.commons.collections.BufferUtils;
import org.apache.commons.collections.buffer.CircularFifoBuffer;
import org.apache.commons.logging.Log;

/**
 * Keeps track of the sessions running on the OpenCms server and
 * provides a session info storage which is used to get an overview
 * about currently authenticated OpenCms users, as well as sending broadcasts between users.

* * For each authenticated OpenCms user, a {@link org.opencms.main.CmsSessionInfo} object * holds the information about the users status.

* * When a user session is invalidated, the user info will be removed. * This happens when a user log out, or when his session times out.

* * Please Note: The current implementation does not provide any permission checking, * so all users can access the methods of this manager. Permission checking * based on the current users OpenCms context may be added in a future OpenCms release.

* * @since 6.0.0 */ public class CmsSessionManager { /** The log object for this class. */ private static final Log LOG = CmsLog.getLog(CmsSessionManager.class); /** Lock object for synchronized session count updates. */ private Object m_lockSessionCount; /** Counter for the currently active sessions. */ private int m_sessionCountCurrent; /** Counter for all sessions created so far. */ private int m_sessionCountTotal; /** Session storage provider instance. */ private I_CmsSessionStorageProvider m_sessionStorageProvider; /** The user session mode. */ private UserSessionMode m_userSessionMode; /** * Creates a new instance of the OpenCms session manager.

*/ protected CmsSessionManager() { // create a lock object for the session counter m_lockSessionCount = new Object(); } /** * Checks whether a new session can be created for the user, and throws an exception if not.

* * @param user the user to check * @throws CmsException if no new session for the user can't be created */ public void checkCreateSessionForUser(CmsUser user) throws CmsException { if (getUserSessionMode() == UserSessionMode.single) { List infos = getSessionInfos(user.getId()); if (!infos.isEmpty()) { throw new CmsCustomLoginException( org.opencms.security.Messages.get().container( org.opencms.security.Messages.ERR_ALREADY_LOGGED_IN_0)); } } } /** * Returns the broadcast queue for the given OpenCms session id.

* * @param sessionId the OpenCms session id to get the broadcast queue for * * @return the broadcast queue for the given OpenCms session id */ public Buffer getBroadcastQueue(String sessionId) { CmsSessionInfo sessionInfo = getSessionInfo(getSessionUUID(sessionId)); if (sessionInfo == null) { // return empty message buffer if the session is gone or not available return BufferUtils.synchronizedBuffer(new CircularFifoBuffer(CmsSessionInfo.QUEUE_SIZE)); } return sessionInfo.getBroadcastQueue(); } /** * Returns the number of sessions currently authenticated in the OpenCms security system.

* * @return the number of sessions currently authenticated in the OpenCms security system */ public int getSessionCountAuthenticated() { // since this method could be called from another thread // we have to prevent access before initialization if (m_sessionStorageProvider == null) { return 0; } return m_sessionStorageProvider.getSize(); } /** * Returns the number of current sessions, including the sessions of not authenticated guest users.

* * @return the number of current sessions, including the sessions of not authenticated guest users */ public int getSessionCountCurrent() { return m_sessionCountCurrent; } /** * Returns the number of total sessions generated so far, including already destroyed sessions.

* * @return the number of total sessions generated so far, including already destroyed sessions */ public int getSessionCountTotal() { return m_sessionCountTotal; } /** * Returns the complete user session info of a user from the session storage, * or null if this session id has no session info attached.

* * @param sessionId the OpenCms session id to return the session info for * * @return the complete user session info of a user from the session storage */ public CmsSessionInfo getSessionInfo(CmsUUID sessionId) { // since this method could be called from another thread // we have to prevent access before initialization if (m_sessionStorageProvider == null) { return null; } return m_sessionStorageProvider.get(sessionId); } /** * Returns the OpenCms user session info for the given request, * or null if no user session is available.

* * @param req the current request * * @return the OpenCms user session info for the given request, or null if no user session is available */ public CmsSessionInfo getSessionInfo(HttpServletRequest req) { HttpSession session = req.getSession(false); if (session == null) { // special case for accessing a session from "outside" requests (e.g. upload applet) String sessionId = req.getHeader(CmsRequestUtil.HEADER_JSESSIONID); return sessionId == null ? null : getSessionInfo(sessionId); } return getSessionInfo(session); } /** * Returns the OpenCms user session info for the given http session, * or null if no user session is available.

* * @param session the current http session * * @return the OpenCms user session info for the given http session, or null if no user session is available */ public CmsSessionInfo getSessionInfo(HttpSession session) { if (session == null) { return null; } CmsUUID sessionId = (CmsUUID)session.getAttribute(CmsSessionInfo.ATTRIBUTE_SESSION_ID); return (sessionId == null) ? null : getSessionInfo(sessionId); } /** * Returns the complete user session info of a user from the session storage, * or null if this session id has no session info attached.

* * @param sessionId the OpenCms session id to return the session info for, * this must be a String representation of a {@link CmsUUID} * * @return the complete user session info of a user from the session storage * * @see #getSessionInfo(CmsUUID) */ public CmsSessionInfo getSessionInfo(String sessionId) { return getSessionInfo(getSessionUUID(sessionId)); } /** * Returns all current session info objects.

* * @return all current session info objects */ public List getSessionInfos() { // since this method could be called from another thread // we have to prevent access before initialization if (m_sessionStorageProvider == null) { return Collections.emptyList(); } return m_sessionStorageProvider.getAll(); } /** * Returns a list of all active session info objects for the specified user.

* * An OpenCms user can have many active sessions. * This is e.g. possible when two people have logged in to the system using the * same username. Even one person can have multiple sessions if he * is logged in to OpenCms with several browser windows at the same time.

* * @param userId the id of the user * * @return a list of all active session info objects for the specified user */ public List getSessionInfos(CmsUUID userId) { // since this method could be called from another thread // we have to prevent access before initialization if (m_sessionStorageProvider == null) { return Collections.emptyList(); } return m_sessionStorageProvider.getAllOfUser(userId); } /** * Gets the user session mode.

* * @return the user session mode */ public UserSessionMode getUserSessionMode() { return m_userSessionMode; } /** * Kills all sessions for the given user.

* * @param cms the current CMS context * @param user the user for whom the sessions should be killed * * @throws CmsException if something goes wrong */ public void killSession(CmsObject cms, CmsUser user) throws CmsException { OpenCms.getRoleManager().checkRole(cms, CmsRole.ACCOUNT_MANAGER); List infos = getSessionInfos(user.getId()); for (CmsSessionInfo info : infos) { m_sessionStorageProvider.remove(info.getSessionId()); } } /** * Destroys a session given the session id. Only allowed for users which have the "account manager" role.

* * @param cms the current CMS context * @param sessionid the session id * * @throws CmsException if something goes wrong */ public void killSession(CmsObject cms, CmsUUID sessionid) throws CmsException { OpenCms.getRoleManager().checkRole(cms, CmsRole.ACCOUNT_MANAGER); m_sessionStorageProvider.remove(sessionid); } /** * Sends a broadcast to all sessions of all currently authenticated users.

* * @param cms the OpenCms user context of the user sending the broadcast * * @param message the message to broadcast */ public void sendBroadcast(CmsObject cms, String message) { if (CmsStringUtil.isEmptyOrWhitespaceOnly(message)) { // don't broadcast empty messages return; } // create the broadcast CmsBroadcast broadcast = new CmsBroadcast(cms.getRequestContext().getCurrentUser(), message); // send the broadcast to all authenticated sessions Iterator i = m_sessionStorageProvider.getAll().iterator(); while (i.hasNext()) { CmsSessionInfo sessionInfo = i.next(); if (m_sessionStorageProvider.get(sessionInfo.getSessionId()) != null) { // double check for concurrent modification sessionInfo.getBroadcastQueue().add(broadcast); } } } /** * Sends a broadcast to the specified user session.

* * @param cms the OpenCms user context of the user sending the broadcast * * @param message the message to broadcast * @param sessionId the OpenCms session uuid target (receiver) of the broadcast */ public void sendBroadcast(CmsObject cms, String message, String sessionId) { if (CmsStringUtil.isEmptyOrWhitespaceOnly(message)) { // don't broadcast empty messages return; } // send the broadcast only to the selected session CmsSessionInfo sessionInfo = m_sessionStorageProvider.get(new CmsUUID(sessionId)); if (sessionInfo != null) { // double check for concurrent modification sessionInfo.getBroadcastQueue().add(new CmsBroadcast(cms.getRequestContext().getCurrentUser(), message)); } } /** * Sends a broadcast to all sessions of a given user.

* * The user sending the message may be a real user like * cms.getRequestContext().currentUser() or * null for a system message.

* * @param fromUser the user sending the broadcast * @param message the message to broadcast * @param toUser the target (receiver) of the broadcast */ public void sendBroadcast(CmsUser fromUser, String message, CmsUser toUser) { if (CmsStringUtil.isEmptyOrWhitespaceOnly(message)) { // don't broadcast empty messages return; } // create the broadcast CmsBroadcast broadcast = new CmsBroadcast(fromUser, message); List userSessions = getSessionInfos(toUser.getId()); Iterator i = userSessions.iterator(); // send the broadcast to all sessions of the selected user while (i.hasNext()) { CmsSessionInfo sessionInfo = i.next(); if (m_sessionStorageProvider.get(sessionInfo.getSessionId()) != null) { // double check for concurrent modification sessionInfo.getBroadcastQueue().add(broadcast); } } } /** * Switches the current user to the given user. The session info is rebuild as if the given user * performs a login at the workplace. * * @param cms the current CmsObject * @param req the current request * @param user the user to switch to * * @return the direct edit target if available * * @throws CmsException if something goes wrong */ public String switchUser(CmsObject cms, HttpServletRequest req, CmsUser user) throws CmsException { // only user with root administrator role are allowed to switch the user OpenCms.getRoleManager().checkRole(cms, CmsRole.ADMINISTRATOR.forOrgUnit(user.getOuFqn())); CmsSessionInfo info = getSessionInfo(req); HttpSession session = req.getSession(false); if ((info == null) || (session == null)) { throw new CmsException(Messages.get().container(Messages.ERR_NO_SESSIONINFO_SESSION_0)); } if (!OpenCms.getRoleManager().hasRole(cms, user.getName(), CmsRole.ELEMENT_AUTHOR)) { throw new CmsSecurityException(Messages.get().container(Messages.ERR_NO_WORKPLACE_PERMISSIONS_0)); } // get the user settings for the given user and set the start project and the site root CmsUserSettings settings = new CmsUserSettings(user); String ouFqn = user.getOuFqn(); CmsProject userProject = cms.readProject( ouFqn + OpenCms.getWorkplaceManager().getDefaultUserSettings().getStartProject()); try { userProject = cms.readProject(settings.getStartProject()); } catch (Exception e) { // ignore, use default } String userSiteRoot = settings.getStartSite(); CmsRequestContext context = new CmsRequestContext( user, userProject, null, cms.getRequestContext().getRequestMatcher(), userSiteRoot, cms.getRequestContext().isSecureRequest(), null, null, null, 0, null, null, ouFqn); // delete the stored workplace settings, so the session has to receive them again session.removeAttribute(CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS); // create a new CmsSessionInfo and store it inside the session map CmsSessionInfo newInfo = new CmsSessionInfo(context, info.getSessionId(), info.getMaxInactiveInterval()); addSessionInfo(newInfo); // set the site root, project and ou fqn to current cms context cms.getRequestContext().setSiteRoot(userSiteRoot); cms.getRequestContext().setCurrentProject(userProject); cms.getRequestContext().setOuFqn(user.getOuFqn()); String directEditTarget = CmsLoginHelper.getDirectEditPath(cms, new CmsUserSettings(user), false); return directEditTarget != null ? OpenCms.getLinkManager().substituteLink(cms, directEditTarget, userSiteRoot) : null; } /** * @see java.lang.Object#toString() */ @Override public String toString() { StringBuffer output = new StringBuffer(); Iterator i = m_sessionStorageProvider.getAll().iterator(); output.append("[CmsSessions]:\n"); while (i.hasNext()) { CmsSessionInfo sessionInfo = i.next(); output.append(sessionInfo.getSessionId().toString()); output.append(" : "); output.append(sessionInfo.getUserId().toString()); output.append('\n'); } return output.toString(); } /** * Updates the the OpenCms session data used for quick authentication of users.

* * This is required if the user data (current group or project) was changed in * the requested document.

* * The user data is only updated if the user was authenticated to the system. * * @param cms the current OpenCms user context * @param req the current request */ public void updateSessionInfo(CmsObject cms, HttpServletRequest req) { if (!cms.getRequestContext().isUpdateSessionEnabled()) { // this request must not update the user session info // this is true for long running "thread" requests, e.g. during project publish return; } if (cms.getRequestContext().getUri().equals(CmsToolManager.VIEW_JSPPAGE_LOCATION)) { // this request must not update the user session info // if not the switch user feature would not work return; } if (!cms.getRequestContext().getCurrentUser().isGuestUser()) { // Guest user requests don't need to update the OpenCms user session information // get the session info object for the user CmsSessionInfo sessionInfo = getSessionInfo(req); if (sessionInfo != null) { // update the users session information sessionInfo.update(cms.getRequestContext()); addSessionInfo(sessionInfo); } else { HttpSession session = req.getSession(false); // only create session info if a session is already available if (session != null) { // create a new session info for the user sessionInfo = new CmsSessionInfo( cms.getRequestContext(), new CmsUUID(), session.getMaxInactiveInterval()); // append the session info to the http session session.setAttribute(CmsSessionInfo.ATTRIBUTE_SESSION_ID, sessionInfo.getSessionId().clone()); // update the session info user data addSessionInfo(sessionInfo); } } } } /** * Updates the the OpenCms session data used for quick authentication of users.

* * This is required if the user data (current group or project) was changed in * the requested document.

* * The user data is only updated if the user was authenticated to the system. * * @param cms the current OpenCms user context * @param session the current session */ public void updateSessionInfo(CmsObject cms, HttpSession session) { if (session == null) { return; } if (!cms.getRequestContext().isUpdateSessionEnabled()) { // this request must not update the user session info // this is true for long running "thread" requests, e.g. during project publish return; } if (cms.getRequestContext().getUri().equals(CmsToolManager.VIEW_JSPPAGE_LOCATION)) { // this request must not update the user session info // if not the switch user feature would not work return; } if (!cms.getRequestContext().getCurrentUser().isGuestUser()) { // Guest user requests don't need to update the OpenCms user session information // get the session info object for the user CmsSessionInfo sessionInfo = getSessionInfo(session); if (sessionInfo != null) { // update the users session information sessionInfo.update(cms.getRequestContext()); addSessionInfo(sessionInfo); } else { sessionInfo = new CmsSessionInfo( cms.getRequestContext(), new CmsUUID(), session.getMaxInactiveInterval()); // append the session info to the http session session.setAttribute(CmsSessionInfo.ATTRIBUTE_SESSION_ID, sessionInfo.getSessionId().clone()); // update the session info user data addSessionInfo(sessionInfo); } } } /** * Updates all session info objects, so that invalid projects * are replaced by the Online project.

* * @param cms the cms context */ public void updateSessionInfos(CmsObject cms) { // get all sessions List userSessions = getSessionInfos(); Iterator i = userSessions.iterator(); while (i.hasNext()) { CmsSessionInfo sessionInfo = i.next(); // check is the project stored in this session is not existing anymore // if so, set it to the online project CmsUUID projectId = sessionInfo.getProject(); try { cms.readProject(projectId); } catch (CmsException e) { // the project does not longer exist, update the project information with the online project sessionInfo.setProject(CmsProject.ONLINE_PROJECT_ID); addSessionInfo(sessionInfo); } } } /** * Adds a new session info into the session storage.

* * @param sessionInfo the session info to store for the id */ protected void addSessionInfo(CmsSessionInfo sessionInfo) { if (getUserSessionMode() == UserSessionMode.standard) { m_sessionStorageProvider.put(sessionInfo); } else if (getUserSessionMode() == UserSessionMode.single) { CmsUUID userId = sessionInfo.getUserId(); List infos = getSessionInfos(userId); if (infos.isEmpty() || ((infos.size() == 1) && infos.get(0).getSessionId().equals(sessionInfo.getSessionId()))) { m_sessionStorageProvider.put(sessionInfo); } else { throw new RuntimeException("Can't create another session for the same user."); } } } /** * Returns the UUID representation for the given session id String.

* * @param sessionId the session id String to return the UUID representation for * * @return the UUID representation for the given session id String */ protected CmsUUID getSessionUUID(String sessionId) { return new CmsUUID(sessionId); } /** * Sets the storage provider.

* * @param sessionStorageProvider the storage provider implementation */ protected void initialize(I_CmsSessionStorageProvider sessionStorageProvider) { m_sessionStorageProvider = sessionStorageProvider; m_sessionStorageProvider.initialize(); } /** * Called by the {@link OpenCmsListener} when a http session is created.

* * @param event the http session event * * @see javax.servlet.http.HttpSessionListener#sessionCreated(javax.servlet.http.HttpSessionEvent) * @see OpenCmsListener#sessionCreated(HttpSessionEvent) */ protected void sessionCreated(HttpSessionEvent event) { HttpServletRequest request = OpenCmsServlet.currentRequest.get(); String tid = "[" + Thread.currentThread().getId() + "] "; synchronized (m_lockSessionCount) { m_sessionCountCurrent = (m_sessionCountCurrent <= 0) ? 1 : (m_sessionCountCurrent + 1); m_sessionCountTotal++; if (LOG.isInfoEnabled()) { LOG.info(tid + Messages.get().getBundle().key( Messages.LOG_SESSION_CREATED_2, new Integer(m_sessionCountTotal), new Integer(m_sessionCountCurrent))); } } if (LOG.isDebugEnabled()) { LOG.debug(tid + Messages.get().getBundle().key(Messages.LOG_SESSION_CREATED_1, event.getSession().getId())); if (request != null) { LOG.debug(tid + "Session created in request: " + request.getRequestURL()); } StringWriter sw = new StringWriter(); new Throwable("").printStackTrace(new PrintWriter(sw)); String stackTrace = sw.toString(); LOG.debug(tid + "Stack = \n" + stackTrace); } } /** * Called by the {@link OpenCmsListener} when a http session is destroyed.

* * @param event the http session event * * @see javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEvent) * @see OpenCmsListener#sessionDestroyed(HttpSessionEvent) */ protected void sessionDestroyed(HttpSessionEvent event) { synchronized (m_lockSessionCount) { m_sessionCountCurrent = (m_sessionCountCurrent <= 0) ? 0 : (m_sessionCountCurrent - 1); if (LOG.isInfoEnabled()) { LOG.info( Messages.get().getBundle().key( Messages.LOG_SESSION_DESTROYED_2, new Integer(m_sessionCountTotal), new Integer(m_sessionCountCurrent))); } } CmsSessionInfo sessionInfo = getSessionInfo(event.getSession()); CmsUUID userId = null; if (sessionInfo != null) { userId = sessionInfo.getUserId(); m_sessionStorageProvider.remove(sessionInfo.getSessionId()); } if ((userId != null) && (getSessionInfos(userId).size() == 0)) { // remove the temporary locks of this user from memory OpenCmsCore.getInstance().getLockManager().removeTempLocks(userId); } HttpSession session = event.getSession(); Enumeration attrNames = session.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String)attrNames.nextElement(); Object attribute = session.getAttribute(attrName); if (attribute instanceof I_CmsSessionDestroyHandler) { try { ((I_CmsSessionDestroyHandler)attribute).onSessionDestroyed(); } catch (Exception e) { LOG.error(e.getLocalizedMessage(), e); } } } if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_SESSION_DESTROYED_1, event.getSession().getId())); } } /** * Sets the user session mode.

* * @param userSessionMode the user session mode */ protected void setUserSessionMode(UserSessionMode userSessionMode) { m_userSessionMode = userSessionMode; } /** * Removes all stored session info objects.

* * @throws Exception if something goes wrong */ protected void shutdown() throws Exception { if (m_sessionStorageProvider != null) { m_sessionStorageProvider.shutdown(); } } /** * Validates the sessions stored in this manager and removes * any sessions that have become invalidated.

*/ protected void validateSessionInfos() { // since this method could be called from another thread // we have to prevent access before initialization if (m_sessionStorageProvider == null) { return; } m_sessionStorageProvider.validate(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy