View Javadoc

1   /* 
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8   *
9   *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17  package org.apache.jetspeed.security;
18  
19  import java.security.Permission;
20  
21  /***
22   * <p>Folder permission.</p>
23   * <p>This code was partially inspired from:</p>
24   * <ul>
25   * <li>The article : <a href="http://www-106.ibm.com/developerworks/library/j-jaas/">
26   * Extend JAAS for class instance-level authorization.</a></li>
27   * <li>The FilePermission implementation from the JDK in order to support recursive permissions & wild card</li>
28   * </ul>
29   * <p/>
30   * This class represents access to a portal content/folder or document.  A FolderPermission consists
31   * of a pathname and a set of actions valid for that pathname.
32   * <p/>
33   * Pathname is the pathname of the folder or document granted the specified
34   * actions. A pathname that ends in "/*" (where "/" is
35   * the  separator character) indicates all the folders and documents contained in that folder.
36   * A pathname that ends with "/-" indicates (recursively) all documents
37   * and subfolders contained in that directory. A pathname consisting of
38   * the special token "&lt;&lt;ALL FILES&gt;&gt;" matches <b>any</b> folder or document.
39   * <p/>
40   *
41   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
42   * @author <a href="mailto:christophe.lombart@sword-technologies.com">Christophe Lombart</a>
43   * @version $Id: FolderPermission.java 516448 2007-03-09 16:25:47Z ate $
44   */
45  public class FolderPermission extends PortalResourcePermission
46  {
47      public static final char RECURSIVE_CHAR = '-';
48      public static final char WILD_CHAR = '*';
49      public static final String WILD_CHAR_STR = new String(new char[]{WILD_CHAR});
50      public static final char FOLDER_SEPARATOR = '/';
51      public static final String FOLDER_SEPARATOR_STR = new String(new char[]{FOLDER_SEPARATOR});
52  
53      // does path indicate a folder? (wildcard or recursive)
54      private boolean folder;
55  
56      // is it a recursive directory specification?
57      private boolean recursive;
58  
59      private String cpath;
60  
61      /***
62       * <p>Constructor for FolderPermission.</p>
63       *
64       * @param name    The portlet name.
65       * @param actions The actions on the portlet.
66       */
67      public FolderPermission(String name, String actions)
68      {
69          super(name, actions);
70          parsePath();
71      }
72  
73      /***
74       * <p>Constructor for FolderPermission.</p>
75       *
76       * @param name The portlet name.
77       * @param mask The mask of actions on the portlet.
78       */
79      public FolderPermission(String name, int mask)
80      {
81          super(name, mask);
82          parsePath();
83      }
84  
85      /***
86       * <p>Parses the path.</p>
87       */
88      private void parsePath()
89      {
90          if ((cpath = getName()) == null)
91              throw new NullPointerException("name can't be null");
92  
93          if (cpath.equals("<<ALL FILES>>"))
94          {
95              folder = true;
96              recursive = true;
97              cpath = "";
98              return;
99          }
100         int len = cpath.length();
101 
102         if (len == 0)
103         {
104             throw new IllegalArgumentException("invalid folder reference");
105         }
106 
107         char last = cpath.charAt(len - 1);
108 
109         if (last == RECURSIVE_CHAR && (len == 1 || cpath.charAt(len - 2) == FOLDER_SEPARATOR))
110         {
111             folder = true;
112             recursive = true;
113             cpath = cpath.substring(0, --len);
114         }
115         else if (last == WILD_CHAR && (len == 1 || cpath.charAt(len - 2) == FOLDER_SEPARATOR))
116         {
117             folder = true;
118             //recursive = false;
119             cpath = cpath.substring(0, --len);
120         }
121     }
122 
123     /***
124      * Checks if this FolderPermission object "implies" the specified permission.
125      * <p/>
126      * More specifically, this method returns true if:<p>
127      * <ul>
128      * <li> <i>p</i> is an instanceof FolderPermission,<p>
129      * <li> <i>p</i>'s actions are a proper subset of this
130      * object's actions, and <p>
131      * <li> <i>p</i>'s pathname is implied by this object's
132      * pathname. For example, "/tmp/*" implies "/tmp/foo", since
133      * "/tmp/*" encompasses the "/tmp" folder and all subfolders or documents in that
134      * directory, including the one named "foo".
135      * </ul>
136      *
137      * @param p the permission to check against.
138      * @return true if the specified permission is implied by this object,
139      *         false if not.
140      */
141     public boolean implies(Permission p)
142     {
143         if (!(p instanceof FolderPermission))
144         {
145             return false;
146         }
147 
148         FolderPermission that = (FolderPermission) p;
149         return ((this.mask & that.mask) == that.mask) && impliesIgnoreMask(that);
150     }
151 
152     /***
153      * Checks if the Permission's actions are a proper subset of the
154      * this object's actions. Returns the effective mask iff the
155      * this FolderPermission's path also implies that FolderPermission's path.
156      *
157      * @param that the FolderPermission to check against.
158      * @return the effective mask
159      */
160     boolean impliesIgnoreMask(FolderPermission that)
161     {
162         if (this.folder)
163         {
164             if (this.recursive)
165             {
166                 // make sure that.path is longer then path so
167                 // something like /foo/- does not imply /foo
168                 if (that.folder)
169                 {
170                     return (that.cpath.length() >= this.cpath.length()) && that.cpath.startsWith(this.cpath);
171                 }
172                 else
173                 {
174                     return ((that.cpath.length() >= this.cpath.length()) && that.cpath.startsWith(this.cpath));
175                 }
176             }
177             else
178             {
179                 if (that.folder)
180                 {
181                     // if the permission passed in is a folder
182                     // specification, make sure that a non-recursive
183                     // permission (i.e., this object) can't imply a recursive
184                     // permission.
185                     if (that.recursive)
186                         return false;
187                     else
188                         return (this.cpath.equals(that.cpath));
189                 }
190                 else
191                 {
192                     int last = that.cpath.lastIndexOf(FOLDER_SEPARATOR);
193                     if (last == -1)
194                         return false;
195                     else
196                     {
197                         // this.cpath.equals(that.cpath.substring(0, last+1));
198                         // Use regionMatches to avoid creating new string
199 
200                         return (this.cpath.length() == (last + 1)) && this.cpath.regionMatches(0, that.cpath, 0, last + 1);
201                     }
202                 }
203             }
204         }
205         else
206         {
207             return (this.cpath.equals(that.cpath));
208         }
209     }
210 
211     /***
212      * Checks two FolderPermission objects for equality. Checks that <i>obj</i> is
213      * a FolderPermission, and has the same pathname and actions as this object.
214      * <p/>
215      *
216      * @param obj the object we are testing for equality with this object.
217      * @return true if obj is a FolderPermission, and has the same pathname and
218      *         actions as this FolderPermission object.
219      */
220     public boolean equals(Object obj)
221     {
222         if (obj == this)
223             return true;
224 
225         if (!(obj instanceof FolderPermission))
226             return false;
227 
228         FolderPermission that = (FolderPermission) obj;
229 
230         return (this.mask == that.mask) && this.cpath.equals(that.cpath) && (this.folder == that.folder)
231                 && (this.recursive == that.recursive);
232     }
233 
234     /***
235      * Returns the hash code value for this object.
236      *
237      * @return a hash code value for this object.
238      */
239 
240     public int hashCode()
241     {
242         return this.cpath.hashCode();
243     }
244 
245 
246 }