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.geronimo.security.util;
019
020 import java.util.Collection;
021 import java.util.HashSet;
022 import java.util.Set;
023
024
025 /**
026 * Utility class for <code>ModuleConfiguration</code>. This class is used to generate qualified patterns, HTTP
027 * method sets, complements of HTTP method sets, and HTTP method sets w/ transport restrictions for URL patterns that
028 * are found in the web deployment descriptor.
029 *
030 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
031 */
032 public class URLPattern {
033 public final static int NA = 0x00;
034 public final static int INTEGRAL = 0x01;
035 public final static int CONFIDENTIAL = 0x02;
036
037 private final URLPatternCheck type;
038 private final String pattern;
039 private final HTTPMethods httpMethods = new HTTPMethods();
040 private int transport;
041 private final HashSet<String> roles = new HashSet<String>();
042
043 /**
044 * Construct an instance of the utility class for <code>WebModuleConfiguration</code>.
045 *
046 * @param pat the URL pattern that this instance is to collect information on
047 * @see "JSR 115, section 3.1.3" Translating Servlet Deployment Descriptors
048 */
049 public URLPattern(String pat) {
050 if (pat == null) throw new java.lang.IllegalArgumentException("URL pattern cannot be null");
051 if (pat.length() == 0) throw new java.lang.IllegalArgumentException("URL pattern cannot be empty");
052
053 if (pat.equals("/") || pat.equals("/*")) {
054 type = DEFAULT;
055 pat = "/";
056 } else if (pat.charAt(0) == '/' && pat.endsWith("/*")) {
057 type = PATH_PREFIX;
058 } else if (pat.charAt(0) == '*') {
059 type = EXTENSION;
060 } else {
061 type = EXACT;
062 }
063 pattern = pat;
064 }
065
066 /**
067 * Get a qualifed URL pattern relative to a particular set of URL patterns. This algorithm is described in
068 * JSR 115, section 3.1.3.1 "Qualified URL Pattern Names".
069 *
070 * @param patterns the set of possible URL patterns that could be used to qualify this pattern
071 * @return a qualifed URL pattern
072 */
073 public String getQualifiedPattern(Set<URLPattern> patterns) {
074 if (type == EXACT) {
075 return pattern;
076 } else {
077 HashSet<String> bucket = new HashSet<String>();
078 StringBuffer result = new StringBuffer(pattern);
079
080 // Collect a set of qualifying patterns, depending on the type of this pattern.
081 for (URLPattern p : patterns) {
082 if (type.check(this, p)) {
083 bucket.add(p.pattern);
084 }
085 }
086
087 // append the set of qualifying patterns
088 for (String aBucket : bucket) {
089 result.append(':');
090 result.append(aBucket);
091 }
092 return result.toString();
093 }
094 }
095
096 /**
097 * Add a method to the union of HTTP methods associated with this URL pattern. An empty string is short hand for
098 * the set of all HTTP methods.
099 *
100 * @param method the HTTP method to be added to the set.
101 */
102 public void addMethod(String method) {
103 httpMethods.add(method);
104 }
105
106 /**
107 * Return the set of HTTP methods that have been associated with this URL pattern.
108 *
109 * @return a set of HTTP methods
110 */
111 public String getMethods() {
112 return httpMethods.getHttpMethods();
113 }
114
115
116 public String getComplementedMethods() {
117 return httpMethods.getComplementedHttpMethods();
118 }
119
120 public HTTPMethods getHTTPMethods() {
121 return httpMethods;
122 }
123
124 public HTTPMethods getComplementedHTTPMethods() {
125 return new HTTPMethods(httpMethods, true);
126 }
127
128 public String getMethodsWithTransport() {
129 return getMethodsWithTransport(httpMethods, transport);
130 }
131
132 public static String getMethodsWithTransport(HTTPMethods methods, int transport) {
133 StringBuffer buffer = methods.getHttpMethodsBuffer();
134
135
136 if (transport != NA) {
137 buffer.append(":");
138
139 if (transport != 0x03) {
140 if (transport == INTEGRAL) {
141 buffer.append("INTEGRAL");
142 } else {
143 buffer.append("CONFIDENTIAL");
144 }
145 }
146 }
147
148 return buffer.toString();
149 }
150
151 public void setTransport(String trans) {
152 switch (transport) {
153 case NA: {
154 if ("INTEGRAL".equals(trans)) {
155 transport = INTEGRAL;
156 } else if ("CONFIDENTIAL".equals(trans)) {
157 transport = CONFIDENTIAL;
158 }
159 break;
160 }
161
162 case INTEGRAL: {
163 if ("CONFIDENTIAL".equals(trans)) {
164 transport = CONFIDENTIAL;
165 }
166 break;
167 }
168 }
169 }
170
171 public int getTransport() {
172 return transport;
173 }
174
175 public void addRole(String role) {
176 roles.add(role);
177 }
178
179 public void addAllRoles(Collection<String> collection) {
180 roles.addAll(collection);
181 }
182
183 public HashSet<String> getRoles() {
184 return roles;
185 }
186
187
188 /**
189 * TODO this is kinda weird without an explanation
190 * @param obj object to compare with
191 * @return if this equals obj
192 */
193 public boolean equals(Object obj) {
194 if (!(obj instanceof URLPattern)) return false;
195
196 URLPattern test = (URLPattern) obj;
197
198 return pattern.equals(test.pattern);
199 }
200
201 public int hashCode() {
202 return pattern.hashCode();
203 }
204
205 boolean matches(URLPattern p) {
206 String test = p.pattern;
207
208 // their pattern values are String equivalent
209 if (pattern.equals(test)) return true;
210
211 return type.matches(pattern, test);
212 }
213
214 private final static URLPatternCheck EXACT = new URLPatternCheck() {
215 public boolean check(URLPattern base, URLPattern test) {
216 return matches(base.pattern, test.pattern);
217 }
218
219 public boolean matches(String base, String test) {
220 return base.equals(test);
221 }
222 };
223
224 private final static URLPatternCheck PATH_PREFIX = new URLPatternCheck() {
225 public boolean check(URLPattern base, URLPattern test) {
226 return ((test.type == PATH_PREFIX || test.type == EXACT)
227 && base.matches(test)
228 && !base.equals(test));
229 }
230
231 /**
232 * This pattern is a path-prefix pattern (that is, it starts with "/" and ends with "/*") and the argument
233 * pattern starts with the substring of this pattern, minus its last 2 characters, and the next character of
234 * the argument pattern, if there is one, is "/"
235 *
236 * @param base the base pattern
237 * @param test the pattern to be tested
238 * @return <code>true</code> if <code>test</code> is matched by <code>base</code>
239 */
240 public boolean matches(String base, String test) {
241 int length = base.length() - 2;
242 if (length > test.length()) return false;
243
244 for (int i = 0; i < length; i++) {
245 if (base.charAt(i) != test.charAt(i)) return false;
246 }
247
248 if (test.length() == length)
249 return true;
250 else if (test.charAt(length) != '/') return false;
251
252 return true;
253 }
254 };
255
256 private final static URLPatternCheck EXTENSION = new URLPatternCheck() {
257 public boolean check(URLPattern base, URLPattern test) {
258 if (test.type == PATH_PREFIX) return true;
259
260 if (test.type == EXACT) return matches(base.pattern, test.pattern);
261
262 return false;
263 }
264
265 /**
266 * This pattern is an extension pattern (that is, it startswith "*.") and the argument pattern ends with
267 * this pattern.
268 *
269 * @param base the base pattern
270 * @param test the pattern to be tested
271 * @return <code>true</code> if <code>test</code> is matched by <code>base</code>
272 */
273 public boolean matches(String base, String test) {
274 return test.endsWith(base.substring(1));
275 }
276 };
277
278 private final static URLPatternCheck DEFAULT = new URLPatternCheck() {
279 public boolean check(URLPattern base, URLPattern test) {
280 return base.matches(test) && !base.equals(test);
281 }
282
283 /**
284 * This pattern is the path-prefix pattern "/*" or the reference pattern is the special default pattern,
285 * "/", which matches all argument patterns.
286 *
287 * @param base the base pattern
288 * @param test the pattern to be tested
289 * @return <code>true</code> if <code>test</code> is matched by <code>base</code>
290 * @see "JSR 115"
291 */
292 public boolean matches(String base, String test) {
293 return true;
294 }
295 };
296 }