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: package org.apache.jetspeed.security;
018:
019: import java.security.Permission;
020:
021: /**
022: * <p>Folder permission.</p>
023: * <p>This code was partially inspired from:</p>
024: * <ul>
025: * <li>The article : <a href="http://www-106.ibm.com/developerworks/library/j-jaas/">
026: * Extend JAAS for class instance-level authorization.</a></li>
027: * <li>The FilePermission implementation from the JDK in order to support recursive permissions & wild card</li>
028: * </ul>
029: * <p/>
030: * This class represents access to a portal content/folder or document. A FolderPermission consists
031: * of a pathname and a set of actions valid for that pathname.
032: * <p/>
033: * Pathname is the pathname of the folder or document granted the specified
034: * actions. A pathname that ends in "/*" (where "/" is
035: * the separator character) indicates all the folders and documents contained in that folder.
036: * A pathname that ends with "/-" indicates (recursively) all documents
037: * and subfolders contained in that directory. A pathname consisting of
038: * the special token "<<ALL FILES>>" matches <b>any</b> folder or document.
039: * <p/>
040: *
041: * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
042: * @author <a href="mailto:christophe.lombart@sword-technologies.com">Christophe Lombart</a>
043: * @version $Id: FolderPermission.java 516448 2007-03-09 16:25:47Z ate $
044: */
045: public class FolderPermission extends PortalResourcePermission {
046: public static final char RECURSIVE_CHAR = '-';
047: public static final char WILD_CHAR = '*';
048: public static final String WILD_CHAR_STR = new String(
049: new char[] { WILD_CHAR });
050: public static final char FOLDER_SEPARATOR = '/';
051: public static final String FOLDER_SEPARATOR_STR = new String(
052: new char[] { FOLDER_SEPARATOR });
053:
054: // does path indicate a folder? (wildcard or recursive)
055: private boolean folder;
056:
057: // is it a recursive directory specification?
058: private boolean recursive;
059:
060: private String cpath;
061:
062: /**
063: * <p>Constructor for FolderPermission.</p>
064: *
065: * @param name The portlet name.
066: * @param actions The actions on the portlet.
067: */
068: public FolderPermission(String name, String actions) {
069: super (name, actions);
070: parsePath();
071: }
072:
073: /**
074: * <p>Constructor for FolderPermission.</p>
075: *
076: * @param name The portlet name.
077: * @param mask The mask of actions on the portlet.
078: */
079: public FolderPermission(String name, int mask) {
080: super (name, mask);
081: parsePath();
082: }
083:
084: /**
085: * <p>Parses the path.</p>
086: */
087: private void parsePath() {
088: if ((cpath = getName()) == null)
089: throw new NullPointerException("name can't be null");
090:
091: if (cpath.equals("<<ALL FILES>>")) {
092: folder = true;
093: recursive = true;
094: cpath = "";
095: return;
096: }
097: int len = cpath.length();
098:
099: if (len == 0) {
100: throw new IllegalArgumentException(
101: "invalid folder reference");
102: }
103:
104: char last = cpath.charAt(len - 1);
105:
106: if (last == RECURSIVE_CHAR
107: && (len == 1 || cpath.charAt(len - 2) == FOLDER_SEPARATOR)) {
108: folder = true;
109: recursive = true;
110: cpath = cpath.substring(0, --len);
111: } else if (last == WILD_CHAR
112: && (len == 1 || cpath.charAt(len - 2) == FOLDER_SEPARATOR)) {
113: folder = true;
114: //recursive = false;
115: cpath = cpath.substring(0, --len);
116: }
117: }
118:
119: /**
120: * Checks if this FolderPermission object "implies" the specified permission.
121: * <p/>
122: * More specifically, this method returns true if:<p>
123: * <ul>
124: * <li> <i>p</i> is an instanceof FolderPermission,<p>
125: * <li> <i>p</i>'s actions are a proper subset of this
126: * object's actions, and <p>
127: * <li> <i>p</i>'s pathname is implied by this object's
128: * pathname. For example, "/tmp/*" implies "/tmp/foo", since
129: * "/tmp/*" encompasses the "/tmp" folder and all subfolders or documents in that
130: * directory, including the one named "foo".
131: * </ul>
132: *
133: * @param p the permission to check against.
134: * @return true if the specified permission is implied by this object,
135: * false if not.
136: */
137: public boolean implies(Permission p) {
138: if (!(p instanceof FolderPermission)) {
139: return false;
140: }
141:
142: FolderPermission that = (FolderPermission) p;
143: return ((this .mask & that.mask) == that.mask)
144: && impliesIgnoreMask(that);
145: }
146:
147: /**
148: * Checks if the Permission's actions are a proper subset of the
149: * this object's actions. Returns the effective mask iff the
150: * this FolderPermission's path also implies that FolderPermission's path.
151: *
152: * @param that the FolderPermission to check against.
153: * @return the effective mask
154: */
155: boolean impliesIgnoreMask(FolderPermission that) {
156: if (this .folder) {
157: if (this .recursive) {
158: // make sure that.path is longer then path so
159: // something like /foo/- does not imply /foo
160: if (that.folder) {
161: return (that.cpath.length() >= this .cpath.length())
162: && that.cpath.startsWith(this .cpath);
163: } else {
164: return ((that.cpath.length() >= this .cpath.length()) && that.cpath
165: .startsWith(this .cpath));
166: }
167: } else {
168: if (that.folder) {
169: // if the permission passed in is a folder
170: // specification, make sure that a non-recursive
171: // permission (i.e., this object) can't imply a recursive
172: // permission.
173: if (that.recursive)
174: return false;
175: else
176: return (this .cpath.equals(that.cpath));
177: } else {
178: int last = that.cpath.lastIndexOf(FOLDER_SEPARATOR);
179: if (last == -1)
180: return false;
181: else {
182: // this.cpath.equals(that.cpath.substring(0, last+1));
183: // Use regionMatches to avoid creating new string
184:
185: return (this .cpath.length() == (last + 1))
186: && this .cpath.regionMatches(0,
187: that.cpath, 0, last + 1);
188: }
189: }
190: }
191: } else {
192: return (this .cpath.equals(that.cpath));
193: }
194: }
195:
196: /**
197: * Checks two FolderPermission objects for equality. Checks that <i>obj</i> is
198: * a FolderPermission, and has the same pathname and actions as this object.
199: * <p/>
200: *
201: * @param obj the object we are testing for equality with this object.
202: * @return true if obj is a FolderPermission, and has the same pathname and
203: * actions as this FolderPermission object.
204: */
205: public boolean equals(Object obj) {
206: if (obj == this )
207: return true;
208:
209: if (!(obj instanceof FolderPermission))
210: return false;
211:
212: FolderPermission that = (FolderPermission) obj;
213:
214: return (this .mask == that.mask)
215: && this .cpath.equals(that.cpath)
216: && (this .folder == that.folder)
217: && (this .recursive == that.recursive);
218: }
219:
220: /**
221: * Returns the hash code value for this object.
222: *
223: * @return a hash code value for this object.
224: */
225:
226: public int hashCode() {
227: return this.cpath.hashCode();
228: }
229:
230: }
|