浏览代码

SEC-1165: Relax the requirement that the ObjectIdentity "type" be a Java class. Modified ObjectIdentity, changing the javaType property to "type" which is now a plain String. Also removes the requirement that the class be present on the classpath when creating the ObjectIdentityImpl instance (e.g. in the case of a permissions administration app which doesn't actually use the domain classes itself).

Luke Taylor 16 年之前
父节点
当前提交
39d76d5b5f

+ 20 - 20
acl/src/main/java/org/springframework/security/acls/domain/ObjectIdentityImpl.java

@@ -34,37 +34,36 @@ import org.springframework.util.ClassUtils;
 public class ObjectIdentityImpl implements ObjectIdentity {
     //~ Instance fields ================================================================================================
 
-    private Class<?> javaType;
+    private final String type;
     private Serializable identifier;
 
     //~ Constructors ===================================================================================================
 
-    public ObjectIdentityImpl(String javaType, Serializable identifier) {
-        Assert.hasText(javaType, "Java Type required");
+    public ObjectIdentityImpl(String type, Serializable identifier) {
+        Assert.hasText(type, "Type required");
         Assert.notNull(identifier, "identifier required");
 
-        try {
-            this.javaType = ClassUtils.forName(javaType, ClassUtils.getDefaultClassLoader());
-        } catch (ClassNotFoundException e) {
-            throw new IllegalStateException("Unable to load javaType: " + javaType, e);
-        }
-
         this.identifier = identifier;
+        this.type = type;
     }
 
+    /**
+     * Constructor which uses the name of the supplied class as the <tt>type</tt> property.
+     */
     public ObjectIdentityImpl(Class<?> javaType, Serializable identifier) {
         Assert.notNull(javaType, "Java Type required");
         Assert.notNull(identifier, "identifier required");
-        this.javaType = javaType;
+        this.type = javaType.getName();
         this.identifier = identifier;
     }
 
     /**
      * Creates the <code>ObjectIdentityImpl</code> based on the passed
      * object instance. The passed object must provide a <code>getId()</code>
-     * method, otherwise an exception will be thrown. The object passed will
-     * be considered the {@link #javaType}, so if more control is required,
-     * an alternate constructor should be used instead.
+     * method, otherwise an exception will be thrown.
+     * <p>
+     * The class name of the object passed will be considered the {@link #type}, so if more control is required,
+     * a different constructor should be used.
      *
      * @param object the domain object instance to create an identity for.
      *
@@ -73,12 +72,13 @@ public class ObjectIdentityImpl implements ObjectIdentity {
     public ObjectIdentityImpl(Object object) throws IdentityUnavailableException {
         Assert.notNull(object, "object cannot be null");
 
-        this.javaType = ClassUtils.getUserClass(object.getClass());
+        Class<?> typeClass = ClassUtils.getUserClass(object.getClass());
+        type = typeClass.getName();
 
         Object result;
 
         try {
-            Method method = this.javaType.getMethod("getId", new Class[] {});
+            Method method = typeClass.getMethod("getId", new Class[] {});
             result = method.invoke(object, new Object[] {});
         } catch (Exception e) {
             throw new IdentityUnavailableException("Could not extract identity from object " + object, e);
@@ -123,15 +123,15 @@ public class ObjectIdentityImpl implements ObjectIdentity {
             }
         }
 
-        return javaType.equals(other.javaType);
+        return type.equals(other.type);
     }
 
     public Serializable getIdentifier() {
         return identifier;
     }
 
-    public Class<?> getJavaType() {
-        return javaType;
+    public String getType() {
+        return type;
     }
 
     /**
@@ -141,7 +141,7 @@ public class ObjectIdentityImpl implements ObjectIdentity {
      */
     public int hashCode() {
         int code = 31;
-        code ^= this.javaType.hashCode();
+        code ^= this.type.hashCode();
         code ^= this.identifier.hashCode();
 
         return code;
@@ -150,7 +150,7 @@ public class ObjectIdentityImpl implements ObjectIdentity {
     public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append(this.getClass().getName()).append("[");
-        sb.append("Java Type: ").append(this.javaType.getName());
+        sb.append("Type: ").append(this.type);
         sb.append("; Identifier: ").append(this.identifier).append("]");
 
         return sb.toString();

+ 2 - 3
acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java

@@ -365,11 +365,10 @@ public final class BasicLookupStrategy implements LookupStrategy {
 
         Set parentsToLookup = (Set) jdbcTemplate.query(sql,
             new PreparedStatementSetter() {
-                public void setValues(PreparedStatement ps)
-                    throws SQLException {
+                public void setValues(PreparedStatement ps) throws SQLException {
                     for (int i = 0; i < objectIdentities.length; i++) {
                         // Determine prepared statement values for this iteration
-                        String javaType = objectIdentities[i].getJavaType().getName();
+                        String javaType = objectIdentities[i].getType();
 
                         // No need to check for nulls, as guaranteed non-null by ObjectIdentity.getIdentifier() interface contract
                         String identifier = objectIdentities[i].getIdentifier().toString();

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

@@ -72,7 +72,7 @@ public class JdbcAclService implements AclService {
     //~ Methods ========================================================================================================
 
     public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
-        Object[] args = {parentIdentity.getIdentifier(), parentIdentity.getJavaType().getName()};
+        Object[] args = {parentIdentity.getIdentifier(), parentIdentity.getType()};
         List<ObjectIdentity> objects = jdbcTemplate.query(selectAclObjectWithParent, args,
                 new RowMapper<ObjectIdentity>() {
                     public ObjectIdentity mapRow(ResultSet rs, int rowNum) throws SQLException {

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

@@ -148,7 +148,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
      */
     protected void createObjectIdentity(ObjectIdentity object, Sid owner) {
         Long sidId = createOrRetrieveSidPrimaryKey(owner, true);
-        Long classId = createOrRetrieveClassPrimaryKey(object.getJavaType(), true);
+        Long classId = createOrRetrieveClassPrimaryKey(object.getType(), true);
         jdbcTemplate.update(insertObjectIdentity, classId, object.getIdentifier(), sidId, Boolean.TRUE);
     }
 
@@ -161,15 +161,15 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
      *
      * @return the primary key or null if not found
      */
-    protected Long createOrRetrieveClassPrimaryKey(Class<?> clazz, boolean allowCreate) {
-        List<Long> classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {clazz.getName()}, Long.class);
+    protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate) {
+        List<Long> classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {type}, Long.class);
 
         if (!classIds.isEmpty()) {
             return classIds.get(0);
         }
 
         if (allowCreate) {
-            jdbcTemplate.update(insertClass, new Object[] {clazz.getName()});
+            jdbcTemplate.update(insertClass, type);
             Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
                     "Transaction must be running");
             return new Long(jdbcTemplate.queryForLong(classIdentityQuery));
@@ -290,7 +290,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
     protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
         try {
             return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey,
-                    new Object[] {oid.getJavaType().getName(), oid.getIdentifier()}));
+                    new Object[] {oid.getType(), oid.getIdentifier()}));
         } catch (DataAccessException notFound) {
             return null;
         }

+ 9 - 9
acl/src/main/java/org/springframework/security/acls/model/ObjectIdentity.java

@@ -21,11 +21,11 @@ import java.io.Serializable;
  * Represents the identity of an individual domain object instance.
  *
  * <p>
- * As implementations of <tt>ObjectIdentity</tt> are used as the key to represent 
+ * As implementations of <tt>ObjectIdentity</tt> are used as the key to represent
  * domain objects in the ACL subsystem, it is essential that implementations provide
  * methods so that object-equality rather than reference-equality can be relied upon
- * reliably. In other words, the ACL subsystem can consider two 
- * <tt>ObjectIdentity</tt>s equal if <tt>identity1.equals(identity2)</tt>, rather than 
+ * reliably. In other words, the ACL subsystem can consider two
+ * <tt>ObjectIdentity</tt>s equal if <tt>identity1.equals(identity2)</tt>, rather than
  * reference-equality of <tt>identity1==identity2</tt>.
  * </p>
  *
@@ -46,10 +46,10 @@ public interface ObjectIdentity extends Serializable {
     /**
      * Obtains the actual identifier. This identifier must not be reused to represent other domain objects with
      * the same <tt>javaType</tt>.
-     * 
+     *
      * <p>Because ACLs are largely immutable, it is strongly recommended to use
      * a synthetic identifier (such as a database sequence number for the primary key). Do not use an identifier with
-     * business meaning, as that business meaning may change in the future such change will cascade to the ACL 
+     * business meaning, as that business meaning may change in the future such change will cascade to the ACL
      * subsystem data.</p>
      *
      * @return the identifier (unique within this <tt>javaType</tt>; never <tt>null</tt>)
@@ -57,12 +57,12 @@ public interface ObjectIdentity extends Serializable {
     Serializable getIdentifier();
 
     /**
-     * Obtains the Java type represented by the domain object. The Java type can be an interface or a class, but is
-     * most often the domain object implementation class.
+     * Obtains the "type" metadata for the domain object. This will often be a Java type name (an interface or a class)
+     * &ndash; traditionally it is the name of the domain object implementation class.
      *
-     * @return the Java type of the domain object (never <tt>null</tt>)
+     * @return the "type" of the domain object (never <tt>null</tt>).
      */
-    Class<?> getJavaType();
+    String getType();
 
     /**
      * @return a hash code representation of the <tt>ObjectIdentity</tt>

+ 4 - 4
acl/src/test/java/org/springframework/security/acls/domain/ObjectIdentityImplTests.java

@@ -64,7 +64,7 @@ public class ObjectIdentityImplTests {
     public void gettersReturnExpectedValues() throws Exception {
         ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
         assertEquals(Long.valueOf(1), obj.getIdentifier());
-        assertEquals(MockIdDomainObject.class, obj.getJavaType());
+        assertEquals(MockIdDomainObject.class.getName(), obj.getType());
     }
 
     @Test
@@ -106,9 +106,9 @@ public class ObjectIdentityImplTests {
         }
     }
 
-    @Test(expected=IllegalStateException.class)
-    public void testConstructorInvalidClassParameter() throws Exception {
-        new ObjectIdentityImpl("not.a.Class", Long.valueOf(1));
+    @Test(expected=IllegalArgumentException.class)
+    public void constructorRejectsInvalidTypeParameter() throws Exception {
+        new ObjectIdentityImpl("", Long.valueOf(1));
     }
 
     @Test