Return-Path: Mailing-List: contact tomcat-dev-help@jakarta.apache.org; run by ezmlm Delivered-To: mailing list tomcat-dev@jakarta.apache.org Received: (qmail 84556 invoked by uid 500); 11 Apr 2000 00:28:54 -0000 Delivered-To: apmail-jakarta-tomcat-cvs@apache.org Received: (qmail 84551 invoked by uid 1059); 11 Apr 2000 00:28:53 -0000 Date: 11 Apr 2000 00:28:53 -0000 Message-ID: <20000411002853.84550.qmail@locus.apache.org> From: craigmcc@locus.apache.org To: jakarta-tomcat-cvs@apache.org Subject: cvs commit: jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/security HttpSecurityValve.java SecurityValve.java craigmcc 00/04/10 17:28:53 Added: proposals/catalina/src/share/org/apache/tomcat/security HttpSecurityValve.java Removed: proposals/catalina/src/share/org/apache/tomcat/security SecurityValve.java Log: Replace SecurityValve with HttpSecurityValve, since the defined mechanisms are all HTTP specific. Revision Changes Path 1.1 jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/security/HttpSecurityValve.java Index: HttpSecurityValve.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/security/HttpSecurityValve.java,v 1.1 2000/04/11 00:28:53 craigmcc Exp $ * $Revision: 1.1 $ * $Date: 2000/04/11 00:28:53 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.tomcat.security; import java.io.IOException; import java.security.Principal; import java.util.Enumeration; import java.util.Vector; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.tomcat.Container; import org.apache.tomcat.Context; import org.apache.tomcat.Lifecycle; import org.apache.tomcat.LifecycleEvent; import org.apache.tomcat.LifecycleException; import org.apache.tomcat.LifecycleListener; import org.apache.tomcat.Realm; import org.apache.tomcat.Request; import org.apache.tomcat.Response; import org.apache.tomcat.Valve; import org.apache.tomcat.deploy.LoginConfig; import org.apache.tomcat.deploy.SecurityConstraint; import org.apache.tomcat.util.LifecycleSupport; import org.apache.tomcat.util.StringManager; import org.apache.tomcat.valves.ValveBase; /** * Implementation of the Valve interface that enforces the * <security-constraint> elements in the web application * deployment descriptor. This functionality is implemented as an * Valve so that it can be omitted in environments that do not require * these features. *

* USAGE CONSTRAINT: 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. *

* USAGE CONSTRAINT: This Valve is only useful when processing HTTP * requests. Requests of any other type will simply be passed through. *

* XXX - Unimplemented Features: *

* * @author Craig R. McClanahan * @version $Revision: 1.1 $ $Date: 2000/04/11 00:28:53 $ */ public final class HttpSecurityValve extends ValveBase implements Lifecycle { // ----------------------------------------------------- Instance Variables /** * The Context to which this Valve is attached. */ private Context context = null; /** * Descriptive information about this implementation. */ private static final String info = "org.apache.tomcat.security.SecurityValve/1.0"; /** * The lifecycle event support for this component. */ protected LifecycleSupport lifecycle = new LifecycleSupport(this); /** * The string manager for this package. */ private StringManager sm = StringManager.getManager(Constants.Package); /** * Has this component been started? */ private boolean started = false; // ------------------------------------------------------------- Properties /** * 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("securityValve.notContext")); this.context = (Context) container; } /** * Return descriptive information about this Valve implementation. */ public String getInfo() { return (this.info); } // --------------------------------------------------------- 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 this is not an HTTP request, do nothing if (!(request.getRequest() instanceof HttpServletRequest) || !(response.getResponse() instanceof HttpServletResponse)) { invokeNext(request, response); return; } // Is this request URI subject to a security constraint? SecurityConstraint constraint = findConstraint(request); if (constraint == null) { invokeNext(request, response); return; } // Enforce any user data constraint for this security constraint if (!checkUserData(request, response, constraint)) { return; } // Authenticate based upon the specified login configuration if (!authenticate(request, response, context.getLoginConfig())) { return; } // Perform access control based on the specified role(s) if (!accessControl(request, response, constraint)) { return; } // Any and all specified constraints have been satisfied invokeNext(request, response); } /** * Invoke the next valve in our pipeline, if any. * * @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 */ private void invokeNext(Request request, Response response) throws IOException, ServletException { if (getNext() == null) throw new IllegalStateException (sm.getString("securityValve.noNext")); getNext().invoke(request, response); } // -------------------------------------------------------- Private Methods /** * Perform access control based on the specified authorization constraint. * Return true if this constraint is satisfied and processing * should continue, or false otherwise. * * @param request Request we are processing * @param response Response we are creating * @param constraint Security constraint we are enforcing * * @exception IOException if an input/output error occurs */ private boolean accessControl(Request request, Response response, SecurityConstraint constraint) throws IOException { // Which user principal have we already authenticated? Principal principal = ((HttpServletRequest) request.getRequest()).getUserPrincipal(); if (principal == null) { ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString("securityValve.missingMethod")); return (false); } // Check each role included in this constraint Realm realm = context.getRealm(); String roles[] = constraint.findAuthRoles(); if (roles == null) roles = new String[0]; if (roles.length == 0) return (true); // Authenticated user is sufficient for (int i = 0; i < roles.length; i++) { if (realm.hasRole(principal, roles[i])) return (true); } // Return a "Forbidden" message denying access to this resource ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_FORBIDDEN, sm.getString("securityValve.forbidden")); return (false); } /** * Authenticate the user making this request, based on the specified * login configuration. Return true if any specified * constraint has been satisfied, or false if we have * created a response challenge already. * * @param request Request we are processing * @param response Response we are creating * @param login LoginConfig describing how authentication * should be performed * * @exception IOException if an input/output error occurs */ private boolean authenticate(Request request, Response response, LoginConfig config) throws IOException { // Has a login configuration element been specified? if (config == null) return (true); // Have we already authenticated someone? Principal principal = ((HttpServletRequest) request.getRequest()).getUserPrincipal(); if (principal != null) return (true); // Identify the requested (or default) login mechanism String method = config.getAuthMethod(); if (method == null) method = Constants.BASIC_METHOD; // XXX - Is this default correct? // Apply the requested login mechanism if (method.equals(Constants.BASIC_METHOD)) return (HttpBasicAuth.authenticate(request, response, config, context.getRealm())); /* else if (method.equals(Constants.CERT_METHOD)) return (ClientCertAuth.authenticate(request, response, config, context.getRealm())); else if (method.equals(Constants.DIGEST_METHOD)) return (HttpDigestAuth.authenticate(request, response, config, context.getRealm())); else if (method.equals(Constants.FORM_METHOD)) return (LoginFormAuth.authenticate(request, response, config, context.getRealm())); */ else { ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString("securityValve.unknownMethod", method)); return (false); } } /** * Enforce any user data constraint required by the security constraint * guarding this request URI. Return true if this constraint * was not violated and processing should continue, or false * if we have created a response already. * * @param request Request we are processing * @param response Response we are creating * @param constraint Security constraint being checked * * @exception IOException if an input/output error occurs */ private boolean checkUserData(Request request, Response response, SecurityConstraint constraint) throws IOException { String userConstraint = constraint.getUserConstraint(); if (userConstraint == null) return (true); if (userConstraint.equals(Constants.NONE_TRANSPORT)) return (true); if (!request.getRequest().isSecure()) { ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_BAD_REQUEST, sm.getString("securityValve.userDataConstraint")); return (false); } return (true); } /** * Return the SecurityConstraint configured to guard the request URI for * this request, or null if there is no such constraint. * * @param request Request we are processing */ private SecurityConstraint findConstraint(Request request) { // Are there any defined security constraints? SecurityConstraint constraints[] = context.findConstraints(); if ((constraints == null) || (constraints.length == 0)) return (null); // Check each defined security constraint HttpServletRequest hreq = (HttpServletRequest) request.getRequest(); String uri = hreq.getRequestURI(); String contextPath = hreq.getContextPath(); if (contextPath.length() > 0) uri = uri.substring(contextPath.length()); String method = hreq.getMethod(); for (int i = 0; i < constraints.length; i++) { if (constraints[i].included(uri, method)) return (constraints[i]); } // No applicable security constraint was found return (null); } // ------------------------------------------------------ Lifecycle Methods /** * Add a lifecycle event listener to this component. * * @param listener The listener to add */ public void addLifecycleListener(LifecycleListener listener) { lifecycle.addLifecycleListener(listener); } /** * 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 configure(), * and before any of the public methods of the component are utilized. * * @exception IllegalStateException if this component has already been * started * @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("securityValve.alreadyStarted")); started = true; lifecycle.fireLifecycleEvent(START_EVENT, null); } /** * 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 IllegalStateException if this component has not been started * @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("securityValve.notStarted")); started = false; lifecycle.fireLifecycleEvent(STOP_EVENT, null); } }