You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2019/04/29 07:54:24 UTC

[commons-jexl] branch master updated: JEXL-253: first code drop, testing waters

This is an automated email from the ASF dual-hosted git repository.

henrib pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git


The following commit(s) were added to refs/heads/master by this push:
     new b508e51  JEXL-253: first code drop, testing waters
     new 028f2c4  Merge origin/master
b508e51 is described below

commit b508e517f7134e43722b055b9cc60b5c7c1a3446
Author: Henri Biestro <hb...@gmail.com>
AuthorDate: Mon Apr 29 09:50:27 2019 +0200

    JEXL-253: first code drop, testing waters
---
 .../internal/introspection/SandboxUberspect.java   |  22 ++--
 .../commons/jexl3/introspection/JexlSandbox.java   | 141 +++++++++++++++++----
 2 files changed, 123 insertions(+), 40 deletions(-)

diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java
index 13778b0..5fe8582 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java
@@ -57,7 +57,7 @@ public final class SandboxUberspect implements JexlUberspect {
     public void setClassLoader(ClassLoader loader) {
         uberspect.setClassLoader(loader);
     }
-        
+
     @Override
     public ClassLoader getClassLoader() {
         return uberspect.getClassLoader();
@@ -72,24 +72,20 @@ public final class SandboxUberspect implements JexlUberspect {
     public JexlMethod getConstructor(final Object ctorHandle, final Object... args) {
         final String className;
         if (ctorHandle instanceof Class<?>) {
-            Class<?> clazz = (Class<?>) ctorHandle;
-            className = clazz.getName();
+            className = sandbox.execute((Class<?>) ctorHandle, "");
         } else if (ctorHandle != null) {
-            className = ctorHandle.toString();
+            className = sandbox.execute(ctorHandle.toString(), "");
         } else {
-            return null;
-        }
-        if (sandbox.execute(className, "") != null) {
-            return uberspect.getConstructor(className, args);
+            className = null;
         }
-        return null;
+        return className != null? uberspect.getConstructor(className, args) : null;
     }
 
     @Override
     public JexlMethod getMethod(final Object obj, final String method, final Object... args) {
         if (obj != null && method != null) {
-            String objClassName = (obj instanceof Class) ? ((Class<?>)obj).getName() : obj.getClass().getName();
-            String actual = sandbox.execute(objClassName, method);
+            Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
+            String actual = sandbox.execute(clazz, method);
             if (actual != null) {
                 return uberspect.getMethod(obj, actual, args);
             }
@@ -113,7 +109,7 @@ public final class SandboxUberspect implements JexlUberspect {
                                           final Object identifier) {
         if (obj != null && identifier != null) {
             String property = identifier.toString();
-            String actual = sandbox.read(obj.getClass().getName(), property);
+            String actual = sandbox.read(obj.getClass(), property);
             if (actual != null) {
                  // no transformation, strict equality: use identifier before string conversion
                 Object pty = actual == property? identifier : actual;
@@ -135,7 +131,7 @@ public final class SandboxUberspect implements JexlUberspect {
                                           final Object arg) {
         if (obj != null && identifier != null) {
             String property = identifier.toString();
-            String actual = sandbox.write(obj.getClass().getName(), property);
+            String actual = sandbox.write(obj.getClass(), property);
             if (actual != null) {
                  // no transformation, strict equality: use identifier before string conversion
                 Object pty = actual == property? identifier : actual;
diff --git a/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java b/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java
index 1b4e299..f71f776 100644
--- a/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java
+++ b/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java
@@ -21,6 +21,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * A sandbox describes permissions on a class by explicitly allowing or forbidding access to methods and properties
@@ -61,6 +62,10 @@ public final class JexlSandbox {
      */
     private final Map<String, Permissions> sandbox;
     /**
+     * Whether permissions can be inherited (through implementation or extension).
+     */
+    private final boolean inherit;
+    /**
      * Default behavior, black or white.
      */
     private final boolean white;
@@ -101,7 +106,19 @@ public final class JexlSandbox {
      * @since 3.1
      */
     protected JexlSandbox(boolean wb, Map<String, Permissions> map) {
+        this(wb, false, map);
+    }
+
+    /**
+     * Creates a sandbox based on an existing permissions map.
+     * @param wb whether this sandbox is white (true) or black (false)
+     * @param inh whether permissions are inherited, default false
+     * @param map the permissions map
+     * @since 3.2
+     */
+    protected JexlSandbox(boolean wb, boolean inh, Map<String, Permissions> map) {
         white = wb;
+        inherit = inh;
         sandbox = map;
     }
 
@@ -109,11 +126,25 @@ public final class JexlSandbox {
      * @return a copy of this sandbox
      */
     public JexlSandbox copy() {
-        Map<String, Permissions> map = new HashMap<String, Permissions>();
+        // modified concurently at runtime so...
+        Map<String, Permissions> map = new ConcurrentHashMap<String, Permissions>();
         for (Map.Entry<String, Permissions> entry : sandbox.entrySet()) {
             map.put(entry.getKey(), entry.getValue().copy());
         }
-        return new JexlSandbox(white, map);
+        return new JexlSandbox(white, inherit, map);
+    }
+
+    /**
+     * Gets a class by name, crude mechanism for backwards (&lt;3.2 ) compatibility.
+     * @param cname the class name
+     * @return the class
+     */
+    static Class<?> forName(String cname) {
+        try {
+            return Class.forName(cname);
+        } catch(Exception xany) {
+            return null;
+        }
     }
 
     /**
@@ -124,7 +155,7 @@ public final class JexlSandbox {
      * @return null if not allowed, the name of the property to use otherwise
      */
     public String read(Class<?> clazz, String name) {
-        return read(clazz.getName(), name);
+        return get(clazz).read().get(name);
     }
 
     /**
@@ -134,13 +165,9 @@ public final class JexlSandbox {
      * @param name the property name
      * @return null if not allowed, the name of the property to use otherwise
      */
+    @Deprecated
     public String read(String clazz, String name) {
-        Permissions permissions = sandbox.get(clazz);
-        if (permissions == null) {
-            return white? name : null;
-        } else {
-            return permissions.read().get(name);
-        }
+        return get(clazz).read().get(name);
     }
 
     /**
@@ -151,7 +178,7 @@ public final class JexlSandbox {
      * @return null if not allowed, the name of the property to use otherwise
      */
     public String write(Class<?> clazz, String name) {
-        return write(clazz.getName(), name);
+        return get(clazz).write().get(name);
     }
 
     /**
@@ -161,13 +188,9 @@ public final class JexlSandbox {
      * @param name the property name
      * @return null if not allowed, the name of the property to use otherwise
      */
+    @Deprecated
     public String write(String clazz, String name) {
-        Permissions permissions = sandbox.get(clazz);
-        if (permissions == null) {
-            return white ? name : null;
-        } else {
-            return permissions.write().get(name);
-        }
+        return get(clazz).write().get(name);
     }
 
     /**
@@ -178,7 +201,8 @@ public final class JexlSandbox {
      * @return null if not allowed, the name of the method to use otherwise
      */
     public String execute(Class<?> clazz, String name) {
-        return execute(clazz.getName(), name);
+        String m = get(clazz).execute().get(name);
+        return "".equals(name) && m != null? clazz.getName() : m;
     }
 
     /**
@@ -188,13 +212,10 @@ public final class JexlSandbox {
      * @param name the method name
      * @return null if not allowed, the name of the method to use otherwise
      */
+    @Deprecated
     public String execute(String clazz, String name) {
-        Permissions permissions = sandbox.get(clazz);
-        if (permissions == null) {
-            return white ? name : null;
-        } else {
-            return permissions.execute().get(name);
-        }
+        String m = get(clazz).execute().get(name);
+        return "".equals(name) && m != null? clazz : m;
     }
 
     /**
@@ -255,6 +276,26 @@ public final class JexlSandbox {
         }
     };
 
+    /*
+     * The block-all name set.
+     */
+    private static final Names BLACK_NAMES = new Names() {
+        @Override
+        public boolean add(String name) {
+            return false;
+        }
+
+        @Override
+        protected Names copy() {
+            return this;
+        }
+
+        @Override
+        public String get(String name) {
+            return null;
+        }
+    };
+
     /**
      * A white set of names.
      */
@@ -327,13 +368,12 @@ public final class JexlSandbox {
      * Contains the white or black lists for properties and methods for a given class.
      */
     public static final class Permissions {
-
+        /** Whether these permissions are inheritable, ie can be used by derived classes. */
+        private final boolean inheritable;
         /** The controlled readable properties. */
         private final Names read;
-
         /** The controlled  writable properties. */
         private final Names write;
-
         /** The controlled methods. */
         private final Names execute;
 
@@ -361,6 +401,7 @@ public final class JexlSandbox {
             this.read = nread != null ? nread : WHITE_NAMES;
             this.write = nwrite != null ? nwrite : WHITE_NAMES;
             this.execute = nexecute != null ? nexecute : WHITE_NAMES;
+            this.inheritable = false;
         }
 
         /**
@@ -371,6 +412,14 @@ public final class JexlSandbox {
         }
 
         /**
+         * Whether this permission applies to derived classes.
+         * @return true
+         */
+        public boolean isInheritable() {
+            return inheritable;
+        }
+
+        /**
          * Adds a list of readable property names to these permissions.
          *
          * @param pnames the property names
@@ -437,10 +486,15 @@ public final class JexlSandbox {
             return execute;
         }
     }
+
     /**
      * The pass-thru permissions.
      */
     private static final Permissions ALL_WHITE = new Permissions(WHITE_NAMES, WHITE_NAMES, WHITE_NAMES);
+    /**
+     * The block-all permissions.
+     */
+    private static final Permissions ALL_BLACK = new Permissions(BLACK_NAMES, BLACK_NAMES, BLACK_NAMES);
 
     /**
      * Creates the set of permissions for a given class.
@@ -484,11 +538,44 @@ public final class JexlSandbox {
      * @return the defined permissions or an all-white permission instance if none were defined
      */
     public Permissions get(String clazz) {
+        if (inherit) {
+            return get(forName(clazz));
+        }
         Permissions permissions = sandbox.get(clazz);
         if (permissions == null) {
-            return ALL_WHITE;
+            return white ? ALL_WHITE : ALL_BLACK;
         } else {
             return permissions;
         }
     }
+
+    /**
+     * Get the permissions associated to a class.
+     * @param clazz the class
+     * @return the permissions
+     */
+    public Permissions get(Class<?> clazz) {
+        Permissions permissions = clazz == null? ALL_BLACK : sandbox.get(clazz.getName());
+        if (permissions == null) {
+            if (inherit) {
+                // clazz can not be null since permissions would be not null and black;
+                // find first inheritable class that defined permissions
+                for (Class<?> ii : clazz.getClasses()) {
+                    permissions = sandbox.get(ii.getName());
+                    if (permissions != null && permissions.isInheritable()) {
+                        break;
+                    }
+                }
+                if (permissions == null) {
+                    permissions = white ? ALL_WHITE : ALL_BLACK;
+                }
+                // store the info to avoid doing this costly look up
+                sandbox.put(clazz.getName(), permissions);
+            } else {
+                permissions = white ? ALL_WHITE : ALL_BLACK;
+            }
+        }
+        return permissions;
+    }
+
 }