001: /*
002: * Copyright 1999-2001,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.catalina.authenticator;
018:
019: import java.io.IOException;
020: import java.lang.reflect.Method;
021: import java.security.MessageDigest;
022: import java.security.NoSuchAlgorithmException;
023: import java.security.Principal;
024: import java.text.SimpleDateFormat;
025: import java.util.Date;
026: import java.util.Locale;
027: import java.util.Random;
028:
029: import javax.servlet.ServletException;
030: import javax.servlet.http.Cookie;
031: import javax.servlet.http.HttpServletRequest;
032: import javax.servlet.http.HttpServletResponse;
033: import javax.servlet.http.HttpSession;
034:
035: import org.apache.catalina.Authenticator;
036: import org.apache.catalina.Container;
037: import org.apache.catalina.Context;
038: import org.apache.catalina.HttpRequest;
039: import org.apache.catalina.HttpResponse;
040: import org.apache.catalina.Lifecycle;
041: import org.apache.catalina.LifecycleException;
042: import org.apache.catalina.LifecycleListener;
043: import org.apache.catalina.Logger;
044: import org.apache.catalina.Manager;
045: import org.apache.catalina.Pipeline;
046: import org.apache.catalina.Realm;
047: import org.apache.catalina.Request;
048: import org.apache.catalina.Response;
049: import org.apache.catalina.Session;
050: import org.apache.catalina.Valve;
051: import org.apache.catalina.ValveContext;
052: import org.apache.catalina.deploy.LoginConfig;
053: import org.apache.catalina.deploy.SecurityConstraint;
054: import org.apache.catalina.util.DateTool;
055: import org.apache.catalina.util.LifecycleSupport;
056: import org.apache.catalina.util.StringManager;
057: import org.apache.catalina.valves.ValveBase;
058: import org.apache.commons.logging.Log;
059: import org.apache.commons.logging.LogFactory;
060:
061: /**
062: * Basic implementation of the <b>Valve</b> interface that enforces the
063: * <code><security-constraint></code> elements in the web application
064: * deployment descriptor. This functionality is implemented as a Valve
065: * so that it can be ommitted in environments that do not require these
066: * features. Individual implementations of each supported authentication
067: * method can subclass this base class as required.
068: * <p>
069: * <b>USAGE CONSTRAINT</b>: When this class is utilized, the Context to
070: * which it is attached (or a parent Container in a hierarchy) must have an
071: * associated Realm that can be used for authenticating users and enumerating
072: * the roles to which they have been assigned.
073: * <p>
074: * <b>USAGE CONSTRAINT</b>: This Valve is only useful when processing HTTP
075: * requests. Requests of any other type will simply be passed through.
076: *
077: * @author Craig R. McClanahan
078: * @version $Revision: 1.19 $ $Date: 2004/04/26 21:54:15 $
079: */
080:
081: public abstract class AuthenticatorBase extends ValveBase implements
082: Authenticator, Lifecycle {
083: private static Log log = LogFactory.getLog(AuthenticatorBase.class);
084:
085: // ----------------------------------------------------- Instance Variables
086:
087: /**
088: * The default message digest algorithm to use if we cannot use
089: * the requested one.
090: */
091: protected static final String DEFAULT_ALGORITHM = "MD5";
092:
093: /**
094: * The number of random bytes to include when generating a
095: * session identifier.
096: */
097: protected static final int SESSION_ID_BYTES = 16;
098:
099: /**
100: * The message digest algorithm to be used when generating session
101: * identifiers. This must be an algorithm supported by the
102: * <code>java.security.MessageDigest</code> class on your platform.
103: */
104: protected String algorithm = DEFAULT_ALGORITHM;
105:
106: /**
107: * Should we cache authenticated Principals if the request is part of
108: * an HTTP session?
109: */
110: protected boolean cache = true;
111:
112: /**
113: * The Context to which this Valve is attached.
114: */
115: protected Context context = null;
116:
117: /**
118: * The debugging detail level for this component.
119: */
120: protected int debug = 0;
121:
122: /**
123: * Return the MessageDigest implementation to be used when
124: * creating session identifiers.
125: */
126: protected MessageDigest digest = null;
127:
128: /**
129: * A String initialization parameter used to increase the entropy of
130: * the initialization of our random number generator.
131: */
132: protected String entropy = null;
133:
134: /**
135: * Descriptive information about this implementation.
136: */
137: protected static final String info = "org.apache.catalina.authenticator.AuthenticatorBase/1.0";
138:
139: /**
140: * Flag to determine if we disable proxy caching, or leave the issue
141: * up to the webapp developer.
142: */
143: protected boolean disableProxyCaching = true;
144:
145: /**
146: * The lifecycle event support for this component.
147: */
148: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
149:
150: /**
151: * A random number generator to use when generating session identifiers.
152: */
153: protected Random random = null;
154:
155: /**
156: * The Java class name of the random number generator class to be used
157: * when generating session identifiers.
158: */
159: protected String randomClass = "java.security.SecureRandom";
160:
161: /**
162: * The string manager for this package.
163: */
164: protected static final StringManager sm = StringManager
165: .getManager(Constants.Package);
166:
167: /**
168: * The SingleSignOn implementation in our request processing chain,
169: * if there is one.
170: */
171: protected SingleSignOn sso = null;
172:
173: /**
174: * Has this component been started?
175: */
176: protected boolean started = false;
177:
178: /**
179: * "Expires" header always set to Date(1), so generate once only
180: */
181: private static final String DATE_ONE = (new SimpleDateFormat(
182: DateTool.HTTP_RESPONSE_DATE_HEADER, Locale.US))
183: .format(new Date(1));
184:
185: // ------------------------------------------------------------- Properties
186:
187: /**
188: * Return the message digest algorithm for this Manager.
189: */
190: public String getAlgorithm() {
191:
192: return (this .algorithm);
193:
194: }
195:
196: /**
197: * Set the message digest algorithm for this Manager.
198: *
199: * @param algorithm The new message digest algorithm
200: */
201: public void setAlgorithm(String algorithm) {
202:
203: this .algorithm = algorithm;
204:
205: }
206:
207: /**
208: * Return the cache authenticated Principals flag.
209: */
210: public boolean getCache() {
211:
212: return (this .cache);
213:
214: }
215:
216: /**
217: * Set the cache authenticated Principals flag.
218: *
219: * @param cache The new cache flag
220: */
221: public void setCache(boolean cache) {
222:
223: this .cache = cache;
224:
225: }
226:
227: /**
228: * Return the Container to which this Valve is attached.
229: */
230: public Container getContainer() {
231:
232: return (this .context);
233:
234: }
235:
236: /**
237: * Set the Container to which this Valve is attached.
238: *
239: * @param container The container to which we are attached
240: */
241: public void setContainer(Container container) {
242:
243: if (!(container instanceof Context))
244: throw new IllegalArgumentException(sm
245: .getString("authenticator.notContext"));
246:
247: super .setContainer(container);
248: this .context = (Context) container;
249:
250: }
251:
252: /**
253: * Return the debugging detail level for this component.
254: */
255: public int getDebug() {
256:
257: return (this .debug);
258:
259: }
260:
261: /**
262: * Set the debugging detail level for this component.
263: *
264: * @param debug The new debugging detail level
265: */
266: public void setDebug(int debug) {
267:
268: this .debug = debug;
269:
270: }
271:
272: /**
273: * Return the entropy increaser value, or compute a semi-useful value
274: * if this String has not yet been set.
275: */
276: public String getEntropy() {
277:
278: // Calculate a semi-useful value if this has not been set
279: if (this .entropy == null)
280: setEntropy(this .toString());
281:
282: return (this .entropy);
283:
284: }
285:
286: /**
287: * Set the entropy increaser value.
288: *
289: * @param entropy The new entropy increaser value
290: */
291: public void setEntropy(String entropy) {
292:
293: this .entropy = entropy;
294:
295: }
296:
297: /**
298: * Return descriptive information about this Valve implementation.
299: */
300: public String getInfo() {
301:
302: return (info);
303:
304: }
305:
306: /**
307: * Return the random number generator class name.
308: */
309: public String getRandomClass() {
310:
311: return (this .randomClass);
312:
313: }
314:
315: /**
316: * Set the random number generator class name.
317: *
318: * @param randomClass The new random number generator class name
319: */
320: public void setRandomClass(String randomClass) {
321:
322: this .randomClass = randomClass;
323:
324: }
325:
326: /**
327: * Return the flag that states if we add headers to disable caching by
328: * proxies.
329: */
330: public boolean getDisableProxyCaching() {
331: return disableProxyCaching;
332: }
333:
334: /**
335: * Set the value of the flag that states if we add headers to disable
336: * caching by proxies.
337: * @param nocache <code>true</code> if we add headers to disable proxy
338: * caching, <code>false</code> if we leave the headers alone.
339: */
340: public void setDisableProxyCaching(boolean nocache) {
341: disableProxyCaching = nocache;
342: }
343:
344: // --------------------------------------------------------- Public Methods
345:
346: /**
347: * Enforce the security restrictions in the web application deployment
348: * descriptor of our associated Context.
349: *
350: * @param request Request to be processed
351: * @param response Response to be processed
352: * @param context The valve context used to invoke the next valve
353: * in the current processing pipeline
354: *
355: * @exception IOException if an input/output error occurs
356: * @exception ServletException if thrown by a processing element
357: */
358: public void invoke(Request request, Response response,
359: ValveContext context) throws IOException, ServletException {
360:
361: // If this is not an HTTP request, do nothing
362: if (!(request instanceof HttpRequest)
363: || !(response instanceof HttpResponse)) {
364: context.invokeNext(request, response);
365: return;
366: }
367: if (!(request.getRequest() instanceof HttpServletRequest)
368: || !(response.getResponse() instanceof HttpServletResponse)) {
369: context.invokeNext(request, response);
370: return;
371: }
372: HttpRequest hrequest = (HttpRequest) request;
373: HttpResponse hresponse = (HttpResponse) response;
374: if (log.isDebugEnabled())
375: log.debug("Security checking request "
376: + ((HttpServletRequest) request.getRequest())
377: .getMethod()
378: + " "
379: + ((HttpServletRequest) request.getRequest())
380: .getRequestURI());
381: LoginConfig config = this .context.getLoginConfig();
382:
383: // Have we got a cached authenticated Principal to record?
384: if (cache) {
385: Principal principal = ((HttpServletRequest) request
386: .getRequest()).getUserPrincipal();
387: if (principal == null) {
388: Session session = getSession(hrequest);
389: if (session != null) {
390: principal = session.getPrincipal();
391: if (principal != null) {
392: if (log.isDebugEnabled())
393: log.debug("We have cached auth type "
394: + session.getAuthType()
395: + " for principal "
396: + session.getPrincipal());
397: hrequest.setAuthType(session.getAuthType());
398: hrequest.setUserPrincipal(principal);
399: }
400: }
401: }
402: }
403:
404: // Special handling for form-based logins to deal with the case
405: // where the login form (and therefore the "j_security_check" URI
406: // to which it submits) might be outside the secured area
407: String contextPath = this .context.getPath();
408: String requestURI = hrequest.getDecodedRequestURI();
409: if (requestURI.startsWith(contextPath)
410: && requestURI.endsWith(Constants.FORM_ACTION)) {
411: if (!authenticate(hrequest, hresponse, config)) {
412: if (log.isDebugEnabled())
413: log.debug(" Failed authenticate() test ??"
414: + requestURI);
415: return;
416: }
417: }
418:
419: Realm realm = this .context.getRealm();
420: // Is this request URI subject to a security constraint?
421: SecurityConstraint[] constraints = realm
422: .findSecurityConstraints(hrequest, this .context);
423:
424: if ((constraints == null) /* &&
425: (!Constants.FORM_METHOD.equals(config.getAuthMethod())) */) {
426: if (log.isDebugEnabled())
427: log.debug(" Not subject to any constraint");
428: context.invokeNext(request, response);
429: return;
430: }
431:
432: // Make sure that constrained resources are not cached by web proxies
433: // or browsers as caching can provide a security hole
434: HttpServletRequest hsrequest = (HttpServletRequest) hrequest
435: .getRequest();
436: if (disableProxyCaching &&
437: // FIXME: Disabled for Mozilla FORM support over SSL
438: // (improper caching issue)
439: //!hsrequest.isSecure() &&
440: !"POST".equalsIgnoreCase(hsrequest.getMethod())) {
441: HttpServletResponse sresponse = (HttpServletResponse) response
442: .getResponse();
443: sresponse.setHeader("Pragma", "No-cache");
444: sresponse.setHeader("Cache-Control", "no-cache");
445: sresponse.setHeader("Expires", DATE_ONE);
446: }
447:
448: int i;
449: // Enforce any user data constraint for this security constraint
450: if (log.isDebugEnabled()) {
451: log.debug(" Calling hasUserDataPermission()");
452: }
453: if (!realm.hasUserDataPermission(hrequest, hresponse,
454: constraints)) {
455: if (log.isDebugEnabled()) {
456: log.debug(" Failed hasUserDataPermission() test");
457: }
458: /*
459: * ASSERT: Authenticator already set the appropriate
460: * HTTP status code, so we do not have to do anything special
461: */
462: return;
463: }
464:
465: for (i = 0; i < constraints.length; i++) {
466: // Authenticate based upon the specified login configuration
467: if (constraints[i].getAuthConstraint()) {
468: if (log.isDebugEnabled()) {
469: log.debug(" Calling authenticate()");
470: }
471: if (!authenticate(hrequest, hresponse, config)) {
472: if (log.isDebugEnabled()) {
473: log.debug(" Failed authenticate() test");
474: }
475: /*
476: * ASSERT: Authenticator already set the appropriate
477: * HTTP status code, so we do not have to do anything
478: * special
479: */
480: return;
481: } else {
482: break;
483: }
484: }
485: }
486: if (log.isDebugEnabled()) {
487: log.debug(" Calling accessControl()");
488: }
489: if (!realm.hasResourcePermission(hrequest, hresponse,
490: constraints, this .context)) {
491: if (log.isDebugEnabled()) {
492: log.debug(" Failed accessControl() test");
493: }
494: /*
495: * ASSERT: AccessControl method has already set the
496: * appropriate HTTP status code, so we do not have to do
497: * anything special
498: */
499: return;
500: }
501:
502: // Any and all specified constraints have been satisfied
503: if (log.isDebugEnabled()) {
504: log.debug(" Successfully passed all security constraints");
505: }
506: context.invokeNext(request, response);
507:
508: }
509:
510: // ------------------------------------------------------ Protected Methods
511:
512: /**
513: * Associate the specified single sign on identifier with the
514: * specified Session.
515: *
516: * @param ssoId Single sign on identifier
517: * @param session Session to be associated
518: */
519: protected void associate(String ssoId, Session session) {
520:
521: if (sso == null)
522: return;
523: sso.associate(ssoId, session);
524:
525: }
526:
527: /**
528: * Authenticate the user making this request, based on the specified
529: * login configuration. Return <code>true</code> if any specified
530: * constraint has been satisfied, or <code>false</code> if we have
531: * created a response challenge already.
532: *
533: * @param request Request we are processing
534: * @param response Response we are creating
535: * @param config Login configuration describing how authentication
536: * should be performed
537: *
538: * @exception IOException if an input/output error occurs
539: */
540: protected abstract boolean authenticate(HttpRequest request,
541: HttpResponse response, LoginConfig config)
542: throws IOException;
543:
544: /**
545: * Generate and return a new session identifier for the cookie that
546: * identifies an SSO principal.
547: */
548: protected synchronized String generateSessionId() {
549:
550: // Generate a byte array containing a session identifier
551: byte bytes[] = new byte[SESSION_ID_BYTES];
552: getRandom().nextBytes(bytes);
553: bytes = getDigest().digest(bytes);
554:
555: // Render the result as a String of hexadecimal digits
556: StringBuffer result = new StringBuffer();
557: for (int i = 0; i < bytes.length; i++) {
558: byte b1 = (byte) ((bytes[i] & 0xf0) >> 4);
559: byte b2 = (byte) (bytes[i] & 0x0f);
560: if (b1 < 10)
561: result.append((char) ('0' + b1));
562: else
563: result.append((char) ('A' + (b1 - 10)));
564: if (b2 < 10)
565: result.append((char) ('0' + b2));
566: else
567: result.append((char) ('A' + (b2 - 10)));
568: }
569: return (result.toString());
570:
571: }
572:
573: /**
574: * Return the MessageDigest object to be used for calculating
575: * session identifiers. If none has been created yet, initialize
576: * one the first time this method is called.
577: */
578: protected synchronized MessageDigest getDigest() {
579:
580: if (this .digest == null) {
581: try {
582: this .digest = MessageDigest.getInstance(algorithm);
583: } catch (NoSuchAlgorithmException e) {
584: try {
585: this .digest = MessageDigest
586: .getInstance(DEFAULT_ALGORITHM);
587: } catch (NoSuchAlgorithmException f) {
588: this .digest = null;
589: }
590: }
591: }
592:
593: return (this .digest);
594:
595: }
596:
597: /**
598: * Return the random number generator instance we should use for
599: * generating session identifiers. If there is no such generator
600: * currently defined, construct and seed a new one.
601: */
602: protected synchronized Random getRandom() {
603:
604: if (this .random == null) {
605: try {
606: Class clazz = Class.forName(randomClass);
607: this .random = (Random) clazz.newInstance();
608: long seed = System.currentTimeMillis();
609: char entropy[] = getEntropy().toCharArray();
610: for (int i = 0; i < entropy.length; i++) {
611: long update = ((byte) entropy[i]) << ((i % 8) * 8);
612: seed ^= update;
613: }
614: this .random.setSeed(seed);
615: } catch (Exception e) {
616: this .random = new java.util.Random();
617: }
618: }
619:
620: return (this .random);
621:
622: }
623:
624: /**
625: * Return the internal Session that is associated with this HttpRequest,
626: * or <code>null</code> if there is no such Session.
627: *
628: * @param request The HttpRequest we are processing
629: */
630: protected Session getSession(HttpRequest request) {
631:
632: return (getSession(request, false));
633:
634: }
635:
636: /**
637: * Return the internal Session that is associated with this HttpRequest,
638: * possibly creating a new one if necessary, or <code>null</code> if
639: * there is no such session and we did not create one.
640: *
641: * @param request The HttpRequest we are processing
642: * @param create Should we create a session if needed?
643: */
644: protected Session getSession(HttpRequest request, boolean create) {
645:
646: HttpServletRequest hreq = (HttpServletRequest) request
647: .getRequest();
648: HttpSession hses = hreq.getSession(create);
649: if (hses == null)
650: return (null);
651: Manager manager = context.getManager();
652: if (manager == null)
653: return (null);
654: else {
655: try {
656: return (manager.findSession(hses.getId()));
657: } catch (IOException e) {
658: return (null);
659: }
660: }
661:
662: }
663:
664: /**
665: * Log a message on the Logger associated with our Container (if any).
666: *
667: * @param message Message to be logged
668: */
669: protected void log(String message) {
670:
671: Logger logger = context.getLogger();
672: if (logger != null)
673: logger.log("Authenticator[" + context.getPath() + "]: "
674: + message);
675: else
676: System.out.println("Authenticator[" + context.getPath()
677: + "]: " + message);
678:
679: }
680:
681: /**
682: * Log a message on the Logger associated with our Container (if any).
683: *
684: * @param message Message to be logged
685: * @param throwable Associated exception
686: */
687: protected void log(String message, Throwable throwable) {
688:
689: Logger logger = context.getLogger();
690: if (logger != null)
691: logger.log("Authenticator[" + context.getPath() + "]: "
692: + message, throwable);
693: else {
694: System.out.println("Authenticator[" + context.getPath()
695: + "]: " + message);
696: throwable.printStackTrace(System.out);
697: }
698:
699: }
700:
701: /**
702: * Attempts reauthentication to the <code>Realm</code> using
703: * the credentials included in argument <code>entry</code>.
704: *
705: * @param ssoId identifier of SingleSignOn session with which the
706: * caller is associated
707: * @param request the request that needs to be authenticated
708: */
709: protected boolean reauthenticateFromSSO(String ssoId,
710: HttpRequest request) {
711:
712: if (sso == null || ssoId == null)
713: return false;
714:
715: boolean reauthenticated = false;
716:
717: Container parent = getContainer();
718: if (parent != null) {
719: Realm realm = parent.getRealm();
720: if (realm != null) {
721: reauthenticated = sso.reauthenticate(ssoId, realm,
722: request);
723: }
724: }
725:
726: if (reauthenticated) {
727: associate(ssoId, getSession(request, true));
728:
729: if (log.isDebugEnabled()) {
730: HttpServletRequest hreq = (HttpServletRequest) request
731: .getRequest();
732: log.debug(" Reauthenticated cached principal '"
733: + hreq.getUserPrincipal().getName()
734: + "' with auth type '" + hreq.getAuthType()
735: + "'");
736: }
737: }
738:
739: return reauthenticated;
740: }
741:
742: /**
743: * Register an authenticated Principal and authentication type in our
744: * request, in the current session (if there is one), and with our
745: * SingleSignOn valve, if there is one. Set the appropriate cookie
746: * to be returned.
747: *
748: * @param request The servlet request we are processing
749: * @param response The servlet response we are generating
750: * @param principal The authenticated Principal to be registered
751: * @param authType The authentication type to be registered
752: * @param username Username used to authenticate (if any)
753: * @param password Password used to authenticate (if any)
754: */
755: protected void register(HttpRequest request, HttpResponse response,
756: Principal principal, String authType, String username,
757: String password) {
758:
759: if (log.isDebugEnabled())
760: log.debug("Authenticated '" + principal.getName()
761: + "' with type '" + authType + "'");
762:
763: // Cache the authentication information in our request
764: request.setAuthType(authType);
765: request.setUserPrincipal(principal);
766:
767: Session session = getSession(request, false);
768: // Cache the authentication information in our session, if any
769: if (cache) {
770: if (session != null) {
771: session.setAuthType(authType);
772: session.setPrincipal(principal);
773: if (username != null)
774: session.setNote(Constants.SESS_USERNAME_NOTE,
775: username);
776: else
777: session.removeNote(Constants.SESS_USERNAME_NOTE);
778: if (password != null)
779: session.setNote(Constants.SESS_PASSWORD_NOTE,
780: password);
781: else
782: session.removeNote(Constants.SESS_PASSWORD_NOTE);
783: }
784: }
785:
786: // Construct a cookie to be returned to the client
787: if (sso == null)
788: return;
789:
790: // Only create a new SSO entry if the SSO did not already set a note
791: // for an existing entry (as it would do with subsequent requests
792: // for DIGEST and SSL authenticated contexts)
793: String ssoId = (String) request
794: .getNote(Constants.REQ_SSOID_NOTE);
795: if (ssoId == null) {
796: // Construct a cookie to be returned to the client
797: HttpServletResponse hres = (HttpServletResponse) response
798: .getResponse();
799: ssoId = generateSessionId();
800: Cookie cookie = new Cookie(Constants.SINGLE_SIGN_ON_COOKIE,
801: ssoId);
802: cookie.setMaxAge(-1);
803: cookie.setPath("/");
804: hres.addCookie(cookie);
805:
806: // Register this principal with our SSO valve
807: sso
808: .register(ssoId, principal, authType, username,
809: password);
810: request.setNote(Constants.REQ_SSOID_NOTE, ssoId);
811:
812: } else {
813: // Update the SSO session with the latest authentication data
814: sso.update(ssoId, principal, authType, username, password);
815: }
816:
817: // Fix for Bug 10040
818: // Always associate a session with a new SSO reqistration.
819: // SSO entries are only removed from the SSO registry map when
820: // associated sessions are destroyed; if a new SSO entry is created
821: // above for this request and the user never revisits the context, the
822: // SSO entry will never be cleared if we don't associate the session
823: if (session == null)
824: session = getSession(request, true);
825: sso.associate(ssoId, session);
826:
827: }
828:
829: // ------------------------------------------------------ Lifecycle Methods
830:
831: /**
832: * Add a lifecycle event listener to this component.
833: *
834: * @param listener The listener to add
835: */
836: public void addLifecycleListener(LifecycleListener listener) {
837:
838: lifecycle.addLifecycleListener(listener);
839:
840: }
841:
842: /**
843: * Get the lifecycle listeners associated with this lifecycle. If this
844: * Lifecycle has no listeners registered, a zero-length array is returned.
845: */
846: public LifecycleListener[] findLifecycleListeners() {
847:
848: return lifecycle.findLifecycleListeners();
849:
850: }
851:
852: /**
853: * Remove a lifecycle event listener from this component.
854: *
855: * @param listener The listener to remove
856: */
857: public void removeLifecycleListener(LifecycleListener listener) {
858:
859: lifecycle.removeLifecycleListener(listener);
860:
861: }
862:
863: /**
864: * Prepare for the beginning of active use of the public methods of this
865: * component. This method should be called after <code>configure()</code>,
866: * and before any of the public methods of the component are utilized.
867: *
868: * @exception LifecycleException if this component detects a fatal error
869: * that prevents this component from being used
870: */
871: public void start() throws LifecycleException {
872:
873: // Validate and update our current component state
874: if (started)
875: throw new LifecycleException(sm
876: .getString("authenticator.alreadyStarted"));
877: lifecycle.fireLifecycleEvent(START_EVENT, null);
878: if ("org.apache.catalina.core.StandardContext".equals(context
879: .getClass().getName())) {
880: try {
881: // XXX What is this ???
882: Class paramTypes[] = new Class[0];
883: Object paramValues[] = new Object[0];
884: Method method = context.getClass().getMethod(
885: "getDebug", paramTypes);
886: Integer result = (Integer) method.invoke(context,
887: paramValues);
888: setDebug(result.intValue());
889: } catch (Exception e) {
890: log.error("Exception getting debug value", e);
891: }
892: }
893: started = true;
894:
895: // Look up the SingleSignOn implementation in our request processing
896: // path, if there is one
897: Container parent = context.getParent();
898: while ((sso == null) && (parent != null)) {
899: if (!(parent instanceof Pipeline)) {
900: parent = parent.getParent();
901: continue;
902: }
903: Valve valves[] = ((Pipeline) parent).getValves();
904: for (int i = 0; i < valves.length; i++) {
905: if (valves[i] instanceof SingleSignOn) {
906: sso = (SingleSignOn) valves[i];
907: break;
908: }
909: }
910: if (sso == null)
911: parent = parent.getParent();
912: }
913: if (log.isDebugEnabled()) {
914: if (sso != null)
915: log.debug("Found SingleSignOn Valve at " + sso);
916: else
917: log.debug("No SingleSignOn Valve is present");
918: }
919:
920: }
921:
922: /**
923: * Gracefully terminate the active use of the public methods of this
924: * component. This method should be the last one called on a given
925: * instance of this component.
926: *
927: * @exception LifecycleException if this component detects a fatal error
928: * that needs to be reported
929: */
930: public void stop() throws LifecycleException {
931:
932: // Validate and update our current component state
933: if (!started)
934: throw new LifecycleException(sm
935: .getString("authenticator.notStarted"));
936: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
937: started = false;
938:
939: sso = null;
940:
941: }
942:
943: }
|