ASF Bugzilla – Attachment 20859 Details for
Bug 12428
request.getUserPrincipal(): Misinterpretation of specification?
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
org.apache.catalina.authenticator.AuthenticatorBase
AuthenticatorBase.java.diff (text/plain), 58.93 KB, created by
Werner Donné
on 2007-09-20 08:24:35 UTC
(
hide
)
Description:
org.apache.catalina.authenticator.AuthenticatorBase
Filename:
MIME Type:
Creator:
Werner Donné
Created:
2007-09-20 08:24:35 UTC
Size:
58.93 KB
patch
obsolete
>--- AuthenticatorBase.java 2007-09-20 17:14:22.551592203 +0200 >+++ /var/local/apache-tomcat-5.5.23-src/container/catalina/src/share/org/apache/catalina/authenticator/AuthenticatorBase.java 2007-09-14 16:48:01.591807635 +0200 >@@ -1,887 +1,905 @@ >-/* >- * 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.catalina.authenticator; >- >- >-import java.io.IOException; >-import java.security.MessageDigest; >-import java.security.NoSuchAlgorithmException; >-import java.security.Principal; >-import java.text.SimpleDateFormat; >-import java.util.Date; >-import java.util.Locale; >-import java.util.Random; >- >-import javax.servlet.ServletException; >-import javax.servlet.http.Cookie; >- >-import org.apache.catalina.Authenticator; >-import org.apache.catalina.Container; >-import org.apache.catalina.Context; >-import org.apache.catalina.Lifecycle; >-import org.apache.catalina.LifecycleException; >-import org.apache.catalina.LifecycleListener; >-import org.apache.catalina.Pipeline; >-import org.apache.catalina.Realm; >-import org.apache.catalina.Session; >-import org.apache.catalina.Valve; >-import org.apache.catalina.connector.Request; >-import org.apache.catalina.connector.Response; >-import org.apache.catalina.deploy.LoginConfig; >-import org.apache.catalina.deploy.SecurityConstraint; >-import org.apache.catalina.util.DateTool; >-import org.apache.catalina.util.LifecycleSupport; >-import org.apache.catalina.util.StringManager; >-import org.apache.catalina.valves.ValveBase; >-import org.apache.commons.logging.Log; >-import org.apache.commons.logging.LogFactory; >- >- >-/** >- * Basic implementation of the <b>Valve</b> interface that enforces the >- * <code><security-constraint></code> elements in the web application >- * deployment descriptor. This functionality is implemented as a Valve >- * so that it can be ommitted in environments that do not require these >- * features. Individual implementations of each supported authentication >- * method can subclass this base class as required. >- * <p> >- * <b>USAGE CONSTRAINT</b>: When this class is utilized, the Context to >- * which it is attached (or a parent Container in a hierarchy) must have an >- * associated Realm that can be used for authenticating users and enumerating >- * the roles to which they have been assigned. >- * <p> >- * <b>USAGE CONSTRAINT</b>: This Valve is only useful when processing HTTP >- * requests. Requests of any other type will simply be passed through. >- * >- * @author Craig R. McClanahan >- * @version $Revision: 496025 $ $Date: 2007-01-13 20:18:06 -0700 (Sat, 13 Jan 2007) $ >- */ >- >- >-public abstract class AuthenticatorBase >- extends ValveBase >- implements Authenticator, Lifecycle { >- private static Log log = LogFactory.getLog(AuthenticatorBase.class); >- >- >- // ----------------------------------------------------- Instance Variables >- >- >- /** >- * The default message digest algorithm to use if we cannot use >- * the requested one. >- */ >- protected static final String DEFAULT_ALGORITHM = "MD5"; >- >- >- /** >- * The number of random bytes to include when generating a >- * session identifier. >- */ >- protected static final int SESSION_ID_BYTES = 16; >- >- >- /** >- * The message digest algorithm to be used when generating session >- * identifiers. This must be an algorithm supported by the >- * <code>java.security.MessageDigest</code> class on your platform. >- */ >- protected String algorithm = DEFAULT_ALGORITHM; >- >- >- /** >- * Should we cache authenticated Principals if the request is part of >- * an HTTP session? >- */ >- protected boolean cache = true; >- >- >- /** >- * The Context to which this Valve is attached. >- */ >- protected Context context = null; >- >- >- /** >- * Return the MessageDigest implementation to be used when >- * creating session identifiers. >- */ >- protected MessageDigest digest = null; >- >- >- /** >- * A String initialization parameter used to increase the entropy of >- * the initialization of our random number generator. >- */ >- protected String entropy = null; >- >- >- /** >- * Descriptive information about this implementation. >- */ >- protected static final String info = >- "org.apache.catalina.authenticator.AuthenticatorBase/1.0"; >- >- /** >- * Flag to determine if we disable proxy caching, or leave the issue >- * up to the webapp developer. >- */ >- protected boolean disableProxyCaching = true; >- >- /** >- * Flag to determine if we disable proxy caching with headers incompatible >- * with IE >- */ >- protected boolean securePagesWithPragma = true; >- >- /** >- * The lifecycle event support for this component. >- */ >- protected LifecycleSupport lifecycle = new LifecycleSupport(this); >- >- >- /** >- * A random number generator to use when generating session identifiers. >- */ >- protected Random random = null; >- >- >- /** >- * The Java class name of the random number generator class to be used >- * when generating session identifiers. >- */ >- protected String randomClass = "java.security.SecureRandom"; >- >- >- /** >- * The string manager for this package. >- */ >- protected static final StringManager sm = >- StringManager.getManager(Constants.Package); >- >- >- /** >- * The SingleSignOn implementation in our request processing chain, >- * if there is one. >- */ >- protected SingleSignOn sso = null; >- >- >- /** >- * Has this component been started? >- */ >- protected boolean started = false; >- >- >- /** >- * "Expires" header always set to Date(1), so generate once only >- */ >- private static final String DATE_ONE = >- (new SimpleDateFormat(DateTool.HTTP_RESPONSE_DATE_HEADER, >- Locale.US)).format(new Date(1)); >- >- >- // ------------------------------------------------------------- Properties >- >- >- /** >- * Return the message digest algorithm for this Manager. >- */ >- public String getAlgorithm() { >- >- return (this.algorithm); >- >- } >- >- >- /** >- * Set the message digest algorithm for this Manager. >- * >- * @param algorithm The new message digest algorithm >- */ >- public void setAlgorithm(String algorithm) { >- >- this.algorithm = algorithm; >- >- } >- >- >- /** >- * Return the cache authenticated Principals flag. >- */ >- public boolean getCache() { >- >- return (this.cache); >- >- } >- >- >- /** >- * Set the cache authenticated Principals flag. >- * >- * @param cache The new cache flag >- */ >- public void setCache(boolean cache) { >- >- this.cache = cache; >- >- } >- >- >- /** >- * Return the Container to which this Valve is attached. >- */ >- public Container getContainer() { >- >- return (this.context); >- >- } >- >- >- /** >- * Set the Container to which this Valve is attached. >- * >- * @param container The container to which we are attached >- */ >- public void setContainer(Container container) { >- >- if (!(container instanceof Context)) >- throw new IllegalArgumentException >- (sm.getString("authenticator.notContext")); >- >- super.setContainer(container); >- this.context = (Context) container; >- >- } >- >- >- /** >- * Return the entropy increaser value, or compute a semi-useful value >- * if this String has not yet been set. >- */ >- public String getEntropy() { >- >- // Calculate a semi-useful value if this has not been set >- if (this.entropy == null) >- setEntropy(this.toString()); >- >- return (this.entropy); >- >- } >- >- >- /** >- * Set the entropy increaser value. >- * >- * @param entropy The new entropy increaser value >- */ >- public void setEntropy(String entropy) { >- >- this.entropy = entropy; >- >- } >- >- >- /** >- * Return descriptive information about this Valve implementation. >- */ >- public String getInfo() { >- >- return (info); >- >- } >- >- >- /** >- * Return the random number generator class name. >- */ >- public String getRandomClass() { >- >- return (this.randomClass); >- >- } >- >- >- /** >- * Set the random number generator class name. >- * >- * @param randomClass The new random number generator class name >- */ >- public void setRandomClass(String randomClass) { >- >- this.randomClass = randomClass; >- >- } >- >- /** >- * Return the flag that states if we add headers to disable caching by >- * proxies. >- */ >- public boolean getDisableProxyCaching() { >- return disableProxyCaching; >- } >- >- /** >- * Set the value of the flag that states if we add headers to disable >- * caching by proxies. >- * @param nocache <code>true</code> if we add headers to disable proxy >- * caching, <code>false</code> if we leave the headers alone. >- */ >- public void setDisableProxyCaching(boolean nocache) { >- disableProxyCaching = nocache; >- } >- >- /** >- * Return the flag that states, if proxy caching is disabled, what headers >- * we add to disable the caching. >- */ >- public boolean getSecurePagesWithPragma() { >- return securePagesWithPragma; >- } >- >- /** >- * Set the value of the flag that states what headers we add to disable >- * proxy caching. >- * @param securePagesWithPragma <code>true</code> if we add headers which >- * are incompatible with downloading office documents in IE under SSL but >- * which fix a caching problem in Mozilla. >- */ >- public void setSecurePagesWithPragma(boolean securePagesWithPragma) { >- this.securePagesWithPragma = securePagesWithPragma; >- } >- >- // --------------------------------------------------------- Public Methods >- >- >- /** >- * Enforce the security restrictions in the web application deployment >- * descriptor of our associated Context. >- * >- * @param request Request to be processed >- * @param response Response to be processed >- * >- * @exception IOException if an input/output error occurs >- * @exception ServletException if thrown by a processing element >- */ >- public void invoke(Request request, Response response) >- throws IOException, ServletException { >- >- if (log.isDebugEnabled()) >- log.debug("Security checking request " + >- request.getMethod() + " " + request.getRequestURI()); >- LoginConfig config = this.context.getLoginConfig(); >- >- // Have we got a cached authenticated Principal to record? >- if (cache) { >- Principal principal = request.getUserPrincipal(); >- if (principal == null) { >- Session session = request.getSessionInternal(false); >- if (session != null) { >- principal = session.getPrincipal(); >- if (principal != null) { >- if (log.isDebugEnabled()) >- log.debug("We have cached auth type " + >- session.getAuthType() + >- " for principal " + >- session.getPrincipal()); >- request.setAuthType(session.getAuthType()); >- request.setUserPrincipal(principal); >- } >- } >- } >- } >- >- // Special handling for form-based logins to deal with the case >- // where the login form (and therefore the "j_security_check" URI >- // to which it submits) might be outside the secured area >- String contextPath = this.context.getPath(); >- String requestURI = request.getDecodedRequestURI(); >- if (requestURI.startsWith(contextPath) && >- requestURI.endsWith(Constants.FORM_ACTION)) { >- if (!authenticate(request, response, config)) { >- if (log.isDebugEnabled()) >- log.debug(" Failed authenticate() test ??" + requestURI ); >- return; >- } >- } >- >- Realm realm = this.context.getRealm(); >- // Is this request URI subject to a security constraint? >- SecurityConstraint [] constraints >- = realm.findSecurityConstraints(request, this.context); >- >- if ((constraints == null) /* && >- (!Constants.FORM_METHOD.equals(config.getAuthMethod())) */ ) { >- if (log.isDebugEnabled()) >- log.debug(" Not subject to any constraint"); >- getNext().invoke(request, response); >- return; >- } >- >- // Make sure that constrained resources are not cached by web proxies >- // or browsers as caching can provide a security hole >- if (disableProxyCaching && >- // FIXME: Disabled for Mozilla FORM support over SSL >- // (improper caching issue) >- //!request.isSecure() && >- !"POST".equalsIgnoreCase(request.getMethod())) { >- if (securePagesWithPragma) { >- // FIXME: These cause problems with downloading office docs >- // from IE under SSL and may not be needed for newer Mozilla >- // clients. >- response.setHeader("Pragma", "No-cache"); >- response.setHeader("Cache-Control", "no-cache"); >- } else { >- response.setHeader("Cache-Control", "private"); >- } >- response.setHeader("Expires", DATE_ONE); >- } >- >- int i; >- // Enforce any user data constraint for this security constraint >- if (log.isDebugEnabled()) { >- log.debug(" Calling hasUserDataPermission()"); >- } >- if (!realm.hasUserDataPermission(request, response, >- constraints)) { >- if (log.isDebugEnabled()) { >- log.debug(" Failed hasUserDataPermission() test"); >- } >- /* >- * ASSERT: Authenticator already set the appropriate >- * HTTP status code, so we do not have to do anything special >- */ >- return; >- } >- >- // Since authenticate modifies the response on failure, >- // we have to check for allow-from-all first. >- boolean authRequired = true; >- for(i=0; i < constraints.length && authRequired; i++) { >- if(!constraints[i].getAuthConstraint()) { >- authRequired = false; >- } else if(!constraints[i].getAllRoles()) { >- String [] roles = constraints[i].findAuthRoles(); >- if(roles == null || roles.length == 0) { >- authRequired = false; >- } >- } >- } >- >- if(authRequired) { >- if (log.isDebugEnabled()) { >- log.debug(" Calling authenticate()"); >- } >- if (!authenticate(request, response, config)) { >- if (log.isDebugEnabled()) { >- log.debug(" Failed authenticate() test"); >- } >- /* >- * ASSERT: Authenticator already set the appropriate >- * HTTP status code, so we do not have to do anything >- * special >- */ >- return; >- } >- } >- >- if (log.isDebugEnabled()) { >- log.debug(" Calling accessControl()"); >- } >- if (!realm.hasResourcePermission(request, response, >- constraints, >- this.context)) { >- if (log.isDebugEnabled()) { >- log.debug(" Failed accessControl() test"); >- } >- /* >- * ASSERT: AccessControl method has already set the >- * appropriate HTTP status code, so we do not have to do >- * anything special >- */ >- return; >- } >- >- // Any and all specified constraints have been satisfied >- if (log.isDebugEnabled()) { >- log.debug(" Successfully passed all security constraints"); >- } >- getNext().invoke(request, response); >- >- } >- >- >- // ------------------------------------------------------ Protected Methods >- >- >- >- >- /** >- * Associate the specified single sign on identifier with the >- * specified Session. >- * >- * @param ssoId Single sign on identifier >- * @param session Session to be associated >- */ >- protected void associate(String ssoId, Session session) { >- >- if (sso == null) >- return; >- sso.associate(ssoId, session); >- >- } >- >- >- /** >- * Authenticate the user making this request, based on the specified >- * login configuration. Return <code>true</code> if any specified >- * constraint has been satisfied, or <code>false</code> if we have >- * created a response challenge already. >- * >- * @param request Request we are processing >- * @param response Response we are creating >- * @param config Login configuration describing how authentication >- * should be performed >- * >- * @exception IOException if an input/output error occurs >- */ >- protected abstract boolean authenticate(Request request, >- Response response, >- LoginConfig config) >- throws IOException; >- >- >- /** >- * Generate and return a new session identifier for the cookie that >- * identifies an SSO principal. >- */ >- protected synchronized String generateSessionId() { >- >- // Generate a byte array containing a session identifier >- byte bytes[] = new byte[SESSION_ID_BYTES]; >- getRandom().nextBytes(bytes); >- bytes = getDigest().digest(bytes); >- >- // Render the result as a String of hexadecimal digits >- StringBuffer result = new StringBuffer(); >- for (int i = 0; i < bytes.length; i++) { >- byte b1 = (byte) ((bytes[i] & 0xf0) >> 4); >- byte b2 = (byte) (bytes[i] & 0x0f); >- if (b1 < 10) >- result.append((char) ('0' + b1)); >- else >- result.append((char) ('A' + (b1 - 10))); >- if (b2 < 10) >- result.append((char) ('0' + b2)); >- else >- result.append((char) ('A' + (b2 - 10))); >- } >- return (result.toString()); >- >- } >- >- >- /** >- * Return the MessageDigest object to be used for calculating >- * session identifiers. If none has been created yet, initialize >- * one the first time this method is called. >- */ >- protected synchronized MessageDigest getDigest() { >- >- if (this.digest == null) { >- try { >- this.digest = MessageDigest.getInstance(algorithm); >- } catch (NoSuchAlgorithmException e) { >- try { >- this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM); >- } catch (NoSuchAlgorithmException f) { >- this.digest = null; >- } >- } >- } >- >- return (this.digest); >- >- } >- >- >- /** >- * Return the random number generator instance we should use for >- * generating session identifiers. If there is no such generator >- * currently defined, construct and seed a new one. >- */ >- protected synchronized Random getRandom() { >- >- if (this.random == null) { >- try { >- Class clazz = Class.forName(randomClass); >- this.random = (Random) clazz.newInstance(); >- long seed = System.currentTimeMillis(); >- char entropy[] = getEntropy().toCharArray(); >- for (int i = 0; i < entropy.length; i++) { >- long update = ((byte) entropy[i]) << ((i % 8) * 8); >- seed ^= update; >- } >- this.random.setSeed(seed); >- } catch (Exception e) { >- this.random = new java.util.Random(); >- } >- } >- >- return (this.random); >- >- } >- >- >- /** >- * Attempts reauthentication to the <code>Realm</code> using >- * the credentials included in argument <code>entry</code>. >- * >- * @param ssoId identifier of SingleSignOn session with which the >- * caller is associated >- * @param request the request that needs to be authenticated >- */ >- protected boolean reauthenticateFromSSO(String ssoId, Request request) { >- >- if (sso == null || ssoId == null) >- return false; >- >- boolean reauthenticated = false; >- >- Container parent = getContainer(); >- if (parent != null) { >- Realm realm = parent.getRealm(); >- if (realm != null) { >- reauthenticated = sso.reauthenticate(ssoId, realm, request); >- } >- } >- >- if (reauthenticated) { >- associate(ssoId, request.getSessionInternal(true)); >- >- if (log.isDebugEnabled()) { >- log.debug(" Reauthenticated cached principal '" + >- request.getUserPrincipal().getName() + >- "' with auth type '" + request.getAuthType() + "'"); >- } >- } >- >- return reauthenticated; >- } >- >- >- /** >- * Register an authenticated Principal and authentication type in our >- * request, in the current session (if there is one), and with our >- * SingleSignOn valve, if there is one. Set the appropriate cookie >- * to be returned. >- * >- * @param request The servlet request we are processing >- * @param response The servlet response we are generating >- * @param principal The authenticated Principal to be registered >- * @param authType The authentication type to be registered >- * @param username Username used to authenticate (if any) >- * @param password Password used to authenticate (if any) >- */ >- protected void register(Request request, Response response, >- Principal principal, String authType, >- String username, String password) { >- >- if (log.isDebugEnabled()) { >- // Bugzilla 39255: http://issues.apache.org/bugzilla/show_bug.cgi?id=39255 >- String name = (principal == null) ? "none" : principal.getName(); >- log.debug("Authenticated '" + name + "' with type '" >- + authType + "'"); >- } >- >- // Cache the authentication information in our request >- request.setAuthType(authType); >- request.setUserPrincipal(principal); >- >- Session session = request.getSessionInternal(false); >- // Cache the authentication information in our session, if any >- if (cache) { >- if (session != null) { >- session.setAuthType(authType); >- session.setPrincipal(principal); >- if (username != null) >- session.setNote(Constants.SESS_USERNAME_NOTE, username); >- else >- session.removeNote(Constants.SESS_USERNAME_NOTE); >- if (password != null) >- session.setNote(Constants.SESS_PASSWORD_NOTE, password); >- else >- session.removeNote(Constants.SESS_PASSWORD_NOTE); >- } >- } >- >- // Construct a cookie to be returned to the client >- if (sso == null) >- return; >- >- // Only create a new SSO entry if the SSO did not already set a note >- // for an existing entry (as it would do with subsequent requests >- // for DIGEST and SSL authenticated contexts) >- String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE); >- if (ssoId == null) { >- // Construct a cookie to be returned to the client >- ssoId = generateSessionId(); >- Cookie cookie = new Cookie(Constants.SINGLE_SIGN_ON_COOKIE, ssoId); >- cookie.setMaxAge(-1); >- cookie.setPath("/"); >- >- // Bugzilla 41217 >- cookie.setSecure(request.isSecure()); >- >- // Bugzilla 34724 >- String ssoDomain = sso.getCookieDomain(); >- if(ssoDomain != null) { >- cookie.setDomain(ssoDomain); >- } >- >- response.addCookie(cookie); >- >- // Register this principal with our SSO valve >- sso.register(ssoId, principal, authType, username, password); >- request.setNote(Constants.REQ_SSOID_NOTE, ssoId); >- >- } else { >- // Update the SSO session with the latest authentication data >- sso.update(ssoId, principal, authType, username, password); >- } >- >- // Fix for Bug 10040 >- // Always associate a session with a new SSO reqistration. >- // SSO entries are only removed from the SSO registry map when >- // associated sessions are destroyed; if a new SSO entry is created >- // above for this request and the user never revisits the context, the >- // SSO entry will never be cleared if we don't associate the session >- if (session == null) >- session = request.getSessionInternal(true); >- sso.associate(ssoId, session); >- >- } >- >- >- // ------------------------------------------------------ Lifecycle Methods >- >- >- /** >- * Add a lifecycle event listener to this component. >- * >- * @param listener The listener to add >- */ >- public void addLifecycleListener(LifecycleListener listener) { >- >- lifecycle.addLifecycleListener(listener); >- >- } >- >- >- /** >- * Get the lifecycle listeners associated with this lifecycle. If this >- * Lifecycle has no listeners registered, a zero-length array is returned. >- */ >- public LifecycleListener[] findLifecycleListeners() { >- >- return lifecycle.findLifecycleListeners(); >- >- } >- >- >- /** >- * Remove a lifecycle event listener from this component. >- * >- * @param listener The listener to remove >- */ >- public void removeLifecycleListener(LifecycleListener listener) { >- >- lifecycle.removeLifecycleListener(listener); >- >- } >- >- >- /** >- * Prepare for the beginning of active use of the public methods of this >- * component. This method should be called after <code>configure()</code>, >- * and before any of the public methods of the component are utilized. >- * >- * @exception LifecycleException if this component detects a fatal error >- * that prevents this component from being used >- */ >- public void start() throws LifecycleException { >- >- // Validate and update our current component state >- if (started) >- throw new LifecycleException >- (sm.getString("authenticator.alreadyStarted")); >- lifecycle.fireLifecycleEvent(START_EVENT, null); >- started = true; >- >- // Look up the SingleSignOn implementation in our request processing >- // path, if there is one >- Container parent = context.getParent(); >- while ((sso == null) && (parent != null)) { >- if (!(parent instanceof Pipeline)) { >- parent = parent.getParent(); >- continue; >- } >- Valve valves[] = ((Pipeline) parent).getValves(); >- for (int i = 0; i < valves.length; i++) { >- if (valves[i] instanceof SingleSignOn) { >- sso = (SingleSignOn) valves[i]; >- break; >- } >- } >- if (sso == null) >- parent = parent.getParent(); >- } >- if (log.isDebugEnabled()) { >- if (sso != null) >- log.debug("Found SingleSignOn Valve at " + sso); >- else >- log.debug("No SingleSignOn Valve is present"); >- } >- >- } >- >- >- /** >- * Gracefully terminate the active use of the public methods of this >- * component. This method should be the last one called on a given >- * instance of this component. >- * >- * @exception LifecycleException if this component detects a fatal error >- * that needs to be reported >- */ >- public void stop() throws LifecycleException { >- >- // Validate and update our current component state >- if (!started) >- throw new LifecycleException >- (sm.getString("authenticator.notStarted")); >- lifecycle.fireLifecycleEvent(STOP_EVENT, null); >- started = false; >- >- sso = null; >- >- } >- >- >-} >+/* >+ * 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.catalina.authenticator; >+ >+ >+import java.io.IOException; >+import java.security.MessageDigest; >+import java.security.NoSuchAlgorithmException; >+import java.security.Principal; >+import java.security.cert.X509Certificate; >+import java.text.SimpleDateFormat; >+import java.util.Date; >+import java.util.Locale; >+import java.util.Random; >+ >+import javax.servlet.ServletException; >+import javax.servlet.http.Cookie; >+ >+import org.apache.catalina.Authenticator; >+import org.apache.catalina.Container; >+import org.apache.catalina.Context; >+import org.apache.catalina.Globals; >+import org.apache.catalina.Lifecycle; >+import org.apache.catalina.LifecycleException; >+import org.apache.catalina.LifecycleListener; >+import org.apache.catalina.Pipeline; >+import org.apache.catalina.Realm; >+import org.apache.catalina.Session; >+import org.apache.catalina.Valve; >+import org.apache.catalina.connector.Request; >+import org.apache.catalina.connector.Response; >+import org.apache.catalina.deploy.LoginConfig; >+import org.apache.catalina.deploy.SecurityConstraint; >+import org.apache.catalina.util.DateTool; >+import org.apache.catalina.util.LifecycleSupport; >+import org.apache.catalina.util.StringManager; >+import org.apache.catalina.valves.ValveBase; >+import org.apache.commons.logging.Log; >+import org.apache.commons.logging.LogFactory; >+ >+ >+/** >+ * Basic implementation of the <b>Valve</b> interface that enforces the >+ * <code><security-constraint></code> elements in the web application >+ * deployment descriptor. This functionality is implemented as a Valve >+ * so that it can be ommitted in environments that do not require these >+ * features. Individual implementations of each supported authentication >+ * method can subclass this base class as required. >+ * <p> >+ * <b>USAGE CONSTRAINT</b>: When this class is utilized, the Context to >+ * which it is attached (or a parent Container in a hierarchy) must have an >+ * associated Realm that can be used for authenticating users and enumerating >+ * the roles to which they have been assigned. >+ * <p> >+ * <b>USAGE CONSTRAINT</b>: This Valve is only useful when processing HTTP >+ * requests. Requests of any other type will simply be passed through. >+ * >+ * @author Craig R. McClanahan >+ * @version $Revision: 496025 $ $Date: 2007-01-14 04:18:06 +0100 (Sun, 14 Jan 2007) $ >+ */ >+ >+ >+public abstract class AuthenticatorBase >+ extends ValveBase >+ implements Authenticator, Lifecycle { >+ private static Log log = LogFactory.getLog(AuthenticatorBase.class); >+ >+ >+ // ----------------------------------------------------- Instance Variables >+ >+ >+ /** >+ * The default message digest algorithm to use if we cannot use >+ * the requested one. >+ */ >+ protected static final String DEFAULT_ALGORITHM = "MD5"; >+ >+ >+ /** >+ * The number of random bytes to include when generating a >+ * session identifier. >+ */ >+ protected static final int SESSION_ID_BYTES = 16; >+ >+ >+ /** >+ * The message digest algorithm to be used when generating session >+ * identifiers. This must be an algorithm supported by the >+ * <code>java.security.MessageDigest</code> class on your platform. >+ */ >+ protected String algorithm = DEFAULT_ALGORITHM; >+ >+ >+ /** >+ * Should we cache authenticated Principals if the request is part of >+ * an HTTP session? >+ */ >+ protected boolean cache = true; >+ >+ >+ /** >+ * The Context to which this Valve is attached. >+ */ >+ protected Context context = null; >+ >+ >+ /** >+ * Return the MessageDigest implementation to be used when >+ * creating session identifiers. >+ */ >+ protected MessageDigest digest = null; >+ >+ >+ /** >+ * A String initialization parameter used to increase the entropy of >+ * the initialization of our random number generator. >+ */ >+ protected String entropy = null; >+ >+ >+ /** >+ * Descriptive information about this implementation. >+ */ >+ protected static final String info = >+ "org.apache.catalina.authenticator.AuthenticatorBase/1.0"; >+ >+ /** >+ * Flag to determine if we disable proxy caching, or leave the issue >+ * up to the webapp developer. >+ */ >+ protected boolean disableProxyCaching = true; >+ >+ /** >+ * Flag to determine if we disable proxy caching with headers incompatible >+ * with IE >+ */ >+ protected boolean securePagesWithPragma = true; >+ >+ /** >+ * The lifecycle event support for this component. >+ */ >+ protected LifecycleSupport lifecycle = new LifecycleSupport(this); >+ >+ >+ /** >+ * A random number generator to use when generating session identifiers. >+ */ >+ protected Random random = null; >+ >+ >+ /** >+ * The Java class name of the random number generator class to be used >+ * when generating session identifiers. >+ */ >+ protected String randomClass = "java.security.SecureRandom"; >+ >+ >+ /** >+ * The string manager for this package. >+ */ >+ protected static final StringManager sm = >+ StringManager.getManager(Constants.Package); >+ >+ >+ /** >+ * The SingleSignOn implementation in our request processing chain, >+ * if there is one. >+ */ >+ protected SingleSignOn sso = null; >+ >+ >+ /** >+ * Has this component been started? >+ */ >+ protected boolean started = false; >+ >+ >+ /** >+ * "Expires" header always set to Date(1), so generate once only >+ */ >+ private static final String DATE_ONE = >+ (new SimpleDateFormat(DateTool.HTTP_RESPONSE_DATE_HEADER, >+ Locale.US)).format(new Date(1)); >+ >+ >+ // ------------------------------------------------------------- Properties >+ >+ >+ /** >+ * Return the message digest algorithm for this Manager. >+ */ >+ public String getAlgorithm() { >+ >+ return (this.algorithm); >+ >+ } >+ >+ >+ /** >+ * Set the message digest algorithm for this Manager. >+ * >+ * @param algorithm The new message digest algorithm >+ */ >+ public void setAlgorithm(String algorithm) { >+ >+ this.algorithm = algorithm; >+ >+ } >+ >+ >+ /** >+ * Return the cache authenticated Principals flag. >+ */ >+ public boolean getCache() { >+ >+ return (this.cache); >+ >+ } >+ >+ >+ /** >+ * Set the cache authenticated Principals flag. >+ * >+ * @param cache The new cache flag >+ */ >+ public void setCache(boolean cache) { >+ >+ this.cache = cache; >+ >+ } >+ >+ >+ /** >+ * Return the Container to which this Valve is attached. >+ */ >+ public Container getContainer() { >+ >+ return (this.context); >+ >+ } >+ >+ >+ /** >+ * Set the Container to which this Valve is attached. >+ * >+ * @param container The container to which we are attached >+ */ >+ public void setContainer(Container container) { >+ >+ if (!(container instanceof Context)) >+ throw new IllegalArgumentException >+ (sm.getString("authenticator.notContext")); >+ >+ super.setContainer(container); >+ this.context = (Context) container; >+ >+ } >+ >+ >+ /** >+ * Return the entropy increaser value, or compute a semi-useful value >+ * if this String has not yet been set. >+ */ >+ public String getEntropy() { >+ >+ // Calculate a semi-useful value if this has not been set >+ if (this.entropy == null) >+ setEntropy(this.toString()); >+ >+ return (this.entropy); >+ >+ } >+ >+ >+ /** >+ * Set the entropy increaser value. >+ * >+ * @param entropy The new entropy increaser value >+ */ >+ public void setEntropy(String entropy) { >+ >+ this.entropy = entropy; >+ >+ } >+ >+ >+ /** >+ * Return descriptive information about this Valve implementation. >+ */ >+ public String getInfo() { >+ >+ return (info); >+ >+ } >+ >+ >+ /** >+ * Return the random number generator class name. >+ */ >+ public String getRandomClass() { >+ >+ return (this.randomClass); >+ >+ } >+ >+ >+ /** >+ * Set the random number generator class name. >+ * >+ * @param randomClass The new random number generator class name >+ */ >+ public void setRandomClass(String randomClass) { >+ >+ this.randomClass = randomClass; >+ >+ } >+ >+ /** >+ * Return the flag that states if we add headers to disable caching by >+ * proxies. >+ */ >+ public boolean getDisableProxyCaching() { >+ return disableProxyCaching; >+ } >+ >+ /** >+ * Set the value of the flag that states if we add headers to disable >+ * caching by proxies. >+ * @param nocache <code>true</code> if we add headers to disable proxy >+ * caching, <code>false</code> if we leave the headers alone. >+ */ >+ public void setDisableProxyCaching(boolean nocache) { >+ disableProxyCaching = nocache; >+ } >+ >+ /** >+ * Return the flag that states, if proxy caching is disabled, what headers >+ * we add to disable the caching. >+ */ >+ public boolean getSecurePagesWithPragma() { >+ return securePagesWithPragma; >+ } >+ >+ /** >+ * Set the value of the flag that states what headers we add to disable >+ * proxy caching. >+ * @param securePagesWithPragma <code>true</code> if we add headers which >+ * are incompatible with downloading office documents in IE under SSL but >+ * which fix a caching problem in Mozilla. >+ */ >+ public void setSecurePagesWithPragma(boolean securePagesWithPragma) { >+ this.securePagesWithPragma = securePagesWithPragma; >+ } >+ >+ // --------------------------------------------------------- Public Methods >+ >+ >+ /** >+ * Enforce the security restrictions in the web application deployment >+ * descriptor of our associated Context. >+ * >+ * @param request Request to be processed >+ * @param response Response to be processed >+ * >+ * @exception IOException if an input/output error occurs >+ * @exception ServletException if thrown by a processing element >+ */ >+ public void invoke(Request request, Response response) >+ throws IOException, ServletException { >+ >+ if (log.isDebugEnabled()) >+ log.debug("Security checking request " + >+ request.getMethod() + " " + request.getRequestURI()); >+ LoginConfig config = this.context.getLoginConfig(); >+ >+ // Have we got a cached authenticated Principal to record? >+ if (cache) { >+ Principal principal = request.getUserPrincipal(); >+ if (principal == null) { >+ Session session = request.getSessionInternal(false); >+ if (session != null) { >+ principal = session.getPrincipal(); >+ if (principal != null) { >+ if (log.isDebugEnabled()) >+ log.debug("We have cached auth type " + >+ session.getAuthType() + >+ " for principal " + >+ session.getPrincipal()); >+ request.setAuthType(session.getAuthType()); >+ request.setUserPrincipal(principal); >+ } >+ } >+ } >+ } >+ >+ // Special handling for form-based logins to deal with the case >+ // where the login form (and therefore the "j_security_check" URI >+ // to which it submits) might be outside the secured area >+ String contextPath = this.context.getPath(); >+ String requestURI = request.getDecodedRequestURI(); >+ if (requestURI.startsWith(contextPath) && >+ requestURI.endsWith(Constants.FORM_ACTION)) { >+ if (!authenticate(request, response, config)) { >+ if (log.isDebugEnabled()) >+ log.debug(" Failed authenticate() test ??" + requestURI ); >+ return; >+ } >+ } >+ >+ Realm realm = this.context.getRealm(); >+ // Is this request URI subject to a security constraint? >+ SecurityConstraint [] constraints >+ = realm.findSecurityConstraints(request, this.context); >+ >+ // Make sure that constrained resources are not cached by web proxies >+ // or browsers as caching can provide a security hole >+ if (disableProxyCaching && >+ // FIXME: Disabled for Mozilla FORM support over SSL >+ // (improper caching issue) >+ //!request.isSecure() && >+ !"POST".equalsIgnoreCase(request.getMethod())) { >+ if (securePagesWithPragma) { >+ // FIXME: These cause problems with downloading office docs >+ // from IE under SSL and may not be needed for newer Mozilla >+ // clients. >+ response.setHeader("Pragma", "No-cache"); >+ response.setHeader("Cache-Control", "no-cache"); >+ } else { >+ response.setHeader("Cache-Control", "private"); >+ } >+ response.setHeader("Expires", DATE_ONE); >+ } >+ >+ int i; >+ if (constraints != null) { >+ // Enforce any user data constraint for this security constraint >+ if (log.isDebugEnabled()) { >+ log.debug(" Calling hasUserDataPermission()"); >+ } >+ if (!realm.hasUserDataPermission(request, response, >+ constraints)) { >+ if (log.isDebugEnabled()) { >+ log.debug(" Failed hasUserDataPermission() test"); >+ } >+ /* >+ * ASSERT: Authenticator already set the appropriate >+ * HTTP status code, so we do not have to do anything special >+ */ >+ return; >+ } >+ } >+ >+ // Since authenticate modifies the response on failure, >+ // we have to check for allow-from-all first. >+ boolean authRequired; >+ if (constraints == null) { >+ authRequired = false; >+ } else { >+ authRequired = true; >+ for(i=0; i < constraints.length && authRequired; i++) { >+ if(!constraints[i].getAuthConstraint()) { >+ authRequired = false; >+ } else if(!constraints[i].getAllRoles()) { >+ String [] roles = constraints[i].findAuthRoles(); >+ if(roles == null || roles.length == 0) { >+ authRequired = false; >+ } >+ } >+ } >+ } >+ >+ if (!authRequired) >+ { >+ authRequired = >+ request.getCoyoteRequest().getMimeHeaders(). >+ getValue("authorization") != null; >+ } >+ >+ if (!authRequired) >+ { >+ X509Certificate[] certs = >+ (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR); >+ >+ authRequired = certs != null && certs.length > 0; >+ } >+ >+ if(authRequired) { >+ if (log.isDebugEnabled()) { >+ log.debug(" Calling authenticate()"); >+ } >+ if (!authenticate(request, response, config)) { >+ if (log.isDebugEnabled()) { >+ log.debug(" Failed authenticate() test"); >+ } >+ /* >+ * ASSERT: Authenticator already set the appropriate >+ * HTTP status code, so we do not have to do anything >+ * special >+ */ >+ return; >+ } >+ } >+ >+ if (constraints != null) { >+ if (log.isDebugEnabled()) { >+ log.debug(" Calling accessControl()"); >+ } >+ if (!realm.hasResourcePermission(request, response, >+ constraints, >+ this.context)) { >+ if (log.isDebugEnabled()) { >+ log.debug(" Failed accessControl() test"); >+ } >+ /* >+ * ASSERT: AccessControl method has already set the >+ * appropriate HTTP status code, so we do not have to do >+ * anything special >+ */ >+ return; >+ } >+ } >+ >+ // Any and all specified constraints have been satisfied >+ if (log.isDebugEnabled()) { >+ log.debug(" Successfully passed all security constraints"); >+ } >+ getNext().invoke(request, response); >+ >+ } >+ >+ >+ // ------------------------------------------------------ Protected Methods >+ >+ >+ >+ >+ /** >+ * Associate the specified single sign on identifier with the >+ * specified Session. >+ * >+ * @param ssoId Single sign on identifier >+ * @param session Session to be associated >+ */ >+ protected void associate(String ssoId, Session session) { >+ >+ if (sso == null) >+ return; >+ sso.associate(ssoId, session); >+ >+ } >+ >+ >+ /** >+ * Authenticate the user making this request, based on the specified >+ * login configuration. Return <code>true</code> if any specified >+ * constraint has been satisfied, or <code>false</code> if we have >+ * created a response challenge already. >+ * >+ * @param request Request we are processing >+ * @param response Response we are creating >+ * @param config Login configuration describing how authentication >+ * should be performed >+ * >+ * @exception IOException if an input/output error occurs >+ */ >+ protected abstract boolean authenticate(Request request, >+ Response response, >+ LoginConfig config) >+ throws IOException; >+ >+ >+ /** >+ * Generate and return a new session identifier for the cookie that >+ * identifies an SSO principal. >+ */ >+ protected synchronized String generateSessionId() { >+ >+ // Generate a byte array containing a session identifier >+ byte bytes[] = new byte[SESSION_ID_BYTES]; >+ getRandom().nextBytes(bytes); >+ bytes = getDigest().digest(bytes); >+ >+ // Render the result as a String of hexadecimal digits >+ StringBuffer result = new StringBuffer(); >+ for (int i = 0; i < bytes.length; i++) { >+ byte b1 = (byte) ((bytes[i] & 0xf0) >> 4); >+ byte b2 = (byte) (bytes[i] & 0x0f); >+ if (b1 < 10) >+ result.append((char) ('0' + b1)); >+ else >+ result.append((char) ('A' + (b1 - 10))); >+ if (b2 < 10) >+ result.append((char) ('0' + b2)); >+ else >+ result.append((char) ('A' + (b2 - 10))); >+ } >+ return (result.toString()); >+ >+ } >+ >+ >+ /** >+ * Return the MessageDigest object to be used for calculating >+ * session identifiers. If none has been created yet, initialize >+ * one the first time this method is called. >+ */ >+ protected synchronized MessageDigest getDigest() { >+ >+ if (this.digest == null) { >+ try { >+ this.digest = MessageDigest.getInstance(algorithm); >+ } catch (NoSuchAlgorithmException e) { >+ try { >+ this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM); >+ } catch (NoSuchAlgorithmException f) { >+ this.digest = null; >+ } >+ } >+ } >+ >+ return (this.digest); >+ >+ } >+ >+ >+ /** >+ * Return the random number generator instance we should use for >+ * generating session identifiers. If there is no such generator >+ * currently defined, construct and seed a new one. >+ */ >+ protected synchronized Random getRandom() { >+ >+ if (this.random == null) { >+ try { >+ Class clazz = Class.forName(randomClass); >+ this.random = (Random) clazz.newInstance(); >+ long seed = System.currentTimeMillis(); >+ char entropy[] = getEntropy().toCharArray(); >+ for (int i = 0; i < entropy.length; i++) { >+ long update = ((byte) entropy[i]) << ((i % 8) * 8); >+ seed ^= update; >+ } >+ this.random.setSeed(seed); >+ } catch (Exception e) { >+ this.random = new java.util.Random(); >+ } >+ } >+ >+ return (this.random); >+ >+ } >+ >+ >+ /** >+ * Attempts reauthentication to the <code>Realm</code> using >+ * the credentials included in argument <code>entry</code>. >+ * >+ * @param ssoId identifier of SingleSignOn session with which the >+ * caller is associated >+ * @param request the request that needs to be authenticated >+ */ >+ protected boolean reauthenticateFromSSO(String ssoId, Request request) { >+ >+ if (sso == null || ssoId == null) >+ return false; >+ >+ boolean reauthenticated = false; >+ >+ Container parent = getContainer(); >+ if (parent != null) { >+ Realm realm = parent.getRealm(); >+ if (realm != null) { >+ reauthenticated = sso.reauthenticate(ssoId, realm, request); >+ } >+ } >+ >+ if (reauthenticated) { >+ associate(ssoId, request.getSessionInternal(true)); >+ >+ if (log.isDebugEnabled()) { >+ log.debug(" Reauthenticated cached principal '" + >+ request.getUserPrincipal().getName() + >+ "' with auth type '" + request.getAuthType() + "'"); >+ } >+ } >+ >+ return reauthenticated; >+ } >+ >+ >+ /** >+ * Register an authenticated Principal and authentication type in our >+ * request, in the current session (if there is one), and with our >+ * SingleSignOn valve, if there is one. Set the appropriate cookie >+ * to be returned. >+ * >+ * @param request The servlet request we are processing >+ * @param response The servlet response we are generating >+ * @param principal The authenticated Principal to be registered >+ * @param authType The authentication type to be registered >+ * @param username Username used to authenticate (if any) >+ * @param password Password used to authenticate (if any) >+ */ >+ protected void register(Request request, Response response, >+ Principal principal, String authType, >+ String username, String password) { >+ >+ if (log.isDebugEnabled()) { >+ // Bugzilla 39255: http://issues.apache.org/bugzilla/show_bug.cgi?id=39255 >+ String name = (principal == null) ? "none" : principal.getName(); >+ log.debug("Authenticated '" + name + "' with type '" >+ + authType + "'"); >+ } >+ >+ // Cache the authentication information in our request >+ request.setAuthType(authType); >+ request.setUserPrincipal(principal); >+ >+ Session session = request.getSessionInternal(false); >+ // Cache the authentication information in our session, if any >+ if (cache) { >+ if (session != null) { >+ session.setAuthType(authType); >+ session.setPrincipal(principal); >+ if (username != null) >+ session.setNote(Constants.SESS_USERNAME_NOTE, username); >+ else >+ session.removeNote(Constants.SESS_USERNAME_NOTE); >+ if (password != null) >+ session.setNote(Constants.SESS_PASSWORD_NOTE, password); >+ else >+ session.removeNote(Constants.SESS_PASSWORD_NOTE); >+ } >+ } >+ >+ // Construct a cookie to be returned to the client >+ if (sso == null) >+ return; >+ >+ // Only create a new SSO entry if the SSO did not already set a note >+ // for an existing entry (as it would do with subsequent requests >+ // for DIGEST and SSL authenticated contexts) >+ String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE); >+ if (ssoId == null) { >+ // Construct a cookie to be returned to the client >+ ssoId = generateSessionId(); >+ Cookie cookie = new Cookie(Constants.SINGLE_SIGN_ON_COOKIE, ssoId); >+ cookie.setMaxAge(-1); >+ cookie.setPath("/"); >+ >+ // Bugzilla 41217 >+ cookie.setSecure(request.isSecure()); >+ >+ // Bugzilla 34724 >+ String ssoDomain = sso.getCookieDomain(); >+ if(ssoDomain != null) { >+ cookie.setDomain(ssoDomain); >+ } >+ >+ response.addCookie(cookie); >+ >+ // Register this principal with our SSO valve >+ sso.register(ssoId, principal, authType, username, password); >+ request.setNote(Constants.REQ_SSOID_NOTE, ssoId); >+ >+ } else { >+ // Update the SSO session with the latest authentication data >+ sso.update(ssoId, principal, authType, username, password); >+ } >+ >+ // Fix for Bug 10040 >+ // Always associate a session with a new SSO reqistration. >+ // SSO entries are only removed from the SSO registry map when >+ // associated sessions are destroyed; if a new SSO entry is created >+ // above for this request and the user never revisits the context, the >+ // SSO entry will never be cleared if we don't associate the session >+ if (session == null) >+ session = request.getSessionInternal(true); >+ sso.associate(ssoId, session); >+ >+ } >+ >+ >+ // ------------------------------------------------------ Lifecycle Methods >+ >+ >+ /** >+ * Add a lifecycle event listener to this component. >+ * >+ * @param listener The listener to add >+ */ >+ public void addLifecycleListener(LifecycleListener listener) { >+ >+ lifecycle.addLifecycleListener(listener); >+ >+ } >+ >+ >+ /** >+ * Get the lifecycle listeners associated with this lifecycle. If this >+ * Lifecycle has no listeners registered, a zero-length array is returned. >+ */ >+ public LifecycleListener[] findLifecycleListeners() { >+ >+ return lifecycle.findLifecycleListeners(); >+ >+ } >+ >+ >+ /** >+ * Remove a lifecycle event listener from this component. >+ * >+ * @param listener The listener to remove >+ */ >+ public void removeLifecycleListener(LifecycleListener listener) { >+ >+ lifecycle.removeLifecycleListener(listener); >+ >+ } >+ >+ >+ /** >+ * Prepare for the beginning of active use of the public methods of this >+ * component. This method should be called after <code>configure()</code>, >+ * and before any of the public methods of the component are utilized. >+ * >+ * @exception LifecycleException if this component detects a fatal error >+ * that prevents this component from being used >+ */ >+ public void start() throws LifecycleException { >+ >+ // Validate and update our current component state >+ if (started) >+ throw new LifecycleException >+ (sm.getString("authenticator.alreadyStarted")); >+ lifecycle.fireLifecycleEvent(START_EVENT, null); >+ started = true; >+ >+ // Look up the SingleSignOn implementation in our request processing >+ // path, if there is one >+ Container parent = context.getParent(); >+ while ((sso == null) && (parent != null)) { >+ if (!(parent instanceof Pipeline)) { >+ parent = parent.getParent(); >+ continue; >+ } >+ Valve valves[] = ((Pipeline) parent).getValves(); >+ for (int i = 0; i < valves.length; i++) { >+ if (valves[i] instanceof SingleSignOn) { >+ sso = (SingleSignOn) valves[i]; >+ break; >+ } >+ } >+ if (sso == null) >+ parent = parent.getParent(); >+ } >+ if (log.isDebugEnabled()) { >+ if (sso != null) >+ log.debug("Found SingleSignOn Valve at " + sso); >+ else >+ log.debug("No SingleSignOn Valve is present"); >+ } >+ >+ } >+ >+ >+ /** >+ * Gracefully terminate the active use of the public methods of this >+ * component. This method should be the last one called on a given >+ * instance of this component. >+ * >+ * @exception LifecycleException if this component detects a fatal error >+ * that needs to be reported >+ */ >+ public void stop() throws LifecycleException { >+ >+ // Validate and update our current component state >+ if (!started) >+ throw new LifecycleException >+ (sm.getString("authenticator.notStarted")); >+ lifecycle.fireLifecycleEvent(STOP_EVENT, null); >+ started = false; >+ >+ sso = null; >+ >+ } >+ >+ >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 12428
:
8384
|
20857
| 20859 |
20860