/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ // // This source code implements specifications defined by the Java // Community Process. In order to remain compliant with the specification // DO NOT add / change / or delete method signatures! // package javax.security.jacc; import java.util.Iterator; import java.util.LinkedList; import javax.servlet.http.HttpServletRequest; /** * @version $Rev$ $Date$ */ final class URLPatternSpec { private final String pattern; private final URLPattern first; private final LinkedList qualifiers = new LinkedList(); public URLPatternSpec(String name) { if (name == null) throw new java.lang.IllegalArgumentException("URLPatternSpec cannot be null"); if (name.length() == 0) name = "/"; pattern = name; String[] tokens = pattern.split(":", -1); first = new URLPattern(tokens[0]); URLPattern candidate; for (int i = 1; i < tokens.length; i++) { candidate = new URLPattern(tokens[i]); // No pattern may exist in the URLPatternList that matches the first pattern. if (candidate.matches(first)) { throw new java.lang.IllegalArgumentException("Qualifier patterns in the URLPatternSpec cannot match the first URLPattern"); } if (first.type == URLPattern.PATH_PREFIX) { // If the first pattern is a path-prefix pattern, only exact patterns // matched by the first pattern and path-prefix patterns matched by, // but different from, the first pattern may occur in the URLPatternList. if (candidate.type == URLPattern.EXACT && !first.matches(candidate)) { throw new java.lang.IllegalArgumentException("Exact qualifier patterns in the URLPatternSpec must be matched by the first URLPattern"); } else if (candidate.type == URLPattern.PATH_PREFIX && !(first.matches(candidate) && first.pattern.length() < candidate.pattern.length())) { throw new java.lang.IllegalArgumentException("path-prefix qualifier patterns in the URLPatternSpec must be matched by, but different from, the first URLPattern"); } else if (candidate.type == URLPattern.EXTENSION) { throw new java.lang.IllegalArgumentException("extension qualifier patterns in the URLPatternSpec are not allowed when the first URLPattern is path-prefix"); } } else if (first.type == URLPattern.EXTENSION) { // If the first pattern is an extension pattern, only exact patterns // that are matched by the first pattern and path-prefix patterns may // occur in the URLPatternList. if (candidate.type == URLPattern.EXACT) { if (!first.matches(candidate)) { throw new java.lang.IllegalArgumentException("Exact qualifier patterns in the URLPatternSpec must be matched when first URLPattern is an extension pattern"); } } else if (candidate.type != URLPattern.PATH_PREFIX) { throw new java.lang.IllegalArgumentException("Only exact and path-prefix qualifiers in the URLPatternSpec are allowed when first URLPattern is an extension pattern"); } } else if (first.type == URLPattern.DEFAULT) { // If the first pattern is the default pattern, "/", any pattern // except the default pattern may occur in the URLPatternList. if (candidate.type == URLPattern.DEFAULT) { //This is actually tested for by the "qualifier must not match first" rule throw new java.lang.IllegalArgumentException("Qualifier patterns must not be default when first URLPattern is a default pattern"); } } else if (first.type == URLPattern.EXACT) { // If the first pattern is an exact pattern a URLPatternList // must not be present in the URLPatternSpec throw new java.lang.IllegalArgumentException("Qualifier patterns must not be present when first URLPattern is an exact pattern"); } qualifiers.add(candidate); } } public boolean equals(URLPatternSpec o) { return implies(o) && o.implies(this); } public int hashCode() { return pattern.hashCode(); } public String getPatternSpec() { return pattern; } public String toString() { return pattern; } public boolean implies(URLPatternSpec p) { // The first URLPattern in the name of the argument permission is // matched by the first URLPattern in the name of this permission. if (!first.matches(p.first)) return false; // The first URLPattern in the name of the argument permission is NOT // matched by any URLPattern in the URLPatternList of the URLPatternSpec // of this permission. Iterator iter1 = qualifiers.iterator(); while (iter1.hasNext()) { if (((URLPattern) iter1.next()).matches(p.first)) return false; } // If the first URLPattern in the name of the argument permission // matches the first URLPattern in the URLPatternSpec of this // permission, then every URLPattern in the URLPatternList of the // URLPatternSpec of this permission is matched by a URLPattern in // the URLPatternList of the argument permission. if (p.first.matches(first)) { Iterator iter2 = p.qualifiers.iterator(); while (iter2.hasNext()) { Iterator iter3 = qualifiers.iterator(); URLPattern test = (URLPattern) iter2.next(); boolean found = false; while (iter3.hasNext()) { if (test.matches((URLPattern) iter3.next())) { found = true; break; } } if (!found) return false; } } return true; } static String encodeColons(HttpServletRequest request) { String result = request.getServletPath() + (request.getPathInfo() == null ? "" : request.getPathInfo()); if (result.indexOf(":") > -1) result = result.replaceAll(":", "%3A"); return result; } private class URLPattern { public final static int EXACT = 0x0; public final static int PATH_PREFIX = 0x1; public final static int EXTENSION = 0x2; public final static int DEFAULT = 0x4; public int type; public String pattern; public URLPattern(String pat) { if (pat == null) throw new java.lang.IllegalArgumentException("URLPattern cannot be null"); if (pat.length() == 0) throw new java.lang.IllegalArgumentException("URLPattern 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; } public boolean matches(URLPattern p) { String test = p.pattern; // their pattern values are String equivalent if (pattern.equals(test)) return true; switch (type) { // 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 "/" case PATH_PREFIX: { int length = pattern.length() - 2; if (length > test.length()) return false; for (int i = 0; i < length; i++) { if (pattern.charAt(i) != test.charAt(i)) return false; } if (test.length() == length) return true; else if (test.charAt(length) != '/') return false; return true; } // this pattern is an extension pattern (that is, it starts // with "*.") and the argument pattern ends with this pattern case EXTENSION: { return test.endsWith(pattern.substring(1)); } // this pattern is the path-prefix pattern "/*" or // the reference pattern is the special default pattern, // "/", which matches all argument patterns case DEFAULT: { return true; } } return false; } } }