After Invocation Handling

Whilst the AccessDecisionManager is called by the AbstractSecurityInterceptor before proceeding with the secure object invocation, some applications need a way of modifying the object actually returned by the secure object invocation. Whilst you could easily implement your own AOP concern to achieve this, Spring Security provides a convenient hook that has several concrete implementations that integrate with its ACL capabilities.

Figure 22.2, “After Invocation Implementation” illustrates Spring Security's AfterInvocationManager and its concrete implementations.

Figure 22.2. After Invocation Implementation

After Invocation Implementation


Like many other parts of Spring Security, AfterInvocationManager has a single concrete implementation, AfterInvocationProviderManager, which polls a list of AfterInvocationProviders. Each AfterInvocationProvider is allowed to modify the return object or throw an AccessDeniedException. Indeed multiple providers can modify the object, as the result of the previous provider is passed to the next in the list. Let's now consider our ACL-aware implementations of AfterInvocationProvider.

Please be aware that if you're using AfterInvocationManager, you will still need configuration attributes that allow the MethodSecurityInterceptor's AccessDecisionManager to allow an operation. If you're using the typical Spring Security included AccessDecisionManager implementations, having no configuration attributes defined for a particular secure method invocation will cause each AccessDecisionVoter to abstain from voting. In turn, if the AccessDecisionManager property "allowIfAllAbstainDecisions" is false, an AccessDeniedException will be thrown. You may avoid this potential issue by either (i) setting "allowIfAllAbstainDecisions" to true (although this is generally not recommended) or (ii) simply ensure that there is at least one configuration attribute that an AccessDecisionVoter will vote to grant access for. This latter (recommended) approach is usually achieved through a ROLE_USER or ROLE_AUTHENTICATED configuration attribute

ACL-Aware AfterInvocationProviders

PLEASE NOTE: Acegi Security 1.0.3 contains a preview of a new ACL module. The new ACL module is a significant rewrite of the existing ACL module. The new module can be found under the org.springframework.security.acls package, with the old ACL module under org.springframework.security.acl. We encourage users to consider testing with the new ACL module and build applications with it. The old ACL module should be considered deprecated and may be removed from a future release. The following information relates to the new ACL package, and is thus recommended.

A common services layer method we've all written at one stage or another looks like this:

public Contact getById(Integer id);

Quite often, only principals with permission to read the Contact should be allowed to obtain it. In this situation the AccessDecisionManager approach provided by the AbstractSecurityInterceptor will not suffice. This is because the identity of the Contact is all that is available before the secure object is invoked. The AclAfterInvocationProvider delivers a solution, and is configured as follows:

<bean id="afterAclRead"
   class="org.springframework.security.afterinvocation.AclEntryAfterInvocationProvider">
  <constructor-arg ref="aclService"/>
  <constructor-arg>
    <list>
      <ref local="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
      <ref local="org.springframework.security.acls.domain.BasePermission.READ"/>
    </list>
  </constructor-arg>
</bean>

In the above example, the Contact will be retrieved and passed to the AclEntryAfterInvocationProvider. The provider will thrown an AccessDeniedException if one of the listed requirePermissions is not held by the Authentication. The AclEntryAfterInvocationProvider queries the AclService to determine the ACL that applies for this domain object to this Authentication.

Similar to the AclEntryAfterInvocationProvider is AclEntryAfterInvocationCollectionFilteringProvider. It is designed to remove Collection or array elements for which a principal does not have access. It never thrown an AccessDeniedException - simply silently removes the offending elements. The provider is configured as follows:

<bean id="afterAclCollectionRead"
    class="org.springframework.security.afterinvocation.AclEntryAfterInvocationCollectionFilteringProvider">
  <constructor-arg ref="aclService"/>
  <constructor-arg>
    <list>
      <ref local="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
      <ref local="org.springframework.security.acls.domain.BasePermission.READ"/>
    </list>
  </constructor-arg>
</bean>
    

As you can imagine, the returned Object must be a Collection or array for this provider to operate. It will remove any element if the AclManager indicates the Authentication does not hold one of the listed requirePermissions.

The Contacts sample application demonstrates these two AfterInvocationProviders.

ACL-Aware AfterInvocationProviders (old ACL module)

PLEASE NOTE: Acegi Security 1.0.3 contains a preview of a new ACL module. The new ACL module is a significant rewrite of the existing ACL module. The new module can be found under the org.springframework.security.acls package, with the old ACL module under org.springframework.security.acl. We encourage users to consider testing with the new ACL module and build applications with it. The old ACL module should be considered deprecated and may be removed from a future release.

A common services layer method we've all written at one stage or another looks like this:

public Contact getById(Integer id);

Quite often, only principals with permission to read the Contact should be allowed to obtain it. In this situation the AccessDecisionManager approach provided by the AbstractSecurityInterceptor will not suffice. This is because the identity of the Contact is all that is available before the secure object is invoked. The BasicAclAfterInvocationProvider delivers a solution, and is configured as follows:

<bean id="afterAclRead"
    class="org.springframework.security.afterinvocation.BasicAclEntryAfterInvocationProvider">
  <property name="aclManager" ref="aclManager"/>
  <property name="requirePermission">
    <list>
      <ref local="org.springframework.security.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
      <ref local="org.springframework.security.acl.basic.SimpleAclEntry.READ"/>
    </list>
  </property>
</bean> 
      

In the above example, the Contact will be retrieved and passed to the BasicAclEntryAfterInvocationProvider. The provider will thrown an AccessDeniedException if one of the listed requirePermissions is not held by the Authentication. The BasicAclEntryAfterInvocationProvider queries the AclManager to determine the ACL that applies for this domain object to this Authentication.

Similar to the BasicAclEntryAfterInvocationProvider is BasicAclEntryAfterInvocationCollectionFilteringProvider. It is designed to remove Collection or array elements for which a principal does not have access. It never thrown an AccessDeniedException - simply silently removes the offending elements. The provider is configured as follows:

<bean id="afterAclCollectionRead"
    class="org.springframework.security.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider">
  <property name="aclManager" ref="aclManager"/>
  <property name="requirePermission">
    <list>
      <ref local="org.springframework.security.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
      <ref local="org.springframework.security.acl.basic.SimpleAclEntry.READ"/>
    </list>
  </property>
</bean> 

As you can imagine, the returned Object must be a Collection or array for this provider to operate. It will remove any element if the AclManager indicates the Authentication does not hold one of the listed requirePermissions.

The Contacts sample application demonstrates these two AfterInvocationProviders.