/** * * Copyright 2003-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.geronimo.security.util; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * Utility class for <code>ModuleConfiguration</code>. This class is used to generate qualified patterns, HTTP * method sets, complements of HTTP method sets, and HTTP method sets w/ transport restrictions for URL patterns that * are found in the web deployment descriptor. * @version $Rev$ $Date$ */ public class URLPattern { private final static String[] HTTP_METHODS = {"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE"}; private final static int[] HTTP_MASKS = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40}; private final static int NA = 0x00; private final static int INTEGRAL = 0x01; private final static int CONFIDENTIAL = 0x02; private final URLPatternCheck type; private final String pattern; private int httpMethodsMask; private int transport; private final HashSet roles = new HashSet(); /** * Construct an instance of the utility class for <code>WebModuleConfiguration</code>. * @param pat the URL pattern that this instance is to collect information on * @see "JSR 115, section 3.1.3" Translating Servlet Deployment Descriptors */ public URLPattern(String pat) { if (pat == null) throw new java.lang.IllegalArgumentException("URL pattern cannot be null"); if (pat.length() == 0) throw new java.lang.IllegalArgumentException("URL pattern cannot be empty"); if (pat.equals("/") || pat.equals("/*")) { type = DEFAULT; } else if (pat.charAt(0) == '/' && pat.endsWith("/*")) { type = PATH_PREFIX; } else if (pat.charAt(0) == '*') { type = EXTENSION; } else { type = EXACT; } pattern = pat; } /** * Get a qualifed URL pattern relative to a particular set of URL patterns. This algorithm is described in * JSR 115, section 3.1.3.1 "Qualified URL Pattern Names". * @param patterns the set of possible URL patterns that could be used to qualify this pattern * @return a qualifed URL pattern */ public String getQualifiedPattern(Set patterns) { if (type == EXACT) { return pattern; } else { HashSet bucket = new HashSet(); StringBuffer result = new StringBuffer(pattern); Iterator iter = patterns.iterator(); // Collect a set of qualifying patterns, depending on the type of this pattern. while (iter.hasNext()) { URLPattern p = (URLPattern) iter.next(); if (type.check(this, p)) { bucket.add(p.pattern); } } // append the set of qualifying patterns iter = bucket.iterator(); while (iter.hasNext()) { result.append(':'); result.append((String) iter.next()); } return result.toString(); } } /** * Add a method to the union of HTTP methods associated with this URL pattern. An empty string is short hand for * the set of all HTTP methods. * @param method the HTTP method to be added to the set. */ public void addMethod(String method) { if (method.length() == 0) { httpMethodsMask = 0xFF; return; } boolean found = false; for (int j = 0; j < HTTP_METHODS.length; j++) { if (method.equals(HTTP_METHODS[j])) { httpMethodsMask |= HTTP_MASKS[j]; found = true; break; } } if (!found) throw new IllegalArgumentException("Invalid HTTP method"); } /** * Return the set of HTTP methods that have been associated with this URL pattern. * @return a set of HTTP methods */ public String getMethods() { StringBuffer buffer = null; for (int i = 0; i < HTTP_MASKS.length; i++) { if ((httpMethodsMask & HTTP_MASKS[i]) > 0) { if (buffer == null) { buffer = new StringBuffer(); } else { buffer.append(","); } buffer.append(HTTP_METHODS[i]); } } return (buffer == null ? "" : buffer.toString()); } public String getComplementedMethods() { StringBuffer buffer = null; for (int i = 0; i < HTTP_MASKS.length; i++) { if ((httpMethodsMask & HTTP_MASKS[i]) == 0) { if (buffer == null) { buffer = new StringBuffer(); } else { buffer.append(","); } buffer.append(HTTP_METHODS[i]); } } return (buffer == null ? "" : buffer.toString()); } public String getMethodsWithTransport() { StringBuffer buffer = new StringBuffer(getMethods()); if (transport != NA) { buffer.append(":"); if (transport != 0x03) { if (transport == INTEGRAL) { buffer.append("INTEGRAL"); } else { buffer.append("CONFIDENTIAL"); } } } return buffer.toString(); } public void setTransport(String trans) { switch (transport) { case NA: { if ("INTEGRAL".equals(trans)) { transport = INTEGRAL; } else if ("CONFIDENTIAL".equals(trans)) { transport = CONFIDENTIAL; } break; } case INTEGRAL: { if ("CONFIDENTIAL".equals(trans)) { transport = CONFIDENTIAL; } break; } } } public void addRole(String role) { roles.add(role); } public void addAllRoles(Collection collection) { roles.addAll(collection); } public HashSet getRoles() { return roles; } public boolean equals(Object obj) { if (!(obj instanceof URLPattern)) return false; URLPattern test = (URLPattern) obj; return pattern.equals(test.pattern); } public int hashCode() { return pattern.hashCode(); } boolean matches(URLPattern p) { String test = p.pattern; // their pattern values are String equivalent if (pattern.equals(test)) return true; return type.matches(pattern, test); } private final static URLPatternCheck EXACT = new URLPatternCheck() { public boolean check(URLPattern base, URLPattern test) { return matches(base.pattern, test.pattern); } public boolean matches(String base, String test) { return base.equals(test); } }; private final static URLPatternCheck PATH_PREFIX = new URLPatternCheck() { public boolean check(URLPattern base, URLPattern test) { return ((test.type == PATH_PREFIX || test.type == EXACT) && base.matches(test) && !base.equals(test)); } /** * This pattern is a path-prefix pattern (that is, it starts with "/" and ends with "/*") and the argument * pattern starts with the substring of this pattern, minus its last 2 characters, and the next character of * the argument pattern, if there is one, is "/" * @param base the base pattern * @param test the pattern to be tested * @return <code>true</code> if <code>test</code> is matched by <code>base</code> */ public boolean matches(String base, String test) { int length = base.length() - 2; if (length > test.length()) return false; for (int i = 0; i < length; i++) { if (base.charAt(i) != test.charAt(i)) return false; } if (test.length() == length) return true; else if (test.charAt(length) != '/') return false; return true; } }; private final static URLPatternCheck EXTENSION = new URLPatternCheck() { public boolean check(URLPattern base, URLPattern test) { if (test.type == PATH_PREFIX) return true; if (test.type == EXACT) return matches(base.pattern, test.pattern); return false; } /** * This pattern is an extension pattern (that is, it startswith "*.") and the argument pattern ends with * this pattern. * @param base the base pattern * @param test the pattern to be tested * @return <code>true</code> if <code>test</code> is matched by <code>base</code> */ public boolean matches(String base, String test) { return test.endsWith(base.substring(1)); } }; private final static URLPatternCheck DEFAULT = new URLPatternCheck() { public boolean check(URLPattern base, URLPattern test) { return base.matches(test) && !base.equals(test); } /** * This pattern is the path-prefix pattern "/*" or the reference pattern is the special default pattern, * "/", which matches all argument patterns. * @param base the base pattern * @param test the pattern to be tested * @return <code>true</code> if <code>test</code> is matched by <code>base</code> * @see "JSR 115" */ public boolean matches(String base, String test) { return true; } }; }