From 3d748e3ff7fbce780c622891dddfed1120e84490 Mon Sep 17 00:00:00 2001 From: Henrib <hbiestro@gmail.com> Date: Wed, 21 Aug 2024 14:56:52 +0200 Subject: [PATCH] JEXL: JexlSandbox clean up; --- .../jexl3/introspection/JexlSandbox.java | 1230 ++++++++--------- 1 file changed, 615 insertions(+), 615 deletions(-) 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 cc9354549..3ab8e32fa 100644 --- a/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java +++ b/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java @@ -67,620 +67,620 @@ * @since 3.0 */ public final class JexlSandbox { - /** - * The marker string for explicitly disallowed null properties. - */ - public static final String NULL = "?"; - - /** - * The pass-thru name set. - */ - static final Names ALLOW_NAMES = new Names() { - @Override - public boolean add(final String name) { - return false; - } - - @Override - public String toString() { - return "allowAll"; - } - }; - - /** - * The block-all name set. - */ - private static final Names BLOCK_NAMES = new Names() { - @Override - public boolean add(final String name) { - return false; - } - - @Override - public String get(final String name) { - return name == null ? NULL : null; - } - - @Override - public String toString() { - return "blockAll"; - } - }; - - /** - * The block-all permissions. - */ - private static final Permissions BLOCK_ALL = new Permissions(false, BLOCK_NAMES, BLOCK_NAMES, BLOCK_NAMES); - /** - * The pass-thru permissions. - */ - private static final Permissions ALLOW_ALL = new Permissions(false, ALLOW_NAMES, ALLOW_NAMES, ALLOW_NAMES); - /** - * The map from class names to permissions. - */ - private final Map<String, Permissions> sandbox; - /** - * Whether permissions can be inherited (through implementation or extension). - */ - private final boolean inherit; - /** - * Default behavior, block or allow. - */ - private final boolean allow; - - /** - * Creates a new default sandbox. - * <p>In the absence of explicit permissions on a class, the - * sandbox is a allow-box, allow-listing that class for all permissions (read, write and execute). - */ - public JexlSandbox() { - this(true, false, null); - } - - /** - * Creates a new default sandbox. - * <p>A allow-box considers no permissions as "everything is allowed" when - * a block-box considers no permissions as "nothing is allowed". - * - * @param ab whether this sandbox is allow (true) or block (false) - * if no permission is explicitly defined for a class. - * @since 3.1 - */ - public JexlSandbox(final boolean ab) { - this(ab, false, null); - } - - /** - * Creates a sandbox. - * - * @param ab whether this sandbox is allow (true) or block (false) - * @param inh whether permissions on interfaces and classes are inherited (true) or not (false) - * @since 3.2 - */ - public JexlSandbox(final boolean ab, final boolean inh) { - this(ab, inh, null); - } - - /** - * Creates a sandbox based on an existing permissions map. - * - * @param ab whether this sandbox is allow (true) or block (false) - * @param inh whether permissions are inherited, default false - * @param map the permissions map - * @since 3.2 - */ - private JexlSandbox(final boolean ab, final boolean inh, final Map<String, Permissions> map) { - allow = ab; - inherit = inh; - sandbox = map != null ? map : new HashMap<>(); - } - - /** - * Gets a class by name, crude mechanism for backwards (<3.2 ) compatibility. - * - * @param cname the class name - * @return the class - */ - static Class<?> forName(final String cname) { - try { - return Class.forName(cname); - } catch (final Exception xany) { - return null; - } - } - - /** - * Creates a new set of permissions based on allow lists for methods and properties for a given class. - * <p>The sandbox inheritance property will apply to the permissions created by this method - * - * @param clazz the allowed class name - * @return the permissions instance - */ - public Permissions allow(final String clazz) { - return permissions(clazz, true, true, true); - } - - /** - * Creates a new set of permissions based on block lists for methods and properties for a given class. - * <p>The sandbox inheritance property will apply to the permissions created by this method - * - * @param clazz the blocked class name - * @return the permissions instance - */ - public Permissions block(final String clazz) { - return permissions(clazz, false, false, false); - } - - /** - * @return a copy of this sandbox - */ - public JexlSandbox copy() { - // modified concurrently at runtime so... - final Map<String, Permissions> map = new ConcurrentHashMap<>(); - for (final Map.Entry<String, Permissions> entry : sandbox.entrySet()) { - map.put(entry.getKey(), entry.getValue().copy()); - } - return new JexlSandbox(allow, inherit, map); - } - - /** - * Gets the execute permission value for a given method of a class. - * - * @param clazz the class - * @param name the method name - * @return null if not allowed, the name of the method to use otherwise - */ - public String execute(final Class<?> clazz, final String name) { - final String m = get(clazz).execute().get(name); - return "".equals(name) && m != null ? clazz.getName() : m; - } - - /** - * Gets the execute permission value for a given method of a class. - * - * @param clazz the class name - * @param name the method name - * @return null if not allowed, the name of the method to use otherwise - * @deprecated 3.3 - */ - @Deprecated - public String execute(final String clazz, final String name) { - final String m = get(clazz).execute().get(name); - return "".equals(name) && m != null ? clazz : m; - } - - /** - * Gets the permissions associated to a class. - * - * @param clazz the class - * @return the permissions - */ - @SuppressWarnings("null") - public Permissions get(final Class<?> clazz) { - // clazz can not be null since permissions would be not null and block; - // we only store the result for classes we actively seek permissions for. - return clazz == null ? BLOCK_ALL : compute(clazz); - } - - /** - * Computes and optionally stores the permissions associated to a class. - * - * @param clazz the class - * @return the permissions - */ - private Permissions compute(final Class<?> clazz) { - Permissions permissions = sandbox.get(clazz.getName()); - if (permissions == null) { - if (inherit) { - // find first inherited interface that defines permissions - Class<?>[] interfaces = clazz.getInterfaces(); - for (int i = 0; permissions == null && i < interfaces.length; ++i) { - permissions = inheritable(sandbox.get(interfaces[i].getName())); - } - // nothing defined yet, find first superclass that defines permissions - if (permissions == null) { - // let's walk all super classes - for (Class<?> zuper = clazz.getSuperclass(); - permissions == null && zuper != null; - zuper = zuper.getSuperclass()) { - permissions = inheritable(sandbox.get(zuper.getName())); - } - } - } - // nothing was determined through inheritance - if (permissions == null) { - permissions = allow ? ALLOW_ALL : BLOCK_ALL; - } - // store the info to avoid doing this costly look-up - sandbox.put(clazz.getName(), permissions); - } - return permissions; - } - - private Permissions inheritable(Permissions p) { - return p != null && p.isInheritable() ? p : null; - } - - /** - * Gets the set of permissions associated to a class. - * - * @param clazz the class name - * @return the defined permissions or an all-allow permission instance if none were defined - */ - public Permissions get(final String clazz) { - if (inherit) { - return get(forName(clazz)); - } - final Permissions permissions = sandbox.get(clazz); - if (permissions == null) { - return allow ? ALLOW_ALL : BLOCK_ALL; - } - return permissions; - } - - /** - * Creates the set of permissions for a given class. - * <p>The sandbox inheritance property will apply to the permissions created by this method - * - * @param clazz the class for which these permissions apply - * @param readFlag whether the readable property list is allow - true - or block - false - - * @param writeFlag whether the writable property list is allow - true - or block - false - - * @param executeFlag whether the executable method list is allow - true - or block - false - - * @return the set of permissions - */ - public Permissions permissions(final String clazz, - final boolean readFlag, - final boolean writeFlag, - final boolean executeFlag) { - return permissions(clazz, inherit, readFlag, writeFlag, executeFlag); - } - - /** - * Creates the set of permissions for a given class. - * - * @param clazz the class for which these permissions apply - * @param inhf whether these permissions are inheritable - * @param readf whether the readable property list is allow - true - or block - false - - * @param writef whether the writable property list is allow - true - or block - false - - * @param execf whether the executable method list is allow - true - or block - false - - * @return the set of permissions - */ - public Permissions permissions(final String clazz, - final boolean inhf, - final boolean readf, - final boolean writef, - final boolean execf) { - final Permissions box = new Permissions(inhf, readf, writef, execf); - sandbox.put(clazz, box); - return box; - } - - /** - * Gets the read permission value for a given property of a class. - * - * @param clazz the class - * @param name the property name - * @return null (or NULL if name is null) if not allowed, the name of the property to use otherwise - */ - public String read(final Class<?> clazz, final String name) { - return get(clazz).read().get(name); - } - - /** - * Gets the read permission value for a given property of a class. - * - * @param clazz the class name - * @param name the property name - * @return null if not allowed, the name of the property to use otherwise - * @deprecated 3.3 - */ - @Deprecated - public String read(final String clazz, final String name) { - return get(clazz).read().get(name); - } - - /** - * Use allow() instead. - * - * @param clazz the allowed class name - * @return the permissions instance - * @deprecated 3.3 - */ - @Deprecated - public Permissions white(final String clazz) { - return allow(clazz); - } - - /** - * Gets the write permission value for a given property of a class. - * - * @param clazz the class - * @param name the property name - * @return null (or NULL if name is null) if not allowed, the name of the property to use otherwise - */ - public String write(final Class<?> clazz, final String name) { - return get(clazz).write().get(name); - } - - /** - * Gets the write permission value for a given property of a class. - * - * @param clazz the class name - * @param name the property name - * @return null if not allowed, the name of the property to use otherwise - * @deprecated 3.3 - */ - @Deprecated - public String write(final String clazz, final String name) { - return get(clazz).write().get(name); - } - - /** - * An allow set of names. - */ - static class AllowSet extends Names { - /** - * The map of controlled names and aliases. - */ - private Map<String, String> names; - - @Override - public boolean add(final String name) { - if (names == null) { - names = new HashMap<>(); - } - return names.put(name, name) == null; - } - - @Override - public boolean alias(final String name, final String alias) { - if (names == null) { - names = new HashMap<>(); - } - return names.put(alias, name) == null; - } - - @Override - protected Names copy() { - final AllowSet copy = new AllowSet(); - copy.names = names == null ? null : new HashMap<>(names); - return copy; - } - - @Override - public String get(final String name) { - if (names == null) { - return name; - } - final String actual = names.get(name); - // if null is not explicitly allowed, explicit null aka NULL - if (name == null && actual == null && !names.containsKey(null)) { - return JexlSandbox.NULL; - } - return actual; - } - - @Override - public String toString() { - return "allow{" + (names == null ? "all" : Objects.toString(names.entrySet())) + "}"; - } - } - - /** - * A block set of names. - */ - static class BlockSet extends Names { - /** - * The set of controlled names. - */ - private Set<String> names; - - @Override - public boolean add(final String name) { - if (names == null) { - names = new HashSet<>(); - } - return names.add(name); - } - - @Override - protected Names copy() { - final BlockSet copy = new BlockSet(); - copy.names = names == null ? null : new HashSet<>(names); - return copy; - } - - @Override - public String get(final String name) { - // if name is null and contained in set, explicit null aka NULL - if (names != null && !names.contains(name)) { - return name; - } else if (name != null) { - return null; - } else { - return NULL; - } - } - - @Override - public String toString() { - return "block{" + (names == null ? "all" : Objects.toString(names)) + "}"; - } - } - - /** - * A base set of names. - */ - public abstract static class Names { - /** - * Adds a name to this set. - * - * @param name the name to add - * @return true if the name was really added, false if not - */ - public abstract boolean add(String name); - - /** - * Adds an alias to a name to this set. - * <p>This only has an effect on allow lists.</p> - * - * @param name the name to alias - * @param alias the alias - * @return true if the alias was added, false if it was already present - */ - public boolean alias(final String name, final String alias) { - return false; - } - - /** - * @return a copy of these Names - */ - protected Names copy() { - return this; - } - - /** - * Whether a given name is allowed or not. - * - * @param name the method/property name to check - * @return null (or NULL if name is null) if not allowed, the actual name to use otherwise - */ - public String get(final String name) { - return name; - } - } - - /** - * Contains the allow or block 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; - - /** - * Creates a new permissions instance. - * - * @param inherit whether these permissions are inheritable - * @param readFlag whether the read property list is allow or block - * @param writeFlag whether the write property list is allow or block - * @param executeFlag whether the method list is allow of block - */ - Permissions(final boolean inherit, final boolean readFlag, final boolean writeFlag, final boolean executeFlag) { - this(inherit, - readFlag ? new AllowSet() : new BlockSet(), - writeFlag ? new AllowSet() : new BlockSet(), - executeFlag ? new AllowSet() : new BlockSet()); - } - - /** - * Creates a new permissions instance. - * - * @param inherit whether these permissions are inheritable - * @param nread the read set - * @param nwrite the write set - * @param nexecute the method set - */ - Permissions(final boolean inherit, final Names nread, final Names nwrite, final Names nexecute) { - this.read = nread != null ? nread : ALLOW_NAMES; - this.write = nwrite != null ? nwrite : ALLOW_NAMES; - this.execute = nexecute != null ? nexecute : ALLOW_NAMES; - this.inheritable = inherit; - } - - /** - * @return a copy of these permissions - */ - Permissions copy() { - return new Permissions(inheritable, read.copy(), write.copy(), execute.copy()); - } - - /** - * Gets the set of method names in these permissions. - * - * @return the set of method names - */ - public Names execute() { - return execute; - } - - /** - * Adds a list of executable methods names to these permissions. - * <p>The constructor is denoted as the empty-string, all other methods by their names.</p> - * - * @param methodNames the method names - * @return this instance of permissions - */ - public Permissions execute(final String... methodNames) { - for (final String methodName : methodNames) { - execute.add(methodName); - } - return this; - } - - /** - * @return whether these permissions apply to derived classes. - */ - public boolean isInheritable() { - return inheritable; - } - - /** - * Gets the set of readable property names in these permissions. - * - * @return the set of property names - */ - public Names read() { - return read; - } - - /** - * Adds a list of readable property names to these permissions. - * - * @param propertyNames the property names - * @return this instance of permissions - */ - public Permissions read(final String... propertyNames) { - for (final String propertyName : propertyNames) { - read.add(propertyName); - } - return this; - } - - /** - * Gets the set of writable property names in these permissions. - * - * @return the set of property names - */ - public Names write() { - return write; - } - - /** - * Adds a list of writable property names to these permissions. - * - * @param propertyNames the property names - * @return this instance of permissions - */ - public Permissions write(final String... propertyNames) { - for (final String propertyName : propertyNames) { - write.add(propertyName); - } - return this; - } - } + /** + * The marker string for explicitly disallowed null properties. + */ + public static final String NULL = "?"; + + /** + * The pass-thru name set. + */ + static final Names ALLOW_NAMES = new Names() { + @Override + public boolean add(final String name) { + return false; + } + + @Override + public String toString() { + return "allowAll"; + } + }; + + /** + * The block-all name set. + */ + private static final Names BLOCK_NAMES = new Names() { + @Override + public boolean add(final String name) { + return false; + } + + @Override + public String get(final String name) { + return name == null ? NULL : null; + } + + @Override + public String toString() { + return "blockAll"; + } + }; + + /** + * The block-all permissions. + */ + private static final Permissions BLOCK_ALL = new Permissions(false, BLOCK_NAMES, BLOCK_NAMES, BLOCK_NAMES); + /** + * The pass-thru permissions. + */ + private static final Permissions ALLOW_ALL = new Permissions(false, ALLOW_NAMES, ALLOW_NAMES, ALLOW_NAMES); + /** + * The map from class names to permissions. + */ + private final Map<String, Permissions> sandbox; + /** + * Whether permissions can be inherited (through implementation or extension). + */ + private final boolean inherit; + /** + * Default behavior, block or allow. + */ + private final boolean allow; + + /** + * Creates a new default sandbox. + * <p>In the absence of explicit permissions on a class, the + * sandbox is an allow-box, allow-listing that class for all permissions (read, write and execute). + */ + public JexlSandbox() { + this(true, false, null); + } + + /** + * Creates a new default sandbox. + * <p>A allow-box considers no permissions as "everything is allowed" when + * a block-box considers no permissions as "nothing is allowed". + * + * @param ab whether this sandbox is allow (true) or block (false) + * if no permission is explicitly defined for a class. + * @since 3.1 + */ + public JexlSandbox(final boolean ab) { + this(ab, false, null); + } + + /** + * Creates a sandbox. + * + * @param ab whether this sandbox is allow (true) or block (false) + * @param inh whether permissions on interfaces and classes are inherited (true) or not (false) + * @since 3.2 + */ + public JexlSandbox(final boolean ab, final boolean inh) { + this(ab, inh, null); + } + + /** + * Creates a sandbox based on an existing permissions map. + * + * @param ab whether this sandbox is allow (true) or block (false) + * @param inh whether permissions are inherited, default false + * @param map the permissions map + * @since 3.2 + */ + private JexlSandbox(final boolean ab, final boolean inh, final Map<String, Permissions> map) { + allow = ab; + inherit = inh; + sandbox = map != null ? map : new HashMap<>(); + } + + /** + * Gets a class by name, crude mechanism for backwards (<3.2 ) compatibility. + * + * @param cname the class name + * @return the class + */ + static Class<?> forName(final String cname) { + try { + return Class.forName(cname); + } catch (final Exception xany) { + return null; + } + } + + /** + * Creates a new set of permissions based on allow lists for methods and properties for a given class. + * <p>The sandbox inheritance property will apply to the permissions created by this method + * + * @param clazz the allowed class name + * @return the permissions instance + */ + public Permissions allow(final String clazz) { + return permissions(clazz, true, true, true); + } + + /** + * Creates a new set of permissions based on block lists for methods and properties for a given class. + * <p>The sandbox inheritance property will apply to the permissions created by this method + * + * @param clazz the blocked class name + * @return the permissions instance + */ + public Permissions block(final String clazz) { + return permissions(clazz, false, false, false); + } + + /** + * @return a copy of this sandbox + */ + public JexlSandbox copy() { + // modified concurrently at runtime so... + final Map<String, Permissions> map = new ConcurrentHashMap<>(); + for (final Map.Entry<String, Permissions> entry : sandbox.entrySet()) { + map.put(entry.getKey(), entry.getValue().copy()); + } + return new JexlSandbox(allow, inherit, map); + } + + /** + * Gets the execute permission value for a given method of a class. + * + * @param clazz the class + * @param name the method name + * @return null if not allowed, the name of the method to use otherwise + */ + public String execute(final Class<?> clazz, final String name) { + final String m = get(clazz).execute().get(name); + return "".equals(name) && m != null ? clazz.getName() : m; + } + + /** + * Gets the execute permission value for a given method of a class. + * + * @param clazz the class name + * @param name the method name + * @return null if not allowed, the name of the method to use otherwise + * @deprecated 3.3 + */ + @Deprecated + public String execute(final String clazz, final String name) { + final String m = get(clazz).execute().get(name); + return "".equals(name) && m != null ? clazz : m; + } + + /** + * Gets the permissions associated to a class. + * + * @param clazz the class + * @return the permissions + */ + @SuppressWarnings("null") + public Permissions get(final Class<?> clazz) { + // argument clazz can not be null since permissions would be not null and block: + // we only store the result for classes we actively seek permissions for. + return clazz == null ? BLOCK_ALL : compute(clazz); + } + + /** + * Computes and optionally stores the permissions associated to a class. + * + * @param clazz the class + * @return the permissions + */ + private Permissions compute(final Class<?> clazz) { + Permissions permissions = sandbox.get(clazz.getName()); + if (permissions == null) { + if (inherit) { + // find first inherited interface that defines permissions + Class<?>[] interfaces = clazz.getInterfaces(); + for (int i = 0; permissions == null && i < interfaces.length; ++i) { + permissions = inheritable(sandbox.get(interfaces[i].getName())); + } + // nothing defined yet, find first superclass that defines permissions + if (permissions == null) { + // let's walk all super classes + for (Class<?> zuper = clazz.getSuperclass(); + permissions == null && zuper != null; + zuper = zuper.getSuperclass()) { + permissions = inheritable(sandbox.get(zuper.getName())); + } + } + } + // nothing was determined through inheritance + if (permissions == null) { + permissions = allow ? ALLOW_ALL : BLOCK_ALL; + } + // store the info to avoid doing this costly look-up + sandbox.put(clazz.getName(), permissions); + } + return permissions; + } + + private Permissions inheritable(Permissions p) { + return p != null && p.isInheritable() ? p : null; + } + + /** + * Gets the set of permissions associated to a class. + * + * @param clazz the class name + * @return the defined permissions or an all-allow permission instance if none were defined + */ + public Permissions get(final String clazz) { + if (inherit) { + return get(forName(clazz)); + } + final Permissions permissions = sandbox.get(clazz); + if (permissions == null) { + return allow ? ALLOW_ALL : BLOCK_ALL; + } + return permissions; + } + + /** + * Creates the set of permissions for a given class. + * <p>The sandbox inheritance property will apply to the permissions created by this method + * + * @param clazz the class for which these permissions apply + * @param readFlag whether the readable property list is allow - true - or block - false - + * @param writeFlag whether the writable property list is allow - true - or block - false - + * @param executeFlag whether the executable method list is allow - true - or block - false - + * @return the set of permissions + */ + public Permissions permissions(final String clazz, + final boolean readFlag, + final boolean writeFlag, + final boolean executeFlag) { + return permissions(clazz, inherit, readFlag, writeFlag, executeFlag); + } + + /** + * Creates the set of permissions for a given class. + * + * @param clazz the class for which these permissions apply + * @param inhf whether these permissions are inheritable + * @param readf whether the readable property list is allow - true - or block - false - + * @param writef whether the writable property list is allow - true - or block - false - + * @param execf whether the executable method list is allow - true - or block - false - + * @return the set of permissions + */ + public Permissions permissions(final String clazz, + final boolean inhf, + final boolean readf, + final boolean writef, + final boolean execf) { + final Permissions box = new Permissions(inhf, readf, writef, execf); + sandbox.put(clazz, box); + return box; + } + + /** + * Gets the read permission value for a given property of a class. + * + * @param clazz the class + * @param name the property name + * @return null (or NULL if name is null) if not allowed, the name of the property to use otherwise + */ + public String read(final Class<?> clazz, final String name) { + return get(clazz).read().get(name); + } + + /** + * Gets the read permission value for a given property of a class. + * + * @param clazz the class name + * @param name the property name + * @return null if not allowed, the name of the property to use otherwise + * @deprecated 3.3 + */ + @Deprecated + public String read(final String clazz, final String name) { + return get(clazz).read().get(name); + } + + /** + * Use allow() instead. + * + * @param clazz the allowed class name + * @return the permissions instance + * @deprecated 3.3 + */ + @Deprecated + public Permissions white(final String clazz) { + return allow(clazz); + } + + /** + * Gets the write permission value for a given property of a class. + * + * @param clazz the class + * @param name the property name + * @return null (or NULL if name is null) if not allowed, the name of the property to use otherwise + */ + public String write(final Class<?> clazz, final String name) { + return get(clazz).write().get(name); + } + + /** + * Gets the write permission value for a given property of a class. + * + * @param clazz the class name + * @param name the property name + * @return null if not allowed, the name of the property to use otherwise + * @deprecated 3.3 + */ + @Deprecated + public String write(final String clazz, final String name) { + return get(clazz).write().get(name); + } + + /** + * An allow set of names. + */ + static class AllowSet extends Names { + /** + * The map of controlled names and aliases. + */ + private Map<String, String> names; + + @Override + public boolean add(final String name) { + if (names == null) { + names = new HashMap<>(); + } + return names.put(name, name) == null; + } + + @Override + public boolean alias(final String name, final String alias) { + if (names == null) { + names = new HashMap<>(); + } + return names.put(alias, name) == null; + } + + @Override + protected Names copy() { + final AllowSet copy = new AllowSet(); + copy.names = names == null ? null : new HashMap<>(names); + return copy; + } + + @Override + public String get(final String name) { + if (names == null) { + return name; + } + final String actual = names.get(name); + // if null is not explicitly allowed, explicit null aka NULL + if (name == null && actual == null && !names.containsKey(null)) { + return JexlSandbox.NULL; + } + return actual; + } + + @Override + public String toString() { + return "allow{" + (names == null ? "all" : Objects.toString(names.entrySet())) + "}"; + } + } + + /** + * A block set of names. + */ + static class BlockSet extends Names { + /** + * The set of controlled names. + */ + private Set<String> names; + + @Override + public boolean add(final String name) { + if (names == null) { + names = new HashSet<>(); + } + return names.add(name); + } + + @Override + protected Names copy() { + final BlockSet copy = new BlockSet(); + copy.names = names == null ? null : new HashSet<>(names); + return copy; + } + + @Override + public String get(final String name) { + // if name is null and contained in set, explicit null aka NULL + if (names != null && !names.contains(name)) { + return name; + } else if (name != null) { + return null; + } else { + return NULL; + } + } + + @Override + public String toString() { + return "block{" + (names == null ? "all" : Objects.toString(names)) + "}"; + } + } + + /** + * A base set of names. + */ + public abstract static class Names { + /** + * Adds a name to this set. + * + * @param name the name to add + * @return true if the name was really added, false if not + */ + public abstract boolean add(String name); + + /** + * Adds an alias to a name to this set. + * <p>This only has an effect on allow lists.</p> + * + * @param name the name to alias + * @param alias the alias + * @return true if the alias was added, false if it was already present + */ + public boolean alias(final String name, final String alias) { + return false; + } + + /** + * @return a copy of these Names + */ + protected Names copy() { + return this; + } + + /** + * Whether a given name is allowed or not. + * + * @param name the method/property name to check + * @return null (or NULL if name is null) if not allowed, the actual name to use otherwise + */ + public String get(final String name) { + return name; + } + } + + /** + * Contains the allow or block 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; + + /** + * Creates a new permissions instance. + * + * @param inherit whether these permissions are inheritable + * @param readFlag whether the read property list is allow or block + * @param writeFlag whether the write property list is allow or block + * @param executeFlag whether the method list is allow of block + */ + Permissions(final boolean inherit, final boolean readFlag, final boolean writeFlag, final boolean executeFlag) { + this(inherit, + readFlag ? new AllowSet() : new BlockSet(), + writeFlag ? new AllowSet() : new BlockSet(), + executeFlag ? new AllowSet() : new BlockSet()); + } + + /** + * Creates a new permissions instance. + * + * @param inherit whether these permissions are inheritable + * @param nread the read set + * @param nwrite the write set + * @param nexecute the method set + */ + Permissions(final boolean inherit, final Names nread, final Names nwrite, final Names nexecute) { + this.read = nread != null ? nread : ALLOW_NAMES; + this.write = nwrite != null ? nwrite : ALLOW_NAMES; + this.execute = nexecute != null ? nexecute : ALLOW_NAMES; + this.inheritable = inherit; + } + + /** + * @return a copy of these permissions + */ + Permissions copy() { + return new Permissions(inheritable, read.copy(), write.copy(), execute.copy()); + } + + /** + * Gets the set of method names in these permissions. + * + * @return the set of method names + */ + public Names execute() { + return execute; + } + + /** + * Adds a list of executable methods names to these permissions. + * <p>The constructor is denoted as the empty-string, all other methods by their names.</p> + * + * @param methodNames the method names + * @return this instance of permissions + */ + public Permissions execute(final String... methodNames) { + for (final String methodName : methodNames) { + execute.add(methodName); + } + return this; + } + + /** + * @return whether these permissions apply to derived classes. + */ + public boolean isInheritable() { + return inheritable; + } + + /** + * Gets the set of readable property names in these permissions. + * + * @return the set of property names + */ + public Names read() { + return read; + } + + /** + * Adds a list of readable property names to these permissions. + * + * @param propertyNames the property names + * @return this instance of permissions + */ + public Permissions read(final String... propertyNames) { + for (final String propertyName : propertyNames) { + read.add(propertyName); + } + return this; + } + + /** + * Gets the set of writable property names in these permissions. + * + * @return the set of property names + */ + public Names write() { + return write; + } + + /** + * Adds a list of writable property names to these permissions. + * + * @param propertyNames the property names + * @return this instance of permissions + */ + public Permissions write(final String... propertyNames) { + for (final String propertyName : propertyNames) { + write.add(propertyName); + } + return this; + } + } }