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: 497904 $ $Date: 2007-01-19 13:49:13 -0500 (Fri, 19 Jan 2007) $ 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 }