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.valves;
018:
019: import java.io.IOException;
020: import java.util.ArrayList;
021:
022: import javax.servlet.ServletException;
023: import javax.servlet.ServletResponse;
024: import javax.servlet.http.HttpServletResponse;
025:
026: import org.apache.catalina.Request;
027: import org.apache.catalina.Response;
028: import org.apache.catalina.ValveContext;
029: import org.apache.catalina.util.StringManager;
030: import org.apache.regexp.RE;
031: import org.apache.regexp.RESyntaxException;
032: import org.apache.tomcat.util.compat.JdkCompat;
033:
034: /**
035: * Implementation of a Valve that performs filtering based on comparing the
036: * appropriate request property (selected based on which subclass you choose
037: * to configure into your Container's pipeline) against a set of regular
038: * expressions configured for this Valve.
039: * <p>
040: * This valve is configured by setting the <code>allow</code> and/or
041: * <code>deny</code> properties to a comma-delimited list of regular
042: * expressions (in the syntax supported by the jakarta-regexp library) to
043: * which the appropriate request property will be compared. Evaluation
044: * proceeds as follows:
045: * <ul>
046: * <li>The subclass extracts the request property to be filtered, and
047: * calls the common <code>process()</code> method.
048: * <li>If there are any deny expressions configured, the property will
049: * be compared to each such expression. If a match is found, this
050: * request will be rejected with a "Forbidden" HTTP response.</li>
051: * <li>If there are any allow expressions configured, the property will
052: * be compared to each such expression. If a match is found, this
053: * request will be allowed to pass through to the next Valve in the
054: * current pipeline.</li>
055: * <li>If one or more deny expressions was specified but no allow expressions,
056: * allow this request to pass through (because none of the deny
057: * expressions matched it).
058: * <li>The request will be rejected with a "Forbidden" HTTP response.</li>
059: * </ul>
060: * <p>
061: * This Valve may be attached to any Container, depending on the granularity
062: * of the filtering you wish to perform.
063: *
064: * @author Craig R. McClanahan
065: * @version $Revision: 1.5 $ $Date: 2004/04/15 01:44:09 $
066: */
067:
068: public abstract class RequestFilterValve extends ValveBase {
069:
070: // ----------------------------------------------------- Class Variables
071:
072: /**
073: * JDK compatibility support
074: */
075: private static final JdkCompat jdkCompat = JdkCompat.getJdkCompat();
076:
077: /**
078: * The descriptive information related to this implementation.
079: */
080: private static final String info = "org.apache.catalina.valves.RequestFilterValve/1.0";
081:
082: /**
083: * The StringManager for this package.
084: */
085: protected static StringManager sm = StringManager
086: .getManager(Constants.Package);
087:
088: // ----------------------------------------------------- Instance Variables
089:
090: /**
091: * The comma-delimited set of <code>allow</code> expressions.
092: */
093: protected String allow = null;
094:
095: /**
096: * The set of <code>allow</code> regular expressions we will evaluate.
097: */
098: protected RE allows[] = new RE[0];
099:
100: /**
101: * The set of <code>deny</code> regular expressions we will evaluate.
102: */
103: protected RE denies[] = new RE[0];
104:
105: /**
106: * The comma-delimited set of <code>deny</code> expressions.
107: */
108: protected String deny = null;
109:
110: // ------------------------------------------------------------- Properties
111:
112: /**
113: * Return a comma-delimited set of the <code>allow</code> expressions
114: * configured for this Valve, if any; otherwise, return <code>null</code>.
115: */
116: public String getAllow() {
117:
118: return (this .allow);
119:
120: }
121:
122: /**
123: * Set the comma-delimited set of the <code>allow</code> expressions
124: * configured for this Valve, if any.
125: *
126: * @param allow The new set of allow expressions
127: */
128: public void setAllow(String allow) {
129:
130: this .allow = allow;
131: allows = precalculate(allow);
132:
133: }
134:
135: /**
136: * Return a comma-delimited set of the <code>deny</code> expressions
137: * configured for this Valve, if any; otherwise, return <code>null</code>.
138: */
139: public String getDeny() {
140:
141: return (this .deny);
142:
143: }
144:
145: /**
146: * Set the comma-delimited set of the <code>deny</code> expressions
147: * configured for this Valve, if any.
148: *
149: * @param deny The new set of deny expressions
150: */
151: public void setDeny(String deny) {
152:
153: this .deny = deny;
154: denies = precalculate(deny);
155:
156: }
157:
158: /**
159: * Return descriptive information about this Valve implementation.
160: */
161: public String getInfo() {
162:
163: return (info);
164:
165: }
166:
167: // --------------------------------------------------------- Public Methods
168:
169: /**
170: * Extract the desired request property, and pass it (along with the
171: * specified request and response objects) to the protected
172: * <code>process()</code> method to perform the actual filtering.
173: * This method must be implemented by a concrete subclass.
174: *
175: * @param request The servlet request to be processed
176: * @param response The servlet response to be created
177: * @param context The valve context used to invoke the next valve
178: * in the current processing pipeline
179: *
180: * @exception IOException if an input/output error occurs
181: * @exception ServletException if a servlet error occurs
182: */
183: public abstract void invoke(Request request, Response response,
184: ValveContext context) throws IOException, ServletException;
185:
186: // ------------------------------------------------------ Protected Methods
187:
188: /**
189: * Return an array of regular expression objects initialized from the
190: * specified argument, which must be <code>null</code> or a comma-delimited
191: * list of regular expression patterns.
192: *
193: * @param list The comma-separated list of patterns
194: *
195: * @exception IllegalArgumentException if one of the patterns has
196: * invalid syntax
197: */
198: protected RE[] precalculate(String list) {
199:
200: if (list == null)
201: return (new RE[0]);
202: list = list.trim();
203: if (list.length() < 1)
204: return (new RE[0]);
205: list += ",";
206:
207: ArrayList reList = new ArrayList();
208: while (list.length() > 0) {
209: int comma = list.indexOf(',');
210: if (comma < 0)
211: break;
212: String pattern = list.substring(0, comma).trim();
213: try {
214: reList.add(new RE(pattern));
215: } catch (RESyntaxException e) {
216: IllegalArgumentException iae = new IllegalArgumentException(
217: sm.getString("requestFilterValve.syntax",
218: pattern));
219: jdkCompat.chainException(iae, e);
220: throw iae;
221: }
222: list = list.substring(comma + 1);
223: }
224:
225: RE reArray[] = new RE[reList.size()];
226: return ((RE[]) reList.toArray(reArray));
227:
228: }
229:
230: /**
231: * Perform the filtering that has been configured for this Valve, matching
232: * against the specified request property.
233: *
234: * @param property The request property on which to filter
235: * @param request The servlet request to be processed
236: * @param response The servlet response to be processed
237: * @param context The valve context used to invoke the next valve
238: * in the current processing pipeline
239: *
240: * @exception IOException if an input/output error occurs
241: * @exception ServletException if a servlet error occurs
242: */
243: protected void process(String property, Request request,
244: Response response, ValveContext context)
245: throws IOException, ServletException {
246:
247: // Check the deny patterns, if any
248: for (int i = 0; i < denies.length; i++) {
249: if (denies[i].match(property)) {
250: ServletResponse sres = response.getResponse();
251: if (sres instanceof HttpServletResponse) {
252: HttpServletResponse hres = (HttpServletResponse) sres;
253: hres.sendError(HttpServletResponse.SC_FORBIDDEN);
254: return;
255: }
256: }
257: }
258:
259: // Check the allow patterns, if any
260: for (int i = 0; i < allows.length; i++) {
261: if (allows[i].match(property)) {
262: context.invokeNext(request, response);
263: return;
264: }
265: }
266:
267: // Allow if denies specified but not allows
268: if ((denies.length > 0) && (allows.length == 0)) {
269: context.invokeNext(request, response);
270: return;
271: }
272:
273: // Deny this request
274: ServletResponse sres = response.getResponse();
275: if (sres instanceof HttpServletResponse) {
276: HttpServletResponse hres = (HttpServletResponse) sres;
277: hres.sendError(HttpServletResponse.SC_FORBIDDEN);
278: return;
279: }
280:
281: }
282:
283: }
|