Browse Source

SEC-925: BasicLookupStrategy - support for schema qualifier. Added setters for ACL SQL statements.

Luke Taylor 16 years ago
parent
commit
40759ab232

+ 81 - 30
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
  * <code>LookupStrategy</code>. This class does not support subclassing, as it is likely to change in future releases
  * and therefore subclassing is unsupported.
+ * <p>
+ * There are two SQL queries executed, one in the <tt>lookupPrimaryKeys</tt> method and one in
+ * <tt>lookupObjectIdentities</tt>. 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<Long> 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 <tt>lookupPrimaryKey</tt> method.
+     */
+    public void setLookupPrimaryKeysWhereClause(String lookupPrimaryKeysWhereClause) {
+        this.lookupPrimaryKeysWhereClause = lookupPrimaryKeysWhereClause;
+    }
+
+    /**
+     * The SQL for the where clause used in the <tt>lookupObjectIdentities</tt> 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<Set<Long>> {
@@ -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");

+ 12 - 2
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<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
         Object[] args = {parentIdentity.getIdentifier(), parentIdentity.getType()};
-        List<ObjectIdentity> objects = jdbcTemplate.query(selectAclObjectWithParent, args,
+        List<ObjectIdentity> objects = jdbcTemplate.query(findChildrenSql, args,
                 new RowMapper<ObjectIdentity>() {
                     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;
+    }
 }

+ 44 - 1
acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java

@@ -47,7 +47,9 @@ import org.springframework.util.Assert;
  * <p>
  * 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.
  * <p>
  * 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)