Hi,
I pretty much gave up on the method interceptor problem. I decided to move on with my proof of concept by manually invoking the AclEntryAfterInvocationCollectionFilteringProvider from my EJB. Here is my solution.
Here is the context.xml file. Note that I am not using spring's authentication as users are already logged in and a principal object exists in the EJB context.
Code:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:util="http://www.springframework.org/schema/util" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
">
<!-- ACL Service Configuration -->
<bean id="aclService"
class="org.springframework.security.acls.jdbc.JdbcAclService">
<constructor-arg ref="dataSource" />
<constructor-arg ref="lookupStrategy" />
<constructor-arg ref="aclCache" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@the:1521:oss" />
<property name="username" value="edufresne" />
<property name="password" value="ossadmin" />
</bean>
<bean class="org.springframework.security.acls.jdbc.BasiLookupStrategy" id="lookupStrategy">
<constructor-arg ref="dataSource" />
<constructor-arg ref="aclCache" />
<constructor-arg ref="aclAuthzStrategy" />
<constructor-arg ref="aclAuditLogger" />
</bean>
<bean class="some.path.NullAclCache" id="aclCache" />
<bean class="org.springframework.security.acls.domain.ConsoleAuditLogger"
id="aclAuditLogger" />
<bean
class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl"
id="aclAuthzStrategy">
<constructor-arg>
<array>
<ref local="aclAdminAuthority" />
<ref local="aclAdminAuthority" />
<ref local="aclAdminAuthority" />
</array>
</constructor-arg>
</bean>
<bean
class="org.springframework.security.core.authority.GrantedAuthorityImpl"
id="aclAdminAuthority">
<constructor-arg value="ROLE_ADMIN" />
</bean>
<!-- Collection Filtering Configuration -->
<bean id="afterAclCollectionRead"
class="org.springframework.security.acls.afterinvocation.AclEntryAfterInvocationCollectionFilteringProvider">
<constructor-arg ref="aclService" />
<constructor-arg>
<list>
<ref local="aclReadPermission" />
</list>
</constructor-arg>
</bean>
<bean id="aclReadPermission"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField">
<value>org.springframework.security.acls.domain.BasePermission.READ</value>
</property>
</bean>
</beans>
I created the ACL database schema using the SQL scripts provided with spring security. I added data using the following SQL:
Code:
insert into acl_class (class) values ('some.path.Item');
insert into acl_sid (principal, sid) values (1, 'someuser');
insert into acl_object_identity (object_id_class,object_id_identity,parent_object,owner_sid,entries_inheriting)
select cl.id, 12345, null, sid.id, 0
from acl_class cl, acl_sid sid
where cl.class='some.path.Item' and sid.sid='someuser';
insert into acl_entry (acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)
select oi.id, 1, si.id, 1, 1, 1, 1
from acl_object_identity oi, acl_sid si
where si.sid = 'someuser';
Since the EJB I wanted to secure resides in an EJP project, I could not trigger the initialization of the spring application context in a web.xml because there is no such file. Therefore, for this simple proof of concept, I initialiazed the application context directly from the EJB that is going to use it. I used the @PostConstruct annotation to trigger the initialization of the ClassPathXmlApplicationContext. Here is the EJB's code:
Code:
...
@Resource
private EJBContext ejbContext;
...
private ApplicationContext springContext;
...
@PostConstruct
public void init() {
springContext = new
ClassPathXmlApplicationContext("classpath*:context.xml");
}
...
@Override
public Collection<Items> retrieveAllItems() {
Collection<Items> results = ... // obtain the items.
// --- Filter out the items the requesting user does not have access to ---
// Create an empty authorities collection.
Collection<GrantedAuthorityImpl> authorities = new ArrayList<GrantedAuthorityImpl>();
// Create an authentication token containing the principal's info.
PreAuthenticatedAuthenticationToken preAuthToken =
new PreAuthenticatedAuthenticationToken(ejbContext.getCallerPrincipal(), null, authorities);
// Create an config collection that hold the required permission name.
SecurityConfig config = new SecurityConfig("AFTER_ACL_COLLECTION_READ");
Collection<ConfigAttribute> configs = new ArrayList<ConfigAttribute>();
configs.add(config);
// Retrieve the afterAclCollectionRead.
AclEntryAfterInvocationCollectionFilteringProvider afterAclCollectionRead =
(AclEntryAfterInvocationCollectionFilteringProvider) springContext.getBean("afterAclCollectionRead");
// Perform the filtering
Collection<Items> filteredResult =
(Collection<Items>) afterAclCollectionRead.decide(preAuthToken, null, configs, results);
return filteredResult;
}
...
The code starts by setting up the required spring objects for collection filtering. It creates a list of empty granted authorities. Usually, this list contains the roles the current user has (SIDs). This list is later used to decide if the a collection entry should be kept or not. My example uses the principal's name as the SID so we leave the list of granted authorities empty. The pre-authentication token is the object spring reads the SIDs from. Therefore, we retrieve the EJB's principal information and insert it in this token along with the empty list of granted authorities. Next we need to specify the security configuration to apply when filtering. This is required as the acl entry after invocation collection filtering provider expects to see this config in order to decide if it must filter the collection or not. We can then retrieve the acl entry after invocation collection filtering provider from the application context. Finally, we invoke the decide method manually and let spring security's ACL implementation perform its magic!
My database only contains two Items instances and only one of them has an ACL entry. Therefore, when I execute the retrievedAllItems method while I am logged on as someuser, I only get the Item that has an ACL entry.
That's it! Thank you very much for all your support. I hope this solution will be usefull for some of you.
Cheers!