| /* |
| * 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; |
| } |
| } |
| } |