001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package javax.security.jacc;
023:
024: import java.io.Serializable;
025: import java.io.ObjectStreamField;
026: import java.io.ObjectInputStream;
027: import java.io.IOException;
028: import java.io.ObjectOutputStream;
029: import java.security.Permission;
030: import java.util.TreeSet;
031: import java.util.StringTokenizer;
032: import java.util.Iterator;
033: import javax.servlet.http.HttpServletRequest;
034:
035: import org.jboss.util.id.SerialVersion;
036:
037: /** Class for Servlet web resource permissions. A WebResourcePermission is a
038: * named permission and has actions.
039: *
040: * The name of a WebResourcePermission (also referred to as the target name)
041: * identifies the Web resources to which the permission pertains.
042: *
043: * Implementations of this class MAY implement newPermissionCollection or
044: * inherit its implementation from the super class.
045: *
046: *
047: * @author Scott.Stark@jboss.org
048: * @author Ron Monzillo, Gary Ellison (javadoc)
049: * @version $Revision: 57196 $
050: */
051: public final class WebResourcePermission extends Permission implements
052: Serializable {
053: /** @since 4.0.2 */
054: private static final long serialVersionUID;
055: private static TreeSet ALL_HTTP_METHODS = new TreeSet();
056: /**
057: * @serialField actions String the actions string.
058: */
059: private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField(
060: "actions", String.class) };
061:
062: static {
063: ALL_HTTP_METHODS.add("GET");
064: ALL_HTTP_METHODS.add("POST");
065: ALL_HTTP_METHODS.add("PUT");
066: ALL_HTTP_METHODS.add("DELETE");
067: ALL_HTTP_METHODS.add("HEAD");
068: ALL_HTTP_METHODS.add("OPTIONS");
069: ALL_HTTP_METHODS.add("TRACE");
070: if (SerialVersion.version == SerialVersion.LEGACY)
071: serialVersionUID = 141000;
072: else
073: serialVersionUID = 1;
074: }
075:
076: private transient URLPatternSpec urlSpec;
077: private transient String httpMethodsString;
078: private transient TreeSet httpMethods;
079:
080: /** Creates a new WebResourcePermission from the HttpServletRequest object.
081: *
082: * @param request - the HttpServletRequest object corresponding to the
083: * Servlet operation to which the permission pertains. The permission name is
084: * the substring of the requestURI (HttpServletRequest.getRequestURI()) that
085: * begins after the contextPath (HttpServletRequest.getContextPath()). When
086: * the substring operation yields the string "/", the permission is
087: * constructed with the empty string as its name. The permission's actions
088: * field is obtained from HttpServletRequest.getMethod().
089: */
090: public WebResourcePermission(HttpServletRequest request) {
091: this (requestURI(request), request.getMethod());
092: }
093:
094: /** Creates a new WebResourcePermission with the specified name and actions.
095:
096: The name contains a URLPatternSpec that identifies the web resources to which
097: the permissions applies. The syntax of a URLPatternSpec is as follows:
098:
099: URLPatternList ::= URLPattern | URLPatternList colon URLPattern
100:
101: URLPatternSpec ::= null | URLPattern | URLPattern colon URLPatternList
102:
103:
104:
105: A null URLPatternSpec is translated to the default URLPattern, "/", by the
106: permission constructor. The empty string is an exact URLPattern, and may
107: occur anywhere in a URLPatternSpec that an exact URLPattern may occur. The
108: first URLPattern in a URLPatternSpec may be any of the pattern types, exact,
109: path-prefix, extension, or default as defined in the Java Servlet
110: Specification). When a URLPatternSpec includes a URLPatternList, the
111: patterns of the URLPatternList identify the resources to which the
112: permission does NOT apply and depend on the pattern type and value of the
113: first pattern as follows:
114:
115: - No pattern may exist in the URLPatternList that matches the first pattern.
116: - If the first pattern is a path-prefix pattern, only exact patterns matched
117: by the first pattern and path-prefix patterns matched by, but different
118: from, the first pattern may occur in the URLPatternList.
119: - If the first pattern is an extension pattern, only exact patterns that are
120: matched by the first pattern and path-prefix patterns may occur in the
121: URLPatternList.
122: - If the first pattern is the default pattern, "/", any pattern except the
123: default pattern may occur in the URLPatternList.
124: - If the first pattern is an exact pattern a URLPatternList must not be
125: present in the URLPatternSpec.
126:
127: The actions parameter contains a comma seperated list of HTTP methods. The
128: syntax of the actions parameter is defined as follows:
129:
130: HTTPMethod ::= "GET" | "POST" | "PUT" | "DELETE" | "HEAD" |
131: "OPTIONS" | "TRACE"
132:
133: HTTPMethodList ::= HTTPMethod | HTTPMethodList comma HTTPMethod
134:
135: HTTPMethodSpec ::= null | HTTPMethodList
136:
137:
138: If duplicates occur in the HTTPMethodSpec they must be eliminated by the
139: permission constructor.
140:
141: A null or empty string HTTPMethodSpec indicates that the permission applies
142: to all HTTP methods at the resources identified by the URL pattern.
143:
144: @param name - the URLPatternSpec that identifies the application specific web
145: resources to which the permission pertains. All URLPatterns in the
146: URLPatternSpec are relative to the context path of the deployed web
147: application module, and the same URLPattern must not occur more than once
148: in a URLPatternSpec. A null URLPatternSpec is translated to the default
149: URLPattern, "/", by the permission constructor.
150: @param actions - identifies the HTTP methods to which the permission
151: pertains. If the value passed through this parameter is null or the empty
152: string, then the permission is constructed with actions corresponding to
153: all the possible HTTP methods.
154: */
155: public WebResourcePermission(String name, String actions) {
156: super (name == null ? "/" : name);
157: if (name == null)
158: name = "/";
159: this .urlSpec = new URLPatternSpec(name);
160: parseActions(actions);
161: }
162:
163: /** Creates a new WebResourcePermission with name corresponding to the
164: * URLPatternSpec, and actions composed from the array of HTTP methods.
165: *
166: * @param urlPatternSpec - the URLPatternSpec that identifies the app
167: * specific web resources to which the permission pertains. All URLPatterns
168: * in the URLPatternSpec are relative to the context path of the deployed web
169: * application module, and the same URLPattern must not occur more than once
170: * in a URLPatternSpec. A null URLPatternSpec is translated to the default
171: * URLPattern, "/", by the permission constructor.
172: * @param httpMethods - an array of strings each element of which contains
173: * the value of an HTTP method. If the value passed through this parameter is
174: * null or is an array with no elements, then the permission is constructed
175: * with actions corresponding to all the possible HTTP methods.
176: */
177: public WebResourcePermission(String urlPatternSpec,
178: String[] httpMethods) {
179: super (urlPatternSpec);
180: this .urlSpec = new URLPatternSpec(urlPatternSpec);
181: Object[] methodInfo = canonicalMethods(httpMethods);
182: this .httpMethods = (TreeSet) methodInfo[0];
183: this .httpMethodsString = (String) methodInfo[1];
184: }
185:
186: /** Checks two WebResourcePermission objects for equality. WebResourcePermission
187: * objects are equivalent if their URLPatternSpec and (canonicalized) actions
188: * values are equivalent. The URLPatternSpec of a reference permission is
189: * equivalent to that of an argument permission if their first patterns are
190: * equivalent, and the patterns of the URLPatternList of the reference
191: * permission collectively match exactly the same set of patterns as are
192: * matched by the patterns of the URLPatternList of the argument permission.
193: *
194: * @param p - the WebResourcePermission object being tested for equality
195: * with this WebResourcePermission.
196: * @return true if the argument WebResourcePermission object is equivalent to
197: * this WebResourcePermission.
198: */
199: public boolean equals(Object p) {
200: boolean equals = false;
201: if (p == null || !(p instanceof WebResourcePermission))
202: return false;
203: WebResourcePermission perm = (WebResourcePermission) p;
204: equals = urlSpec.equals(perm.urlSpec);
205: if (equals == true) {
206: String a0 = getActions();
207: String a1 = perm.getActions();
208: equals = (a0 != null && a0.equals(a1)) || (a0 == a1);
209: }
210: return equals;
211: }
212:
213: /** Returns a canonical String representation of the actions of this
214: * WebResourcePermission. WebResourcePermission actions are canonicalized by
215: * sorting the HTTP methods into ascending lexical order. There may be no
216: * duplicate HTTP methods in the canonical form, and the canonical form of
217: * the set of all HTTP methods is the value null.
218: *
219: * @return a String containing the canonicalized actions of this
220: * WebResourcePermission (or the null value).
221: */
222: public String getActions() {
223: return httpMethodsString;
224: }
225:
226: /** Returns the hash code value for this WebResourcePermission. The properties
227: * of the returned hash code must be as follows:
228: *
229: * - During the lifetime of a Java application, the hashCode method must return
230: * the same integer value, every time it is called on a WebResourcePermission
231: * object. The value returned by hashCode for a particular
232: * WebResourcePermission need not remain consistent from one execution of an
233: * application to another.
234: * - If two WebResourcePermission objects are equal according to the equals
235: * method, then calling the hashCode method on each of the two Permission
236: * objects must produce the same integer result (within an application).
237: * @return the integer hash code value for this object.
238: */
239: public int hashCode() {
240: int hashCode = urlSpec.hash();
241: if (httpMethods != null)
242: hashCode += httpMethods.hashCode();
243: return hashCode;
244: }
245:
246: /** Determines if the argument Permission is "implied by" this
247: * WebResourcePermission. For this to be the case, all of the following must
248: * be true:
249:
250: * - The argument is an instanceof WebResourcePermission
251: * - The first URLPattern in the name of the argument permission is matched
252: * by the first URLPattern in the name of this permission.
253: * - The first URLPattern in the name of the argument permission is NOT
254: * matched by any URLPattern in the URLPatternList of the URLPatternSpec
255: * of this permission.
256: * - If the first URLPattern in the name of the argument permission matches
257: * the first URLPattern in the URLPatternSpec of this permission, then every
258: * URLPattern in the URLPatternList of the URLPatternSpec of this permission
259: * is matched by a URLPattern in the URLPatternList of the argument permission.
260: * - The HTTP methods in the actions of the argument permission are a subset
261: * of the HTTP methods in the actions of this permission.
262: *
263: * URLPattern matching is performed using the Servlet matching rules where
264: * two URL patterns match if they are related as follows:
265: * - their pattern values are String equivalent, or
266: * - this pattern is the path-prefix pattern "/*", or
267: * - this pattern is a path-prefix pattern (that is, it starts with "/" and
268: * ends with "/*") and the argument pattern starts with the substring of this
269: * pattern, minus its last 2 characters, and the next character of the argument
270: * pattern, if there is one, is "/", or
271: * - this pattern is an extension pattern (that is, it starts with "*.") and
272: * the argument pattern ends with this pattern, or
273: * - the reference pattern is the special default pattern, "/", which matches
274: * all argument patterns.
275: *
276: * All of the comparisons described above are case sensitive.
277: *
278: * @param p
279: * @return
280: */
281: public boolean implies(Permission p) {
282: if (p == null || !(p instanceof WebResourcePermission))
283: return false;
284: WebResourcePermission perm = (WebResourcePermission) p;
285: // Check the URL patterns
286: boolean implies = urlSpec.implies(perm.urlSpec);
287: if (implies == true) {
288: // Check the http methods
289: if (httpMethods != null)
290: implies = httpMethods.containsAll(perm.httpMethods);
291: }
292:
293: return implies;
294: }
295:
296: /** Build a permission name from the substring of the
297: * HttpServletRequest.getRequestURI()) that begins after the contextPath
298: * (HttpServletRequest.getContextPath()). When the substring operation yields
299: * the string "/", the permission is constructed with the empty string as
300: * its name.
301: * @param request - the servlet request
302: * @return the resource permission name
303: */
304: static String requestURI(HttpServletRequest request) {
305: String uri = request.getRequestURI();
306: if (uri != null) {
307: String contextPath = request.getContextPath();
308: int length = contextPath == null ? 0 : contextPath.length();
309: if (length > 0) {
310: uri = uri.substring(length);
311: }
312: if (uri.equals("/")) {
313: uri = "";
314: }
315: } else {
316: uri = "";
317: }
318: return uri;
319: }
320:
321: static Object[] canonicalMethods(String[] methods) {
322: TreeSet actions = new TreeSet();
323: int length = methods != null ? methods.length : 0;
324: for (int n = 0; n < length; n++) {
325: actions.add(methods[n]);
326: }
327: return canonicalMethods(actions);
328: }
329:
330: /** Parse the comma seperated http methods into a
331: * @param methods
332: * @return
333: */
334: static Object[] canonicalMethods(String methods) {
335: if (methods == null || methods.length() == 0)
336: return new Object[] { ALL_HTTP_METHODS, null };
337:
338: StringTokenizer tokenizer = new StringTokenizer(methods, ",");
339: TreeSet actions = new TreeSet();
340: while (tokenizer.hasMoreTokens()) {
341: String action = tokenizer.nextToken();
342: actions.add(action);
343: }
344: return canonicalMethods(actions);
345: }
346:
347: static Object[] canonicalMethods(TreeSet actions) {
348: Object[] info = { null, null };
349: if (actions.equals(ALL_HTTP_METHODS) || actions.size() == 0)
350: return info;
351:
352: info[0] = actions;
353: Iterator iter = actions.iterator();
354: StringBuffer tmp = new StringBuffer();
355: while (iter.hasNext()) {
356: tmp.append(iter.next());
357: tmp.append(',');
358: }
359: if (tmp.length() > 0)
360: tmp.setLength(tmp.length() - 1);
361: info[1] = tmp.toString();
362: return info;
363: }
364:
365: // Private -------------------------------------------------------
366: private void parseActions(String actions) {
367: Object[] methodInfo = canonicalMethods(actions);
368: this .httpMethods = (TreeSet) methodInfo[0];
369: this .httpMethodsString = (String) methodInfo[1];
370: }
371:
372: private void readObject(ObjectInputStream ois)
373: throws ClassNotFoundException, IOException {
374: ObjectInputStream.GetField fields = ois.readFields();
375: String actions = (String) fields.get("actions", null);
376: parseActions(actions);
377: }
378:
379: private void writeObject(ObjectOutputStream oos) throws IOException {
380: ObjectOutputStream.PutField fields = oos.putFields();
381: fields.put("actions", this.getActions());
382: oos.writeFields();
383: }
384: }
|