/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.auth.impl; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletConfig; import org.apache.avalon.framework.CascadingRuntimeException; import org.apache.avalon.framework.activity.Disposable; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.context.Context; import org.apache.avalon.framework.context.ContextException; import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; import org.apache.avalon.framework.thread.ThreadSafe; import org.apache.cocoon.components.ContextHelper; import org.apache.cocoon.environment.ObjectModelHelper; import org.apache.cocoon.environment.Request; import org.apache.cocoon.environment.Session; import org.apache.cocoon.servlet.CocoonServlet; import org.apache.commons.lang.ObjectUtils; import org.apache.cocoon.auth.Application; import org.apache.cocoon.auth.ApplicationManager; import org.apache.cocoon.auth.ApplicationUtil; import org.apache.cocoon.auth.User; /** * This is the default implementation of the * {@link org.apache.cocoon.auth.ApplicationManager}. * * @version $Id$ */ public class StandardApplicationManager extends AbstractLogEnabled implements ApplicationManager, Contextualizable, Serviceable, ThreadSafe, Disposable { /** The key used to store the login information in the session. */ protected static final String LOGIN_INFO_KEY = StandardApplicationManager.class.getName() + "/logininfo"; /** The prefix used to store the application data object in the session. */ protected static final String APPLICATION_KEY_PREFIX = StandardApplicationManager.class.getName() + "/app:"; /** The component context. */ protected Context context; /** The service manager. */ protected ServiceManager manager; /** * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) */ public void contextualize(final Context aContext) throws ContextException { this.context = aContext; try { ServletConfig config = (ServletConfig)this.context.get(CocoonServlet.CONTEXT_SERVLET_CONFIG); config.getServletContext().setAttribute(StandardApplicationManager.class.getName(), this); } catch (ContextException ignore) { // we ignore this if we are not running inside a servlet environment } } /** * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) */ public void service(final ServiceManager aManager) throws ServiceException { this.manager = aManager; } /** * @see org.apache.avalon.framework.activity.Disposable#dispose() */ public void dispose() { this.manager = null; } /** * Get the application with the given name. This method tries to lookup the * applicating using the current sitemap service manager. * @param appName The name of the application. * @return The application object. * @throws Exception If the application can't be found. */ protected Application getApplication(final String appName) throws Exception { final ServiceManager current = (ServiceManager) this.context.get(ContextHelper.CONTEXT_SITEMAP_SERVICE_MANAGER); Object o = current.lookup(Application.class.getName() + '/' + appName); if ( o == null ) { throw new ConfigurationException( "Application '" + appName + "' not found." ); } // to avoid messy release stuff later on, we just release the app now // as an application is thread safe this isn't really a problem current.release(o); return (Application)o; } /** * @see org.apache.cocoon.auth.ApplicationManager#isLoggedIn(java.lang.String) */ public boolean isLoggedIn(final String appName) { Object appData = null; final Map objectModel = ContextHelper.getObjectModel( this.context ); final Request req = ObjectModelHelper.getRequest(objectModel); final Session session = req.getSession(false); if ( session != null ) { appData = session.getAttribute(APPLICATION_KEY_PREFIX + appName); // if the user is logged in, we set the current application, data and user if ( appData != null ) { try { final Application application = this.getApplication(appName); final User user = (User)session.getAttribute(USER + '-' + appName); final Application oldApp = (Application)objectModel.get(ApplicationManager.APPLICATION); objectModel.put(ApplicationManager.APPLICATION, application); objectModel.put(ApplicationManager.APPLICATION_DATA, appData); objectModel.put(ApplicationManager.USER, user); // notify application // The application is only notified once per request. if ( oldApp == null || !oldApp.equals(application) ) { application.userIsAccessing(user); } } catch (Exception ignore) { throw new CascadingRuntimeException("Unable to get application '" + appName + "'", ignore); } } } return (appData != null); } /** * @see org.apache.cocoon.auth.ApplicationManager#login(java.lang.String, java.util.Map) */ public User login(final String appName, final Map loginContext) throws Exception { User user = null; final Map objectModel = ContextHelper.getObjectModel( this.context ); // first check, if we are already logged in if ( this.isLoggedIn(appName) ) { user = ApplicationUtil.getUser(objectModel); } else { final Request req = ObjectModelHelper.getRequest(objectModel); Session session = req.getSession(false); final Application app = this.getApplication(appName); LoginInfo info = null; Map loginInfos = null; if ( session != null ) { // is the user already logged in on the security handler? loginInfos = (Map)session.getAttribute(LOGIN_INFO_KEY); if ( loginInfos != null && loginInfos.containsKey(app.getSecurityHandler().getId()) ) { info = (LoginInfo)loginInfos.get(app.getSecurityHandler().getId()); user = info.user; } } if ( user == null ) { user = app.getSecurityHandler().login(loginContext); if ( user != null ) { // create new login info session = req.getSession(); loginInfos = (Map)session.getAttribute(LOGIN_INFO_KEY); if ( loginInfos == null ) { loginInfos = new HashMap(); } info = new LoginInfo(user); loginInfos.put(app.getSecurityHandler().getId(), info); } } // user can be null, if login failed if ( user != null ) { info.incUsageCounter(appName); session.setAttribute(LOGIN_INFO_KEY, loginInfos); // set the user in the session session.setAttribute(USER + '-' + appName, user); objectModel.put(ApplicationManager.USER, user); // set the application in the object model objectModel.put(ApplicationManager.APPLICATION, app); // notify the application app.userDidLogin(user, loginContext); // set the application data in the session Object data = ObjectUtils.NULL; if ( app.getApplicationStore() != null ) { data = app.getApplicationStore().loadApplicationData(user, app); } session.setAttribute(APPLICATION_KEY_PREFIX + appName, data); objectModel.put(ApplicationManager.APPLICATION_DATA, data); // notify application app.userIsAccessing(user); } } return user; } /** * @see org.apache.cocoon.auth.ApplicationManager#logout(java.lang.String, java.util.Map) */ public void logout(final String appName, final Map logoutContext) { final Map objectModel = ContextHelper.getObjectModel( this.context ); final Request req = ObjectModelHelper.getRequest(objectModel); final Session session = req.getSession(false); if ( session != null ) { Application app; try { app = this.getApplication(appName); } catch (Exception ignore) { throw new CascadingRuntimeException("Unable to get application '" + appName + "'", ignore); } // remove application data from session session.removeAttribute(APPLICATION_KEY_PREFIX + appName); // remove application from object model if ( app.equals( ApplicationUtil.getApplication(objectModel) ) ) { objectModel.remove(ApplicationManager.APPLICATION); objectModel.remove(ApplicationManager.APPLICATION_DATA); objectModel.remove(ApplicationManager.USER); } // remove user session.removeAttribute(USER + '-' + appName); // decrement logininfo counter final Map loginInfos = (Map)session.getAttribute(LOGIN_INFO_KEY); if ( loginInfos != null ) { final LoginInfo info = (LoginInfo)loginInfos.get(app.getSecurityHandler().getId()); if ( info != null ) { // notify the application app.userWillLogout(info.user, logoutContext); info.decUsageCounter(appName); if ( info.isUsed() ) { session.setAttribute(LOGIN_INFO_KEY, loginInfos); } else { // logout from security handler app.getSecurityHandler().logout(logoutContext, info.user); // remove user info loginInfos.remove(app.getSecurityHandler().getId()); if ( loginInfos.size() > 0 ) { session.setAttribute(LOGIN_INFO_KEY, loginInfos); } else { session.removeAttribute(LOGIN_INFO_KEY); // the user has left all applications, test the mode: String mode = null; if ( logoutContext != null ) { mode = (String)logoutContext.get(LOGOUT_CONTEXT_MODE_KEY); } if ( mode == null || mode.equals(LOGOUT_MODE_TERMINATE_SESSION_IF_UNUSED) ) { session.invalidate(); } } } } } } } }