View Javadoc

1   /* Copyright 2004, 2005 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.acegisecurity.acl.basic;
17  
18  import org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  import org.springframework.util.Assert;
21  
22  import java.util.Arrays;
23  
24  
25  /***
26   * Abstract implementation of {@link BasicAclEntry}.
27   * 
28   * <P>
29   * Provides core bit mask handling methods.
30   * </p>
31   *
32   * @author Ben Alex
33   * @version $Id: AbstractBasicAclEntry.java,v 1.5 2005/11/17 00:55:47 benalex Exp $
34   */
35  public abstract class AbstractBasicAclEntry implements BasicAclEntry {
36      //~ Static fields/initializers =============================================
37  
38      private static final Log logger = LogFactory.getLog(AbstractBasicAclEntry.class);
39  
40      //~ Instance fields ========================================================
41  
42      private AclObjectIdentity aclObjectIdentity;
43      private AclObjectIdentity aclObjectParentIdentity;
44      private Object recipient;
45      private int[] validPermissions;
46      private int mask = 0; // default means no permissions
47  
48      //~ Constructors ===========================================================
49  
50      public AbstractBasicAclEntry(Object recipient,
51          AclObjectIdentity aclObjectIdentity,
52          AclObjectIdentity aclObjectParentIdentity, int mask) {
53          Assert.notNull(recipient, "recipient cannot be null");
54  
55          Assert.notNull(aclObjectIdentity, "aclObjectIdentity cannot be null");
56  
57          validPermissions = getValidPermissions();
58          Arrays.sort(validPermissions);
59  
60          for (int i = 0; i < validPermissions.length; i++) {
61              if (logger.isDebugEnabled()) {
62                  logger.debug("Valid permission:   "
63                          + printPermissionsBlock(validPermissions[i]) + " "
64                          + printBinary(validPermissions[i]) + " ("
65                          + validPermissions[i] + ")");
66              }
67          }
68  
69          this.recipient = recipient;
70          this.aclObjectIdentity = aclObjectIdentity;
71          this.aclObjectParentIdentity = aclObjectParentIdentity;
72          this.mask = mask;
73      }
74  
75      /***
76       * A protected constructor for use by Hibernate.
77       */
78      protected AbstractBasicAclEntry() {
79          validPermissions = getValidPermissions();
80          Arrays.sort(validPermissions);
81      }
82  
83      //~ Methods ================================================================
84  
85      public void setAclObjectIdentity(AclObjectIdentity aclObjectIdentity) {
86          this.aclObjectIdentity = aclObjectIdentity;
87      }
88  
89      public AclObjectIdentity getAclObjectIdentity() {
90          return this.aclObjectIdentity;
91      }
92  
93      public void setAclObjectParentIdentity(
94          AclObjectIdentity aclObjectParentIdentity) {
95          this.aclObjectParentIdentity = aclObjectParentIdentity;
96      }
97  
98      public AclObjectIdentity getAclObjectParentIdentity() {
99          return this.aclObjectParentIdentity;
100     }
101 
102     /***
103      * Subclasses must indicate the permissions they support. Each base
104      * permission should be an integer with a base 2. ie: the first permission
105      * is 2^^0 (1), the second permission is 2^^1 (2), the third permission is
106      * 2^^2 (4) etc. Each base permission should be exposed by the subclass as
107      * a <code>public static final int</code>. It is further recommended that
108      * valid combinations of permissions are also exposed as <code>public
109      * static final int</code>s.
110      * 
111      * <P>
112      * This method returns all permission integers that are allowed to be used
113      * together. <B>This must include any combinations of valid
114      * permissions</b>. So if the permissions indicated by 2^^2 (4) and 2^^1
115      * (2) can be used together, one of the integers returned by this method
116      * must be 6 (4 + 2). Otherwise attempts to set the permission will be
117      * rejected, as the final resulting mask will be rejected.
118      * </p>
119      * 
120      * <P>
121      * Whilst it may seem unduly time onerous to return every valid permission
122      * <B>combination</B>, doing so delivers maximum flexibility in ensuring
123      * ACLs only reflect logical combinations. For example, it would be
124      * inappropriate to grant a "read" and "write" permission along with an
125      * "unrestricted" permission, as the latter implies the former
126      * permissions.
127      * </p>
128      *
129      * @return <b>every</b> valid combination of permissions
130      */
131     public abstract int[] getValidPermissions();
132 
133     /***
134      * Outputs the permissions in a human-friendly format. For example, this
135      * method may return "CR-D" to indicate the passed integer permits create,
136      * permits read, does not permit update, and permits delete.
137      *
138      * @param i the integer containing the mask which should be printed
139      *
140      * @return the human-friend formatted block
141      */
142     public abstract String printPermissionsBlock(int i);
143 
144     public void setMask(int mask) {
145         this.mask = mask;
146     }
147 
148     public int getMask() {
149         return this.mask;
150     }
151 
152     public boolean isPermitted(int permissionToCheck) {
153         return isPermitted(this.mask, permissionToCheck);
154     }
155 
156     public void setRecipient(Object recipient) {
157         this.recipient = recipient;
158     }
159 
160     public Object getRecipient() {
161         return this.recipient;
162     }
163 
164     public int addPermission(int permissionToAdd) {
165         return addPermissions(new int[] {permissionToAdd});
166     }
167 
168     public int addPermissions(int[] permissionsToAdd) {
169         if (logger.isDebugEnabled()) {
170             logger.debug("BEFORE Permissions: " + printPermissionsBlock(mask)
171                 + " " + printBinary(mask) + " (" + mask + ")");
172         }
173 
174         for (int i = 0; i < permissionsToAdd.length; i++) {
175             if (logger.isDebugEnabled()) {
176                 logger.debug("Add     permission: "
177                     + printPermissionsBlock(permissionsToAdd[i]) + " "
178                     + printBinary(permissionsToAdd[i]) + " ("
179                     + permissionsToAdd[i] + ")");
180             }
181 
182             this.mask |= permissionsToAdd[i];
183         }
184 
185         if (Arrays.binarySearch(validPermissions, this.mask) < 0) {
186             throw new IllegalArgumentException(
187                 "Resulting permission set will be invalid.");
188         } else {
189             if (logger.isDebugEnabled()) {
190                 logger.debug("AFTER  Permissions: "
191                     + printPermissionsBlock(mask) + " " + printBinary(mask)
192                     + " (" + mask + ")");
193             }
194 
195             return this.mask;
196         }
197     }
198 
199     public int deletePermission(int permissionToDelete) {
200         return deletePermissions(new int[] {permissionToDelete});
201     }
202 
203     public int deletePermissions(int[] permissionsToDelete) {
204         if (logger.isDebugEnabled()) {
205             logger.debug("BEFORE Permissions: " + printPermissionsBlock(mask)
206                 + " " + printBinary(mask) + " (" + mask + ")");
207         }
208 
209         for (int i = 0; i < permissionsToDelete.length; i++) {
210             if (logger.isDebugEnabled()) {
211                 logger.debug("Delete  permission: "
212                     + printPermissionsBlock(permissionsToDelete[i]) + " "
213                     + printBinary(permissionsToDelete[i]) + " ("
214                     + permissionsToDelete[i] + ")");
215             }
216 
217             this.mask &= ~permissionsToDelete[i];
218         }
219 
220         if (Arrays.binarySearch(validPermissions, this.mask) < 0) {
221             throw new IllegalArgumentException(
222                 "Resulting permission set will be invalid.");
223         } else {
224             if (logger.isDebugEnabled()) {
225                 logger.debug("AFTER  Permissions: "
226                     + printPermissionsBlock(mask) + " " + printBinary(mask)
227                     + " (" + mask + ")");
228             }
229 
230             return this.mask;
231         }
232     }
233 
234     /***
235      * Outputs the permissions in human-friendly format for the current
236      * <code>AbstractBasicAclEntry</code>'s mask.
237      *
238      * @return the human-friendly formatted block for this instance
239      */
240     public String printPermissionsBlock() {
241         return printPermissionsBlock(this.mask);
242     }
243 
244     public String toString() {
245         StringBuffer sb = new StringBuffer();
246         sb.append(getClass().getName());
247         sb.append("[").append(aclObjectIdentity).append(",").append(recipient);
248         sb.append("=").append(printPermissionsBlock(mask)).append(" ");
249         sb.append(printBinary(mask)).append(" (");
250         sb.append(mask).append(")").append("]");
251 
252         return sb.toString();
253     }
254 
255     public int togglePermission(int permissionToToggle) {
256         this.mask ^= permissionToToggle;
257 
258         if (Arrays.binarySearch(validPermissions, this.mask) < 0) {
259             throw new IllegalArgumentException(
260                 "Resulting permission set will be invalid.");
261         } else {
262             return this.mask;
263         }
264     }
265 
266     protected boolean isPermitted(int maskToCheck, int permissionToCheck) {
267         return ((maskToCheck & permissionToCheck) == permissionToCheck);
268     }
269 
270     private String printBinary(int i) {
271         String s = Integer.toString(i, 2);
272 
273         String pattern = "................................";
274 
275         String temp1 = pattern.substring(0, pattern.length() - s.length());
276 
277         String temp2 = temp1 + s;
278 
279         return temp2.replace('0', '.');
280     }
281 }