1 /**
2 *
3 * Copyright 2003-2004 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * 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 package org.apache.geronimo.security.util;
19
20 import java.util.Collection;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.Set;
24
25
26 /**
27 * Utility class for <code>ModuleConfiguration</code>. This class is used to generate qualified patterns, HTTP
28 * method sets, complements of HTTP method sets, and HTTP method sets w/ transport restrictions for URL patterns that
29 * are found in the web deployment descriptor.
30 * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $
31 */
32 public class URLPattern {
33 private final static String[] HTTP_METHODS = {"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE"};
34 private final static int[] HTTP_MASKS = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40};
35 private final static int NA = 0x00;
36 private final static int INTEGRAL = 0x01;
37 private final static int CONFIDENTIAL = 0x02;
38
39 private final URLPatternCheck type;
40 private final String pattern;
41 private int httpMethodsMask;
42 private int transport;
43 private final HashSet roles = new HashSet();
44
45 /**
46 * Construct an instance of the utility class for <code>WebModuleConfiguration</code>.
47 * @param pat the URL pattern that this instance is to collect information on
48 * @see "JSR 115, section 3.1.3" Translating Servlet Deployment Descriptors
49 */
50 public URLPattern(String pat) {
51 if (pat == null) throw new java.lang.IllegalArgumentException("URL pattern cannot be null");
52 if (pat.length() == 0) throw new java.lang.IllegalArgumentException("URL pattern cannot be empty");
53
54 if (pat.equals("/") || pat.equals("/*")) {
55 type = DEFAULT;
56 } else if (pat.charAt(0) == '/' && pat.endsWith("/*")) {
57 type = PATH_PREFIX;
58 } else if (pat.charAt(0) == '*') {
59 type = EXTENSION;
60 } else {
61 type = EXACT;
62 }
63 pattern = pat;
64 }
65
66 /**
67 * Get a qualifed URL pattern relative to a particular set of URL patterns. This algorithm is described in
68 * JSR 115, section 3.1.3.1 "Qualified URL Pattern Names".
69 * @param patterns the set of possible URL patterns that could be used to qualify this pattern
70 * @return a qualifed URL pattern
71 */
72 public String getQualifiedPattern(Set patterns) {
73 if (type == EXACT) {
74 return pattern;
75 } else {
76 HashSet bucket = new HashSet();
77 StringBuffer result = new StringBuffer(pattern);
78 Iterator iter = patterns.iterator();
79
80
81 while (iter.hasNext()) {
82 URLPattern p = (URLPattern) iter.next();
83 if (type.check(this, p)) {
84 bucket.add(p.pattern);
85 }
86 }
87
88
89 iter = bucket.iterator();
90 while (iter.hasNext()) {
91 result.append(':');
92 result.append((String) iter.next());
93 }
94 return result.toString();
95 }
96 }
97
98 /**
99 * Add a method to the union of HTTP methods associated with this URL pattern. An empty string is short hand for
100 * the set of all HTTP methods.
101 * @param method the HTTP method to be added to the set.
102 */
103 public void addMethod(String method) {
104 if (method.length() == 0) {
105 httpMethodsMask = 0xFF;
106 return;
107 }
108
109 boolean found = false;
110 for (int j = 0; j < HTTP_METHODS.length; j++) {
111 if (method.equals(HTTP_METHODS[j])) {
112 httpMethodsMask |= HTTP_MASKS[j];
113 found = true;
114
115 break;
116 }
117 }
118 if (!found) throw new IllegalArgumentException("Invalid HTTP method");
119 }
120
121 /**
122 * Return the set of HTTP methods that have been associated with this URL pattern.
123 * @return a set of HTTP methods
124 */
125 public String getMethods() {
126 StringBuffer buffer = null;
127
128 for (int i = 0; i < HTTP_MASKS.length; i++) {
129 if ((httpMethodsMask & HTTP_MASKS[i]) > 0) {
130 if (buffer == null) {
131 buffer = new StringBuffer();
132 } else {
133 buffer.append(",");
134 }
135 buffer.append(HTTP_METHODS[i]);
136 }
137 }
138
139 return (buffer == null ? "" : buffer.toString());
140 }
141
142 public String getComplementedMethods() {
143 StringBuffer buffer = null;
144
145 for (int i = 0; i < HTTP_MASKS.length; i++) {
146 if ((httpMethodsMask & HTTP_MASKS[i]) == 0) {
147 if (buffer == null) {
148 buffer = new StringBuffer();
149 } else {
150 buffer.append(",");
151 }
152 buffer.append(HTTP_METHODS[i]);
153 }
154 }
155
156 return (buffer == null ? "" : buffer.toString());
157 }
158
159 public String getMethodsWithTransport() {
160 StringBuffer buffer = new StringBuffer(getMethods());
161
162
163 if (transport != NA) {
164 buffer.append(":");
165
166 if (transport != 0x03) {
167 if (transport == INTEGRAL) {
168 buffer.append("INTEGRAL");
169 } else {
170 buffer.append("CONFIDENTIAL");
171 }
172 }
173 }
174
175 return buffer.toString();
176 }
177
178 public void setTransport(String trans) {
179 switch (transport) {
180 case NA:
181 {
182 if ("INTEGRAL".equals(trans)) {
183 transport = INTEGRAL;
184 } else if ("CONFIDENTIAL".equals(trans)) {
185 transport = CONFIDENTIAL;
186 }
187 break;
188 }
189
190 case INTEGRAL:
191 {
192 if ("CONFIDENTIAL".equals(trans)) {
193 transport = CONFIDENTIAL;
194 }
195 break;
196 }
197 }
198 }
199
200 public void addRole(String role) {
201 roles.add(role);
202 }
203
204 public void addAllRoles(Collection collection) {
205 roles.addAll(collection);
206 }
207
208 public HashSet getRoles() {
209 return roles;
210 }
211
212 public boolean equals(Object obj) {
213 if (!(obj instanceof URLPattern)) return false;
214
215 URLPattern test = (URLPattern) obj;
216
217 return pattern.equals(test.pattern);
218 }
219
220 public int hashCode() {
221 return pattern.hashCode();
222 }
223
224 boolean matches(URLPattern p) {
225 String test = p.pattern;
226
227
228 if (pattern.equals(test)) return true;
229
230 return type.matches(pattern, test);
231 }
232
233 private final static URLPatternCheck EXACT = new URLPatternCheck() {
234 public boolean check(URLPattern base, URLPattern test) {
235 return matches(base.pattern, test.pattern);
236 }
237
238 public boolean matches(String base, String test) {
239 return base.equals(test);
240 }
241 };
242
243 private final static URLPatternCheck PATH_PREFIX = new URLPatternCheck() {
244 public boolean check(URLPattern base, URLPattern test) {
245 return ((test.type == PATH_PREFIX || test.type == EXACT)
246 && base.matches(test)
247 && !base.equals(test));
248 }
249
250 /**
251 * This pattern is a path-prefix pattern (that is, it starts with "/" and ends with "/*") and the argument
252 * pattern starts with the substring of this pattern, minus its last 2 characters, and the next character of
253 * the argument pattern, if there is one, is "/"
254 * @param base the base pattern
255 * @param test the pattern to be tested
256 * @return <code>true</code> if <code>test</code> is matched by <code>base</code>
257 */
258 public boolean matches(String base, String test) {
259 int length = base.length() - 2;
260 if (length > test.length()) return false;
261
262 for (int i = 0; i < length; i++) {
263 if (base.charAt(i) != test.charAt(i)) return false;
264 }
265
266 if (test.length() == length)
267 return true;
268 else if (test.charAt(length) != '/') return false;
269
270 return true;
271 }
272 };
273
274 private final static URLPatternCheck EXTENSION = new URLPatternCheck() {
275 public boolean check(URLPattern base, URLPattern test) {
276 if (test.type == PATH_PREFIX) return true;
277
278 if (test.type == EXACT) return matches(base.pattern, test.pattern);
279
280 return false;
281 }
282
283 /**
284 * This pattern is an extension pattern (that is, it startswith "*.") and the argument pattern ends with
285 * this pattern.
286 * @param base the base pattern
287 * @param test the pattern to be tested
288 * @return <code>true</code> if <code>test</code> is matched by <code>base</code>
289 */
290 public boolean matches(String base, String test) {
291 return test.endsWith(base.substring(1));
292 }
293 };
294
295 private final static URLPatternCheck DEFAULT = new URLPatternCheck() {
296 public boolean check(URLPattern base, URLPattern test) {
297 return base.matches(test) && !base.equals(test);
298 }
299
300 /**
301 * This pattern is the path-prefix pattern "/*" or the reference pattern is the special default pattern,
302 * "/", which matches all argument patterns.
303 * @param base the base pattern
304 * @param test the pattern to be tested
305 * @return <code>true</code> if <code>test</code> is matched by <code>base</code>
306 * @see "JSR 115"
307 */
308 public boolean matches(String base, String test) {
309 return true;
310 }
311 };
312 }