1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.catalina.valves;
20
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.regex.Pattern;
25 import java.util.regex.PatternSyntaxException;
26
27 import javax.servlet.ServletException;
28 import javax.servlet.http.HttpServletResponse;
29
30 import org.apache.catalina.connector.Request;
31 import org.apache.catalina.connector.Response;
32 import org.apache.catalina.util.StringManager;
33
34 /**
35 * Implementation of a Valve that performs filtering based on comparing the
36 * appropriate request property (selected based on which subclass you choose
37 * to configure into your Container's pipeline) against a set of regular
38 * expressions configured for this Valve.
39 * <p>
40 * This valve is configured by setting the <code>allow</code> and/or
41 * <code>deny</code> properties to a comma-delimited list of regular
42 * expressions (in the syntax supported by the jakarta-regexp library) to
43 * which the appropriate request property will be compared. Evaluation
44 * proceeds as follows:
45 * <ul>
46 * <li>The subclass extracts the request property to be filtered, and
47 * calls the common <code>process()</code> method.
48 * <li>If there are any deny expressions configured, the property will
49 * be compared to each such expression. If a match is found, this
50 * request will be rejected with a "Forbidden" HTTP response.</li>
51 * <li>If there are any allow expressions configured, the property will
52 * be compared to each such expression. If a match is found, this
53 * request will be allowed to pass through to the next Valve in the
54 * current pipeline.</li>
55 * <li>If one or more deny expressions was specified but no allow expressions,
56 * allow this request to pass through (because none of the deny
57 * expressions matched it).
58 * <li>The request will be rejected with a "Forbidden" HTTP response.</li>
59 * </ul>
60 * <p>
61 * This Valve may be attached to any Container, depending on the granularity
62 * of the filtering you wish to perform.
63 *
64 * @author Craig R. McClanahan
65 * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
66 */
67
68 public abstract class RequestFilterValve
69 extends ValveBase {
70
71
72 // ----------------------------------------------------- Class Variables
73
74
75 /**
76 * The descriptive information related to this implementation.
77 */
78 private static final String info =
79 "org.apache.catalina.valves.RequestFilterValve/1.0";
80
81
82 /**
83 * The StringManager for this package.
84 */
85 protected static StringManager sm =
86 StringManager.getManager(Constants.Package);
87
88
89 // ----------------------------------------------------- Instance Variables
90
91
92 /**
93 * The comma-delimited set of <code>allow</code> expressions.
94 */
95 protected String allow = null;
96
97
98 /**
99 * The set of <code>allow</code> regular expressions we will evaluate.
100 */
101 protected Pattern allows[] = new Pattern[0];
102
103
104 /**
105 * The set of <code>deny</code> regular expressions we will evaluate.
106 */
107 protected Pattern denies[] = new Pattern[0];
108
109
110 /**
111 * The comma-delimited set of <code>deny</code> expressions.
112 */
113 protected String deny = null;
114
115
116 // ------------------------------------------------------------- Properties
117
118
119 /**
120 * Return a comma-delimited set of the <code>allow</code> expressions
121 * configured for this Valve, if any; otherwise, return <code>null</code>.
122 */
123 public String getAllow() {
124
125 return (this.allow);
126
127 }
128
129
130 /**
131 * Set the comma-delimited set of the <code>allow</code> expressions
132 * configured for this Valve, if any.
133 *
134 * @param allow The new set of allow expressions
135 */
136 public void setAllow(String allow) {
137
138 this.allow = allow;
139 allows = precalculate(allow);
140
141 }
142
143
144 /**
145 * Return a comma-delimited set of the <code>deny</code> expressions
146 * configured for this Valve, if any; otherwise, return <code>null</code>.
147 */
148 public String getDeny() {
149
150 return (this.deny);
151
152 }
153
154
155 /**
156 * Set the comma-delimited set of the <code>deny</code> expressions
157 * configured for this Valve, if any.
158 *
159 * @param deny The new set of deny expressions
160 */
161 public void setDeny(String deny) {
162
163 this.deny = deny;
164 denies = precalculate(deny);
165
166 }
167
168
169 /**
170 * Return descriptive information about this Valve implementation.
171 */
172 public String getInfo() {
173
174 return (info);
175
176 }
177
178
179 // --------------------------------------------------------- Public Methods
180
181
182 /**
183 * Extract the desired request property, and pass it (along with the
184 * specified request and response objects) to the protected
185 * <code>process()</code> method to perform the actual filtering.
186 * This method must be implemented by a concrete subclass.
187 *
188 * @param request The servlet request to be processed
189 * @param response The servlet response to be created
190 *
191 * @exception IOException if an input/output error occurs
192 * @exception ServletException if a servlet error occurs
193 */
194 public abstract void invoke(Request request, Response response)
195 throws IOException, ServletException;
196
197
198 // ------------------------------------------------------ Protected Methods
199
200
201 /**
202 * Return an array of regular expression objects initialized from the
203 * specified argument, which must be <code>null</code> or a comma-delimited
204 * list of regular expression patterns.
205 *
206 * @param list The comma-separated list of patterns
207 *
208 * @exception IllegalArgumentException if one of the patterns has
209 * invalid syntax
210 */
211 protected Pattern[] precalculate(String list) {
212
213 if (list == null)
214 return (new Pattern[0]);
215 list = list.trim();
216 if (list.length() < 1)
217 return (new Pattern[0]);
218 list += ",";
219
220 ArrayList reList = new ArrayList();
221 while (list.length() > 0) {
222 int comma = list.indexOf(',');
223 if (comma < 0)
224 break;
225 String pattern = list.substring(0, comma).trim();
226 try {
227 reList.add(Pattern.compile(pattern));
228 } catch (PatternSyntaxException e) {
229 IllegalArgumentException iae = new IllegalArgumentException
230 (sm.getString("requestFilterValve.syntax", pattern));
231 iae.initCause(e);
232 throw iae;
233 }
234 list = list.substring(comma + 1);
235 }
236
237 Pattern reArray[] = new Pattern[reList.size()];
238 return ((Pattern[]) reList.toArray(reArray));
239
240 }
241
242
243 /**
244 * Perform the filtering that has been configured for this Valve, matching
245 * against the specified request property.
246 *
247 * @param property The request property on which to filter
248 * @param request The servlet request to be processed
249 * @param response The servlet response to be processed
250 *
251 * @exception IOException if an input/output error occurs
252 * @exception ServletException if a servlet error occurs
253 */
254 protected void process(String property,
255 Request request, Response response)
256 throws IOException, ServletException {
257
258 // Check the deny patterns, if any
259 for (int i = 0; i < denies.length; i++) {
260 if (denies[i].matcher(property).matches()) {
261 response.sendError(HttpServletResponse.SC_FORBIDDEN);
262 return;
263 }
264 }
265
266 // Check the allow patterns, if any
267 for (int i = 0; i < allows.length; i++) {
268 if (allows[i].matcher(property).matches()) {
269 getNext().invoke(request, response);
270 return;
271 }
272 }
273
274 // Allow if denies specified but not allows
275 if ((denies.length > 0) && (allows.length == 0)) {
276 getNext().invoke(request, response);
277 return;
278 }
279
280 // Deny this request
281 response.sendError(HttpServletResponse.SC_FORBIDDEN);
282
283 }
284
285
286 }