org.apache.commons.jexl3.introspection.JexlSandbox Maven / Gradle / Ivy
Show all versions of commons-jexl3 Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jexl3.introspection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A sandbox describes permissions on a class by explicitly allowing or forbidding access to methods and properties
* through "whitelists" and "blacklists".
*
* A whitelist explicitly allows methods/properties for a class;
*
*
* - If a whitelist is empty and thus does not contain any names,
* all properties/methods are allowed for its class.
* - If it is not empty, the only allowed properties/methods are the ones contained.
*
*
* A blacklist explicitly forbids methods/properties for a class;
*
*
* - If a blacklist is empty and thus does not contain any names,
* all properties/methods are forbidden for its class.
* - If it is not empty, the only forbidden properties/methods are the ones contained.
*
*
* Permissions are composed of three lists, read, write, execute, each being "white" or "black":
*
*
* - read controls readable properties
* - write controls writable properties
* - execute controls executable methods and constructor
*
*
* Note that a JexlUberspect always uses a copy of the JexlSandbox used to built it to avoid synchronization and/or
* concurrent modifications at runtime.
*
* @since 3.0
*/
public final class JexlSandbox {
/**
* The map from class names to permissions.
*/
private final Map sandbox;
/**
* Default behavior, black or white.
*/
private final boolean white;
/**
* Creates a new default sandbox.
* In the absence of explicit permissions on a class, the
* sandbox is a white-box, white-listing that class for all permissions (read, write and execute).
*/
public JexlSandbox() {
this(true, new HashMap());
}
/**
* Creates a new default sandbox.
* A white-box considers no permissions as "everything is allowed" when
* a black-box considers no permissions as "nothing is allowed".
* @param wb whether this sandbox is white (true) or black (false)
* if no permission is explicitly defined for a class.
* @since 3.1
*/
public JexlSandbox(boolean wb) {
this(wb, new HashMap());
}
/**
* Creates a sandbox based on an existing permissions map.
* @param map the permissions map
*/
protected JexlSandbox(Map map) {
this(true, map);
}
/**
* Creates a sandbox based on an existing permissions map.
* @param wb whether this sandbox is white (true) or black (false)
* @param map the permissions map
* @since 3.1
*/
protected JexlSandbox(boolean wb, Map map) {
white = wb;
sandbox = map;
}
/**
* @return a copy of this sandbox
*/
public JexlSandbox copy() {
Map map = new HashMap();
for (Map.Entry entry : sandbox.entrySet()) {
map.put(entry.getKey(), entry.getValue().copy());
}
return new JexlSandbox(white, map);
}
/**
* Gets the read permission value for a given property of a class.
*
* @param clazz the class
* @param name the property name
* @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);
}
/**
* 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
*/
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);
}
}
/**
* Gets the write permission value for a given property of a class.
*
* @param clazz the class
* @param name the property name
* @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);
}
/**
* 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
*/
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);
}
}
/**
* 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(Class> clazz, String name) {
return execute(clazz.getName(), name);
}
/**
* 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
*/
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);
}
}
/**
* 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.
* This only has an effect on white lists.
*
* @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(String name, String alias) {
return false;
}
/**
* Whether a given name is allowed or not.
*
* @param name the method/property name to check
* @return null if not allowed, the actual name to use otherwise
*/
public String get(String name) {
return name;
}
/**
* @return a copy of these Names
*/
protected Names copy() {
return this;
}
}
/**
* The pass-thru name set.
*/
private static final Names WHITE_NAMES = new Names() {
@Override
public boolean add(String name) {
return false;
}
@Override
protected Names copy() {
return this;
}
};
/**
* A white set of names.
*/
public static final class WhiteSet extends Names {
/** The map of controlled names and aliases. */
private Map names = null;
@Override
protected Names copy() {
WhiteSet copy = new WhiteSet();
copy.names = names == null ? null : new HashMap(names);
return copy;
}
@Override
public boolean add(String name) {
if (names == null) {
names = new HashMap();
}
return names.put(name, name) == null;
}
@Override
public boolean alias(String name, String alias) {
if (names == null) {
names = new HashMap();
}
return names.put(alias, name) == null;
}
@Override
public String get(String name) {
if (names == null) {
return name;
} else {
return names.get(name);
}
}
}
/**
* A black set of names.
*/
public static final class BlackSet extends Names {
/** The set of controlled names. */
private Set names = null;
@Override
protected Names copy() {
BlackSet copy = new BlackSet();
copy.names = names == null ? null : new HashSet(names);
return copy;
}
@Override
public boolean add(String name) {
if (names == null) {
names = new HashSet();
}
return names.add(name);
}
@Override
public String get(String name) {
return names != null && !names.contains(name) ? name : null;
}
}
/**
* Contains the white or black lists for properties and methods for a given class.
*/
public static final class Permissions {
/** 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 readFlag whether the read property list is white or black
* @param writeFlag whether the write property list is white or black
* @param executeFlag whether the method list is white of black
*/
Permissions(boolean readFlag, boolean writeFlag, boolean executeFlag) {
this(readFlag ? new WhiteSet() : new BlackSet(),
writeFlag ? new WhiteSet() : new BlackSet(),
executeFlag ? new WhiteSet() : new BlackSet());
}
/**
* Creates a new permissions instance.
*
* @param nread the read set
* @param nwrite the write set
* @param nexecute the method set
*/
Permissions(Names nread, Names nwrite, Names nexecute) {
this.read = nread != null ? nread : WHITE_NAMES;
this.write = nwrite != null ? nwrite : WHITE_NAMES;
this.execute = nexecute != null ? nexecute : WHITE_NAMES;
}
/**
* @return a copy of these permissions
*/
Permissions copy() {
return new Permissions(read.copy(), write.copy(), execute.copy());
}
/**
* Adds a list of readable property names to these permissions.
*
* @param pnames the property names
* @return this instance of permissions
*/
public Permissions read(String... pnames) {
for (String pname : pnames) {
read.add(pname);
}
return this;
}
/**
* Adds a list of writable property names to these permissions.
*
* @param pnames the property names
* @return this instance of permissions
*/
public Permissions write(String... pnames) {
for (String pname : pnames) {
write.add(pname);
}
return this;
}
/**
* Adds a list of executable methods names to these permissions.
* The constructor is denoted as the empty-string, all other methods by their names.
*
* @param mnames the method names
* @return this instance of permissions
*/
public Permissions execute(String... mnames) {
for (String mname : mnames) {
execute.add(mname);
}
return this;
}
/**
* Gets the set of readable property names in these permissions.
*
* @return the set of property names
*/
public Names read() {
return read;
}
/**
* Gets the set of writable property names in these permissions.
*
* @return the set of property names
*/
public Names write() {
return write;
}
/**
* Gets the set of method names in these permissions.
*
* @return the set of method names
*/
public Names execute() {
return execute;
}
}
/**
* The pass-thru permissions.
*/
private static final Permissions ALL_WHITE = new Permissions(WHITE_NAMES, WHITE_NAMES, WHITE_NAMES);
/**
* Creates the set of permissions for a given class.
*
* @param clazz the class for which these permissions apply
* @param readFlag whether the readable property list is white - true - or black - false -
* @param writeFlag whether the writable property list is white - true - or black - false -
* @param executeFlag whether the executable method list is white white - true - or black - false -
* @return the set of permissions
*/
public Permissions permissions(String clazz, boolean readFlag, boolean writeFlag, boolean executeFlag) {
Permissions box = new Permissions(readFlag, writeFlag, executeFlag);
sandbox.put(clazz, box);
return box;
}
/**
* Creates a new set of permissions based on white lists for methods and properties for a given class.
*
* @param clazz the whitened class name
* @return the permissions instance
*/
public Permissions white(String clazz) {
return permissions(clazz, true, true, true);
}
/**
* Creates a new set of permissions based on black lists for methods and properties for a given class.
*
* @param clazz the blackened class name
* @return the permissions instance
*/
public Permissions black(String clazz) {
return permissions(clazz, false, false, false);
}
/**
* Gets the set of permissions associated to a class.
*
* @param clazz the class name
* @return the defined permissions or an all-white permission instance if none were defined
*/
public Permissions get(String clazz) {
Permissions permissions = sandbox.get(clazz);
if (permissions == null) {
return ALL_WHITE;
} else {
return permissions;
}
}
}