View Javadoc

1   /**
2    *
3    * Copyright 2003-2004 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  //
19  // This source code implements specifications defined by the Java
20  // Community Process. In order to remain compliant with the specification
21  // DO NOT add / change / or delete method signatures!
22  //
23  
24  package javax.security.jacc;
25  
26  import java.util.Iterator;
27  import java.util.LinkedList;
28  import javax.servlet.http.HttpServletRequest;
29  
30  
31  /**
32   * @version $Rev: 431818 $ $Date: 2006-08-15 21:55:26 -0700 (Tue, 15 Aug 2006) $
33   */
34  final class URLPatternSpec {
35  
36      private final String pattern;
37      private final URLPattern first;
38      private final LinkedList qualifiers = new LinkedList();
39  
40      public URLPatternSpec(String name) {
41          if (name == null) throw new java.lang.IllegalArgumentException("URLPatternSpec cannot be null");
42          if (name.length() == 0) name = "/";
43  
44          pattern = name;
45  
46          String[] tokens = pattern.split(":", -1);
47          first = new URLPattern(tokens[0]);
48  
49          URLPattern candidate;
50          for (int i = 1; i < tokens.length; i++) {
51              candidate = new URLPattern(tokens[i]);
52  
53              // No pattern may exist in the URLPatternList that matches the first pattern.
54              if (candidate.matches(first)) {
55                  throw new java.lang.IllegalArgumentException("Qualifier patterns in the URLPatternSpec cannot match the first URLPattern");
56              }
57  
58              if (first.type == URLPattern.PATH_PREFIX) {
59  
60                  // If the first pattern is a path-prefix pattern, only exact patterns
61                  // matched by the first pattern and path-prefix patterns matched by,
62                  // but different from, the first pattern may occur in the URLPatternList.
63  
64                  if (candidate.type == URLPattern.EXACT && !first.matches(candidate)) {
65                      throw new java.lang.IllegalArgumentException("Exact qualifier patterns in the URLPatternSpec must be matched by the first URLPattern");
66                  } else if (candidate.type == URLPattern.PATH_PREFIX
67                             && !(first.matches(candidate) && first.pattern.length() < candidate.pattern.length()))
68                  {
69                      throw new java.lang.IllegalArgumentException("path-prefix qualifier patterns in the URLPatternSpec must be matched by, but different from, the first URLPattern");
70                  } else if (candidate.type == URLPattern.EXTENSION) {
71                      throw new java.lang.IllegalArgumentException("extension qualifier patterns in the URLPatternSpec are not allowed when the first URLPattern is path-prefix");
72                  }
73              } else if (first.type == URLPattern.EXTENSION) {
74  
75                  // If the first pattern is an extension pattern, only exact patterns
76                  // that are matched by the first pattern and path-prefix patterns may
77                  // occur in the URLPatternList.
78  
79                  if (candidate.type == URLPattern.EXACT && !first.matches(candidate)) {
80                      throw new java.lang.IllegalArgumentException("Exact qualifier patterns in the URLPatternSpec must be matched when first URLPattern is an extension pattern");
81                  } else if (candidate.type != URLPattern.PATH_PREFIX) {
82                      throw new java.lang.IllegalArgumentException("Only exact and path-prefix qualifiers in the URLPatternSpec are allowed when first URLPattern is an extension pattern");
83                  }
84              } else if (first.type == URLPattern.DEFAULT) {
85  
86                  // If the first pattern is the default pattern, "/", any pattern
87                  // except the default pattern may occur in the URLPatternList.
88  
89                  if (candidate.type == URLPattern.DEFAULT) {
90                      throw new java.lang.IllegalArgumentException("Qualifier patterns must not be default when first URLPattern is a default pattern");
91                  }
92              } else if (first.type == URLPattern.EXACT) {
93  
94                  // If the first pattern is an exact pattern a URLPatternList
95                  // must not be present in the URLPatternSpec
96  
97                  throw new java.lang.IllegalArgumentException("Qualifier patterns must be present when first URLPattern is an exact pattern");
98              }
99  
100             qualifiers.add(candidate);
101         }
102     }
103 
104     public boolean equals(URLPatternSpec o) {
105         return implies(o) && o.implies(this);
106     }
107 
108     public int hashCode() {
109         return pattern.hashCode();
110     }
111 
112     public String getPatternSpec() {
113         return pattern;
114     }
115 
116     public boolean implies(URLPatternSpec p) {
117 
118         // The first URLPattern in the name of the argument permission is
119         // matched by the first URLPattern in the name of this permission.
120         if (!first.matches(p.first)) return false;
121 
122         // The first URLPattern in the name of the argument permission is NOT
123         // matched by any URLPattern in the URLPatternList of the URLPatternSpec
124         // of this permission.
125         Iterator iter1 = qualifiers.iterator();
126         while (iter1.hasNext()) {
127             if (((URLPattern) iter1.next()).matches(p.first)) return false;
128         }
129 
130         // If the first URLPattern in the name of the argument permission
131         // matches the first URLPattern in the URLPatternSpec of this
132         // permission, then every URLPattern in the URLPatternList of the
133         // URLPatternSpec of this permission is matched by a URLPattern in
134         // the URLPatternList of the argument permission.
135         if (p.first.matches(first)) {
136             Iterator iter2 = p.qualifiers.iterator();
137 
138             while (iter2.hasNext()) {
139                 Iterator iter3 = qualifiers.iterator();
140                 URLPattern test = (URLPattern) iter2.next();
141                 boolean found = false;
142 
143                 while (iter3.hasNext()) {
144                     if (test.matches((URLPattern) iter3.next())) {
145                         found = true;
146                         break;
147                     }
148                 }
149                 if (!found) return false;
150             }
151         }
152 
153         return true;
154     }
155 
156     static String encodeColons(HttpServletRequest request) {
157         String result = request.getServletPath() + (request.getPathInfo() == null ? "" : request.getPathInfo());
158 
159         if (result.indexOf("%3A") > -1) result = result.replaceAll("%3A", "%3A%3A");
160         if (result.indexOf(":") > -1) result = result.replaceAll(":", "%3A");
161 
162         return result;
163     }
164 
165     private class URLPattern {
166 
167         public final static int EXACT = 0x0;
168         public final static int PATH_PREFIX = 0x1;
169         public final static int EXTENSION = 0x2;
170         public final static int DEFAULT = 0x4;
171 
172         public int type;
173         public String pattern;
174 
175         public URLPattern(String pat) {
176             if (pat == null) throw new java.lang.IllegalArgumentException("URLPattern cannot be null");
177             if (pat.length() == 0) throw new java.lang.IllegalArgumentException("URLPattern cannot be empty");
178 
179             if (pat.equals("/") || pat.equals("/*")) {
180                 type = DEFAULT;
181             } else if (pat.charAt(0) == '/' && pat.endsWith("/*")) {
182                 type = PATH_PREFIX;
183             } else if (pat.charAt(0) == '*') {
184                 type = EXTENSION;
185             } else {
186                 type = EXACT;
187             }
188             pattern = pat;
189         }
190 
191         public boolean matches(URLPattern p) {
192 
193             String test = p.pattern;
194 
195             // their pattern values are String equivalent
196             if (pattern.equals(test)) return true;
197 
198             switch (type) {
199 
200                 // this pattern is a path-prefix pattern (that is, it starts
201                 // with "/" and ends with "/*") and the argument pattern
202                 // starts with the substring of this pattern, minus its last
203                 // 2 characters, and the next character of the argument pattern,
204                 // if there is one, is "/"
205                 case PATH_PREFIX: {
206                     int length = pattern.length() - 2;
207                     if (length > test.length()) return false;
208 
209                     for (int i = 0; i < length; i++) {
210                         if (pattern.charAt(i) != test.charAt(i)) return false;
211                     }
212 
213                     if (test.length() == length) return true;
214                     else if (test.charAt(length) != '/') return false;
215 
216                     return true;
217                 }
218 
219                 // this pattern is an extension pattern (that is, it starts
220                 // with "*.") and the argument pattern ends with this pattern
221                 case EXTENSION: {
222                     return test.endsWith(pattern.substring(1));
223                 }
224 
225                 // this pattern is the path-prefix pattern "/*" or
226                 // the reference pattern is the special default pattern,
227                 // "/", which matches all argument patterns
228                 case DEFAULT: {
229                     return true;
230                 }
231             }
232             return false;
233         }
234     }
235 }