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.vote;
    17
    18  import org.acegisecurity.AuthorizationServiceException;
    19  import org.acegisecurity.ConfigAttribute;
    20  import org.acegisecurity.acl.AclEntry;
    21  import org.acegisecurity.acl.AclManager;
    22
    23  import org.aopalliance.intercept.MethodInvocation;
    24
    25  import org.springframework.util.Assert;
    26
    27  import java.lang.reflect.Method;
    28
    29
    30  /***
    31   * <p>
    32   * Given a domain object instance passed as a method argument, ensures the
    33   * principal has appropriate permission as defined by the {@link AclManager}.
    34   * </p>
    35   *
    36   * <p>
    37   * The <code>AclManager</code> is used to retrieve the access control list
    38   * (ACL) permissions associated with a domain object instance for the current
    39   * <code>Authentication</code> object. This class is designed to process
    40   * {@link AclEntry}s that are subclasses of {@link
    41   * org.acegisecurity.acl.basic.BasicAclEntry} only. Generally these are
    42   * obtained by using the {@link
    43   * org.acegisecurity.acl.basic.BasicAclProvider}.
    44   * </p>
    45   *
    46   * <p>
    47   * The voter will vote if any  {@link ConfigAttribute#getAttribute()} matches
    48   * the {@link #processConfigAttribute}. The provider will then locate the
    49   * first method argument of type {@link #processDomainObjectClass}. Assuming
    50   * that method argument is non-null, the provider will then lookup the ACLs
    51   * from the <code>AclManager</code> and ensure the principal is {@link
    52   * org.acegisecurity.acl.basic.BasicAclEntry#isPermitted(int)} for at least
    53   * one of the {@link #requirePermission}s.
    54   * </p>
    55   *
    56   * <p>
    57   * If the method argument is <code>null</code>, the voter will abstain from
    58   * voting. If the method argument could not be found, an {@link
    59   * org.acegisecurity.AuthorizationServiceException} will be thrown.
    60   * </p>
    61   *
    62   * <p>
    63   * In practical terms users will typically setup a number of
    64   * <code>BasicAclEntryVoter</code>s. Each will have a different {@link
    65   * #processDomainObjectClass}, {@link #processConfigAttribute} and {@link
    66   * #requirePermission} combination. For example, a small application might
    67   * employ the following instances of <code>BasicAclEntryVoter</code>:
    68   *
    69   * <ul>
    70   * <li>
    71   * Process domain object class <code>BankAccount</code>, configuration
    72   * attribute <code>VOTE_ACL_BANK_ACCONT_READ</code>, require permission
    73   * <code>SimpleAclEntry.READ</code>
    74   * </li>
    75   * <li>
    76   * Process domain object class <code>BankAccount</code>, configuration
    77   * attribute <code>VOTE_ACL_BANK_ACCOUNT_WRITE</code>, require permission list
    78   * <code>SimpleAclEntry.WRITE</code> and <code>SimpleAclEntry.CREATE</code>
    79   * (allowing the principal to have <b>either</b> of these two permissions
    80   * </li>
    81   * <li>
    82   * Process domain object class <code>Customer</code>, configuration attribute
    83   * <code>VOTE_ACL_CUSTOMER_READ</code>, require permission
    84   * <code>SimpleAclEntry.READ</code>
    85   * </li>
    86   * <li>
    87   * Process domain object class <code>Customer</code>, configuration attribute
    88   * <code>VOTE_ACL_CUSTOMER_WRITE</code>, require permission list
    89   * <code>SimpleAclEntry.WRITE</code> and <code>SimpleAclEntry.CREATE</code>
    90   * </li>
    91   * </ul>
    92   *
    93   * Alternatively, you could have used a common superclass or interface for the
    94   * {@link #processDomainObjectClass} if both <code>BankAccount</code> and
    95   * <code>Customer</code> had common parents.
    96   * </p>
    97   *
    98   * <p>
    99   * If the principal does not have sufficient permissions, the voter will vote
    100  * to deny access.
    101  * </p>
    102  *
    103  * <p>
    104  * The <code>AclManager</code> is allowed to return any implementations of
    105  * <code>AclEntry</code> it wishes. However, this provider will only be able
    106  * to validate against <code>AbstractBasicAclEntry</code>s, and thus a vote to
    107  * deny access will be made if no <code>AclEntry</code> is of type
    108  * <code>AbstractBasicAclEntry</code>.
    109  * </p>
    110  *
    111  * <p>
    112  * All comparisons and prefixes are case sensitive.
    113  * </p>
    114  *
    115  * @author Ben Alex
    116  * @version $Id: AbstractAclVoter.java,v 1.2 2005/11/17 00:55:47 benalex Exp $
    117  */
    118 public abstract class AbstractAclVoter implements AccessDecisionVoter {
    119     //~ Instance fields ========================================================
    120
    121     private Class processDomainObjectClass;
    122
    123     //~ Methods ================================================================
    124
    125     public void setProcessDomainObjectClass(Class processDomainObjectClass) {
    126         Assert.notNull(processDomainObjectClass,
    127             "processDomainObjectClass cannot be set to null");
    128         this.processDomainObjectClass = processDomainObjectClass;
    129     }
    130
    131     public Class getProcessDomainObjectClass() {
    132         return processDomainObjectClass;
    133     }
    134
    135     /***
    136      * This implementation supports only
    137      * <code>MethodSecurityInterceptor</code>, because it queries the
    138      * presented <code>MethodInvocation</code>.
    139      *
    140      * @param clazz the secure object
    141      *
    142      * @return <code>true</code> if the secure object is
    143      *         <code>MethodInvocation</code>, <code>false</code> otherwise
    144      */
    145     public boolean supports(Class clazz) {
    146         return (MethodInvocation.class.isAssignableFrom(clazz));
    147     }
    148
    149     protected Object getDomainObjectInstance(Object secureObject) {
    150         MethodInvocation invocation = (MethodInvocation) secureObject;
    151
    152         // Check if this MethodInvocation provides the required argument
    153         Method method = invocation.getMethod();
    154         Class[] params = method.getParameterTypes();
    155
    156         for (int i = 0; i < params.length; i++) {
    157             if (processDomainObjectClass.isAssignableFrom(params[i])) {
    158                 return invocation.getArguments()[i];
    159             }
    160         }
    161
    162         throw new AuthorizationServiceException("MethodInvocation: "
    163             + invocation + " did not provide any argument of type: "
    164             + processDomainObjectClass);
    165     }
    166 }