View Javadoc

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