diff --git a/README.md b/README.md
index 19a9343..303369a 100644
--- a/README.md
+++ b/README.md
@@ -18,15 +18,15 @@ mvn clean install
```
## Options
-| options | description | must | remark |
-|-----------------------|----------------------------------------------|------|-----------------------------------------------------------|
-| `-m, --model` | The path of the model file or model text | y | Please wrap it with `""` and separate each line with `\|` |
-| `-p, --policy` | The path of the policy file or policy text | y | Please wrap it with `""` and separate each line with `\|` |
-| `-e, --enforce` | Check permissions | n | Please wrap it with `""` |
-| `-ex, --enforceEx` | Check permissions and get which policy it is | n | Please wrap it with `""` |
-| `-AF, --addFuntion` | Add custom funtion | n | Please wrap it with `""` and separate each line with `\|` |
-| `-ap, --addPolicy` | Add a policy rule to the policy file | n | Please wrap it with `""` |
-| `-rp, --removePolicy` | Remove a policy rule from the policy file | n | Please wrap it with `""` |
+| options | description | must | remark |
+|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------|-----------------------------------------------------------|
+| `-m, --model` | The path of the model file or model text | y | Please wrap it with `""` and separate each line with `\|` |
+| `-p, --policy` | The path of the policy file or policy text | y | Please wrap it with `""` and separate each line with `\|` |
+| `-e, --enforce` | Check permissions | n | Please wrap it with `""` |
+| `-ex, --enforceEx` | Check permissions and get which policy it is | n | Please wrap it with `""` |
+| `-AF, --addFuntion` | Add custom funtion using text or function file.
file format see [function.conf](https://github.com/jcasbin/casbin-java-cli/blob/master/examples/keymatch_function.conf) | n | Please wrap it with `""` and separate each line with `\|` |
+| `-ap, --addPolicy` | Add a policy rule to the policy file | n | Please wrap it with `""` |
+| `-rp, --removePolicy` | Remove a policy rule from the policy file | n | Please wrap it with `""` |
## Get started
diff --git a/examples/keymatch_function.conf b/examples/keymatch_function.conf
new file mode 100644
index 0000000..831b3f5
--- /dev/null
+++ b/examples/keymatch_function.conf
@@ -0,0 +1,25 @@
+[function_definition]
+public static boolean keyMatchTest(String key1, String key2) {
+ int i = key2.indexOf('*');
+ if (i == -1) {
+ return key1.equals(key2);
+ }
+
+ if (key1.length() > i) {
+ return key1.substring(0, i).equals(key2.substring(0, i));
+ }
+ return key1.equals(key2.substring(0, i));
+}
+
+[function_definition]
+public static boolean keyMatchTest2(String key1, String key2) {
+ int i = key2.indexOf('*');
+ if (i == -1) {
+ return key1.equals(key2);
+ }
+
+ if (key1.length() > i) {
+ return key1.substring(0, i).equals(key2.substring(0, i));
+ }
+ return key1.equals(key2.substring(0, i));
+}
diff --git a/src/main/java/org/casbin/Client.java b/src/main/java/org/casbin/Client.java
index a2dadf2..f5763ee 100644
--- a/src/main/java/org/casbin/Client.java
+++ b/src/main/java/org/casbin/Client.java
@@ -43,10 +43,12 @@ public static String run(String... args) {
NewEnforcer enforcer = new NewEnforcer(model, policy);
if(cmd.hasOption("AF")) {
- String codes = cmd.getOptionValue("AF");
- String methodName = Util.getMethodName(codes);
- CustomFunction customFunction = DynamicClassGenerator.generateClass(methodName, codes);
- enforcer.addFunction(methodName, customFunction);
+ List codes = Util.parse(cmd.getOptionValue("AF"));
+ for (String code : codes) {
+ String methodName = Util.getMethodName(code);
+ CustomFunction customFunction = DynamicClassGenerator.generateClass(methodName, code);
+ enforcer.addFunction(methodName, customFunction);
+ }
}
CommandExecutor commandExecutor = new CommandExecutor(enforcer, commandName, cmd.getArgs());
Object o = commandExecutor.outputResult();
@@ -110,7 +112,7 @@ private static void printHelpMessage() {
" Options:\n" +
" -m, --model The path of the model file or model text. Please wrap it with \"\" and separate each line with \"|\"\n" +
" -p, --policy The path of the policy file or policy text. Please wrap it with \"\" and separate each line with \"|\"\n" +
- " -AF, --addFunction Add custom function. Please wrap it with \"\" and separate each line with \"|\"\n" +
+ " -AF, --addFunction The path of the function file or function text. Please wrap it with \"\" and separate each line with \"|\"\n" +
"\n" +
" args:\n" +
" Parameters required for the method\n" +
diff --git a/src/main/java/org/casbin/util/Util.java b/src/main/java/org/casbin/util/Util.java
index 56f0ce4..e1413ea 100644
--- a/src/main/java/org/casbin/util/Util.java
+++ b/src/main/java/org/casbin/util/Util.java
@@ -1,5 +1,9 @@
package org.casbin.util;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -26,4 +30,30 @@ public static int getArgsNum(String methodCodes) {
}
return 0;
}
+
+ /**
+ * Parse the input string to get the function definitions List
+ * @param input
+ * @return List of function definitions
+ */
+ public static List parse(String input) throws IOException {
+ if (input == null || input.trim().isEmpty()) {
+ throw new IllegalArgumentException("Input cannot be null or empty");
+ }
+ List codes = new ArrayList<>();
+
+ // Check if input is an existing file
+ File file = new File(input);
+ if (file.exists() && file.isFile()) {
+ String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
+
+ String[] lines = content.split("\\[function_definition\\]");
+ for (int i = 1; i < lines.length; i++) {
+ codes.add(lines[i].trim());
+ }
+ } else {
+ codes.add(input);
+ }
+ return codes;
+ }
}
diff --git a/src/test/java/org/casbin/ClientTest.java b/src/test/java/org/casbin/ClientTest.java
index b3df73e..a9c2b1b 100644
--- a/src/test/java/org/casbin/ClientTest.java
+++ b/src/test/java/org/casbin/ClientTest.java
@@ -116,6 +116,41 @@ public void testCustomFunction() throws ParseException {
assertEquals(Client.run(new String[]{"enforce", "-m", model, "-p", "examples/keymatch_policy.csv", "-AF", func, "cathy", "/cathy_data", "POST"}), "{\"allow\":true,\"explain\":null}");
assertEquals(Client.run(new String[]{"enforce", "-m", model, "-p", "examples/keymatch_policy.csv", "-AF", func, "cathy", "/cathy_data", "DELETE"}), "{\"allow\":false,\"explain\":null}");
+ // test add Function using file
+ String methodName2 = "keyMatchTest2";
+ String model2 = "[request_definition]\n" +
+ "r = sub, obj, act\n" +
+ "\n" +
+ "[policy_definition]\n" +
+ "p = sub, obj, act\n" +
+ "\n" +
+ "[policy_effect]\n" +
+ "e = some(where (p.eft == allow))\n" +
+ "\n" +
+ "[matchers]\n" +
+ "m = r.sub == p.sub && " + methodName + "(r.obj, p.obj) && " + methodName2 + "(r.obj, p.obj)" + "&& regexMatch(r.act, p.act)\n";
+
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "alice", "/alice_data/resource1", "GET"}), "{\"allow\":true,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "alice", "/alice_data/resource1", "POST"}), "{\"allow\":true,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "alice", "/alice_data/resource2", "GET"}), "{\"allow\":true,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "alice", "/alice_data/resource2", "POST"}), "{\"allow\":false,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "alice", "/bob_data/resource1", "GET"}), "{\"allow\":false,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "alice", "/bob_data/resource1", "POST"}), "{\"allow\":false,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "alice", "/bob_data/resource2", "GET"}), "{\"allow\":false,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "alice", "/bob_data/resource2", "POST"}), "{\"allow\":false,\"explain\":null}");
+
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "bob", "/alice_data/resource1", "GET"}), "{\"allow\":false,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "bob", "/alice_data/resource1", "POST"}), "{\"allow\":false,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "bob", "/alice_data/resource2", "GET"}), "{\"allow\":true,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "bob", "/alice_data/resource2", "POST"}), "{\"allow\":false,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "bob", "/bob_data/resource1", "GET"}), "{\"allow\":false,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "bob", "/bob_data/resource1", "POST"}), "{\"allow\":true,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "bob", "/bob_data/resource2", "GET"}), "{\"allow\":false,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "bob", "/bob_data/resource2", "POST"}), "{\"allow\":true,\"explain\":null}");
+
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "cathy", "/cathy_data", "GET"}), "{\"allow\":true,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "cathy", "/cathy_data", "POST"}), "{\"allow\":true,\"explain\":null}");
+ assertEquals(Client.run(new String[]{"enforce", "-m", model2, "-p", "examples/keymatch_policy.csv", "-AF", "examples/keymatch_function.conf", "cathy", "/cathy_data", "DELETE"}), "{\"allow\":false,\"explain\":null}");
}
@Test