Parcourir la source

SEC-936: NPE in AbstractFallbackMethodDefinitionSource
http://jira.springframework.org/browse/SEC-936. Changed to check if the value of MethodInvocation.getThis() is null to prevent NPE. MapBasedMethodDefinitionSource now ignores calls to findAttributes() with a null target class (all its entries require a class) and the fallback option in AbstractFallbackMethodDefinitionSource is used if the targetClass is null (i.e. Method.getDeclaringClass() will be used as the Class)

Luke Taylor il y a 17 ans
Parent
commit
3bf5e406b7

+ 108 - 107
core/src/main/java/org/springframework/security/intercept/method/AbstractFallbackMethodDefinitionSource.java

@@ -18,11 +18,11 @@ import org.springframework.util.ObjectUtils;
  * Abstract implementation of {@link MethodDefinitionSource} that supports both Spring AOP and AspectJ and
  * Abstract implementation of {@link MethodDefinitionSource} that supports both Spring AOP and AspectJ and
  * caches configuration attribute resolution from: 1. specific target method; 2. target class;  3. declaring method;
  * caches configuration attribute resolution from: 1. specific target method; 2. target class;  3. declaring method;
  * 4. declaring class/interface.
  * 4. declaring class/interface.
- * 
+ *
  * <p>
  * <p>
  * This class mimics the behaviour of Spring's AbstractFallbackTransactionAttributeSource class.
  * This class mimics the behaviour of Spring's AbstractFallbackTransactionAttributeSource class.
  * </p>
  * </p>
- * 
+ *
  * <p>
  * <p>
  * Note that this class cannot extract security metadata where that metadata is expressed by way of
  * Note that this class cannot extract security metadata where that metadata is expressed by way of
  * a target method/class (ie #1 and #2 above) AND the target method/class is encapsulated in another
  * a target method/class (ie #1 and #2 above) AND the target method/class is encapsulated in another
@@ -31,7 +31,7 @@ import org.springframework.util.ObjectUtils;
  * another proxy), move the metadata to declared methods or interfaces the proxy implements, or provide
  * another proxy), move the metadata to declared methods or interfaces the proxy implements, or provide
  * your own replacement <tt>MethodDefinitionSource</tt>.
  * your own replacement <tt>MethodDefinitionSource</tt>.
  * </p>
  * </p>
- * 
+ *
  * @author Ben Alex
  * @author Ben Alex
  * @version $Id$
  * @version $Id$
  * @since 2.0
  * @since 2.0
@@ -39,15 +39,16 @@ import org.springframework.util.ObjectUtils;
 public abstract class AbstractFallbackMethodDefinitionSource implements MethodDefinitionSource {
 public abstract class AbstractFallbackMethodDefinitionSource implements MethodDefinitionSource {
 
 
     private static final Log logger = LogFactory.getLog(AbstractFallbackMethodDefinitionSource.class);
     private static final Log logger = LogFactory.getLog(AbstractFallbackMethodDefinitionSource.class);
-	private final static Object NULL_CONFIG_ATTRIBUTE = new Object();
+    private final static Object NULL_CONFIG_ATTRIBUTE = new Object();
     private final Map attributeCache = new HashMap();
     private final Map attributeCache = new HashMap();
 
 
     public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException {
     public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException {
         Assert.notNull(object, "Object cannot be null");
         Assert.notNull(object, "Object cannot be null");
 
 
         if (object instanceof MethodInvocation) {
         if (object instanceof MethodInvocation) {
-        	MethodInvocation mi = (MethodInvocation) object;
-            return getAttributes(mi.getMethod(), mi.getThis().getClass());
+            MethodInvocation mi = (MethodInvocation) object;
+            Object target = mi.getThis();
+            return getAttributes(mi.getMethod(), target == null ? null : target.getClass());
         }
         }
 
 
         if (object instanceof JoinPoint) {
         if (object instanceof JoinPoint) {
@@ -59,143 +60,143 @@ public abstract class AbstractFallbackMethodDefinitionSource implements MethodDe
 
 
             Method method = ClassUtils.getMethodIfAvailable(declaringType, targetMethodName, types);
             Method method = ClassUtils.getMethodIfAvailable(declaringType, targetMethodName, types);
             Assert.notNull(method, "Could not obtain target method from JoinPoint: '"+ jp + "'");
             Assert.notNull(method, "Could not obtain target method from JoinPoint: '"+ jp + "'");
-            
+
             return getAttributes(method, targetClass);
             return getAttributes(method, targetClass);
         }
         }
 
 
         throw new IllegalArgumentException("Object must be a MethodInvocation or JoinPoint");
         throw new IllegalArgumentException("Object must be a MethodInvocation or JoinPoint");
     }
     }
-    
+
     public final boolean supports(Class clazz) {
     public final boolean supports(Class clazz) {
         return (MethodInvocation.class.isAssignableFrom(clazz) || JoinPoint.class.isAssignableFrom(clazz));
         return (MethodInvocation.class.isAssignableFrom(clazz) || JoinPoint.class.isAssignableFrom(clazz));
     }
     }
-    
+
     public ConfigAttributeDefinition getAttributes(Method method, Class targetClass) {
     public ConfigAttributeDefinition getAttributes(Method method, Class targetClass) {
-		// First, see if we have a cached value.
-		Object cacheKey = new DefaultCacheKey(method, targetClass);
-		synchronized (this.attributeCache) {
-			Object cached = this.attributeCache.get(cacheKey);
-			if (cached != null) {
-				// Value will either be canonical value indicating there is no config attribute,
-				// or an actual config attribute.
-				if (cached == NULL_CONFIG_ATTRIBUTE) {
-					return null;
-				}
-				else {
-					return (ConfigAttributeDefinition) cached;
-				}
-			}
-			else {
-				// We need to work it out.
-				ConfigAttributeDefinition cfgAtt = computeAttributes(method, targetClass);
-				// Put it in the cache.
-				if (cfgAtt == null) {
-					this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
-				}
-				else {
-					if (logger.isDebugEnabled()) {
-						logger.debug("Adding security method [" + cacheKey + "] with attribute [" + cfgAtt + "]");
-					}
-					this.attributeCache.put(cacheKey, cfgAtt);
-				}
-				return cfgAtt;
-			}
-		}
+        // First, see if we have a cached value.
+        Object cacheKey = new DefaultCacheKey(method, targetClass);
+        synchronized (this.attributeCache) {
+            Object cached = this.attributeCache.get(cacheKey);
+            if (cached != null) {
+                // Value will either be canonical value indicating there is no config attribute,
+                // or an actual config attribute.
+                if (cached == NULL_CONFIG_ATTRIBUTE) {
+                    return null;
+                }
+                else {
+                    return (ConfigAttributeDefinition) cached;
+                }
+            }
+            else {
+                // We need to work it out.
+                ConfigAttributeDefinition cfgAtt = computeAttributes(method, targetClass);
+                // Put it in the cache.
+                if (cfgAtt == null) {
+                    this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
+                }
+                else {
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("Adding security method [" + cacheKey + "] with attribute [" + cfgAtt + "]");
+                    }
+                    this.attributeCache.put(cacheKey, cfgAtt);
+                }
+                return cfgAtt;
+            }
+        }
     }
     }
-    
+
     /**
     /**
-     * 
-	 * @param method the method for the current invocation (never <code>null</code>)
-	 * @param targetClass the target class for this invocation (may be <code>null</code>)
+     *
+     * @param method the method for the current invocation (never <code>null</code>)
+     * @param targetClass the target class for this invocation (may be <code>null</code>)
      * @return
      * @return
      */
      */
     private ConfigAttributeDefinition computeAttributes(Method method, Class targetClass) {
     private ConfigAttributeDefinition computeAttributes(Method method, Class targetClass) {
-    	// The method may be on an interface, but we need attributes from the target class.
-		// If the target class is null, the method will be unchanged.
-		Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
-		// First try is the method in the target class.
-		ConfigAttributeDefinition attr = findAttributes(specificMethod, targetClass);
-		if (attr != null) {
-			return attr;
-		}
-
-		// Second try is the config attribute on the target class.
-		attr = findAttributes(specificMethod.getDeclaringClass());
-		if (attr != null) {
-			return attr;
-		}
-
-		if (specificMethod != method) {
-			// Fallback is to look at the original method.
-			attr = findAttributes(method, method.getDeclaringClass());
-			if (attr != null) {
-				return attr;
-			}
-			// Last fallback is the class of the original method.
-			return findAttributes(method.getDeclaringClass());
-		}
-		return null;
+        // The method may be on an interface, but we need attributes from the target class.
+        // If the target class is null, the method will be unchanged.
+        Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
+        // First try is the method in the target class.
+        ConfigAttributeDefinition attr = findAttributes(specificMethod, targetClass);
+        if (attr != null) {
+            return attr;
+        }
+
+        // Second try is the config attribute on the target class.
+        attr = findAttributes(specificMethod.getDeclaringClass());
+        if (attr != null) {
+            return attr;
+        }
+
+        if (specificMethod != method || targetClass == null) {
+            // Fallback is to look at the original method.
+            attr = findAttributes(method, method.getDeclaringClass());
+            if (attr != null) {
+                return attr;
+            }
+            // Last fallback is the class of the original method.
+            return findAttributes(method.getDeclaringClass());
+        }
+        return null;
 
 
     }
     }
-    
+
     /**
     /**
      * Obtains the security metadata applicable to the specified method invocation.
      * Obtains the security metadata applicable to the specified method invocation.
-     * 
+     *
      * <p>
      * <p>
      * Note that the {@link Method#getDeclaringClass()} may not equal the <code>targetClass</code>.
      * Note that the {@link Method#getDeclaringClass()} may not equal the <code>targetClass</code>.
      * Both parameters are provided to assist subclasses which may wish to provide advanced
      * Both parameters are provided to assist subclasses which may wish to provide advanced
      * capabilities related to method metadata being "registered" against a method even if the
      * capabilities related to method metadata being "registered" against a method even if the
      * target class does not declare the method (i.e. the subclass may only inherit the method).
      * target class does not declare the method (i.e. the subclass may only inherit the method).
-     * 
+     *
      * @param method the method for the current invocation (never <code>null</code>)
      * @param method the method for the current invocation (never <code>null</code>)
      * @param targetClass the target class for the invocation (may be <code>null</code>)
      * @param targetClass the target class for the invocation (may be <code>null</code>)
      * @return the security metadata (or null if no metadata applies)
      * @return the security metadata (or null if no metadata applies)
      */
      */
     protected abstract ConfigAttributeDefinition findAttributes(Method method, Class targetClass);
     protected abstract ConfigAttributeDefinition findAttributes(Method method, Class targetClass);
-    
+
     /**
     /**
      * Obtains the security metadata registered against the specified class.
      * Obtains the security metadata registered against the specified class.
-     * 
+     *
      * <p>
      * <p>
      * Subclasses should only return metadata expressed at a class level. Subclasses should NOT
      * Subclasses should only return metadata expressed at a class level. Subclasses should NOT
      * aggregate metadata for each method registered against a class, as the abstract superclass
      * aggregate metadata for each method registered against a class, as the abstract superclass
      * will separate invoke {@link #findAttributes(Method, Class)} for individual methods as
      * will separate invoke {@link #findAttributes(Method, Class)} for individual methods as
-     * appropriate. 
-     * 
+     * appropriate.
+     *
      * @param clazz the target class for the invocation (never <code>null</code>)
      * @param clazz the target class for the invocation (never <code>null</code>)
      * @return the security metadata (or null if no metadata applies)
      * @return the security metadata (or null if no metadata applies)
      */
      */
     protected abstract ConfigAttributeDefinition findAttributes(Class clazz);
     protected abstract ConfigAttributeDefinition findAttributes(Class clazz);
-    
-	private static class DefaultCacheKey {
-
-		private final Method method;
-		private final Class targetClass;
-
-		public DefaultCacheKey(Method method, Class targetClass) {
-			this.method = method;
-			this.targetClass = targetClass;
-		}
-
-		public boolean equals(Object other) {
-			if (this == other) {
-				return true;
-			}
-			if (!(other instanceof DefaultCacheKey)) {
-				return false;
-			}
-			DefaultCacheKey otherKey = (DefaultCacheKey) other;
-			return (this.method.equals(otherKey.method) &&
-					ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
-		}
-
-		public int hashCode() {
-			return this.method.hashCode() * 21 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
-		}
-
-		public String toString() {
-			return "CacheKey[" + (targetClass == null ? "-" : targetClass.getName()) + "; " + method + "]";
-		}
-	}
-    
+
+    private static class DefaultCacheKey {
+
+        private final Method method;
+        private final Class targetClass;
+
+        public DefaultCacheKey(Method method, Class targetClass) {
+            this.method = method;
+            this.targetClass = targetClass;
+        }
+
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof DefaultCacheKey)) {
+                return false;
+            }
+            DefaultCacheKey otherKey = (DefaultCacheKey) other;
+            return (this.method.equals(otherKey.method) &&
+                    ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
+        }
+
+        public int hashCode() {
+            return this.method.hashCode() * 21 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
+        }
+
+        public String toString() {
+            return "CacheKey[" + (targetClass == null ? "-" : targetClass.getName()) + "; " + method + "]";
+        }
+    }
+
 }
 }

+ 7 - 3
core/src/main/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSource.java

@@ -88,6 +88,10 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
      * Will walk the method inheritance tree to find the most specific declaration applicable.
      * Will walk the method inheritance tree to find the most specific declaration applicable.
      */
      */
     protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) {
     protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) {
+        if (targetClass == null) {
+            return null;
+        }
+
         return findAttributesSpecifiedAgainst(method, targetClass);
         return findAttributesSpecifiedAgainst(method, targetClass);
     }
     }
 
 
@@ -179,9 +183,9 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
 
 
     /**
     /**
      * Adds configuration attributes for a specific method, for example where the method has been
      * Adds configuration attributes for a specific method, for example where the method has been
-     * matched using a pointcut expression. If a match already exists in the map for the method, then 
-     * the existing match will be retained, so that if this method is called for a more general pointcut 
-     * it will not override a more specific one which has already been added. This  
+     * matched using a pointcut expression. If a match already exists in the map for the method, then
+     * the existing match will be retained, so that if this method is called for a more general pointcut
+     * it will not override a more specific one which has already been added. This
      */
      */
     public void addSecureMethod(Class javaType, Method method, ConfigAttributeDefinition attr) {
     public void addSecureMethod(Class javaType, Method method, ConfigAttributeDefinition attr) {
         RegisteredMethod key = new RegisteredMethod(method, javaType);
         RegisteredMethod key = new RegisteredMethod(method, javaType);