001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.webapps.authentication.components;
018:
019: import java.io.IOException;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024:
025: import org.apache.avalon.framework.activity.Disposable;
026: import org.apache.avalon.framework.component.Component;
027: import org.apache.avalon.framework.configuration.ConfigurationException;
028: import org.apache.avalon.framework.container.ContainerUtil;
029: import org.apache.avalon.framework.context.Context;
030: import org.apache.avalon.framework.context.ContextException;
031: import org.apache.avalon.framework.context.Contextualizable;
032: import org.apache.avalon.framework.logger.AbstractLogEnabled;
033: import org.apache.avalon.framework.service.ServiceException;
034: import org.apache.avalon.framework.service.ServiceManager;
035: import org.apache.avalon.framework.service.Serviceable;
036: import org.apache.avalon.framework.thread.ThreadSafe;
037: import org.apache.cocoon.ProcessingException;
038: import org.apache.cocoon.components.ContextHelper;
039: import org.apache.cocoon.components.SitemapConfigurable;
040: import org.apache.cocoon.components.SitemapConfigurationHolder;
041: import org.apache.cocoon.environment.Redirector;
042: import org.apache.cocoon.environment.Request;
043: import org.apache.cocoon.environment.Session;
044: import org.apache.cocoon.util.ClassUtils;
045: import org.apache.cocoon.util.Deprecation;
046: import org.apache.cocoon.webapps.authentication.AuthenticationConstants;
047: import org.apache.cocoon.webapps.authentication.AuthenticationManager;
048: import org.apache.cocoon.webapps.authentication.configuration.ApplicationConfiguration;
049: import org.apache.cocoon.webapps.authentication.configuration.HandlerConfiguration;
050: import org.apache.cocoon.webapps.authentication.context.AuthenticationContext;
051: import org.apache.cocoon.webapps.authentication.user.RequestState;
052: import org.apache.cocoon.webapps.authentication.user.UserHandler;
053: import org.apache.cocoon.webapps.authentication.user.UserState;
054: import org.apache.cocoon.webapps.session.ContextManager;
055: import org.apache.cocoon.webapps.session.SessionConstants;
056: import org.apache.cocoon.webapps.session.SessionManager;
057: import org.apache.cocoon.webapps.session.context.SessionContext;
058: import org.apache.excalibur.source.SourceParameters;
059: import org.apache.excalibur.source.SourceResolver;
060: import org.apache.excalibur.source.SourceUtil;
061: import org.apache.excalibur.xml.xpath.XPathProcessor;
062: import org.w3c.dom.DocumentFragment;
063: import org.w3c.dom.Node;
064: import org.xml.sax.SAXException;
065:
066: /**
067: * This is the basis authentication component.
068: *
069: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
070: * @deprecated This block is deprecated and will be removed in future versions.
071: * @version CVS $Id: DefaultAuthenticationManager.java 433543 2006-08-22 06:22:54Z crossley $
072: */
073: public class DefaultAuthenticationManager extends AbstractLogEnabled
074: implements AuthenticationManager, SitemapConfigurable,
075: Serviceable, Disposable, ThreadSafe, Contextualizable,
076: Component {
077:
078: /** The name of the session attribute storing the user status */
079: public final static String SESSION_ATTRIBUTE_USER_STATUS = DefaultAuthenticationManager.class
080: .getName()
081: + "/UserStatus";
082:
083: /** The manager for the authentication handlers */
084: protected SitemapConfigurationHolder holder;
085:
086: /** The Service Manager */
087: protected ServiceManager manager;
088:
089: /** The Source Resolver */
090: protected SourceResolver resolver;
091:
092: /** The context */
093: protected Context context;
094:
095: /** Instantiated authenticators */
096: protected Map authenticators = new HashMap();
097:
098: /** The xpath processor */
099: protected XPathProcessor xpathProcessor;
100:
101: /** This is the key used to store the current request state in the request object */
102: private static final String REQUEST_STATE_KEY = RequestState.class
103: .getName();
104:
105: /**
106: * Set the sitemap configuration containing the handlers
107: */
108: public void configure(SitemapConfigurationHolder holder)
109: throws ConfigurationException {
110: Deprecation.logger
111: .warn("The authentication-fw block is deprecated. Please use the auth block instead.");
112: this .holder = holder;
113: }
114:
115: /**
116: * Get the handler configuration for the current sitemap
117: */
118: private Map getHandlerConfigurations() throws ProcessingException {
119: Map configs = (Map) this .holder.getPreparedConfiguration();
120: if (null == configs) {
121: try {
122: configs = DefaultHandlerManager
123: .prepareHandlerConfiguration(ContextHelper
124: .getObjectModel(this .context),
125: this .holder);
126: } catch (ConfigurationException ce) {
127: throw new ProcessingException("Configuration error.",
128: ce);
129: }
130: }
131: return configs;
132: }
133:
134: /**
135: * Get the handler configuration
136: * @param name The handler name
137: * @return The configuration or null.
138: */
139: private HandlerConfiguration getHandlerConfiguration(String name)
140: throws ProcessingException {
141: final Map configs = this .getHandlerConfigurations();
142: HandlerConfiguration c = null;
143: if (configs != null) {
144: c = (HandlerConfiguration) configs.get(name);
145: }
146: return c;
147: }
148:
149: private Request getRequest() {
150: return ContextHelper.getRequest(this .context);
151: }
152:
153: private Session getSession(boolean create) {
154: return this .getRequest().getSession(create);
155: }
156:
157: private UserState getUserState() {
158: final Session session = this .getSession(false);
159: UserState status = null;
160: if (session != null) {
161: status = (UserState) session
162: .getAttribute(SESSION_ATTRIBUTE_USER_STATUS);
163: }
164: return status;
165: }
166:
167: private UserState createUserState() {
168: UserState status = this .getUserState();
169: if (status == null) {
170: final Session session = this .getSession(true);
171: status = new UserState();
172: session.setAttribute(SESSION_ATTRIBUTE_USER_STATUS, status);
173: }
174: return status;
175: }
176:
177: private UserHandler getUserHandler(String name) {
178: final UserState status = this .getUserState();
179: if (status != null) {
180: return status.getHandler(name);
181: }
182: return null;
183: }
184:
185: private void updateUserState() {
186: final Session session = this .getSession(true);
187: Object status = session
188: .getAttribute(SESSION_ATTRIBUTE_USER_STATUS);
189: session.setAttribute(SESSION_ATTRIBUTE_USER_STATUS, status);
190: }
191:
192: /* (non-Javadoc)
193: * @see org.apache.cocoon.webapps.authentication.components.Manager#authenticate(java.lang.String, java.lang.String, org.apache.excalibur.source.SourceParameters)
194: */
195: public UserHandler login(String handlerName,
196: String applicationName, SourceParameters parameters)
197: throws ProcessingException {
198: HandlerConfiguration config = this
199: .getHandlerConfiguration(handlerName);
200: if (config == null) {
201: throw new ProcessingException(
202: "Unknown handler to authenticate: " + handlerName);
203: }
204: // are we already logged in?
205: UserHandler handler = this .getUserHandler(handlerName);
206: if (handler != null) {
207: throw new ProcessingException(
208: "User is already authenticated using handler: "
209: + handlerName);
210: }
211:
212: Authenticator authenticator = this .lookupAuthenticator(config);
213: try {
214: Authenticator.AuthenticationResult result = authenticator
215: .authenticate(config, parameters);
216: if (result != null) {
217: if (result.valid) {
218: AuthenticationContext authContext = new AuthenticationContext(
219: this .context, this .xpathProcessor,
220: this .resolver);
221: handler = new UserHandler(config, authContext);
222: // store the authentication data in the context
223: authContext.init(result.result);
224: } else {
225: // now set the failure information in the temporary context
226: ContextManager contextManager = null;
227: try {
228: contextManager = (ContextManager) this .manager
229: .lookup(ContextManager.ROLE);
230: SessionContext temp = contextManager
231: .getContext(SessionConstants.TEMPORARY_CONTEXT);
232:
233: final DocumentFragment fragment = result.result
234: .createDocumentFragment();
235: final Node root = result.result
236: .getDocumentElement();
237: root.normalize();
238: Node child;
239: boolean appendedNode = false;
240: while (root.hasChildNodes()) {
241: child = root.getFirstChild();
242: root.removeChild(child);
243: // Leave out empty text nodes before any other node
244: if (appendedNode
245: || child.getNodeType() != Node.TEXT_NODE
246: || child.getNodeValue().trim()
247: .length() > 0) {
248: fragment.appendChild(child);
249: appendedNode = true;
250: }
251: }
252: temp.appendXML("/", fragment);
253: } catch (ServiceException se) {
254: throw new ProcessingException(
255: "Unable to lookup session manager.", se);
256: } finally {
257: this .manager.release(contextManager);
258: }
259: }
260: }
261: } finally {
262: this .releaseAuthenticator(authenticator, config);
263: }
264:
265: if (handler != null) {
266: // create UserStatus
267: final UserState status = this .createUserState();
268:
269: status.addHandler(handler);
270: this .updateUserState();
271:
272: // update RequestState
273: RequestState state = new RequestState(handler,
274: applicationName);
275: this .setState(state);
276: state.initialize(this .resolver);
277:
278: // And now load applications
279: Iterator applications = handler.getHandlerConfiguration()
280: .getApplications().values().iterator();
281:
282: while (applications.hasNext()) {
283: ApplicationConfiguration appHandler = (ApplicationConfiguration) applications
284: .next();
285: if (!appHandler.getLoadOnDemand()) {
286: handler.getContext().loadApplicationXML(appHandler,
287: this .resolver);
288: }
289: }
290: }
291:
292: return handler;
293: }
294:
295: /**
296: * Release the used authenticator
297: */
298: protected void releaseAuthenticator(Authenticator authenticator,
299: HandlerConfiguration config) {
300: // all authenticators are released on dispose
301: }
302:
303: /**
304: * The authenticator used to authenticate a user
305: */
306: protected Authenticator lookupAuthenticator(
307: HandlerConfiguration config) throws ProcessingException {
308: final String name = config.getAuthenticatorClassName();
309: Authenticator authenticator = (Authenticator) this .authenticators
310: .get(name);
311: if (authenticator == null) {
312: synchronized (this ) {
313: authenticator = (Authenticator) this .authenticators
314: .get(name);
315: if (authenticator == null) {
316: try {
317: authenticator = (Authenticator) ClassUtils
318: .newInstance(name);
319: ContainerUtil.enableLogging(authenticator, this
320: .getLogger());
321: ContainerUtil.contextualize(authenticator,
322: this .context);
323: ContainerUtil.service(authenticator,
324: this .manager);
325: ContainerUtil.initialize(authenticator);
326: this .authenticators.put(name, authenticator);
327:
328: } catch (Exception e) {
329: throw new ProcessingException(
330: "Unable to initialize authenticator from class "
331: + name, e);
332: }
333: }
334: }
335: }
336: return authenticator;
337: }
338:
339: /* (non-Javadoc)
340: * @see org.apache.cocoon.webapps.authentication.components.Manager#checkAuthentication(org.apache.cocoon.environment.Redirector, java.lang.String, java.lang.String)
341: */
342: public boolean checkAuthentication(Redirector redirector,
343: String handlerName, String applicationName)
344: throws IOException, ProcessingException {
345: HandlerConfiguration config = this
346: .getHandlerConfiguration(handlerName);
347: if (config == null) {
348: throw new ProcessingException("Unknown handler to check: "
349: + handlerName);
350: }
351: // are we already logged in?
352: UserHandler handler = this .getUserHandler(handlerName);
353: final boolean authenticated = (handler != null);
354: if (!authenticated) {
355: if (redirector != null) {
356: // create parameters
357: SourceParameters parameters = config
358: .getRedirectParameters();
359: if (parameters == null)
360: parameters = new SourceParameters();
361: final Request request = this .getRequest();
362: String resource = request.getRequestURI();
363: if (request.getQueryString() != null) {
364: resource += '?' + request.getQueryString();
365: }
366:
367: parameters
368: .setSingleParameterValue("resource", resource);
369: final String redirectURI = config.getRedirectURI();
370: redirector.globalRedirect(false, SourceUtil
371: .appendParameters(redirectURI, parameters));
372: }
373: } else {
374: // update state
375: RequestState state = new RequestState(handler,
376: applicationName);
377: this .setState(state);
378: state.initialize(this .resolver);
379: }
380:
381: return authenticated;
382: }
383:
384: public String getForwardingURI(String handlerName)
385: throws ProcessingException {
386: HandlerConfiguration config = this
387: .getHandlerConfiguration(handlerName);
388: SourceParameters parameters = config.getRedirectParameters();
389: if (parameters == null)
390: parameters = new SourceParameters();
391: final Request request = this .getRequest();
392: String resource = request.getRequestURI();
393: if (request.getQueryString() != null) {
394: resource += '?' + request.getQueryString();
395: }
396:
397: parameters.setSingleParameterValue("resource", resource);
398: final String redirectURI = config.getRedirectURI();
399: return SourceUtil.appendParameters(redirectURI, parameters);
400: }
401:
402: /* (non-Javadoc)
403: * @see org.apache.cocoon.webapps.authentication.components.Manager#isAuthenticated(java.lang.String)
404: */
405: public UserHandler isAuthenticated(String handlerName)
406: throws ProcessingException {
407: return this .getUserHandler(handlerName);
408: }
409:
410: /* (non-Javadoc)
411: * @see org.apache.cocoon.webapps.authentication.components.Manager#logout(java.lang.String, java.lang.String)
412: */
413: public void logout(String handlerName, int mode)
414: throws ProcessingException {
415: HandlerConfiguration config = this
416: .getHandlerConfiguration(handlerName);
417: if (config == null) {
418: throw new ProcessingException("Unknown handler to logout: "
419: + handlerName);
420: }
421: // are we logged in?
422: UserHandler handler = this .getUserHandler(handlerName);
423: // we don't throw an exception if we are already logged out!
424: if (handler != null) {
425:
426: // Do we save something on logout?
427: /*
428:
429: if ( config.saveOnLogout()
430: && config.getSaveResource() != null) {
431: final AuthenticationContext authContext = handler.getContext();
432: try {
433: // This might not work, because of the missing state
434: authContext.saveXML("/authentication",
435: null,
436: ContextHelper.getObjectModel(this.context),
437: this.resolver, this.manager);
438: } catch (Exception ignore) {
439: // we don't want to stop the logout process
440: // because of errors during save
441: this.getLogger().error("Exception while saving authentication information.", ignore);
442: }
443: }
444: // save applications (if configured)
445: Iterator iter = config.getApplications().values().iterator();
446: while ( iter.hasNext() ) {
447: ApplicationConfiguration appConfig = (ApplicationConfiguration) iter.next();
448: if ( appConfig.saveOnLogout()
449: && appConfig.getSaveResource() != null ) {
450: // ???
451: }
452: }
453: */
454: // notify the authenticator
455: try {
456: this .lookupAuthenticator(config).logout(handler);
457: } catch (Exception ignore) {
458: // we really ignore any exception!
459: }
460:
461: List applicationContexts = handler.getApplicationContexts();
462: if (applicationContexts != null) {
463: ContextManager contextManager = null;
464:
465: try {
466: contextManager = (ContextManager) this .manager
467: .lookup(ContextManager.ROLE);
468:
469: Iterator i = applicationContexts.iterator();
470: while (i.hasNext()) {
471: final String current = (String) i.next();
472: contextManager.deleteContext(current);
473: }
474: } catch (ServiceException ce) {
475: throw new ProcessingException(
476: "Unable to create session context.", ce);
477: } finally {
478: this .manager.release(contextManager);
479: }
480: }
481:
482: UserState status = this .getUserState();
483: status.removeHandler(handlerName);
484: this .updateUserState();
485:
486: // handling of session termination
487: SessionManager sessionManager = null;
488: try {
489: sessionManager = (SessionManager) this .manager
490: .lookup(SessionManager.ROLE);
491:
492: if (mode == AuthenticationConstants.LOGOUT_MODE_IMMEDIATELY) {
493: sessionManager.terminateSession(true);
494: } else if (mode == AuthenticationConstants.LOGOUT_MODE_IF_UNUSED) {
495: if (!status.hasHandler()) {
496: sessionManager.terminateSession(false);
497: }
498:
499: } else if (mode == AuthenticationConstants.LOGOUT_MODE_IF_NOT_AUTHENTICATED) {
500: if (!status.hasHandler()) {
501: sessionManager.terminateSession(true);
502: }
503: } else {
504: throw new ProcessingException(
505: "Unknown logout mode: " + mode);
506: }
507:
508: } catch (ServiceException se) {
509: throw new ProcessingException(
510: "Unable to lookup session manager.", se);
511: } finally {
512: this .manager.release(sessionManager);
513: }
514: }
515: }
516:
517: /**
518: * Serviceable
519: */
520: public void service(ServiceManager manager) throws ServiceException {
521: this .manager = manager;
522: this .resolver = (SourceResolver) this .manager
523: .lookup(SourceResolver.ROLE);
524: this .xpathProcessor = (XPathProcessor) this .manager
525: .lookup(XPathProcessor.ROLE);
526: }
527:
528: /* (non-Javadoc)
529: * @see org.apache.avalon.framework.activity.Disposable#dispose()
530: */
531: public void dispose() {
532: Iterator iter = this .authenticators.values().iterator();
533: while (iter.hasNext()) {
534: final Authenticator authenticator = (Authenticator) iter
535: .next();
536: ContainerUtil.dispose(authenticator);
537: }
538: if (this .manager != null) {
539: this .manager.release(this .resolver);
540: this .manager.release(this .xpathProcessor);
541: this .resolver = null;
542: this .xpathProcessor = null;
543: this .manager = null;
544: }
545: }
546:
547: /**
548: * Get the current state of authentication
549: */
550: public RequestState getState() {
551: return getRequestState(this .context);
552: }
553:
554: public static RequestState getRequestState(Context context) {
555: final Request req = ContextHelper.getRequest(context);
556: return (RequestState) req.getAttribute(REQUEST_STATE_KEY);
557: }
558:
559: /* (non-Javadoc)
560: * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
561: */
562: public void contextualize(Context context) throws ContextException {
563: this .context = context;
564: }
565:
566: protected void setState(RequestState status) {
567: final Request req = ContextHelper.getRequest(this .context);
568: if (status != null) {
569: req.setAttribute(REQUEST_STATE_KEY, status);
570: } else {
571: req.removeAttribute(REQUEST_STATE_KEY);
572: }
573: }
574:
575: /**
576: * Create Application Context.
577: * This context is destroyed when the user logs out of the handler
578: */
579: public SessionContext createApplicationContext(String name,
580: String loadURI, String saveURI) throws ProcessingException {
581: RequestState state = this .getState();
582: UserHandler handler = state.getHandler();
583:
584: SessionContext context = null;
585:
586: if (handler != null) {
587: ContextManager contextManager = null;
588: try {
589: contextManager = (ContextManager) this .manager
590: .lookup(ContextManager.ROLE);
591: // create new context
592: context = contextManager.createContext(name, loadURI,
593: saveURI);
594: handler.addApplicationContext(name);
595:
596: } catch (ServiceException ce) {
597: throw new ProcessingException(
598: "Unable to create session context.", ce);
599: } catch (IOException ioe) {
600: throw new ProcessingException(
601: "Unable to create session context.", ioe);
602: } catch (SAXException saxe) {
603: throw new ProcessingException(
604: "Unable to create session context.", saxe);
605: } finally {
606: manager.release(contextManager);
607: }
608: } else {
609: throw new ProcessingException(
610: "No handler defined. Unable to create application context.");
611: }
612:
613: return context;
614: }
615: }
|