mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-28 23:02:15 +00:00
SEC-655: Evict from the cache any children ACLs of the ACL being updated.
This commit is contained in:
parent
5cf5140029
commit
340020ad3a
@ -87,6 +87,10 @@ public class JdbcAclService implements AclService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (objects.size() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (ObjectIdentityImpl[]) objects.toArray(new ObjectIdentityImpl[objects.size()]);
|
return (ObjectIdentityImpl[]) objects.toArray(new ObjectIdentityImpl[objects.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,11 +240,11 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
|||||||
// Recursively call this method for children, or handle children if they don't want automatic recursion
|
// Recursively call this method for children, or handle children if they don't want automatic recursion
|
||||||
ObjectIdentity[] children = findChildren(objectIdentity);
|
ObjectIdentity[] children = findChildren(objectIdentity);
|
||||||
|
|
||||||
if (deleteChildren) {
|
if (deleteChildren && children != null) {
|
||||||
for (int i = 0; i < children.length; i++) {
|
for (int i = 0; i < children.length; i++) {
|
||||||
deleteAcl(children[i], true);
|
deleteAcl(children[i], true);
|
||||||
}
|
}
|
||||||
} else if (children.length > 0) {
|
} else if (children != null) {
|
||||||
throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.length
|
throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.length
|
||||||
+ " children)");
|
+ " children)");
|
||||||
}
|
}
|
||||||
@ -332,13 +332,24 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
|||||||
// Change the mutable columns in acl_object_identity
|
// Change the mutable columns in acl_object_identity
|
||||||
updateObjectIdentity(acl);
|
updateObjectIdentity(acl);
|
||||||
|
|
||||||
// Clear the cache
|
// Clear the cache, including children
|
||||||
aclCache.evictFromCache(acl.getObjectIdentity());
|
clearCacheIncludingChildren(acl.getObjectIdentity());
|
||||||
|
|
||||||
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc)
|
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc)
|
||||||
return (MutableAcl) super.readAclById(acl.getObjectIdentity());
|
return (MutableAcl) super.readAclById(acl.getObjectIdentity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void clearCacheIncludingChildren(ObjectIdentity objectIdentity) {
|
||||||
|
Assert.notNull(objectIdentity, "ObjectIdentity required");
|
||||||
|
ObjectIdentity[] children = findChildren(objectIdentity);
|
||||||
|
if (children != null) {
|
||||||
|
for (int i = 0; i < children.length; i++) {
|
||||||
|
clearCacheIncludingChildren(children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aclCache.evictFromCache(objectIdentity);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates an existing acl_object_identity row, with new information presented in the passed MutableAcl
|
* Updates an existing acl_object_identity row, with new information presented in the passed MutableAcl
|
||||||
* object. Also will create an acl_sid entry if needed for the Sid that owns the MutableAcl.
|
* object. Also will create an acl_sid entry if needed for the Sid that owns the MutableAcl.
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="dataSource" class="org.springframework.security.TestDataSource">
|
<bean id="dataSource" class="org.springframework.security.TestDataSource">
|
||||||
<constructor-arg value="test" />
|
<constructor-arg value="acltest" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@ -0,0 +1,209 @@
|
|||||||
|
package org.springframework.security.acls.jdbc;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import net.sf.ehcache.CacheManager;
|
||||||
|
import net.sf.ehcache.Ehcache;
|
||||||
|
|
||||||
|
import org.springframework.cache.ehcache.EhCacheFactoryBean;
|
||||||
|
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||||
|
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||||
|
import org.springframework.security.Authentication;
|
||||||
|
import org.springframework.security.GrantedAuthority;
|
||||||
|
import org.springframework.security.GrantedAuthorityImpl;
|
||||||
|
import org.springframework.security.acls.MutableAcl;
|
||||||
|
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
|
||||||
|
import org.springframework.security.acls.domain.AclImpl;
|
||||||
|
import org.springframework.security.acls.domain.BasePermission;
|
||||||
|
import org.springframework.security.acls.domain.ConsoleAuditLogger;
|
||||||
|
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
|
||||||
|
import org.springframework.security.acls.sid.GrantedAuthoritySid;
|
||||||
|
import org.springframework.security.acls.sid.PrincipalSid;
|
||||||
|
import org.springframework.security.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||||
|
|
||||||
|
public class AclPermissionInheritanceTests extends TestCase {
|
||||||
|
|
||||||
|
private JdbcMutableAclService aclService;
|
||||||
|
private JdbcTemplate jdbcTemplate;
|
||||||
|
private DriverManagerDataSource dataSource;
|
||||||
|
private DataSourceTransactionManager txManager;
|
||||||
|
private TransactionStatus txStatus;
|
||||||
|
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
|
||||||
|
dataSource = new DriverManagerDataSource();
|
||||||
|
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
|
||||||
|
dataSource.setUrl("jdbc:hsqldb:mem:permissiontest");
|
||||||
|
dataSource.setUsername("sa");
|
||||||
|
dataSource.setPassword("");
|
||||||
|
|
||||||
|
jdbcTemplate = new JdbcTemplate(dataSource);
|
||||||
|
|
||||||
|
txManager = new DataSourceTransactionManager();
|
||||||
|
txManager.setDataSource(dataSource);
|
||||||
|
|
||||||
|
txStatus = txManager.getTransaction(new DefaultTransactionDefinition());
|
||||||
|
|
||||||
|
aclService = createAclService(dataSource);
|
||||||
|
|
||||||
|
Authentication auth = new UsernamePasswordAuthenticationToken(
|
||||||
|
"system", "secret", new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_IGNORED")});
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
txManager.rollback(txStatus);
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void test1() throws Exception {
|
||||||
|
|
||||||
|
createAclSchema(jdbcTemplate);
|
||||||
|
|
||||||
|
ObjectIdentityImpl rootObject =
|
||||||
|
new ObjectIdentityImpl(TestDomainObject.class, new Long(1));
|
||||||
|
|
||||||
|
MutableAcl parent = aclService.createAcl(rootObject);
|
||||||
|
MutableAcl child = aclService.createAcl(new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
|
||||||
|
child.setParent(parent);
|
||||||
|
aclService.updateAcl(child);
|
||||||
|
|
||||||
|
parent = (AclImpl) aclService.readAclById(rootObject);
|
||||||
|
parent.insertAce(null, BasePermission.READ,
|
||||||
|
new PrincipalSid("john"), true);
|
||||||
|
aclService.updateAcl(parent);
|
||||||
|
|
||||||
|
parent = (AclImpl) aclService.readAclById(rootObject);
|
||||||
|
parent.insertAce(null, BasePermission.READ,
|
||||||
|
new PrincipalSid("joe"), true);
|
||||||
|
aclService.updateAcl(parent);
|
||||||
|
|
||||||
|
child = (MutableAcl) aclService.readAclById(
|
||||||
|
new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
|
||||||
|
|
||||||
|
parent = (MutableAcl) child.getParentAcl();
|
||||||
|
|
||||||
|
assertEquals("Fails because child has a stale reference to its parent",
|
||||||
|
2, parent.getEntries().length);
|
||||||
|
assertEquals(1, parent.getEntries()[0].getPermission().getMask());
|
||||||
|
assertEquals(new PrincipalSid("john"), parent.getEntries()[0].getSid());
|
||||||
|
assertEquals(1, parent.getEntries()[1].getPermission().getMask());
|
||||||
|
assertEquals(new PrincipalSid("joe"), parent.getEntries()[1].getSid());
|
||||||
|
|
||||||
|
}
|
||||||
|
public void test2() throws Exception {
|
||||||
|
|
||||||
|
createAclSchema(jdbcTemplate);
|
||||||
|
|
||||||
|
ObjectIdentityImpl rootObject =
|
||||||
|
new ObjectIdentityImpl(TestDomainObject.class, new Long(1));
|
||||||
|
|
||||||
|
MutableAcl parent = aclService.createAcl(rootObject);
|
||||||
|
MutableAcl child = aclService.createAcl(new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
|
||||||
|
child.setParent(parent);
|
||||||
|
aclService.updateAcl(child);
|
||||||
|
|
||||||
|
parent.insertAce(null, BasePermission.ADMINISTRATION,
|
||||||
|
new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), true);
|
||||||
|
aclService.updateAcl(parent);
|
||||||
|
|
||||||
|
parent.insertAce(null, BasePermission.DELETE, new PrincipalSid("terry"), true);
|
||||||
|
aclService.updateAcl(parent);
|
||||||
|
|
||||||
|
child = (MutableAcl) aclService.readAclById(
|
||||||
|
new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
|
||||||
|
|
||||||
|
parent = (MutableAcl) child.getParentAcl();
|
||||||
|
|
||||||
|
assertEquals(2, parent.getEntries().length);
|
||||||
|
assertEquals(16, parent.getEntries()[0].getPermission().getMask());
|
||||||
|
assertEquals(new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), parent.getEntries()[0].getSid());
|
||||||
|
assertEquals(8, parent.getEntries()[1].getPermission().getMask());
|
||||||
|
assertEquals(new PrincipalSid("terry"), parent.getEntries()[1].getSid());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private JdbcMutableAclService createAclService(DriverManagerDataSource ds)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
GrantedAuthorityImpl adminAuthority = new GrantedAuthorityImpl("ROLE_ADMINISTRATOR");
|
||||||
|
AclAuthorizationStrategyImpl authStrategy = new AclAuthorizationStrategyImpl(
|
||||||
|
new GrantedAuthorityImpl[]{adminAuthority,adminAuthority,adminAuthority});
|
||||||
|
|
||||||
|
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
|
||||||
|
ehCacheManagerFactoryBean.afterPropertiesSet();
|
||||||
|
CacheManager cacheManager = (CacheManager) ehCacheManagerFactoryBean.getObject();
|
||||||
|
|
||||||
|
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
|
||||||
|
ehCacheFactoryBean.setCacheName("aclAche");
|
||||||
|
ehCacheFactoryBean.setCacheManager(cacheManager);
|
||||||
|
ehCacheFactoryBean.afterPropertiesSet();
|
||||||
|
Ehcache ehCache = (Ehcache) ehCacheFactoryBean.getObject();
|
||||||
|
|
||||||
|
AclCache aclAche = new EhCacheBasedAclCache(ehCache);
|
||||||
|
|
||||||
|
BasicLookupStrategy lookupStrategy =
|
||||||
|
new BasicLookupStrategy(ds, aclAche, authStrategy, new ConsoleAuditLogger());
|
||||||
|
|
||||||
|
return new JdbcMutableAclService(ds,lookupStrategy, aclAche);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createAclSchema(JdbcTemplate jdbcTemplate) {
|
||||||
|
|
||||||
|
jdbcTemplate.execute("DROP TABLE ACL_ENTRY IF EXISTS;");
|
||||||
|
jdbcTemplate.execute("DROP TABLE ACL_OBJECT_IDENTITY IF EXISTS;");
|
||||||
|
jdbcTemplate.execute("DROP TABLE ACL_CLASS IF EXISTS");
|
||||||
|
jdbcTemplate.execute("DROP TABLE ACL_SID IF EXISTS");
|
||||||
|
|
||||||
|
jdbcTemplate.execute(
|
||||||
|
"CREATE TABLE ACL_SID(" +
|
||||||
|
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
|
||||||
|
"PRINCIPAL BOOLEAN NOT NULL," +
|
||||||
|
"SID VARCHAR_IGNORECASE(100) NOT NULL," +
|
||||||
|
"CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL));");
|
||||||
|
jdbcTemplate.execute(
|
||||||
|
"CREATE TABLE ACL_CLASS(" +
|
||||||
|
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
|
||||||
|
"CLASS VARCHAR_IGNORECASE(100) NOT NULL," +
|
||||||
|
"CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));");
|
||||||
|
jdbcTemplate.execute(
|
||||||
|
"CREATE TABLE ACL_OBJECT_IDENTITY(" +
|
||||||
|
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
|
||||||
|
"OBJECT_ID_CLASS BIGINT NOT NULL," +
|
||||||
|
"OBJECT_ID_IDENTITY BIGINT NOT NULL," +
|
||||||
|
"PARENT_OBJECT BIGINT," +
|
||||||
|
"OWNER_SID BIGINT," +
|
||||||
|
"ENTRIES_INHERITING BOOLEAN NOT NULL," +
|
||||||
|
"CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY)," +
|
||||||
|
"CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID)," +
|
||||||
|
"CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID)," +
|
||||||
|
"CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));");
|
||||||
|
jdbcTemplate.execute(
|
||||||
|
"CREATE TABLE ACL_ENTRY(" +
|
||||||
|
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
|
||||||
|
"ACL_OBJECT_IDENTITY BIGINT NOT NULL,ACE_ORDER INT NOT NULL,SID BIGINT NOT NULL," +
|
||||||
|
"MASK INTEGER NOT NULL,GRANTING BOOLEAN NOT NULL,AUDIT_SUCCESS BOOLEAN NOT NULL," +
|
||||||
|
"AUDIT_FAILURE BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY,ACE_ORDER)," +
|
||||||
|
"CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID)," +
|
||||||
|
"CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID));");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestDomainObject {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user