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.ArrayList;
29  import java.util.Arrays;
30  import java.util.regex.Pattern;
31  
32  
33  /**
34   * @version $Rev: 792822 $ $Date: 2009-07-10 00:19:06 -0700 (Fri, 10 Jul 2009) $
35   */
36  final class HTTPMethodSpec {
37  
38      private final static String[] HTTP_METHODS = {"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE"};
39      private final static int[] HTTP_MASKS = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40};
40  
41      final static int NA = 0x00;
42      final static int INTEGRAL = 0x01;
43      final static int CONFIDENTIAL = 0x02;
44      final static int NONE = INTEGRAL | CONFIDENTIAL;
45  
46      private final int mask;
47      private final String[] extensionMethods;
48      private final boolean isExcluded;
49      private final int transport;
50      private String actions;
51      private static final String[] NO_METHODS = new String[0];
52      private static final Pattern TOKEN_PATTERN = Pattern.compile("[!-~&&[^\\(\\)\\<\\>@,;:\\\\\"/\\[\\]\\?=\\{\\}]]*");
53  
54      public HTTPMethodSpec(String[] HTTPMethods) {
55          this(HTTPMethods, null);
56      }
57  
58      public HTTPMethodSpec(String name, boolean parseTransportType) {
59          if (parseTransportType) {
60              if (name == null || name.length() == 0) {
61                  this.transport = NONE;
62              } else {
63                  String[] tokens = name.split(":", 2);
64  
65                  if (tokens.length == 2) {
66                      if (tokens[1].equals("NONE")) {
67                          this.transport = NONE;
68                      } else if (tokens[1].equals("INTEGRAL")) {
69                          this.transport = INTEGRAL;
70                      } else if (tokens[1].equals("CONFIDENTIAL")) {
71                          this.transport = CONFIDENTIAL;
72                      } else {
73                          throw new IllegalArgumentException("Invalid transportType: " + tokens[1]);
74                      }
75                  } else {
76                      this.transport = NONE;
77                  }
78                  name = tokens[0];
79              }
80          } else {
81              this.transport = NA;
82          }
83  
84          if (name == null || name.length() == 0) {
85              this.mask = 0x00;
86              this.extensionMethods = NO_METHODS;
87              this.isExcluded = true;
88          } else {
89              ArrayList<String> extensions = null;
90              if (isExcluded = name.charAt(0) == '!') {
91                  name = name.substring(1);
92              }
93              int tmpMask = 0;
94  
95              if (name.length() > 0) {
96                  String[] methods = name.split(",", -1);
97                  for (int i = 0; i < methods.length; i++) {
98                      boolean found = false;
99  
100                     for (int j = 0; j < HTTP_METHODS.length; j++) {
101                         if (methods[i].equals(HTTP_METHODS[j])) {
102                             tmpMask |= HTTP_MASKS[j];
103                             found = true;
104 
105                             break;
106                         }
107                     }
108                     if (!found) {
109                         checkToken(methods[i]);
110                         if (extensions == null) {
111                             extensions = new ArrayList<String>(methods.length);
112                         }
113                         add(extensions, methods[i]);
114                     }
115                 }
116             }
117             this.mask = tmpMask;
118             if (extensions == null) {
119                 extensionMethods = NO_METHODS;
120             } else {
121                 extensionMethods = extensions.toArray(new String[extensions.size()]);
122             }
123         }
124     }
125 
126     public HTTPMethodSpec(String[] HTTPMethods, String transport) {
127         boolean parseTransportType = transport != null;
128 
129         if (HTTPMethods == null || HTTPMethods.length == 0) {
130             this.mask = 0x00;
131             this.extensionMethods = NO_METHODS;
132             this.isExcluded = true;
133         } else {
134             int tmpMask = 0;
135             this.isExcluded = false;
136             ArrayList<String> extensions = null;
137 
138             for (int i = 0; i < HTTPMethods.length; i++) {
139                 boolean found = false;
140 
141                 for (int j = 0; j < HTTP_METHODS.length; j++) {
142                     if (HTTPMethods[i].equals(HTTP_METHODS[j])) {
143                         tmpMask |= HTTP_MASKS[j];
144                         found = true;
145 
146                         break;
147                     }
148                 }
149                 if (!found) {
150                     checkToken(HTTPMethods[i]);
151                     if (extensions == null) {
152                         extensions = new ArrayList<String>(HTTPMethods.length);
153                     }
154                     add(extensions, HTTPMethods[i]);
155                 }
156             }
157             this.mask = tmpMask;
158             if (extensions == null) {
159                 extensionMethods = NO_METHODS;
160             } else {
161                 extensionMethods = extensions.toArray(new String[extensions.size()]);
162             }
163         }
164 
165         if (parseTransportType) {
166             if (transport.length() == 0 || transport.equals("NONE")) {
167                 this.transport = NONE;
168             } else if (transport.equals("INTEGRAL")) {
169                 this.transport = INTEGRAL;
170             } else if (transport.equals("CONFIDENTIAL")) {
171                 this.transport = CONFIDENTIAL;
172             } else {
173                 throw new IllegalArgumentException("Invalid transport");
174             }
175         } else {
176             this.transport = NA;
177         }
178     }
179 
180     public HTTPMethodSpec(String singleMethod, int transport) {
181         int tmpMask = 0;
182 
183         for (int j = 0; j < HTTP_METHODS.length; j++) {
184             if (HTTP_METHODS[j].equals(singleMethod)) {
185                 tmpMask = HTTP_MASKS[j];
186 
187                 break;
188             }
189         }
190         if (tmpMask == 0) {
191             checkToken(singleMethod);
192             this.extensionMethods = new String[]{singleMethod};
193         } else {
194             this.extensionMethods = NO_METHODS;
195         }
196 
197         this.mask = tmpMask;
198         this.isExcluded = false;
199         this.transport = transport;
200     }
201 
202 
203     private void checkToken(String method) {
204         if (!TOKEN_PATTERN.matcher(method).matches()) {
205             throw new IllegalArgumentException("Invalid HTTPMethodSpec");
206         }
207     }
208 
209     private void add(ArrayList<String> extensions, String httpMethod) {
210         for (int i = 0; i < extensions.size(); i++) {
211             String existingMethod = extensions.get(i);
212             int compare = existingMethod.compareTo(httpMethod);
213             if (compare == 0) {
214                 return;
215             }
216             if (compare > 0) {
217                 extensions.add(i, httpMethod);
218                 return;
219             }
220         }
221         extensions.add(httpMethod);
222     }
223 
224 
225     public boolean equals(HTTPMethodSpec o) {
226         return mask == o.mask && transport == o.transport && isExcluded == o.isExcluded && Arrays.equals(extensionMethods, o.extensionMethods);
227     }
228 
229     public String getActions() {
230         if (actions == null) {
231             StringBuilder buffer;
232             if (isAllHttpActions()) {
233                 if (hasTransportGuarantee()) {
234                     buffer = new StringBuilder();
235                 } else {
236                     return "";
237                 }
238             } else {
239                 buffer = new StringBuilder();
240                 if (isExcluded) {
241                     buffer.append("!");
242                 }
243 
244                 boolean first = true;
245                 for (int i = 0; i < HTTP_MASKS.length; i++) {
246                     if ((mask & HTTP_MASKS[i]) > 0) {
247                         if (first) {
248                             first = false;
249                         } else {
250                             buffer.append(",");
251                         }
252                         buffer.append(HTTP_METHODS[i]);
253                     }
254                 }
255                 for (String method : extensionMethods) {
256                     if (first) {
257                         first = false;
258                     } else {
259                         buffer.append(",");
260                     }
261                     buffer.append(method);
262                 }
263             }
264 
265             if (hasTransportGuarantee()) {
266                 if (transport == INTEGRAL) {
267                     buffer.append(":INTEGRAL");
268                 } else if (transport == CONFIDENTIAL) {
269                     buffer.append(":CONFIDENTIAL");
270                 }
271             }
272 
273             actions = buffer.toString();
274         }
275         return actions;
276     }
277 
278     private boolean isAllHttpActions() {
279         return isExcluded && mask == 0x00 && extensionMethods.length == 0;
280     }
281 
282     private boolean hasTransportGuarantee() {
283         return  !(transport == NA || transport == NONE);
284     }
285 
286     public int hashCode() {
287         return mask ^ (transport <<8) ^ (isExcluded? 0:0x200);
288     }
289 
290     public String toString() {
291         return getActions();
292     }
293 
294     public boolean implies(HTTPMethodSpec p) {
295         if ((transport & p.transport) != p.transport) {
296             return false;
297         }
298         if (isExcluded) {
299             if (p.isExcluded) {
300                 return ((mask & p.mask) == mask) && contains(p.extensionMethods, extensionMethods);
301             } else {
302                 return ((mask & p.mask) == 0x00) && isDisjoint(extensionMethods, p.extensionMethods);
303             }
304         } else {
305             if (p.isExcluded) {
306                 return false;
307             } else {
308                 return ((mask & p.mask) == p.mask) && contains(extensionMethods, p.extensionMethods);
309             }
310         }
311     }
312 
313     private boolean isDisjoint(String[] a, String[] b) {
314         int start = 0;
315         for (int i = 0; i < a.length; i++) {
316             String s = a[i];
317             for (int j = start; j < b.length; j++) {
318                 String s1 = b[j];
319                 int compare = s.compareTo(s1);
320                 if (compare == 0) {
321                     return false;
322                 }
323                 if (compare < 0) {
324                     start = j;
325                     break;
326                 }
327             }
328         }
329         return true;
330     }
331 
332     private boolean contains(String[] set, String[] subset) {
333         int start = 0;
334         for (int i = 0; i < subset.length; i++) {
335             boolean found = false;
336             String s = subset[i];
337             for (int j = start; j < set.length; j++) {
338                 String s1 = set[j];
339                 int compare = s.compareTo(s1);
340                 if (compare == 0) {
341                     found = true;
342                     start = j + 1;
343                     break;
344                 }
345                 if (compare < 0) {
346                     return false;
347                 }
348             }
349             if (!found) {
350                 return false;
351             }
352         }
353         return true;
354     }
355 }