SEC-670: Deadlock avoidance.
This commit is contained in:
parent
64442b6645
commit
8a7bfafce9
|
@ -60,6 +60,7 @@ import javax.sql.DataSource;
|
||||||
public class JdbcMutableAclService extends JdbcAclService implements MutableAclService {
|
public class JdbcMutableAclService extends JdbcAclService implements MutableAclService {
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
|
private boolean foreignKeysInDatabase = false;
|
||||||
private AclCache aclCache;
|
private AclCache aclCache;
|
||||||
private String deleteClassByClassNameString = "DELETE FROM acl_class WHERE class=?";
|
private String deleteClassByClassNameString = "DELETE FROM acl_class WHERE class=?";
|
||||||
private String deleteEntryByObjectIdentityForeignKey = "DELETE FROM acl_entry WHERE acl_object_identity=?";
|
private String deleteEntryByObjectIdentityForeignKey = "DELETE FROM acl_entry WHERE acl_object_identity=?";
|
||||||
|
@ -73,8 +74,6 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||||
+ "(object_id_class, object_id_identity, owner_sid, entries_inheriting) " + "VALUES (?, ?, ?, ?)";
|
+ "(object_id_class, object_id_identity, owner_sid, entries_inheriting) " + "VALUES (?, ?, ?, ?)";
|
||||||
private String insertSid = "INSERT INTO acl_sid (principal, sid) VALUES (?, ?)";
|
private String insertSid = "INSERT INTO acl_sid (principal, sid) VALUES (?, ?)";
|
||||||
private String selectClassPrimaryKey = "SELECT id FROM acl_class WHERE class=?";
|
private String selectClassPrimaryKey = "SELECT id FROM acl_class WHERE class=?";
|
||||||
private String selectCountObjectIdentityRowsForParticularClassNameString = "SELECT COUNT(acl_object_identity.id) "
|
|
||||||
+ "FROM acl_object_identity, acl_class WHERE acl_class.id = acl_object_identity.object_id_class and class=?";
|
|
||||||
private String selectObjectIdentityPrimaryKey = "SELECT acl_object_identity.id FROM acl_object_identity, acl_class "
|
private String selectObjectIdentityPrimaryKey = "SELECT acl_object_identity.id FROM acl_object_identity, acl_class "
|
||||||
+ "WHERE acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
|
+ "WHERE acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
|
||||||
+ "and acl_object_identity.object_id_identity = ?";
|
+ "and acl_object_identity.object_id_identity = ?";
|
||||||
|
@ -237,58 +236,60 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||||
Assert.notNull(objectIdentity, "Object Identity required");
|
Assert.notNull(objectIdentity, "Object Identity required");
|
||||||
Assert.notNull(objectIdentity.getIdentifier(), "Object Identity doesn't provide an identifier");
|
Assert.notNull(objectIdentity.getIdentifier(), "Object Identity doesn't provide an identifier");
|
||||||
|
|
||||||
// Recursively call this method for children, or handle children if they don't want automatic recursion
|
if (deleteChildren) {
|
||||||
ObjectIdentity[] children = findChildren(objectIdentity);
|
ObjectIdentity[] children = findChildren(objectIdentity);
|
||||||
|
if (children != null) {
|
||||||
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 != null) {
|
}
|
||||||
|
} else {
|
||||||
|
if (!foreignKeysInDatabase) {
|
||||||
|
// We need to perform a manual verification for what a FK would normally do
|
||||||
|
// We generally don't do this, in the interests of deadlock management
|
||||||
|
ObjectIdentity[] children = findChildren(objectIdentity);
|
||||||
|
if (children != null) {
|
||||||
throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.length
|
throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.length
|
||||||
+ " children)");
|
+ " children)");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);
|
||||||
|
|
||||||
// Delete this ACL's ACEs in the acl_entry table
|
// Delete this ACL's ACEs in the acl_entry table
|
||||||
deleteEntries(objectIdentity);
|
deleteEntries(oidPrimaryKey);
|
||||||
|
|
||||||
// Delete this ACL's acl_object_identity row
|
// Delete this ACL's acl_object_identity row
|
||||||
deleteObjectIdentityAndOptionallyClass(objectIdentity);
|
deleteObjectIdentity(oidPrimaryKey);
|
||||||
|
|
||||||
// Clear the cache
|
// Clear the cache
|
||||||
aclCache.evictFromCache(objectIdentity);
|
aclCache.evictFromCache(objectIdentity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes all ACEs defined in the acl_entry table belonging to the presented ObjectIdentity
|
* Deletes all ACEs defined in the acl_entry table belonging to the presented ObjectIdentity primary key.
|
||||||
*
|
*
|
||||||
* @param oid the rows in acl_entry to delete
|
* @param oidPrimaryKey the rows in acl_entry to delete
|
||||||
*/
|
*/
|
||||||
protected void deleteEntries(ObjectIdentity oid) {
|
protected void deleteEntries(Long oidPrimaryKey) {
|
||||||
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey,
|
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey,
|
||||||
new Object[] {retrieveObjectIdentityPrimaryKey(oid)});
|
new Object[] {oidPrimaryKey});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a single row from acl_object_identity that is associated with the presented ObjectIdentity. In
|
* Deletes a single row from acl_object_identity that is associated with the presented ObjectIdentity primary key.
|
||||||
* addition, deletes the corresponding row from acl_class if there are no more entries in acl_object_identity that
|
|
||||||
* use that particular acl_class. This keeps the acl_class table reasonably small.
|
|
||||||
*
|
*
|
||||||
* @param oid to delete the acl_object_identity (and clean up acl_class for that class name if appropriate)
|
* <p>
|
||||||
|
* We do not delete any entries from acl_class, even if no classes are using that class any longer. This is a
|
||||||
|
* deadlock avoidance approach.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param oidPrimaryKey to delete the acl_object_identity
|
||||||
*/
|
*/
|
||||||
protected void deleteObjectIdentityAndOptionallyClass(ObjectIdentity oid) {
|
protected void deleteObjectIdentity(Long oidPrimaryKey) {
|
||||||
// Delete the acl_object_identity row
|
// Delete the acl_object_identity row
|
||||||
jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, new Object[] {retrieveObjectIdentityPrimaryKey(oid)});
|
jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, new Object[] {oidPrimaryKey});
|
||||||
|
|
||||||
// Delete the acl_class row, assuming there are no other references to it in acl_object_identity
|
|
||||||
Object[] className = {oid.getJavaType().getName()};
|
|
||||||
long numObjectIdentities = jdbcTemplate.queryForLong(selectCountObjectIdentityRowsForParticularClassNameString,
|
|
||||||
className);
|
|
||||||
|
|
||||||
if (numObjectIdentities == 0) {
|
|
||||||
// No more rows
|
|
||||||
jdbcTemplate.update(deleteClassByClassNameString, className);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -324,7 +325,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||||
Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier");
|
Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier");
|
||||||
|
|
||||||
// Delete this ACL's ACEs in the acl_entry table
|
// Delete this ACL's ACEs in the acl_entry table
|
||||||
deleteEntries(acl.getObjectIdentity());
|
deleteEntries(retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity()));
|
||||||
|
|
||||||
// Create this ACL's ACEs in the acl_entry table
|
// Create this ACL's ACEs in the acl_entry table
|
||||||
createEntries(acl);
|
createEntries(acl);
|
||||||
|
|
|
@ -326,21 +326,6 @@ public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringCo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDeleteAllAclsRemovesAclClassRecord() throws Exception {
|
|
||||||
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
|
|
||||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")});
|
|
||||||
auth.setAuthenticated(true);
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
|
||||||
|
|
||||||
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
|
|
||||||
|
|
||||||
// Remove all acls associated with a certain class type
|
|
||||||
jdbcMutableAclService.deleteAcl(topParentOid, true);
|
|
||||||
|
|
||||||
// Check the acl_class table is empty
|
|
||||||
assertEquals(0, getJdbcTemplate().queryForList(SELECT_ALL_CLASSES, new Object[] {"org.springframework.security.TargetObject"} ).size());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testDeleteAclRemovesRowsFromDatabase() throws Exception {
|
public void testDeleteAclRemovesRowsFromDatabase() throws Exception {
|
||||||
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
|
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
|
||||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")});
|
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")});
|
||||||
|
|
Loading…
Reference in New Issue