001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.valves;
019:
020: import java.io.IOException;
021: import java.util.ArrayList;
022: import java.util.regex.Pattern;
023: import java.util.regex.PatternSyntaxException;
024:
025: import javax.servlet.ServletException;
026: import javax.servlet.http.HttpServletResponse;
027:
028: import org.apache.catalina.connector.Request;
029: import org.apache.catalina.connector.Response;
030: import org.apache.catalina.util.StringManager;
031:
032: /**
033: * Implementation of a Valve that performs filtering based on comparing the
034: * appropriate request property (selected based on which subclass you choose
035: * to configure into your Container's pipeline) against a set of regular
036: * expressions configured for this Valve.
037: * <p>
038: * This valve is configured by setting the <code>allow</code> and/or
039: * <code>deny</code> properties to a comma-delimited list of regular
040: * expressions (in the syntax supported by the jakarta-regexp library) to
041: * which the appropriate request property will be compared. Evaluation
042: * proceeds as follows:
043: * <ul>
044: * <li>The subclass extracts the request property to be filtered, and
045: * calls the common <code>process()</code> method.
046: * <li>If there are any deny expressions configured, the property will
047: * be compared to each such expression. If a match is found, this
048: * request will be rejected with a "Forbidden" HTTP response.</li>
049: * <li>If there are any allow expressions configured, the property will
050: * be compared to each such expression. If a match is found, this
051: * request will be allowed to pass through to the next Valve in the
052: * current pipeline.</li>
053: * <li>If one or more deny expressions was specified but no allow expressions,
054: * allow this request to pass through (because none of the deny
055: * expressions matched it).
056: * <li>The request will be rejected with a "Forbidden" HTTP response.</li>
057: * </ul>
058: * <p>
059: * This Valve may be attached to any Container, depending on the granularity
060: * of the filtering you wish to perform.
061: *
062: * @author Craig R. McClanahan
063: * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
064: */
065:
066: public abstract class RequestFilterValve extends ValveBase {
067:
068: // ----------------------------------------------------- Class Variables
069:
070: /**
071: * The descriptive information related to this implementation.
072: */
073: private static final String info = "org.apache.catalina.valves.RequestFilterValve/1.0";
074:
075: /**
076: * The StringManager for this package.
077: */
078: protected static StringManager sm = StringManager
079: .getManager(Constants.Package);
080:
081: // ----------------------------------------------------- Instance Variables
082:
083: /**
084: * The comma-delimited set of <code>allow</code> expressions.
085: */
086: protected String allow = null;
087:
088: /**
089: * The set of <code>allow</code> regular expressions we will evaluate.
090: */
091: protected Pattern allows[] = new Pattern[0];
092:
093: /**
094: * The set of <code>deny</code> regular expressions we will evaluate.
095: */
096: protected Pattern denies[] = new Pattern[0];
097:
098: /**
099: * The comma-delimited set of <code>deny</code> expressions.
100: */
101: protected String deny = null;
102:
103: // ------------------------------------------------------------- Properties
104:
105: /**
106: * Return a comma-delimited set of the <code>allow</code> expressions
107: * configured for this Valve, if any; otherwise, return <code>null</code>.
108: */
109: public String getAllow() {
110:
111: return (this .allow);
112:
113: }
114:
115: /**
116: * Set the comma-delimited set of the <code>allow</code> expressions
117: * configured for this Valve, if any.
118: *
119: * @param allow The new set of allow expressions
120: */
121: public void setAllow(String allow) {
122:
123: this .allow = allow;
124: allows = precalculate(allow);
125:
126: }
127:
128: /**
129: * Return a comma-delimited set of the <code>deny</code> expressions
130: * configured for this Valve, if any; otherwise, return <code>null</code>.
131: */
132: public String getDeny() {
133:
134: return (this .deny);
135:
136: }
137:
138: /**
139: * Set the comma-delimited set of the <code>deny</code> expressions
140: * configured for this Valve, if any.
141: *
142: * @param deny The new set of deny expressions
143: */
144: public void setDeny(String deny) {
145:
146: this .deny = deny;
147: denies = precalculate(deny);
148:
149: }
150:
151: /**
152: * Return descriptive information about this Valve implementation.
153: */
154: public String getInfo() {
155:
156: return (info);
157:
158: }
159:
160: // --------------------------------------------------------- Public Methods
161:
162: /**
163: * Extract the desired request property, and pass it (along with the
164: * specified request and response objects) to the protected
165: * <code>process()</code> method to perform the actual filtering.
166: * This method must be implemented by a concrete subclass.
167: *
168: * @param request The servlet request to be processed
169: * @param response The servlet response to be created
170: *
171: * @exception IOException if an input/output error occurs
172: * @exception ServletException if a servlet error occurs
173: */
174: public abstract void invoke(Request request, Response response)
175: throws IOException, ServletException;
176:
177: // ------------------------------------------------------ Protected Methods
178:
179: /**
180: * Return an array of regular expression objects initialized from the
181: * specified argument, which must be <code>null</code> or a comma-delimited
182: * list of regular expression patterns.
183: *
184: * @param list The comma-separated list of patterns
185: *
186: * @exception IllegalArgumentException if one of the patterns has
187: * invalid syntax
188: */
189: protected Pattern[] precalculate(String list) {
190:
191: if (list == null)
192: return (new Pattern[0]);
193: list = list.trim();
194: if (list.length() < 1)
195: return (new Pattern[0]);
196: list += ",";
197:
198: ArrayList reList = new ArrayList();
199: while (list.length() > 0) {
200: int comma = list.indexOf(',');
201: if (comma < 0)
202: break;
203: String pattern = list.substring(0, comma).trim();
204: try {
205: reList.add(Pattern.compile(pattern));
206: } catch (PatternSyntaxException e) {
207: IllegalArgumentException iae = new IllegalArgumentException(
208: sm.getString("requestFilterValve.syntax",
209: pattern));
210: iae.initCause(e);
211: throw iae;
212: }
213: list = list.substring(comma + 1);
214: }
215:
216: Pattern reArray[] = new Pattern[reList.size()];
217: return ((Pattern[]) reList.toArray(reArray));
218:
219: }
220:
221: /**
222: * Perform the filtering that has been configured for this Valve, matching
223: * against the specified request property.
224: *
225: * @param property The request property on which to filter
226: * @param request The servlet request to be processed
227: * @param response The servlet response to be processed
228: *
229: * @exception IOException if an input/output error occurs
230: * @exception ServletException if a servlet error occurs
231: */
232: protected void process(String property, Request request,
233: Response response) throws IOException, ServletException {
234:
235: // Check the deny patterns, if any
236: for (int i = 0; i < denies.length; i++) {
237: if (denies[i].matcher(property).matches()) {
238: response.sendError(HttpServletResponse.SC_FORBIDDEN);
239: return;
240: }
241: }
242:
243: // Check the allow patterns, if any
244: for (int i = 0; i < allows.length; i++) {
245: if (allows[i].matcher(property).matches()) {
246: getNext().invoke(request, response);
247: return;
248: }
249: }
250:
251: // Allow if denies specified but not allows
252: if ((denies.length > 0) && (allows.length == 0)) {
253: getNext().invoke(request, response);
254: return;
255: }
256:
257: // Deny this request
258: response.sendError(HttpServletResponse.SC_FORBIDDEN);
259:
260: }
261:
262: }
|