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 }