The ExpressionEvaluator class provides a sandboxed expression evaluator for Casbin's eval() function. It ensures that expressions only use standard Casbin operations, preventing the use of aviatorscript-specific features that would break cross-platform compatibility.
The previous implementation of eval() directly used aviatorscript, which allowed the use of aviatorscript-specific syntax such as:
seq.list('A', 'B') - Collection operationsstring.startsWith(), string.endsWith() - String namespace methodsmath.abs() - Math namespace functionslambda(), fn() - Function definitionslet, for, while - Control flow statementsThese features are:
The ExpressionEvaluator validates expressions before evaluation to ensure they only contain standard Casbin operations.
The following operations are allowed in eval() expressions:
Access object properties using dot notation:
r.sub.name r.sub.age r.obj.owner p.sub_rule
r.sub.age > 18 r.sub.age >= 18 r.sub.age < 60 r.sub.age <= 60 r.sub.name == 'alice' r.sub.name != 'bob'
r.sub.age > 18 && r.sub.age < 60 r.sub.name == 'alice' || r.sub.name == 'bob' !r.sub.isBlocked
r.sub.age + 10 > 28 r.sub.age * 2 < 100 r.sub.score - 10 >= 80 r.sub.total / 2 > 50 r.sub.value % 10 == 0
'alice', 'data1'18, 60, 3.14true, falseCustom functions registered with the enforcer can be called:
custom(r.obj) keyMatch(r.sub, '/api/*') regexMatch(r.obj, '^/data\\d+$')
The following aviatorscript-specific operations are NOT allowed and will cause validation to fail:
seq.list('A', 'B') // ❌ Blocked string.startsWith(r.obj, '/') // ❌ Blocked math.abs(r.sub.age) // ❌ Blocked
fn add(a, b) { return a + b } // ❌ Blocked lambda(x) -> x + 1 // ❌ Blocked
let x = 10 // ❌ Blocked for (i = 0; i < 10; i++) // ❌ Blocked while (true) // ❌ Blocked
new java.util.ArrayList() // ❌ Blocked import java.util.List // ❌ Blocked
Define expressions in your policy files that will be evaluated:
Model (abac_rule_model.conf):
[request_definition] r = sub, obj, act [policy_definition] p = sub_rule, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = eval(p.sub_rule) && r.obj == p.obj && r.act == p.act
Policy (abac_rule_policy.csv):
p, r.sub.Age > 18, /data1, read p, r.sub.Age < 60, /data2, write
import org.casbin.jcasbin.main.Enforcer; // Create enforcer Enforcer enforcer = new Enforcer("model.conf", "policy.csv"); // Create request subject with attributes class User { private String name; private int age; // getters... } User alice = new User("alice", 25); // Enforce - the eval() function will be called with the expression from policy boolean allowed = enforcer.enforce(alice, "/data1", "read"); // Returns true because alice.age (25) > 18
The ExpressionEvaluator class provides:
The EvalFunc class (the wrapper for the eval() function) has been updated to use ExpressionEvaluator:
@Override public AviatorObject call(Map<String, Object> env, AviatorObject arg1) { String eval = FunctionUtils.getStringValue(arg1, env); eval = replaceTargets(eval, env); // Validate expression to ensure it only uses standard Casbin operations try { ExpressionEvaluator.validateExpression(eval); } catch (IllegalArgumentException e) { Util.logPrintfWarn("Invalid eval expression: {}", e.getMessage()); return AviatorBoolean.valueOf(false); } return AviatorBoolean.valueOf(BuiltInFunctions.eval(eval, env, getAviatorEval())); }
When an invalid expression is detected:
IllegalArgumentException is thrown with a descriptive error messagefalse to fail safelyExample error message:
Expression contains non-standard Casbin operations.
Please use only standard operators and registered functions.
Expression: seq.list('A', 'B')
If your existing policies use standard Casbin syntax (property access, comparison operators, logical operators), no changes are needed. The validator allows all standard operations.
If you have policies that use aviatorscript-specific features, you need to migrate them:
Before (aviatorscript-specific):
p, include(seq.list('admin', 'moderator'), r.sub.role), /data1, read p, string.startsWith(r.obj, '/api'), /api, GET
After (standard Casbin):
p, r.sub.role == 'admin' || r.sub.role == 'moderator', /data1, read p, keyMatch(r.obj, '/api/*'), /api, GET
Or use registered custom functions for complex logic:
// Register a custom function enforcer.addFunction("checkRole", new CustomRoleFunction());
Comprehensive tests are provided in ExpressionEvaluatorTest.java:
Run tests:
mvn test -Dtest=ExpressionEvaluatorTest