This is an automated email from the ASF dual-hosted git repository.
hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-jcasbin.git
The following commit(s) were added to refs/heads/master by this push:
new 1eca2fb fix: scope g() result cache to each enforcer
1eca2fb is described below
commit 1eca2fb60310843e49adb2e16301b6b0a56b8d21
Author: Yang Luo <[email protected]>
AuthorDate: Tue Oct 20 22:28:42 2026 +0800
fix: scope g() result cache to each enforcer
---
.../java/org/casbin/jcasbin/main/CoreEnforcer.java | 7 ++-
.../org/casbin/jcasbin/util/BuiltInFunctions.java | 30 ++++++++--
.../jcasbin/main/SyncedEnforcerUnitTest.java | 70 ++++++++++++++++++++++
3 files changed, 99 insertions(+), 8 deletions(-)
diff --git a/src/main/java/org/casbin/jcasbin/main/CoreEnforcer.java
b/src/main/java/org/casbin/jcasbin/main/CoreEnforcer.java
index 577ee99..d6079f3 100644
--- a/src/main/java/org/casbin/jcasbin/main/CoreEnforcer.java
+++ b/src/main/java/org/casbin/jcasbin/main/CoreEnforcer.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Expression;
+import com.googlecode.aviator.runtime.type.AviatorBoolean;
import com.googlecode.aviator.runtime.type.AviatorFunction;
import org.casbin.jcasbin.effect.DefaultEffector;
import org.casbin.jcasbin.effect.Effect;
@@ -42,6 +43,7 @@ import org.casbin.jcasbin.util.EnforceContext;
import org.casbin.jcasbin.util.Util;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiPredicate;
import java.util.function.Function;
@@ -68,6 +70,7 @@ public class CoreEnforcer {
boolean acceptJsonRequest = false;
private AviatorEvaluatorInstance aviatorEval;
+ private final Map<String, Map<String, AviatorBoolean>> gFunctionCache =
new ConcurrentHashMap<>();
void initialize() {
rmMap = new HashMap<>();
@@ -541,13 +544,13 @@ public class CoreEnforcer {
RoleManager rm = ast.rm;
if (rm != null){
- AviatorFunction aviatorFunction =
BuiltInFunctions.GenerateGFunctionClass.generateGFunction(key, rm);
+ AviatorFunction aviatorFunction =
BuiltInFunctions.GenerateGFunctionClass.generateGFunction(key, rm,
gFunctionCache);
gFunctions.put(key, aviatorFunction);
}
ConditionalRoleManager condRM = ast.condRM;
if (condRM != null){
- AviatorFunction aviatorFunction =
BuiltInFunctions.GenerateConditionalGFunctionClass.generateConditionalGFunction(key,
condRM);
+ AviatorFunction aviatorFunction =
BuiltInFunctions.GenerateConditionalGFunctionClass.generateConditionalGFunction(key,
condRM, gFunctionCache);
gFunctions.put(key, aviatorFunction);
}
diff --git a/src/main/java/org/casbin/jcasbin/util/BuiltInFunctions.java
b/src/main/java/org/casbin/jcasbin/util/BuiltInFunctions.java
index 5c03755..7cb39b0 100644
--- a/src/main/java/org/casbin/jcasbin/util/BuiltInFunctions.java
+++ b/src/main/java/org/casbin/jcasbin/util/BuiltInFunctions.java
@@ -400,9 +400,6 @@ public class BuiltInFunctions {
public static class GenerateGFunctionClass {
- // key:name such as g,g2 value:user-role mapping
- private static Map<String, Map<String, AviatorBoolean>> memorizedMap =
new ConcurrentHashMap<>();
-
/**
* generateGFunction is the factory method of the g(_, _) function.
*
@@ -411,6 +408,18 @@ public class BuiltInFunctions {
* @return the function.
*/
public static AviatorFunction generateGFunction(String name,
RoleManager rm) {
+ return generateGFunction(name, rm, new ConcurrentHashMap<>());
+ }
+
+ /**
+ * generateGFunction is the factory method of the g(_, _) function.
+ *
+ * @param name the name of the g(_, _) function, can be "g",
"g2", ..
+ * @param rm the role manager used by the function.
+ * @param memorizedMap the cache of g(_, _) results owned by the
caller, key: name such as g, g2.
+ * @return the function.
+ */
+ public static AviatorFunction generateGFunction(String name,
RoleManager rm, Map<String, Map<String, AviatorBoolean>> memorizedMap) {
memorizedMap.put(name, new ConcurrentHashMap<>());
return new AbstractVariadicFunction() {
@@ -458,9 +467,6 @@ public class BuiltInFunctions {
}
public static class GenerateConditionalGFunctionClass {
- // key:name such as g,g2 value:user-role mapping
- private static Map<String, Map<String, AviatorBoolean>> memorizedMap =
new ConcurrentHashMap<>();
-
/**
* GenerateConditionalGFunction is the factory method of the g(_, _[,
_]) function with conditions.
*
@@ -469,6 +475,18 @@ public class BuiltInFunctions {
* @return the function.
*/
public static AviatorFunction generateConditionalGFunction(String
name, ConditionalRoleManager condRm) {
+ return generateConditionalGFunction(name, condRm, new
ConcurrentHashMap<>());
+ }
+
+ /**
+ * GenerateConditionalGFunction is the factory method of the g(_, _[,
_]) function with conditions.
+ *
+ * @param name the name of the g(_, _) function, can be "g",
"g2", ..
+ * @param condRm the conditional role manager used by the
function.
+ * @param memorizedMap the cache of g(_, _) results owned by the
caller, key: name such as g, g2.
+ * @return the function.
+ */
+ public static AviatorFunction generateConditionalGFunction(String
name, ConditionalRoleManager condRm, Map<String, Map<String, AviatorBoolean>>
memorizedMap) {
memorizedMap.put(name, new ConcurrentHashMap<>());
return new AbstractVariadicFunction() {
diff --git a/src/test/java/org/casbin/jcasbin/main/SyncedEnforcerUnitTest.java
b/src/test/java/org/casbin/jcasbin/main/SyncedEnforcerUnitTest.java
index b42bb17..05dc00c 100644
--- a/src/test/java/org/casbin/jcasbin/main/SyncedEnforcerUnitTest.java
+++ b/src/test/java/org/casbin/jcasbin/main/SyncedEnforcerUnitTest.java
@@ -17,20 +17,27 @@
package org.casbin.jcasbin.main;
+import com.googlecode.aviator.runtime.type.AviatorBoolean;
+import com.googlecode.aviator.runtime.type.AviatorObject;
import org.casbin.jcasbin.model.Model;
import org.casbin.jcasbin.persist.Adapter;
import org.casbin.jcasbin.persist.file_adapter.FileAdapter;
+import org.casbin.jcasbin.util.function.CustomFunction;
import org.testng.annotations.Test;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import static java.util.Arrays.asList;
import static org.casbin.jcasbin.main.CoreEnforcer.newModel;
import static org.casbin.jcasbin.main.TestUtil.*;
import static org.casbin.jcasbin.main.TestUtil.testEnforceEx;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
public class SyncedEnforcerUnitTest {
@@ -38,6 +45,69 @@ public class SyncedEnforcerUnitTest {
assertEquals(res, e.enforce(sub, obj, act));
}
+ public static void testEnforceSync(SyncedEnforcer e, String sub, Object
obj, boolean res) {
+ assertEquals(res, e.enforce(sub, obj));
+ }
+
+ private static class GateFunction extends CustomFunction {
+ final CountDownLatch entered = new CountDownLatch(1);
+ final CountDownLatch release = new CountDownLatch(1);
+ volatile boolean block;
+
+ @Override
+ public AviatorObject call(Map<String, Object> env, AviatorObject arg) {
+ if (block) {
+ entered.countDown();
+ try {
+ release.await(10, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ return AviatorBoolean.TRUE;
+ }
+
+ @Override
+ public String getName() {
+ return "gate";
+ }
+ }
+
+ private static SyncedEnforcer newGatedEnforcer(GateFunction gate, boolean
hasRole) {
+ Model m = newModel();
+ m.addDef("r", "r", "sub, obj");
+ m.addDef("p", "p", "sub, obj");
+ m.addDef("g", "g", "_, _");
+ m.addDef("e", "e", "some(where (p.eft == allow))");
+ m.addDef("m", "m", "gate(r.sub) && g(r.sub, p.sub) && r.obj == p.obj");
+ SyncedEnforcer e = new SyncedEnforcer(m);
+ e.addFunction(gate.getName(), gate);
+ e.addPolicy("admin", "data");
+ if (hasRole) {
+ e.addGroupingPolicy("alice", "admin");
+ }
+ return e;
+ }
+
+ @Test
+ public void testRoleCacheIsolatedBetweenEnforcers() throws
InterruptedException {
+ GateFunction gate = new GateFunction();
+ SyncedEnforcer isolated = newGatedEnforcer(gate, false);
+ SyncedEnforcer privileged = newGatedEnforcer(new GateFunction(), true);
+ testEnforceSync(isolated, "alice", "data", false);
+
+ gate.block = true;
+ AtomicReference<Boolean> result = new AtomicReference<>();
+ Thread t = new Thread(() -> result.set(isolated.enforce("alice",
"data")));
+ t.start();
+ assertTrue(gate.entered.await(10, TimeUnit.SECONDS));
+ testEnforceSync(privileged, "alice", "data", true);
+ gate.release.countDown();
+ t.join(10000);
+
+ assertEquals(result.get(), Boolean.FALSE);
+ }
+
@Test
public void testSync(){
SyncedEnforcer e = new SyncedEnforcer("examples/basic_model.conf",
"examples/basic_policy.csv");
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]