Skip to content

Commit

Permalink
feat: support enforceEx (#300)
Browse files Browse the repository at this point in the history
Signed-off-by: imp2002 <[email protected]>

Signed-off-by: imp2002 <[email protected]>
  • Loading branch information
imp2002 authored Sep 17, 2022
1 parent 9fd266f commit fc8ef73
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 27 deletions.
60 changes: 40 additions & 20 deletions src/main/java/org/casbin/jcasbin/main/CoreEnforcer.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@
import org.casbin.jcasbin.util.EnforceContext;
import org.casbin.jcasbin.util.Util;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.BiPredicate;

/**
Expand Down Expand Up @@ -428,14 +425,15 @@ public void buildRoleLinks() {

/**
* enforce use a custom matcher to decide whether a "subject" can access a "object" with the operation "action",
* input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "" or null.
* input parameters are usually: (matcher, explain, sub, obj, act), use model matcher by default when matcher is "" or null.
*
* @param matcher the custom matcher.
* @param explain to explain enforcement by informing matched rules
* @param rvals the request needs to be mediated, usually an array
* of strings, can be class instances if ABAC is used.
* @return whether to allow the request.
*/
private boolean enforce(String matcher, Object... rvals) {
private boolean enforce(String matcher, List<String> explain, Object... rvals) {
if (!enabled) {
return true;
}
Expand Down Expand Up @@ -499,7 +497,7 @@ private boolean enforce(String matcher, Object... rvals) {

Effect[] policyEffects;
float[] matcherResults;
int policyLen;
int policyLen, explainIndex = -1;
if ((policyLen = model.model.get("p").get(pType).policy.size()) != 0) {
policyEffects = new Effect[policyLen];
matcherResults = new float[policyLen];
Expand Down Expand Up @@ -555,6 +553,7 @@ private boolean enforce(String matcher, Object... rvals) {
if (streamEffector != null) {
boolean done = streamEffector.push(policyEffects[i], i, policyLen);
if (done) {
explainIndex = i;
break;
}
} else {
Expand Down Expand Up @@ -603,19 +602,11 @@ private boolean enforce(String matcher, Object... rvals) {
result = eft.mergeEffects(model.model.get("e").get(eType).value, policyEffects, matcherResults);
}

StringBuilder reqStr = new StringBuilder("Request: ");
for (int i = 0; i < rvals.length; i++) {
String rval = rvals[i].toString();

if (i != rvals.length - 1) {
reqStr.append(String.format("%s, ", rval));
} else {
reqStr.append(String.format("%s", rval));
}
if (explain != null && explainIndex != -1) {
explain.addAll(model.model.get("p").get(pType).policy.get(explainIndex));
}
reqStr.append(String.format(" ---> %s", result));
Util.logPrint(reqStr.toString());

Util.logEnforce(rvals, result, explain);
return result;
}

Expand All @@ -628,7 +619,7 @@ private boolean enforce(String matcher, Object... rvals) {
* @return whether to allow the request.
*/
public boolean enforce(Object... rvals) {
return enforce(null, rvals);
return enforce(null, null, rvals);
}

/**
Expand All @@ -641,7 +632,36 @@ public boolean enforce(Object... rvals) {
* @return whether to allow the request.
*/
public boolean enforceWithMatcher(String matcher, Object... rvals) {
return enforce(matcher, rvals);
return enforce(matcher, null, rvals);
}

/**
* enforceEx decides whether a "subject" can access "object" with
* the operation "action", input parameters are usually: (sub, obj, act).
* the list explain, store matching rule.
*
* @param rvals the request needs to be mediated, usually an array
* of strings, can be class instances if ABAC is used.
* @return whether to allow the request.
*/
public boolean enforceEx(Object... rvals) {
List<String> explain = new ArrayList<>();
return enforce("", explain, rvals);
}

/**
* enforceExWithMatcher use a custom matcher to decide whether a "subject" can access a "object" with the operation "action",
* input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "" or null.
* the list explain, store matching rule.
*
* @param matcher the custom matcher.
* @param rvals the request needs to be mediated, usually an array
* of strings, can be class instances if ABAC is used.
* @return whether to allow the request.
*/
public boolean enforceExWithMatcher(String matcher, Object... rvals) {
List<String> explain = new ArrayList<>();
return enforce(matcher, explain, rvals);
}

/**
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/org/casbin/jcasbin/main/SyncedEnforcer.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,35 @@ public boolean enforceWithMatcher(String matcher, Object... rvals) {
return runSynchronized(() -> super.enforceWithMatcher(matcher, rvals), READ_WRITE_LOCK.readLock());
}

/**
* enforceEx decides whether a "subject" can access "object" with
* the operation "action", input parameters are usually: (sub, obj, act).
* the list explain, store matching rule.
*
* @param rvals the request needs to be mediated, usually an array
* of strings, can be class instances if ABAC is used.
* @return whether to allow the request.
*/
@Override
public boolean enforceEx(Object... rvals) {
return runSynchronized(() -> super.enforceEx(rvals), READ_WRITE_LOCK.readLock());
}

/**
* enforceExWithMatcher use a custom matcher to decide whether a "subject" can access a "object" with the operation "action",
* input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "" or null.
* the list explain, store matching rule.
*
* @param matcher the custom matcher.
* @param rvals the request needs to be mediated, usually an array
* of strings, can be class instances if ABAC is used.
* @return whether to allow the request.
*/
@Override
public boolean enforceExWithMatcher(String matcher, Object... rvals) {
return runSynchronized(() -> super.enforceExWithMatcher(matcher, rvals), READ_WRITE_LOCK.readLock());
}

/**
* batchEnforce enforce in batches
*
Expand Down
22 changes: 17 additions & 5 deletions src/main/java/org/casbin/jcasbin/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -91,6 +87,22 @@ public static void logPrintfError(String format, Object... v) {
}
}

/**
* logEnforce prints the log of Enforce.
*
* @param request the Enforce request.
* @param result the Enforce result.
* @param explain to explain enforcement by matched rules.
*/
public static void logEnforce(Object[] request, boolean result, List<String> explain) {

This comment has been minimized.

Copy link
@abdurrahmankutlu

abdurrahmankutlu Sep 26, 2022

Is it possible to do whatever we want for this part? Logging only is not sufficient for my use case. Can it be possible to add a function parameter to be called instead of this logging method. Something like below:

enforceEx(Function<Object, Object> onSuccess, Object... rvals)

This comment has been minimized.

Copy link
@hsluoyz

hsluoyz Sep 26, 2022

Member

@abdurrahmankutlu plz create an issue

if (enableLog) {
LOGGER.info("Request: " + Arrays.toString(request) + " ---> " + result);
if (explain != null) {
LOGGER.info("Hit Policy: " + explain);
}
}
}

/**
* escapeAssertion escapes the dots in the assertion, because the expression evaluation doesn't support such variable names.
*
Expand Down
30 changes: 30 additions & 0 deletions src/test/java/org/casbin/jcasbin/main/EnforcerUnitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,36 @@ public void testEnableLog() {
testEnforce(e, "bob", "data2", "write", true);
}

@Test
public void testEnforceExLog() {
Enforcer e = new Enforcer("examples/basic_model.conf", "examples/basic_policy.csv", true);

// the previous matcher is
// m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
testEnforceEx(e, "alice", "data1", "read", true);
testEnforceEx(e, "bob", "data2", "write", true);
testEnforceEx(e, "root", "data2", "read", false);
testEnforceEx(e, "root", "data3", "read", false);
testEnforceEx(e, "jack", "data3", "read", false);

// custom matcher
String matcher = "m = r.sub == 'root' || r.sub == p.sub && r.obj == p.obj && r.act == p.act";
TestUtil.testEnforceExWithMatcher(e, matcher, "alice", "data1", "read", true);
TestUtil.testEnforceExWithMatcher(e, matcher, "bob", "data2", "write", true);
TestUtil.testEnforceExWithMatcher(e, matcher, "root", "data2", "read", true);
TestUtil.testEnforceExWithMatcher(e, matcher, "root", "data3", "read", true);
TestUtil.testEnforceExWithMatcher(e, matcher, "jack", "data3", "read", false);

// the previous matcher is
// m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
e = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv", true);
testEnforceEx(e, "alice", "data1", "read", true);
testEnforceEx(e, "alice", "data2", "read", true);
testEnforceEx(e, "alice", "data2", "write", true);
testEnforceEx(e, "bob", "data1", "write", false);
testEnforceEx(e, "bob", "data2", "write", true);
}

@Test
public void testEnableAutoSave() {
Enforcer e = new Enforcer("examples/basic_model.conf", "examples/basic_policy.csv");
Expand Down
34 changes: 32 additions & 2 deletions src/test/java/org/casbin/jcasbin/main/SyncedEnforcerUnitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

import static java.util.Arrays.asList;
import static org.casbin.jcasbin.main.CoreEnforcer.newModel;
import static org.casbin.jcasbin.main.TestUtil.testEnforce;
import static org.casbin.jcasbin.main.TestUtil.testGetPolicy;
import static org.casbin.jcasbin.main.TestUtil.*;
import static org.casbin.jcasbin.main.TestUtil.testEnforceEx;

public class SyncedEnforcerUnitTest {
@Test
Expand Down Expand Up @@ -258,6 +258,36 @@ public void testEnableEnforce() {
testEnforce(e, "bob", "data2", "write", true);
}

@Test
public void testEnforceExLog() {
Enforcer e = new SyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv", true);

// the previous matcher is
// m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
testEnforceEx(e, "alice", "data1", "read", true);
testEnforceEx(e, "bob", "data2", "write", true);
testEnforceEx(e, "root", "data2", "read", false);
testEnforceEx(e, "root", "data3", "read", false);
testEnforceEx(e, "jack", "data3", "read", false);

// custom matcher
String matcher = "m = r.sub == 'root' || r.sub == p.sub && r.obj == p.obj && r.act == p.act";
TestUtil.testEnforceExWithMatcher(e, matcher, "alice", "data1", "read", true);
TestUtil.testEnforceExWithMatcher(e, matcher, "bob", "data2", "write", true);
TestUtil.testEnforceExWithMatcher(e, matcher, "root", "data2", "read", true);
TestUtil.testEnforceExWithMatcher(e, matcher, "root", "data3", "read", true);
TestUtil.testEnforceExWithMatcher(e, matcher, "jack", "data3", "read", false);

// the previous matcher is
// m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
e = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv", true);
testEnforceEx(e, "alice", "data1", "read", true);
testEnforceEx(e, "alice", "data2", "read", true);
testEnforceEx(e, "alice", "data2", "write", true);
testEnforceEx(e, "bob", "data1", "write", false);
testEnforceEx(e, "bob", "data2", "write", true);
}

@Test
public void testEnableLog() {
Enforcer e = new SyncedEnforcer("examples/basic_model.conf", "examples/basic_policy.csv", true);
Expand Down
8 changes: 8 additions & 0 deletions src/test/java/org/casbin/jcasbin/main/TestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ static void testEnforceWithMatcher(Enforcer e, String matcher, Object sub, Objec
assertEquals(res, e.enforceWithMatcher(matcher, sub, obj, act));
}

static void testEnforceEx(Enforcer e, Object sub, Object obj, String act, boolean res) {
assertEquals(res, e.enforceEx(sub, obj, act));
}

static void testEnforceExWithMatcher(Enforcer e, String matcher, Object sub, Object obj, String act, boolean res) {
assertEquals(res, e.enforceExWithMatcher(matcher, sub, obj, act));
}

static void testEnforceWithoutUsers(Enforcer e, String obj, String act, boolean res) {
assertEquals(res, e.enforce(obj, act));
}
Expand Down

0 comments on commit fc8ef73

Please sign in to comment.