From 40759ab232c51c0ffab2648448c60c174e55a180 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Wed, 30 Sep 2009 12:32:22 +0000 Subject: [PATCH] SEC-925: BasicLookupStrategy - support for schema qualifier. Added setters for ACL SQL statements. --- .../acls/jdbc/BasicLookupStrategy.java | 111 +++++++++++++----- .../security/acls/jdbc/JdbcAclService.java | 14 ++- .../acls/jdbc/JdbcMutableAclService.java | 45 ++++++- 3 files changed, 137 insertions(+), 33 deletions(-) diff --git a/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java b/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java index 44ff55ce80..1a505f8c45 100644 --- a/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java +++ b/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java @@ -64,11 +64,47 @@ import org.springframework.util.Assert; * you are likely to achieve better performance. In such situations you will need to provide your own custom * LookupStrategy. This class does not support subclassing, as it is likely to change in future releases * and therefore subclassing is unsupported. + *

+ * There are two SQL queries executed, one in the lookupPrimaryKeys method and one in + * lookupObjectIdentities. These are built from the same select and "order by" clause, using a different + * where clause in each case. In order to use custom schema or column names, each of these SQL clauses can be + * customized, but they must be consistent with each other and with the expected result set + * generated by the the default values. * * @author Ben Alex * @version $Id$ */ public final class BasicLookupStrategy implements LookupStrategy { + + public final static String DEFAULT_SELECT_CLAUSE = "select acl_object_identity.object_id_identity, " + + "acl_entry.ace_order, " + + "acl_object_identity.id as acl_id, " + + "acl_object_identity.parent_object, " + + "acl_object_identity.entries_inheriting, " + + "acl_entry.id as ace_id, " + + "acl_entry.mask, " + + "acl_entry.granting, " + + "acl_entry.audit_success, " + + "acl_entry.audit_failure, " + + "acl_sid.principal as ace_principal, " + + "acl_sid.sid as ace_sid, " + + "acli_sid.principal as acl_principal, " + + "acli_sid.sid as acl_sid, " + + "acl_class.class " + + "from acl_object_identity " + + "left join acl_sid acli_sid on acli_sid.id = acl_object_identity.owner_sid " + + "left join acl_class on acl_class.id = acl_object_identity.object_id_class " + + "left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity " + + "left join acl_sid on acl_entry.sid = acl_sid.id " + + "where ( "; + + private final static String DEFAULT_LOOKUP_KEYS_WHERE_CLAUSE = "(acl_object_identity.id = ?)"; + + private final static String DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE = "(acl_object_identity.object_id_identity = ? and acl_class.class = ?)"; + + public final static String DEFAULT_ORDER_BY_CLAUSE = ") order by acl_object_identity.object_id_identity" + + " asc, acl_entry.ace_order asc"; + //~ Instance fields ================================================================================================ private AclAuthorizationStrategy aclAuthorizationStrategy; @@ -81,6 +117,12 @@ public final class BasicLookupStrategy implements LookupStrategy { private final Field fieldAces = FieldUtils.getField(AclImpl.class, "aces"); private final Field fieldAcl = FieldUtils.getField(AccessControlEntryImpl.class, "acl"); + // SQL Customization fields + private String selectClause = DEFAULT_SELECT_CLAUSE; + private String lookupPrimaryKeysWhereClause = DEFAULT_LOOKUP_KEYS_WHERE_CLAUSE; + private String lookupObjectIdentitiesWhereClause = DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE; + private String orderByClause = DEFAULT_ORDER_BY_CLAUSE; + //~ Constructors =================================================================================================== /** @@ -106,33 +148,12 @@ public final class BasicLookupStrategy implements LookupStrategy { //~ Methods ======================================================================================================== - private static String computeRepeatingSql(String repeatingSql, int requiredRepetitions) { + private String computeRepeatingSql(String repeatingSql, int requiredRepetitions) { assert requiredRepetitions > 0 : "requiredRepetitions must be > 0"; - final String startSql = "select acl_object_identity.object_id_identity, " - + "acl_entry.ace_order, " - + "acl_object_identity.id as acl_id, " - + "acl_object_identity.parent_object, " - + "acl_object_identity.entries_inheriting, " - + "acl_entry.id as ace_id, " - + "acl_entry.mask, " - + "acl_entry.granting, " - + "acl_entry.audit_success, " - + "acl_entry.audit_failure, " - + "acl_sid.principal as ace_principal, " - + "acl_sid.sid as ace_sid, " - + "acli_sid.principal as acl_principal, " - + "acli_sid.sid as acl_sid, " - + "acl_class.class " - + "from acl_object_identity " - + "left join acl_sid acli_sid on acli_sid.id = acl_object_identity.owner_sid " - + "left join acl_class on acl_class.id = acl_object_identity.object_id_class " - + "left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity " - + "left join acl_sid on acl_entry.sid = acl_sid.id " - + "where ( "; - - final String endSql = ") order by acl_object_identity.object_id_identity" - + " asc, acl_entry.ace_order asc"; + final String startSql = selectClause; + + final String endSql = orderByClause; StringBuilder sqlStringBldr = new StringBuilder(startSql.length() + endSql.length() + requiredRepetitions * (repeatingSql.length() + 4)); @@ -239,7 +260,7 @@ public final class BasicLookupStrategy implements LookupStrategy { Assert.notNull(acls, "ACLs are required"); Assert.notEmpty(findNow, "Items to find now required"); - String sql = computeRepeatingSql("(acl_object_identity.id = ?)", findNow.size()); + String sql = computeRepeatingSql(lookupPrimaryKeysWhereClause, findNow.size()); Set parentsToLookup = jdbcTemplate.query(sql, new PreparedStatementSetter() { @@ -358,7 +379,7 @@ public final class BasicLookupStrategy implements LookupStrategy { // Make the "acls" map contain all requested objectIdentities // (including markers to each parent in the hierarchy) - String sql = computeRepeatingSql("(acl_object_identity.object_id_identity = ? and acl_class.class = ?)", + String sql = computeRepeatingSql(lookupObjectIdentitiesWhereClause , objectIdentities.length); Set parentsToLookup = (Set) jdbcTemplate.query(sql, @@ -400,11 +421,41 @@ public final class BasicLookupStrategy implements LookupStrategy { return resultMap; } - public void setBatchSize(int batchSize) { this.batchSize = batchSize; } + /** + * The SQL for the select clause. If customizing in order to modify + * column names, schema etc, the other SQL customization fields must also be set to match. + * + * @param selectClause the select clause, which defaults to {@link #DEFAULT_SELECT_CLAUSE}. + */ + public void setSelectClause(String selectClause) { + this.selectClause = selectClause; + } + + /** + * The SQL for the where clause used in the lookupPrimaryKey method. + */ + public void setLookupPrimaryKeysWhereClause(String lookupPrimaryKeysWhereClause) { + this.lookupPrimaryKeysWhereClause = lookupPrimaryKeysWhereClause; + } + + /** + * The SQL for the where clause used in the lookupObjectIdentities method. + */ + public void setLookupObjectIdentitiesWhereClause(String lookupObjectIdentitiesWhereClause) { + this.lookupObjectIdentitiesWhereClause = lookupObjectIdentitiesWhereClause; + } + + /** + * The SQL for the "order by" clause used in both queries. + */ + public void setOrderByClause(String orderByClause) { + this.orderByClause = orderByClause; + } + //~ Inner Classes ================================================================================================== private class ProcessResultSet implements ResultSetExtractor> { @@ -479,13 +530,13 @@ public final class BasicLookupStrategy implements LookupStrategy { if (acl == null) { // Make an AclImpl and pop it into the Map ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString("class"), - new Long(rs.getLong("object_id_identity"))); + Long.valueOf(rs.getLong("object_id_identity"))); Acl parentAcl = null; long parentAclId = rs.getLong("parent_object"); if (parentAclId != 0) { - parentAcl = new StubAclParent(new Long(parentAclId)); + parentAcl = new StubAclParent(Long.valueOf(parentAclId)); } boolean entriesInheriting = rs.getBoolean("entries_inheriting"); diff --git a/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcAclService.java b/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcAclService.java index 67139d88e1..35fdb6e9b3 100644 --- a/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcAclService.java +++ b/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcAclService.java @@ -49,7 +49,7 @@ public class JdbcAclService implements AclService { //~ Static fields/initializers ===================================================================================== protected static final Log log = LogFactory.getLog(JdbcAclService.class); - private static final String selectAclObjectWithParent = "select obj.object_id_identity as obj_id, class.class as class " + private static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL = "select obj.object_id_identity as obj_id, class.class as class " + "from acl_object_identity obj, acl_object_identity parent, acl_class class " + "where obj.parent_object = parent.id and obj.object_id_class = class.id " + "and parent.object_id_identity = ? and parent.object_id_class = (" @@ -59,6 +59,7 @@ public class JdbcAclService implements AclService { protected JdbcTemplate jdbcTemplate; private LookupStrategy lookupStrategy; + private String findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL; //~ Constructors =================================================================================================== @@ -73,7 +74,7 @@ public class JdbcAclService implements AclService { public List findChildren(ObjectIdentity parentIdentity) { Object[] args = {parentIdentity.getIdentifier(), parentIdentity.getType()}; - List objects = jdbcTemplate.query(selectAclObjectWithParent, args, + List objects = jdbcTemplate.query(findChildrenSql, args, new RowMapper() { public ObjectIdentity mapRow(ResultSet rs, int rowNum) throws SQLException { String javaType = rs.getString("class"); @@ -118,4 +119,13 @@ public class JdbcAclService implements AclService { return result; } + + /** + * Allows customization of the SQL query used to find child object identities. + * + * @param findChildrenSql + */ + public void setFindChildrenQuery(String findChildrenSql) { + this.findChildrenSql = findChildrenSql; + } } diff --git a/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java b/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java index 59c171a81f..69a7b7d536 100644 --- a/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java +++ b/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java @@ -47,7 +47,9 @@ import org.springframework.util.Assert; *

* The default settings are for HSQLDB. If you are using a different database you * will probably need to set the {@link #setSidIdentityQuery(String) sidIdentityQuery} and - * {@link #setClassIdentityQuery(String) classIdentityQuery} properties appropriately. + * {@link #setClassIdentityQuery(String) classIdentityQuery} properties appropriately. The other queries, + * SQL inserts and updates can also be customized to accomodate schema variations, but must produce results + * consistent with those expected by the defaults. *

* See the appendix of the Spring Security reference manual for more information on the expected schema * and how it is used. Information on using PostgreSQL is also included. @@ -383,6 +385,47 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS Assert.hasText(sidIdentityQuery, "New sidIdentityQuery query is required"); this.sidIdentityQuery = sidIdentityQuery; } + + public void setDeleteEntryByObjectIdentityForeignKeySql(String deleteEntryByObjectIdentityForeignKey) { + this.deleteEntryByObjectIdentityForeignKey = deleteEntryByObjectIdentityForeignKey; + } + + public void setDeleteObjectIdentityByPrimaryKeySql(String deleteObjectIdentityByPrimaryKey) { + this.deleteObjectIdentityByPrimaryKey = deleteObjectIdentityByPrimaryKey; + } + + public void setInsertClassSql(String insertClass) { + this.insertClass = insertClass; + } + + public void setInsertEntrySql(String insertEntry) { + this.insertEntry = insertEntry; + } + + public void setInsertObjectIdentitySql(String insertObjectIdentity) { + this.insertObjectIdentity = insertObjectIdentity; + } + + public void setInsertSidSql(String insertSid) { + this.insertSid = insertSid; + } + + public void setClassPrimaryKeyQuery(String selectClassPrimaryKey) { + this.selectClassPrimaryKey = selectClassPrimaryKey; + } + + public void setObjectIdentityPrimaryKeyQuery(String selectObjectIdentityPrimaryKey) { + this.selectObjectIdentityPrimaryKey = selectObjectIdentityPrimaryKey; + } + + public void setSidPrimaryKeyQuery(String selectSidPrimaryKey) { + this.selectSidPrimaryKey = selectSidPrimaryKey; + } + + public void setUpdateObjectIdentity(String updateObjectIdentity) { + this.updateObjectIdentity = updateObjectIdentity; + } + /** * @param foreignKeysInDatabase if false this class will perform additional FK constrain checking, which may * cause deadlocks (the default is true, so deadlocks are avoided but the database is expected to enforce FKs)