I'm trying to implement spring security acl into a project. After building the main configuration part and creating the according database schema, I'm trying to create some ACEs and let the magic happen. But I'm facing this exception over and over again
java.lang.IllegalArgumentException: Transaction must be running
org.springframework.util.Assert.isTrue(Assert.java:65)
org.springframework.security.acls.jdbc.JdbcMutableAclService.createOrRetrieveSidPrimaryKey(JdbcMutableAclService.java:219)
org.springframework.security.acls.jdbc.JdbcMutableAclService$1.setValues(JdbcMutableAclService.java:136)
org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:892)
org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:1)
org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:586)
org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:614)
org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:883)
org.springframework.security.acls.jdbc.JdbcMutableAclService.createEntries(JdbcMutableAclService.java:123)
org.springframework.security.acls.jdbc.JdbcMutableAclService.updateAcl(JdbcMutableAclService.java:314)
My basic configuration parts
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" >
<property name="driverClassName" value="${core.db.driverClassName}" />
<property name="url" value="${core.db.jdbcUrl}" />
<property name="username" value="${core.db.user}" />
<property name="password" value="${core.db.password}" />
</bean>
<bean id="aclCache"
class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
<constructor-arg>
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />
</property>
<property name="cacheName" value="aclCache" />
</bean>
</constructor-arg>
</bean>
<bean id="auditLogger"
class="org.springframework.security.acls.domain.ConsoleAuditLogger" />
<bean id="aclAuthorizationStrategy"
class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
<constructor-arg name="auths">
<list>
<bean
class="org.springframework.security.core.authority.GrantedAuthorityImpl">
<constructor-arg value="ACL_ADMIN" />
</bean>
<bean
class="org.springframework.security.core.authority.GrantedAuthorityImpl">
<constructor-arg value="ACL_ADMIN" />
</bean>
<bean
class="org.springframework.security.core.authority.GrantedAuthorityImpl">
<constructor-arg value="ACL_ADMIN" />
</bean>
</list>
</constructor-arg>
</bean>
<bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
<constructor-arg name="dataSource" ref="dataSource"/>
<constructor-arg name="aclCache" ref="aclCache"/>
<constructor-arg name="aclAuthorizationStrategy" ref="aclAuthorizationStrategy"/>
<constructor-arg name="auditLogger" ref="auditLogger"/>
</bean>
<bean id="aclService"
class="org.springframework.security.acls.jdbc.JdbcMutableAclService" >
<constructor-arg name="dataSource" ref="dataSource" />
<constructor-arg name="lookupStrategy" ref="lookupStrategy" />
<constructor-arg name="aclCache" ref="aclCache" />
<property name="sidIdentityQuery" value="SELECT id FROM acl_sid" />
<property name="classIdentityQuery" value="SELECT id FROM acl_class" />
</bean>
<bean id="jdbcTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref local="jdbcTransactionManager"/></property>
<property name="target"><ref local="aclService" /></property>
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
It seems that I'm missing something because the transaction should be active through the TransactionProxy.
Accessing the service in a controller this way
...
ObjectIdentity oi = new ObjectIdentityImpl(X.class, vm.hashCode());
Sid sid = new PrincipalSid(userDn);
Permission p = BasePermission.READ;
// Create or update the relevant ACL
MutableAcl acl = null;
try {
acl = (MutableAcl) aclService.readAclById(oi);
} catch (NotFoundException nfe) {
acl = aclService.createAcl(oi);
}
// Now grant some permissions via an access control entry (ACE)
acl.insertAce(acl.getEntries().size(), p, sid, true);
aclService.updateAcl(acl);
...
Try to cover the calling aclService methods with a transaction template:
TransactionTemplate tt = new TransactionTemplate(transactionManager);
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
ObjectIdentity oid = new ObjectId
entityImpl(clazz.getCanonicalName(), securedObject.getId());
// your aclService operation here:
aclService.deleteAcl(oid, true);
}
});
The error you mentioned only happens when the sid is not present in the acl_sid table and is inserted by Spring ACL automatically. Try adding the rows manually and then rerun the code. This worked for me.
Refer http://forum.springsource.org/showthread.php?55490-ACL-Transaction-must-be-running
The @Transactional annotation above method or service (containing the method) may solve the problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With