1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
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 }