Author: henrib Date: Mon Nov 20 14:58:39 2017 New Revision: 1815813 URL: http://svn.apache.org/viewvc?rev=1815813&view=rev Log: JEXL: Made permissions an explicit instance in preparation for future / further sandboxing capabilities
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java?rev=1815813&r1=1815812&r2=1815813&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/ClassMap.java Mon Nov 20 14:58:39 2017 @@ -63,8 +63,8 @@ final class ClassMap { * This is the cache to store and look up the method information. * <p> * It stores the association between: - * - a key made of a method name and an array of argument types. - * - a method. + * - a key made of a method name and an array of argument types. + * - a method. * </p> * <p> * Since the invocation of the associated method is dynamic, there is no need (nor way) to differentiate between @@ -87,18 +87,19 @@ final class ClassMap { * Standard constructor. * * @param aClass the class to deconstruct. - * @param log the logger. + * @param permissions the permissions to apply during introspection + * @param log the logger. */ @SuppressWarnings("LeakingThisInConstructor") - ClassMap(Class<?> aClass, Log log) { + ClassMap(Class<?> aClass, Permissions permissions, Log log) { // eagerly cache methods - create(this, aClass, log); + create(this, permissions, aClass, log); // eagerly cache public fields Field[] fields = aClass.getFields(); if (fields.length > 0) { Map<String, Field> cache = new HashMap<String, Field>(); for (Field field : fields) { - if (Modifier.isPublic(field.getModifiers()) && Permissions.allow(field)) { + if (permissions.allow(field)) { cache.put(field.getName(), field); } } @@ -149,16 +150,16 @@ final class ClassMap { /** * Find a Method using the method name and parameter objects. - *<p> - * Look in the methodMap for an entry. If found, + * <p> + * Look in the methodMap for an entry. If found, * it'll either be a CACHE_MISS, in which case we * simply give up, or it'll be a Method, in which * case, we return it. - *</p> + * </p> * <p> * If nothing is found, then we must actually go * and introspect the method from the MethodMap. - *</p> + * </p> * @param methodKey the method key * @return A Method object representing the method to invoke or null. * @throws MethodKey.AmbiguousException When more than one method is a match for the parameters. @@ -195,11 +196,12 @@ final class ClassMap { /** * Populate the Map of direct hits. These are taken from all the public methods * that our class, its parents and their implemented interfaces provide. - * @param cache the ClassMap instance we create + * @param cache the ClassMap instance we create + * @param permissions the permissions to apply during introspection * @param classToReflect the class to cache - * @param log the Log + * @param log the Log */ - private static void create(ClassMap cache, Class<?> classToReflect, Log log) { + private static void create(ClassMap cache, Permissions permissions, Class<?> classToReflect, Log log) { // // Build a list of all elements in the class hierarchy. This one is bottom-first (i.e. we start // with the actual declaring class and its interfaces and then move up (superclass etc.) until we @@ -210,11 +212,11 @@ final class ClassMap { // for (; classToReflect != null; classToReflect = classToReflect.getSuperclass()) { if (Modifier.isPublic(classToReflect.getModifiers())) { - populateWithClass(cache, classToReflect, log); + populateWithClass(cache, permissions, classToReflect, log); } Class<?>[] interfaces = classToReflect.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { - populateWithInterface(cache, interfaces[i], log); + populateWithInterface(cache, permissions, interfaces[i], log); } } // now that we've got all methods keyed in, lets organize them by name @@ -253,31 +255,33 @@ final class ClassMap { /** * Recurses up interface hierarchy to get all super interfaces. * @param cache the cache to fill + * @param permissions the permissions to apply during introspection * @param iface the interface to populate the cache from - * @param log the Log + * @param log the Log */ - private static void populateWithInterface(ClassMap cache, Class<?> iface, Log log) { + private static void populateWithInterface(ClassMap cache, Permissions permissions, Class<?> iface, Log log) { if (Modifier.isPublic(iface.getModifiers())) { - populateWithClass(cache, iface, log); - } - Class<?>[] supers = iface.getInterfaces(); - for (int i = 0; i < supers.length; i++) { - populateWithInterface(cache, supers[i], log); + populateWithClass(cache, permissions, iface, log); + Class<?>[] supers = iface.getInterfaces(); + for (int i = 0; i < supers.length; i++) { + populateWithInterface(cache, permissions, supers[i], log); + } } } /** * Recurses up class hierarchy to get all super classes. * @param cache the cache to fill + * @param permissions the permissions to apply during introspection * @param clazz the class to populate the cache from - * @param log the Log + * @param log the Log */ - private static void populateWithClass(ClassMap cache, Class<?> clazz, Log log) { + private static void populateWithClass(ClassMap cache, Permissions permissions, Class<?> clazz, Log log) { try { Method[] methods = clazz.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Method mi = methods[i]; - if (Modifier.isPublic(mi.getModifiers()) && Permissions.allow(mi)) { + if (permissions.allow(mi)) { // add method to byKey cache; do not override cache.byKey.putIfAbsent(new MethodKey(mi), mi); } Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java?rev=1815813&r1=1815812&r2=1815813&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java Mon Nov 20 14:58:39 2017 @@ -21,7 +21,6 @@ import org.apache.commons.logging.Log; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; @@ -68,6 +67,10 @@ public final class Introspector { */ private ClassLoader loader; /** + * The permissions. + */ + private final Permissions permissions; + /** * The read/write lock. */ private final ReadWriteLock lock = new ReentrantReadWriteLock(); @@ -90,8 +93,19 @@ public final class Introspector { * @param cloader the class loader */ public Introspector(Log log, ClassLoader cloader) { + this(log, cloader, null); + } + + /** + * Create the introspector. + * @param log the logger to use + * @param cloader the class loader + * @param perms the permissions + */ + public Introspector(Log log, ClassLoader cloader, Permissions perms) { this.rlog = log; - loader = cloader; + this.loader = cloader; + this.permissions = perms != null? perms : Permissions.DEFAULT; } /** @@ -247,7 +261,7 @@ public final class Introspector { } List<Constructor<?>> l = new ArrayList<Constructor<?>>(); for (Constructor<?> ictor : clazz.getConstructors()) { - if (Modifier.isPublic(ictor.getModifiers()) && Permissions.allow(ictor)) { + if (permissions.allow(ictor)) { l.add(ictor); } } @@ -298,7 +312,7 @@ public final class Introspector { // try again classMap = classMethodMaps.get(c); if (classMap == null) { - classMap = new ClassMap(c, rlog); + classMap = new ClassMap(c, permissions, rlog); classMethodMaps.put(c, classMap); } } finally { Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java?rev=1815813&r1=1815812&r2=1815813&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Permissions.java Mon Nov 20 14:58:39 2017 @@ -20,25 +20,42 @@ package org.apache.commons.jexl3.interna import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import org.apache.commons.jexl3.annotations.NoJexl; /** - * Checks whether an element (ctor, field or method) is visible by JEXL introspection - * by checking if has been annotated with NoJexl. + * Checks whether an element (ctor, field or method) is visible by JEXL introspection. + * Default implementation does this by checking if element has been annotated with NoJexl. */ public class Permissions { /** Make non instantiable. */ private Permissions() { } + /** + * The default singleton. + */ + public static final Permissions DEFAULT = new Permissions(); /** - * Checks whether a class or one of its superclasses or implemented interfaces + * Checks whether a package explicitly disallows JEXL introspection. + * @param pack the package + * @return true if JEXL is allowed to introspect, false otherwise + */ + public boolean allow(Package pack) { + if (pack != null && pack.getAnnotation(NoJexl.class) != null) { + return false; + } + return true; + } + + /** + * Checks whether a class or one of its super-classes or implemented interfaces * explicitly disallows JEXL introspection. * @param clazz the class to check * @return true if JEXL is allowed to introspect, false otherwise */ - public static boolean allow(Class<?> clazz) { - return allow(clazz, true); + public boolean allow(Class<?> clazz) { + return clazz != null && allow(clazz.getPackage()) && allow(clazz, true); } /** @@ -46,10 +63,13 @@ public class Permissions { * @param ctor the constructor to check * @return true if JEXL is allowed to introspect, false otherwise */ - public static boolean allow(Constructor<?> ctor) { + public boolean allow(Constructor<?> ctor) { if (ctor == null) { return false; } + if (!Modifier.isPublic(ctor.getModifiers())) { + return false; + } Class<?> clazz = ctor.getDeclaringClass(); if (!allow(clazz, false)) { return false; @@ -67,10 +87,13 @@ public class Permissions { * @param field the field to check * @return true if JEXL is allowed to introspect, false otherwise */ - public static boolean allow(Field field) { + public boolean allow(Field field) { if (field == null) { return false; } + if (!Modifier.isPublic(field.getModifiers())) { + return false; + } Class<?> clazz = field.getDeclaringClass(); if (!allow(clazz, false)) { return false; @@ -85,15 +108,18 @@ public class Permissions { /** * Checks whether a method explicitly disallows JEXL introspection. - * <p>Since methods can be overriden, this also checks that no superclass or interface + * <p>Since methods can be overridden, this also checks that no superclass or interface * explictly disallows this methods.</p> * @param method the method to check * @return true if JEXL is allowed to introspect, false otherwise */ - public static boolean allow(Method method) { + public boolean allow(Method method) { if (method == null) { return false; } + if (!Modifier.isPublic(method.getModifiers())) { + return false; + } // is method annotated with nojexl ? NoJexl nojexl = method.getAnnotation(NoJexl.class); if (nojexl != null) { @@ -134,9 +160,7 @@ public class Permissions { if (clazz == null) { return false; } - // is package annotated with nojexl ? - Package pack = clazz.getPackage(); - if (pack != null && pack.getAnnotation(NoJexl.class) != null) { + if (!Modifier.isPublic(clazz.getModifiers())) { return false; } // lets walk all interfaces