diff --git a/Core/pom.xml b/Core/pom.xml
index 5b84eff..fdd87ea 100644
--- a/Core/pom.xml
+++ b/Core/pom.xml
@@ -52,6 +52,12 @@
4.8.108
compile
+
+
+ com.fathzer
+ javaluator
+ 3.0.3
+
diff --git a/Core/src/main/java/fr/pandacube/lib/core/permissions/PermEntity.java b/Core/src/main/java/fr/pandacube/lib/core/permissions/PermEntity.java
index 40d2643..6d3bba9 100644
--- a/Core/src/main/java/fr/pandacube/lib/core/permissions/PermEntity.java
+++ b/Core/src/main/java/fr/pandacube/lib/core/permissions/PermEntity.java
@@ -101,12 +101,19 @@ public abstract class PermEntity {
public Boolean hasPermission(String permission, String server, String world) {
Boolean ret = Permissions.resolver.getEffectivePermission(name, type, permission, server, world);
- if (Log.isDebugEnabled()) {
- Log.debug("[Perm] For " + type.toString().toLowerCase() + " " + getName() + ", '" + permission + "' is " + ret);
- }
+ Log.debug("[Perm] For " + type.toString().toLowerCase() + " " + getName() + ", '" + permission + "' is " + ret);
return ret;
}
+ public boolean hasPermissionOr(String permission, String server, String world, boolean deflt) {
+ Boolean ret = hasPermission(permission, server, world);
+ return ret != null ? ret : deflt;
+ }
+
+ public boolean hasPermissionExpression(String permExpression, String server, String world) {
+ return PermissionExpressionParser.evaluate(permExpression, p -> hasPermissionOr(p, server, world, false));
+ }
+
public DisplayTreeNode debugPermission(String permission) {
return debugPermission(permission, null, null);
diff --git a/Core/src/main/java/fr/pandacube/lib/core/permissions/PermissionExpressionParser.java b/Core/src/main/java/fr/pandacube/lib/core/permissions/PermissionExpressionParser.java
new file mode 100644
index 0000000..cb20e5e
--- /dev/null
+++ b/Core/src/main/java/fr/pandacube/lib/core/permissions/PermissionExpressionParser.java
@@ -0,0 +1,115 @@
+package fr.pandacube.lib.core.permissions;
+
+import java.util.Iterator;
+import java.util.function.Function;
+
+import com.fathzer.soft.javaluator.AbstractEvaluator;
+import com.fathzer.soft.javaluator.BracketPair;
+import com.fathzer.soft.javaluator.Operator;
+import com.fathzer.soft.javaluator.Operator.Associativity;
+import com.fathzer.soft.javaluator.Parameters;
+
+public class PermissionExpressionParser {
+
+ private static final PermissionEvaluator PERMISSION_EVALUATOR = new PermissionEvaluator();
+
+ public static boolean evaluate(String permString, LitteralPermissionTester permTester) {
+ try {
+ return PERMISSION_EVALUATOR.evaluate(permString, permTester);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Can’t evaluate the provided permission expression: '" + permString + "'", e);
+ }
+ }
+
+ public interface LitteralPermissionTester extends Function { }
+
+
+
+
+
+ private static class PermissionEvaluator extends AbstractEvaluator {
+
+ private static final Operator NOT = new Operator("!", 1, Associativity.LEFT, 3);
+ private static final Operator AND = new Operator("&&", 2, Associativity.LEFT, 2);
+ private static final Operator OR = new Operator("||", 2, Associativity.LEFT, 1);
+
+
+ private static final Parameters PARAMETERS;
+
+ static {
+ PARAMETERS = new Parameters();
+ PARAMETERS.add(NOT);
+ PARAMETERS.add(AND);
+ PARAMETERS.add(OR);
+ PARAMETERS.addExpressionBracket(BracketPair.PARENTHESES);
+ }
+
+
+
+ public PermissionEvaluator() {
+ super(PARAMETERS);
+ }
+
+ @Override
+ protected Boolean toValue(String literal, Object evaluationContext) {
+ if (literal.contains(" ") || literal.contains("|") || literal.contains("&"))
+ throw new IllegalArgumentException("Unable to parse the following part of permission expression as one permission node: '" + literal + "'");
+ return evaluationContext instanceof LitteralPermissionTester pt ? pt.apply(literal) : false;
+ }
+
+ @Override
+ protected Boolean evaluate(Operator operator, Iterator operands, Object evaluationContext) {
+ if (operator == NOT) {
+ return !operands.next();
+ } else if (operator == OR) {
+ Boolean o1 = operands.next();
+ Boolean o2 = operands.next();
+ return o1 || o2;
+ } else if (operator == AND) {
+ Boolean o1 = operands.next();
+ Boolean o2 = operands.next();
+ return o1 && o2;
+ } else {
+ return super.evaluate(operator, operands, evaluationContext);
+ }
+ }
+ }
+
+
+ /*
+ public static void main(String[] args) {
+ java.util.List pList = java.util.Arrays.asList("p1.cmd", "p1.toto", "p2.lol");
+ LitteralPermissionTester tester = p -> pList.contains(p);
+
+ for (String permExpr : java.util.Arrays.asList(
+ "p1.cmd", // true
+ "p1.notexist", // false
+ "p2lol.lol", // false
+ "!p1.notexist", // true
+ "!p1.cmd", // false
+ "p1.cmd!", // false
+ "p1.cmd! p2.lol", // exception
+ "p1.cmd || p1.toto", // true || true == true
+ "p1.cmd || p1.notexist", // true || false == true
+ "p1.fefef || p2.lol", // false || true == true
+ "p1.fefef || p2.lolilol", // false || false == false
+ "p1.cmd && p1.toto", // true && true == true
+ "p1.cmd && p1.notexist", // true && false == false
+ "p1.fefef && p2.lol", // false && true == false
+ "p1.fefef && p2.lolilol", // false && false == false
+ "p1.cmd && !p1.toto ", // true && !true == false
+ " !p1.cmd && p1.toto", // !true && true == false
+ "!p1.cmd & p1.toto", // exception
+ "!p1.cmd | p1.toto", // exception
+ "p1.not exist" // exception
+ )) {
+ try {
+ System.out.println(permExpr + " -> " + evaluate(permExpr, tester));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ */
+
+}
diff --git a/Core/src/main/java/fr/pandacube/lib/core/players/IOffPlayer.java b/Core/src/main/java/fr/pandacube/lib/core/players/IOffPlayer.java
index 5a84203..ee1c602 100644
--- a/Core/src/main/java/fr/pandacube/lib/core/players/IOffPlayer.java
+++ b/Core/src/main/java/fr/pandacube/lib/core/players/IOffPlayer.java
@@ -122,6 +122,27 @@ public interface IOffPlayer {
Boolean res = getPermissionUser().hasPermission(permission);
return res != null ? res : false;
}
+
+ /**
+ * Tells if this player has the permission resulted from the provided expression.
+ * If the player is online, this will redirect the
+ * method call to the {@link IOnlinePlayer} instance,
+ * that MUST override this current method to avoid recussive
+ * loop.
+ * If the player is offline, it just call the Pandacube
+ * permission system.
+ * @param permission the permission node to test
+ * @return whether this player has the provided permission
+ */
+ public default boolean hasPermissionExpression(String permissionExpression) {
+ IOnlinePlayer online = getOnlineInstance();
+
+ if (online != null)
+ return online.hasPermissionExpression(permissionExpression);
+
+ // at this point, the player is offline
+ return getPermissionUser().hasPermissionExpression(permissionExpression, null, null);
+ }
/**
* Tells if the this player is part of the specified group
diff --git a/Core/src/main/java/fr/pandacube/lib/core/players/IOnlinePlayer.java b/Core/src/main/java/fr/pandacube/lib/core/players/IOnlinePlayer.java
index 85df13f..783aa7f 100644
--- a/Core/src/main/java/fr/pandacube/lib/core/players/IOnlinePlayer.java
+++ b/Core/src/main/java/fr/pandacube/lib/core/players/IOnlinePlayer.java
@@ -70,6 +70,14 @@ public interface IOnlinePlayer extends IOffPlayer {
* or it may result in a {@link StackOverflowError}.
*/
public abstract boolean hasPermission(String permission);
+
+ /**
+ * Tells if this online player has the permission resulted from the provided expression.
+ * @implSpec the implementation of this method must not directly or
+ * indirectly call the method {@link IOffPlayer#hasPermissionExpression(String)},
+ * or it may result in a {@link StackOverflowError}.
+ */
+ public abstract boolean hasPermissionExpression(String permission);