001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/valves/RequestFilterValve.java,v 1.4 2001/07/22 20:25:15 pier Exp $
003: * $Revision: 1.4 $
004: * $Date: 2001/07/22 20:25:15 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 1999-2001 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: * [Additional notices, if required by prior licensing conditions]
061: *
062: */
063:
064: package org.apache.catalina.valves;
065:
066: import java.io.IOException;
067: import java.util.ArrayList;
068: import javax.servlet.ServletException;
069: import javax.servlet.ServletRequest;
070: import javax.servlet.ServletResponse;
071: import javax.servlet.http.HttpServletResponse;
072: import org.apache.regexp.RE;
073: import org.apache.regexp.RESyntaxException;
074: import org.apache.catalina.Container;
075: import org.apache.catalina.Request;
076: import org.apache.catalina.Response;
077: import org.apache.catalina.Valve;
078: import org.apache.catalina.ValveContext;
079: import org.apache.catalina.util.StringManager;
080:
081: /**
082: * Implementation of a Valve that performs filtering based on comparing the
083: * appropriate request property (selected based on which subclass you choose
084: * to configure into your Container's pipeline) against a set of regular
085: * expressions configured for this Valve.
086: * <p>
087: * This valve is configured by setting the <code>allow</code> and/or
088: * <code>deny</code> properties to a comma-delimited list of regular
089: * expressions (in the syntax supported by the jakarta-regexp library) to
090: * which the appropriate request property will be compared. Evaluation
091: * proceeds as follows:
092: * <ul>
093: * <li>The subclass extracts the request property to be filtered, and
094: * calls the common <code>process()</code> method.
095: * <li>If there are any deny expressions configured, the property will
096: * be compared to each such expression. If a match is found, this
097: * request will be rejected with a "Forbidden" HTTP response.</li>
098: * <li>If there are any allow expressions configured, the property will
099: * be compared to each such expression. If a match is found, this
100: * request will be allowed to pass through to the next Valve in the
101: * current pipeline.</li>
102: * <li>If one or more deny expressions was specified but no allow expressions,
103: * allow this request to pass through (because none of the deny
104: * expressions matched it).
105: * <li>The request will be rejected with a "Forbidden" HTTP response.</li>
106: * </ul>
107: * <p>
108: * This Valve may be attached to any Container, depending on the granularity
109: * of the filtering you wish to perform.
110: *
111: * @author Craig R. McClanahan
112: * @version $Revision: 1.4 $ $Date: 2001/07/22 20:25:15 $
113: */
114:
115: public abstract class RequestFilterValve extends ValveBase {
116:
117: // ----------------------------------------------------- Instance Variables
118:
119: /**
120: * The comma-delimited set of <code>allow</code> expressions.
121: */
122: protected String allow = null;
123:
124: /**
125: * The set of <code>allow</code> regular expressions we will evaluate.
126: */
127: protected RE allows[] = new RE[0];
128:
129: /**
130: * The set of <code>deny</code> regular expressions we will evaluate.
131: */
132: protected RE denies[] = new RE[0];
133:
134: /**
135: * The comma-delimited set of <code>deny</code> expressions.
136: */
137: protected String deny = null;
138:
139: /**
140: * The descriptive information related to this implementation.
141: */
142: private static final String info = "org.apache.catalina.valves.RequestFilterValve/1.0";
143:
144: /**
145: * The StringManager for this package.
146: */
147: protected static StringManager sm = StringManager
148: .getManager(Constants.Package);
149:
150: // ------------------------------------------------------------- Properties
151:
152: /**
153: * Return a comma-delimited set of the <code>allow</code> expressions
154: * configured for this Valve, if any; otherwise, return <code>null</code>.
155: */
156: public String getAllow() {
157:
158: return (this .allow);
159:
160: }
161:
162: /**
163: * Set the comma-delimited set of the <code>allow</code> expressions
164: * configured for this Valve, if any.
165: *
166: * @param allow The new set of allow expressions
167: */
168: public void setAllow(String allow) {
169:
170: this .allow = allow;
171: allows = precalculate(allow);
172:
173: }
174:
175: /**
176: * Return a comma-delimited set of the <code>deny</code> expressions
177: * configured for this Valve, if any; otherwise, return <code>null</code>.
178: */
179: public String getDeny() {
180:
181: return (this .deny);
182:
183: }
184:
185: /**
186: * Set the comma-delimited set of the <code>deny</code> expressions
187: * configured for this Valve, if any.
188: *
189: * @param deny The new set of deny expressions
190: */
191: public void setDeny(String deny) {
192:
193: this .deny = deny;
194: denies = precalculate(deny);
195:
196: }
197:
198: /**
199: * Return descriptive information about this Valve implementation.
200: */
201: public String getInfo() {
202:
203: return (info);
204:
205: }
206:
207: // --------------------------------------------------------- Public Methods
208:
209: /**
210: * Extract the desired request property, and pass it (along with the
211: * specified request and response objects) to the protected
212: * <code>process()</code> method to perform the actual filtering.
213: * This method must be implemented by a concrete subclass.
214: *
215: * @param request The servlet request to be processed
216: * @param response The servlet response to be created
217: * @param context The valve context used to invoke the next valve
218: * in the current processing pipeline
219: *
220: * @exception IOException if an input/output error occurs
221: * @exception ServletException if a servlet error occurs
222: */
223: public abstract void invoke(Request request, Response response,
224: ValveContext context) throws IOException, ServletException;
225:
226: // ------------------------------------------------------ Protected Methods
227:
228: /**
229: * Return an array of regular expression objects initialized from the
230: * specified argument, which must be <code>null</code> or a comma-delimited
231: * list of regular expression patterns.
232: *
233: * @param list The comma-separated list of patterns
234: *
235: * @exception IllegalArgumentException if one of the patterns has
236: * invalid syntax
237: */
238: protected RE[] precalculate(String list) {
239:
240: if (list == null)
241: return (new RE[0]);
242: list = list.trim();
243: if (list.length() < 1)
244: return (new RE[0]);
245: list += ",";
246:
247: ArrayList reList = new ArrayList();
248: while (list.length() > 0) {
249: int comma = list.indexOf(',');
250: if (comma < 0)
251: break;
252: String pattern = list.substring(0, comma).trim();
253: try {
254: reList.add(new RE(pattern));
255: } catch (RESyntaxException e) {
256: throw new IllegalArgumentException(sm.getString(
257: "requestFilterValve.syntax", pattern));
258: }
259: list = list.substring(comma + 1);
260: }
261:
262: RE reArray[] = new RE[reList.size()];
263: return ((RE[]) reList.toArray(reArray));
264:
265: }
266:
267: /**
268: * Perform the filtering that has been configured for this Valve, matching
269: * against the specified request property.
270: *
271: * @param property The request property on which to filter
272: * @param request The servlet request to be processed
273: * @param response The servlet response to be processed
274: * @param context The valve context used to invoke the next valve
275: * in the current processing pipeline
276: *
277: * @exception IOException if an input/output error occurs
278: * @exception ServletException if a servlet error occurs
279: */
280: protected void process(String property, Request request,
281: Response response, ValveContext context)
282: throws IOException, ServletException {
283:
284: // Check the deny patterns, if any
285: for (int i = 0; i < denies.length; i++) {
286: if (denies[i].match(property)) {
287: ServletResponse sres = response.getResponse();
288: if (sres instanceof HttpServletResponse) {
289: HttpServletResponse hres = (HttpServletResponse) sres;
290: hres.sendError(HttpServletResponse.SC_FORBIDDEN);
291: return;
292: }
293: }
294: }
295:
296: // Check the allow patterns, if any
297: for (int i = 0; i < allows.length; i++) {
298: if (allows[i].match(property)) {
299: context.invokeNext(request, response);
300: return;
301: }
302: }
303:
304: // Allow if denies specified but not allows
305: if ((denies.length > 0) && (allows.length == 0)) {
306: context.invokeNext(request, response);
307: return;
308: }
309:
310: // Deny this request
311: ServletResponse sres = response.getResponse();
312: if (sres instanceof HttpServletResponse) {
313: HttpServletResponse hres = (HttpServletResponse) sres;
314: hres.sendError(HttpServletResponse.SC_FORBIDDEN);
315: return;
316: }
317:
318: }
319:
320: }
|