SourceForge.net Logo

Declarative Management of ACL Entries

 

One of the issues with ACL security is to manage (insert, delete) ACL entries for the domain objects which are created and deleted during user scenarios. For example, we may have such a security requirement; any Book object which is privately added to library should only be accessed by its owner that is user who added that book into the library. Similar to creation, we may need to remove previously created ACL entries because of the removal of corresponding domain objects from the system, or because of some other security requirement.

 

Acegi provides a BasicAclExtendedDao interface and JdbcExtendedDaoImpl as its concrete implementation to provide methods for management of ACL entries in the database. However, it would be very nice to have a higher level mechanism to decleratively specify when to create or delete those ACL entries, and automatically create or delete them.

 

We had developed such a mechanism in one of my previous projects. I have revised the solution and adapted it to support Java 5 annotations. Let’s walk over the solution.

 

public interface LibraryService {

      @Secured({"ROLE_READER","ROLE_WRITER"})

      @AclCreate(recipientMaskPairs={"ROLE_READER=r","ROLE_WRITER=rw"})

      @Transactional(propagation=Propagation.REQUIRED)

      public void insertBook(Book book);

     

      @Secured({"ROLE_READER","ROLE_WRITER"})

      @AclCreate(recipientMaskPairs={"principal=rwc"})

      @Transactional(propagation=Propagation.REQUIRED)

      public void insertPrivateBook(Book book);

           

      @Secured({"ROLE_READER","ROLE_WRITER","ACL_DELETE"})

      @AclDelete(processDomainObjectClass=Book.class)

      @Transactional(propagation=Propagation.REQUIRED)

      public void deleteBook(Book book);

}

 

When a new book object inserted using insertBook method, we declare with AclCreate annotation that, users with ROLE_READER will have READ, and users with ROLE_WRITER will have WRITE permisson on that object.

 

If we create that object using insertPrivateBook method, then AclCreate annotation declares that only current user(principal) will have READ, WRITE and CREATE permissions, and no one else will be able to access that book object.

 

When we delete a book instance from database, AclDelete also instructs that its corresponding ACL entries should also be deleted. processDomainObjectClass attribute is available for both annotations, and is used to specify exactly for what type of domain objects those annotations are applicable. If not specified they are applicable for all types.

 

<bean id="aclManagementInterceptor" class="org.acegisecurity.acl.basic.management.

interceptor.AclManagementInterceptor">

            <property name="disabled">

                  <value>false</value>

            </property>

            <property name="aclAttributeSource">

                  <ref local="aclAttributeSource"/>

            </property>

            <property name="aclService">

                  <ref local="aclService"/>

            </property>

      </bean>

     

<bean id="aclAttributeSource" class="org.acegisecurity.acl.basic.management.

attributes.AclAttributeSource">

            <property name="attributes">

                  <ref local="aclManagementAnnotationAttributes"/>

            </property>

      </bean>

     

<bean id="aclManagementAnnotationAttributes" class="org.acegisecurity.acl.basic.management.

attributes.annotation.AclManagementAnnotationAttributes"/>

 

We employ aclManagementInterceptor to intercept those service method calls and process any available AclCreate and AclDelete attributes. Real work is done by aclService. You can look at autoProxyCreator bean mentioned previously to see how this interceptor is configured.

 

      <bean id="aclService" class="org.acegisecurity.acl.basic.management.interceptor.AclServiceImpl">

            <property name="basicAclExtendedDao">

                  <ref local="basicAclExtendedDao"/>

            </property>

            <property name="objectAccessDecisionManager">

                  <ref bean="accessDecisionManager"/>

            </property>

      </bean>

 

aclService bean uses basicAclExtendedDao to perform its database inserts and deletes. Apart from basic ACL entry insertions and deletetions, aclService provides methods to get existing ACLs of a domain object, or check if a domain object is updateable or deleteable. It is also possible to update permission masks of existing ACL entries.

 

Supported permission mask characters for recipientMaskPairs are as follows;

 

rR

SimpleAclEntry.READ

wW

SimpleAclEntry.WRITE

dD

SimpleAclEntry.DELETE

aA

SimpleAclEntry.ADMINISTRATION

cC

SimpleAclEntry.CREATE

nN

SimpleAclEntry.NOTHING

 

You can combine them in any order.

ACL Tables and Sample Entries

 

There are two tables used by ACL infrastructure. There is enough information in Reference guide of Acegi Security about how to create and configure those tables in your own database. Here I want to show sample entries here in order to make the big picture a bit clearer.

 

The first table is acl_object_identity. It keeps which domain objects have ACL entries and type of those entries.

 

 

For example, domain objects with  com.ksevindik.acegi.portlet.samples.libraryservice.Book type with ids 1, 2 and 3 have acl entries in our sample. Notice that object_identity is kept in className + “:” + id format. It is therefore very important for your domain objects to have surrogate keys accessible with getId() method.

 

The second table is called as acl_permission which keeps which permissions, corresponding to acl entries in the first table, are defined for specific users or roles.

 

 

For example, records with id 1,2 and 3 in acl_object_identity have permission READ (2) for role ROLE_READER, and permission READ + WRITE (6) for ROLE_READER. Moreover, acl_object_identity record with id 3 has permission READ + WRITE for principal testuser.