001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.security.util;
019    
020    import java.util.Collection;
021    import java.util.HashSet;
022    import java.util.Set;
023    
024    
025    /**
026     * Utility class for <code>ModuleConfiguration</code>.  This class is used to generate qualified patterns, HTTP
027     * method sets, complements of HTTP method sets, and HTTP method sets w/ transport restrictions for URL patterns that
028     * are found in the web deployment descriptor.
029     *
030     * @version $Rev: 497904 $ $Date: 2007-01-19 13:49:13 -0500 (Fri, 19 Jan 2007) $
031     */
032    public class URLPattern {
033        public final static int NA = 0x00;
034        public final static int INTEGRAL = 0x01;
035        public final static int CONFIDENTIAL = 0x02;
036    
037        private final URLPatternCheck type;
038        private final String pattern;
039        private final HTTPMethods httpMethods = new HTTPMethods();
040        private int transport;
041        private final HashSet<String> roles = new HashSet<String>();
042    
043        /**
044         * Construct an instance of the utility class for <code>WebModuleConfiguration</code>.
045         *
046         * @param pat the URL pattern that this instance is to collect information on
047         * @see "JSR 115, section 3.1.3" Translating Servlet Deployment Descriptors
048         */
049        public URLPattern(String pat) {
050            if (pat == null) throw new java.lang.IllegalArgumentException("URL pattern cannot be null");
051            if (pat.length() == 0) throw new java.lang.IllegalArgumentException("URL pattern cannot be empty");
052    
053            if (pat.equals("/") || pat.equals("/*")) {
054                type = DEFAULT;
055                pat = "/";
056            } else if (pat.charAt(0) == '/' && pat.endsWith("/*")) {
057                type = PATH_PREFIX;
058            } else if (pat.charAt(0) == '*') {
059                type = EXTENSION;
060            } else {
061                type = EXACT;
062            }
063            pattern = pat;
064        }
065    
066        /**
067         * Get a qualifed URL pattern relative to a particular set of URL patterns.  This algorithm is described in
068         * JSR 115, section 3.1.3.1 "Qualified URL Pattern Names".
069         *
070         * @param patterns the set of possible URL patterns that could be used to qualify this pattern
071         * @return a qualifed URL pattern
072         */
073        public String getQualifiedPattern(Set<URLPattern> patterns) {
074            if (type == EXACT) {
075                return pattern;
076            } else {
077                HashSet<String> bucket = new HashSet<String>();
078                StringBuffer result = new StringBuffer(pattern);
079    
080                // Collect a set of qualifying patterns, depending on the type of this pattern.
081                for (URLPattern p : patterns) {
082                    if (type.check(this, p)) {
083                        bucket.add(p.pattern);
084                    }
085                }
086    
087                // append the set of qualifying patterns
088                for (String aBucket : bucket) {
089                    result.append(':');
090                    result.append(aBucket);
091                }
092                return result.toString();
093            }
094        }
095    
096        /**
097         * Add a method to the union of HTTP methods associated with this URL pattern.  An empty string is short hand for
098         * the set of all HTTP methods.
099         *
100         * @param method the HTTP method to be added to the set.
101         */
102        public void addMethod(String method) {
103            httpMethods.add(method);
104        }
105    
106        /**
107         * Return the set of HTTP methods that have been associated with this URL pattern.
108         *
109         * @return a set of HTTP methods
110         */
111        public String getMethods() {
112            return httpMethods.getHttpMethods();
113        }
114    
115    
116        public String getComplementedMethods() {
117            return httpMethods.getComplementedHttpMethods();
118        }
119    
120        public HTTPMethods getHTTPMethods() {
121            return httpMethods;
122        }
123    
124        public HTTPMethods getComplementedHTTPMethods() {
125            return new HTTPMethods(httpMethods, true);
126        }
127    
128        public String getMethodsWithTransport() {
129            return getMethodsWithTransport(httpMethods, transport);
130        }
131    
132        public static String getMethodsWithTransport(HTTPMethods methods, int transport) {
133            StringBuffer buffer = methods.getHttpMethodsBuffer();
134    
135    
136            if (transport != NA) {
137                buffer.append(":");
138    
139                if (transport != 0x03) {
140                    if (transport == INTEGRAL) {
141                        buffer.append("INTEGRAL");
142                    } else {
143                        buffer.append("CONFIDENTIAL");
144                    }
145                }
146            }
147    
148            return buffer.toString();
149        }
150    
151        public void setTransport(String trans) {
152            switch (transport) {
153                case NA: {
154                    if ("INTEGRAL".equals(trans)) {
155                        transport = INTEGRAL;
156                    } else if ("CONFIDENTIAL".equals(trans)) {
157                        transport = CONFIDENTIAL;
158                    }
159                    break;
160                }
161    
162                case INTEGRAL: {
163                    if ("CONFIDENTIAL".equals(trans)) {
164                        transport = CONFIDENTIAL;
165                    }
166                    break;
167                }
168            }
169        }
170    
171        public int getTransport() {
172            return transport;
173        }
174    
175        public void addRole(String role) {
176            roles.add(role);
177        }
178    
179        public void addAllRoles(Collection<String> collection) {
180            roles.addAll(collection);
181        }
182    
183        public HashSet<String> getRoles() {
184            return roles;
185        }
186    
187    
188        /**
189         * TODO this is kinda weird without an explanation
190         * @param obj object to compare with
191         * @return if this equals obj
192         */
193        public boolean equals(Object obj) {
194            if (!(obj instanceof URLPattern)) return false;
195    
196            URLPattern test = (URLPattern) obj;
197    
198            return pattern.equals(test.pattern);
199        }
200    
201        public int hashCode() {
202            return pattern.hashCode();
203        }
204    
205        boolean matches(URLPattern p) {
206            String test = p.pattern;
207    
208            // their pattern values are String equivalent
209            if (pattern.equals(test)) return true;
210    
211            return type.matches(pattern, test);
212        }
213    
214        private final static URLPatternCheck EXACT = new URLPatternCheck() {
215            public boolean check(URLPattern base, URLPattern test) {
216                return matches(base.pattern, test.pattern);
217            }
218    
219            public boolean matches(String base, String test) {
220                return base.equals(test);
221            }
222        };
223    
224        private final static URLPatternCheck PATH_PREFIX = new URLPatternCheck() {
225            public boolean check(URLPattern base, URLPattern test) {
226                return ((test.type == PATH_PREFIX || test.type == EXACT)
227                        && base.matches(test)
228                        && !base.equals(test));
229            }
230    
231            /**
232             * This pattern is a path-prefix pattern (that is, it starts with "/" and ends with "/*") and the argument
233             * pattern starts with the substring of this pattern, minus its last 2 characters, and the next character of
234             * the argument pattern, if there is one, is "/"
235             *
236             * @param base the base pattern
237             * @param test the pattern to be tested
238             * @return <code>true</code> if <code>test</code> is matched by <code>base</code>
239             */
240            public boolean matches(String base, String test) {
241                int length = base.length() - 2;
242                if (length > test.length()) return false;
243    
244                for (int i = 0; i < length; i++) {
245                    if (base.charAt(i) != test.charAt(i)) return false;
246                }
247    
248                if (test.length() == length)
249                    return true;
250                else if (test.charAt(length) != '/') return false;
251    
252                return true;
253            }
254        };
255    
256        private final static URLPatternCheck EXTENSION = new URLPatternCheck() {
257            public boolean check(URLPattern base, URLPattern test) {
258                if (test.type == PATH_PREFIX) return true;
259    
260                if (test.type == EXACT) return matches(base.pattern, test.pattern);
261    
262                return false;
263            }
264    
265            /**
266             * This pattern is an extension pattern (that is, it startswith "*.") and the argument pattern ends with
267             * this pattern.
268             *
269             * @param base the base pattern
270             * @param test the pattern to be tested
271             * @return <code>true</code> if <code>test</code> is matched by <code>base</code>
272             */
273            public boolean matches(String base, String test) {
274                return test.endsWith(base.substring(1));
275            }
276        };
277    
278        private final static URLPatternCheck DEFAULT = new URLPatternCheck() {
279            public boolean check(URLPattern base, URLPattern test) {
280                return base.matches(test) && !base.equals(test);
281            }
282    
283            /**
284             * This pattern is the path-prefix pattern "/*" or the reference pattern is the special default pattern,
285             * "/", which matches all argument patterns.
286             *
287             * @param base the base pattern
288             * @param test the pattern to be tested
289             * @return <code>true</code> if <code>test</code> is matched by <code>base</code>
290             * @see "JSR 115"
291             */
292            public boolean matches(String base, String test) {
293                return true;
294            }
295        };
296    }