001 /** 002 * 003 * Copyright 2003-2004 The Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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.Iterator; 023 import java.util.Set; 024 025 026 /** 027 * Utility class for <code>ModuleConfiguration</code>. This class is used to generate qualified patterns, HTTP 028 * method sets, complements of HTTP method sets, and HTTP method sets w/ transport restrictions for URL patterns that 029 * are found in the web deployment descriptor. 030 * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $ 031 */ 032 public class URLPattern { 033 private final static String[] HTTP_METHODS = {"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE"}; 034 private final static int[] HTTP_MASKS = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40}; 035 private final static int NA = 0x00; 036 private final static int INTEGRAL = 0x01; 037 private final static int CONFIDENTIAL = 0x02; 038 039 private final URLPatternCheck type; 040 private final String pattern; 041 private int httpMethodsMask; 042 private int transport; 043 private final HashSet roles = new HashSet(); 044 045 /** 046 * Construct an instance of the utility class for <code>WebModuleConfiguration</code>. 047 * @param pat the URL pattern that this instance is to collect information on 048 * @see "JSR 115, section 3.1.3" Translating Servlet Deployment Descriptors 049 */ 050 public URLPattern(String pat) { 051 if (pat == null) throw new java.lang.IllegalArgumentException("URL pattern cannot be null"); 052 if (pat.length() == 0) throw new java.lang.IllegalArgumentException("URL pattern cannot be empty"); 053 054 if (pat.equals("/") || pat.equals("/*")) { 055 type = DEFAULT; 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 * @param patterns the set of possible URL patterns that could be used to qualify this pattern 070 * @return a qualifed URL pattern 071 */ 072 public String getQualifiedPattern(Set patterns) { 073 if (type == EXACT) { 074 return pattern; 075 } else { 076 HashSet bucket = new HashSet(); 077 StringBuffer result = new StringBuffer(pattern); 078 Iterator iter = patterns.iterator(); 079 080 // Collect a set of qualifying patterns, depending on the type of this pattern. 081 while (iter.hasNext()) { 082 URLPattern p = (URLPattern) iter.next(); 083 if (type.check(this, p)) { 084 bucket.add(p.pattern); 085 } 086 } 087 088 // append the set of qualifying patterns 089 iter = bucket.iterator(); 090 while (iter.hasNext()) { 091 result.append(':'); 092 result.append((String) iter.next()); 093 } 094 return result.toString(); 095 } 096 } 097 098 /** 099 * 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 }