blob: 3ee79e329a65a25fd34a44cff79297e7dfa9fee2 [file] [log] [blame]
/*
* 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.webapps.authentication.components;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.container.ContainerUtil;
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.ProcessingException;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.components.SitemapConfigurable;
import org.apache.cocoon.components.SitemapConfigurationHolder;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.Session;
import org.apache.cocoon.util.ClassUtils;
import org.apache.cocoon.util.Deprecation;
import org.apache.cocoon.webapps.authentication.AuthenticationConstants;
import org.apache.cocoon.webapps.authentication.AuthenticationManager;
import org.apache.cocoon.webapps.authentication.configuration.ApplicationConfiguration;
import org.apache.cocoon.webapps.authentication.configuration.HandlerConfiguration;
import org.apache.cocoon.webapps.authentication.context.AuthenticationContext;
import org.apache.cocoon.webapps.authentication.user.RequestState;
import org.apache.cocoon.webapps.authentication.user.UserHandler;
import org.apache.cocoon.webapps.authentication.user.UserState;
import org.apache.cocoon.webapps.session.ContextManager;
import org.apache.cocoon.webapps.session.SessionConstants;
import org.apache.cocoon.webapps.session.SessionManager;
import org.apache.cocoon.webapps.session.context.SessionContext;
import org.apache.excalibur.source.SourceParameters;
import org.apache.excalibur.source.SourceResolver;
import org.apache.excalibur.source.SourceUtil;
import org.apache.excalibur.xml.xpath.XPathProcessor;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
/**
* This is the basis authentication component.
*
* @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
* @deprecated This block is deprecated and will be removed in future versions.
* @version CVS $Id$
*/
public class DefaultAuthenticationManager
extends AbstractLogEnabled
implements AuthenticationManager,
SitemapConfigurable,
Serviceable,
Disposable,
ThreadSafe,
Contextualizable,
Component {
/** The name of the session attribute storing the user status */
public final static String SESSION_ATTRIBUTE_USER_STATUS = DefaultAuthenticationManager.class.getName() + "/UserStatus";
/** The manager for the authentication handlers */
protected SitemapConfigurationHolder holder;
/** The Service Manager */
protected ServiceManager manager;
/** The Source Resolver */
protected SourceResolver resolver;
/** The context */
protected Context context;
/** Instantiated authenticators */
protected Map authenticators = new HashMap();
/** The xpath processor */
protected XPathProcessor xpathProcessor;
/** This is the key used to store the current request state in the request object */
private static final String REQUEST_STATE_KEY = RequestState.class.getName();
/**
* Set the sitemap configuration containing the handlers
*/
public void configure(SitemapConfigurationHolder holder)
throws ConfigurationException {
Deprecation.logger.warn("The authentication-fw block is deprecated. Please use the auth block instead.");
this.holder = holder;
}
/**
* Get the handler configuration for the current sitemap
*/
private Map getHandlerConfigurations()
throws ProcessingException {
Map configs = (Map) this.holder.getPreparedConfiguration();
if ( null == configs ) {
try {
configs = DefaultHandlerManager.prepareHandlerConfiguration(ContextHelper.getObjectModel(this.context),
this.holder);
} catch (ConfigurationException ce) {
throw new ProcessingException("Configuration error.", ce);
}
}
return configs;
}
/**
* Get the handler configuration
* @param name The handler name
* @return The configuration or null.
*/
private HandlerConfiguration getHandlerConfiguration(String name)
throws ProcessingException {
final Map configs = this.getHandlerConfigurations();
HandlerConfiguration c = null;
if ( configs != null) {
c = (HandlerConfiguration)configs.get( name );
}
return c;
}
private Request getRequest() {
return ContextHelper.getRequest(this.context);
}
private Session getSession(boolean create) {
return this.getRequest().getSession(create);
}
private UserState getUserState() {
final Session session = this.getSession( false );
UserState status = null;
if ( session != null) {
status = (UserState) session.getAttribute(SESSION_ATTRIBUTE_USER_STATUS);
}
return status;
}
private UserState createUserState() {
UserState status = this.getUserState();
if ( status == null ) {
final Session session = this.getSession(true);
status = new UserState();
session.setAttribute(SESSION_ATTRIBUTE_USER_STATUS, status);
}
return status;
}
private UserHandler getUserHandler(String name) {
final UserState status = this.getUserState();
if ( status != null ) {
return status.getHandler( name );
}
return null;
}
private void updateUserState() {
final Session session = this.getSession(true);
Object status = session.getAttribute(SESSION_ATTRIBUTE_USER_STATUS);
session.setAttribute(SESSION_ATTRIBUTE_USER_STATUS, status);
}
/* (non-Javadoc)
* @see org.apache.cocoon.webapps.authentication.components.Manager#authenticate(java.lang.String, java.lang.String, org.apache.excalibur.source.SourceParameters)
*/
public UserHandler login(String handlerName,
String applicationName,
SourceParameters parameters)
throws ProcessingException {
HandlerConfiguration config = this.getHandlerConfiguration( handlerName );
if ( config == null ) {
throw new ProcessingException("Unknown handler to authenticate: " + handlerName);
}
// are we already logged in?
UserHandler handler = this.getUserHandler( handlerName );
if ( handler != null ) {
throw new ProcessingException("User is already authenticated using handler: " + handlerName);
}
Authenticator authenticator = this.lookupAuthenticator( config );
try {
Authenticator.AuthenticationResult result = authenticator.authenticate( config, parameters );
if (result != null) {
if (result.valid) {
AuthenticationContext authContext = new AuthenticationContext(this.context, this.xpathProcessor, this.resolver);
handler = new UserHandler(config, authContext);
// store the authentication data in the context
authContext.init(result.result);
} else {
// now set the failure information in the temporary context
ContextManager contextManager = null;
try {
contextManager = (ContextManager) this.manager.lookup( ContextManager.ROLE );
SessionContext temp = contextManager.getContext( SessionConstants.TEMPORARY_CONTEXT );
final DocumentFragment fragment = result.result.createDocumentFragment();
final Node root = result.result.getDocumentElement();
root.normalize();
Node child;
boolean appendedNode = false;
while (root.hasChildNodes() ) {
child = root.getFirstChild();
root.removeChild(child);
// Leave out empty text nodes before any other node
if (appendedNode
|| child.getNodeType() != Node.TEXT_NODE
|| child.getNodeValue().trim().length() > 0) {
fragment.appendChild(child);
appendedNode = true;
}
}
temp.appendXML("/", fragment);
} catch ( ServiceException se ) {
throw new ProcessingException("Unable to lookup session manager.", se);
} finally {
this.manager.release( contextManager );
}
}
}
} finally {
this.releaseAuthenticator( authenticator, config );
}
if ( handler != null ) {
// create UserStatus
final UserState status = this.createUserState();
status.addHandler( handler );
this.updateUserState();
// update RequestState
RequestState state = new RequestState( handler, applicationName);
this.setState( state );
state.initialize( this.resolver );
// And now load applications
Iterator applications = handler.getHandlerConfiguration().getApplications().values().iterator();
while ( applications.hasNext() ) {
ApplicationConfiguration appHandler = (ApplicationConfiguration)applications.next();
if ( !appHandler.getLoadOnDemand() ) {
handler.getContext().loadApplicationXML( appHandler, this.resolver );
}
}
}
return handler;
}
/**
* Release the used authenticator
*/
protected void releaseAuthenticator(Authenticator authenticator, HandlerConfiguration config) {
// all authenticators are released on dispose
}
/**
* The authenticator used to authenticate a user
*/
protected Authenticator lookupAuthenticator(HandlerConfiguration config)
throws ProcessingException {
final String name = config.getAuthenticatorClassName();
Authenticator authenticator = (Authenticator) this.authenticators.get(name);
if ( authenticator == null ) {
synchronized (this) {
authenticator = (Authenticator) this.authenticators.get(name);
if ( authenticator == null ) {
try {
authenticator = (Authenticator) ClassUtils.newInstance(name);
ContainerUtil.enableLogging( authenticator, this.getLogger() );
ContainerUtil.contextualize( authenticator, this.context);
ContainerUtil.service( authenticator, this.manager );
ContainerUtil.initialize( authenticator );
this.authenticators.put(name, authenticator);
} catch (Exception e ) {
throw new ProcessingException("Unable to initialize authenticator from class " + name, e);
}
}
}
}
return authenticator;
}
/* (non-Javadoc)
* @see org.apache.cocoon.webapps.authentication.components.Manager#checkAuthentication(org.apache.cocoon.environment.Redirector, java.lang.String, java.lang.String)
*/
public boolean checkAuthentication(Redirector redirector,
String handlerName,
String applicationName)
throws IOException, ProcessingException {
HandlerConfiguration config = this.getHandlerConfiguration( handlerName );
if ( config == null ) {
throw new ProcessingException("Unknown handler to check: " + handlerName);
}
// are we already logged in?
UserHandler handler = this.getUserHandler( handlerName );
final boolean authenticated = ( handler != null );
if ( !authenticated ) {
if (redirector != null) {
// create parameters
SourceParameters parameters = config.getRedirectParameters();
if (parameters == null) parameters = new SourceParameters();
final Request request = this.getRequest();
String resource = request.getRequestURI();
if (request.getQueryString() != null) {
resource += '?' + request.getQueryString();
}
parameters.setSingleParameterValue("resource", resource);
final String redirectURI = config.getRedirectURI();
redirector.globalRedirect(false, SourceUtil.appendParameters(redirectURI, parameters));
}
} else {
// update state
RequestState state = new RequestState( handler, applicationName );
this.setState( state );
state.initialize( this.resolver );
}
return authenticated;
}
public String getForwardingURI(String handlerName) throws ProcessingException {
HandlerConfiguration config = this.getHandlerConfiguration( handlerName );
SourceParameters parameters = config.getRedirectParameters();
if (parameters == null) parameters = new SourceParameters();
final Request request = this.getRequest();
String resource = request.getRequestURI();
if (request.getQueryString() != null) {
resource += '?' + request.getQueryString();
}
parameters.setSingleParameterValue("resource", resource);
final String redirectURI = config.getRedirectURI();
return SourceUtil.appendParameters(redirectURI, parameters);
}
/* (non-Javadoc)
* @see org.apache.cocoon.webapps.authentication.components.Manager#isAuthenticated(java.lang.String)
*/
public UserHandler isAuthenticated(String handlerName)
throws ProcessingException {
return this.getUserHandler( handlerName );
}
/* (non-Javadoc)
* @see org.apache.cocoon.webapps.authentication.components.Manager#logout(java.lang.String, java.lang.String)
*/
public void logout(String handlerName, int mode)
throws ProcessingException {
HandlerConfiguration config = this.getHandlerConfiguration( handlerName );
if ( config == null ) {
throw new ProcessingException("Unknown handler to logout: " + handlerName);
}
// are we logged in?
UserHandler handler = this.getUserHandler( handlerName );
// we don't throw an exception if we are already logged out!
if ( handler != null ) {
// Do we save something on logout?
/*
if ( config.saveOnLogout()
&& config.getSaveResource() != null) {
final AuthenticationContext authContext = handler.getContext();
try {
// This might not work, because of the missing state
authContext.saveXML("/authentication",
null,
ContextHelper.getObjectModel(this.context),
this.resolver, this.manager);
} catch (Exception ignore) {
// we don't want to stop the logout process
// because of errors during save
this.getLogger().error("Exception while saving authentication information.", ignore);
}
}
// save applications (if configured)
Iterator iter = config.getApplications().values().iterator();
while ( iter.hasNext() ) {
ApplicationConfiguration appConfig = (ApplicationConfiguration) iter.next();
if ( appConfig.saveOnLogout()
&& appConfig.getSaveResource() != null ) {
// ???
}
}
*/
// notify the authenticator
try {
this.lookupAuthenticator(config).logout(handler);
} catch (Exception ignore) {
// we really ignore any exception!
}
List applicationContexts = handler.getApplicationContexts();
if ( applicationContexts != null ) {
ContextManager contextManager = null;
try {
contextManager = (ContextManager)this.manager.lookup(ContextManager.ROLE);
Iterator i = applicationContexts.iterator();
while ( i.hasNext() ) {
final String current = (String)i.next();
contextManager.deleteContext( current );
}
} catch (ServiceException ce) {
throw new ProcessingException("Unable to create session context.", ce);
} finally {
this.manager.release( contextManager);
}
}
UserState status = this.getUserState();
status.removeHandler( handlerName );
this.updateUserState();
// handling of session termination
SessionManager sessionManager = null;
try {
sessionManager = (SessionManager)this.manager.lookup( SessionManager.ROLE );
if ( mode == AuthenticationConstants.LOGOUT_MODE_IMMEDIATELY ) {
sessionManager.terminateSession(true);
} else if ( mode == AuthenticationConstants.LOGOUT_MODE_IF_UNUSED ) {
if ( !status.hasHandler()) {
sessionManager.terminateSession( false );
}
} else if ( mode == AuthenticationConstants.LOGOUT_MODE_IF_NOT_AUTHENTICATED) {
if ( !status.hasHandler()) {
sessionManager.terminateSession( true );
}
} else {
throw new ProcessingException("Unknown logout mode: " + mode);
}
} catch (ServiceException se) {
throw new ProcessingException("Unable to lookup session manager.", se);
} finally {
this.manager.release( sessionManager );
}
}
}
/**
* Serviceable
*/
public void service(ServiceManager manager)
throws ServiceException {
this.manager = manager;
this.resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
this.xpathProcessor = (XPathProcessor)this.manager.lookup(XPathProcessor.ROLE);
}
/* (non-Javadoc)
* @see org.apache.avalon.framework.activity.Disposable#dispose()
*/
public void dispose() {
Iterator iter = this.authenticators.values().iterator();
while ( iter.hasNext() ) {
final Authenticator authenticator = (Authenticator) iter.next();
ContainerUtil.dispose( authenticator );
}
if ( this.manager != null) {
this.manager.release( this.resolver );
this.manager.release(this.xpathProcessor);
this.resolver = null;
this.xpathProcessor = null;
this.manager = null;
}
}
/**
* Get the current state of authentication
*/
public RequestState getState() {
return getRequestState(this.context);
}
public static RequestState getRequestState(Context context) {
final Request req = ContextHelper.getRequest(context);
return (RequestState)req.getAttribute( REQUEST_STATE_KEY);
}
/* (non-Javadoc)
* @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
*/
public void contextualize(Context context) throws ContextException {
this.context = context;
}
protected void setState(RequestState status) {
final Request req = ContextHelper.getRequest(this.context);
if ( status != null ) {
req.setAttribute( REQUEST_STATE_KEY, status);
} else {
req.removeAttribute( REQUEST_STATE_KEY );
}
}
/**
* Create Application Context.
* This context is destroyed when the user logs out of the handler
*/
public SessionContext createApplicationContext(String name,
String loadURI,
String saveURI)
throws ProcessingException {
RequestState state = this.getState();
UserHandler handler = state.getHandler();
SessionContext context = null;
if ( handler != null ) {
ContextManager contextManager = null;
try {
contextManager = (ContextManager)this.manager.lookup(ContextManager.ROLE);
// create new context
context = contextManager.createContext(name, loadURI, saveURI);
handler.addApplicationContext( name );
} catch (ServiceException ce) {
throw new ProcessingException("Unable to create session context.", ce);
} catch (IOException ioe) {
throw new ProcessingException("Unable to create session context.", ioe);
} catch (SAXException saxe) {
throw new ProcessingException("Unable to create session context.", saxe);
} finally {
manager.release( contextManager);
}
} else {
throw new ProcessingException("No handler defined. Unable to create application context.");
}
return context;
}
}