View Javadoc

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