View Javadoc

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              // Collect a set of qualifying patterns, depending on the type of this pattern.
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              // append the set of qualifying patterns
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         // their pattern values are String equivalent
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 }