Mostly javadoc, and also some fixes there and there

This commit is contained in:
2022-08-10 03:04:12 +02:00
parent f976350ee1
commit 54bc8ab99a
42 changed files with 1671 additions and 733 deletions

View File

@@ -65,13 +65,14 @@
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/MANIFEDT.MF</exclude>
</excludes>
</filter>
</filters>
<relocations>
<relocation>
<pattern>com.fathzer.soft.javaluator</pattern>
<shadedPattern>fr.pandacube.shaded.javaluator</shadedPattern>
<shadedPattern>fr.pandacube.lib.permissions.shaded.javaluator</shadedPattern>
</relocation>
</relocations>
</configuration>

View File

@@ -8,98 +8,219 @@ import java.util.Set;
import java.util.stream.LongStream;
import fr.pandacube.lib.chat.ChatTreeNode;
import fr.pandacube.lib.permissions.PermissionExpressionParser.LitteralPermissionTester;
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedEntity;
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
import fr.pandacube.lib.util.Log;
public abstract class PermEntity {
protected final String name;
protected final EntityType type;
protected PermEntity(String n, EntityType t) {
/**
* Represents an entity in the permission system, either a group or a player.
*/
public sealed abstract class PermEntity permits PermPlayer, PermGroup {
/* package */ final String name;
/* package */ final EntityType type;
/* package */ PermEntity(String n, EntityType t) {
name = n; type = t;
}
protected abstract CachedEntity getBackendEntity();
/* package */ abstract CachedEntity getBackendEntity();
/**
* Gets all the groups this entity inherits from.
* @return a list of all the groups this entity inherits from.
*/
public abstract List<PermGroup> getInheritances();
/**
* Gets all the group names this entity inherits from.
* @return a list of all the group names this entity inherits from.
*/
public abstract List<String> getInheritancesString();
/**
* Gets the name of this entity.
* @return the name of this entity.
*/
public abstract String getName();
/**
* Gets the name of this entity, as it is stored in the database.
* @return the name of this entity, as it is stored in the database.
*/
public String getInternalName() {
return name;
}
/**
* Tells if the current entity inherits directly or indirectly from the specified group
* Tells if the current entity inherits directly or indirectly from the specified group.
* @param group the group to search for
* @param recursive true to search in the inheritance tree, or false to search only in the inheritance list of the current entity.
* @return true if the current entity inherits directly or indirectly from the specified group, false otherwise.
*/
public boolean inheritsFromGroup(String group, boolean recursive) {
if (group == null)
return false;
return getInheritances().stream().anyMatch(g -> g.name.equals(group) || (recursive && g.inheritsFromGroup(group, true)));
}
/**
* Gets the effective prefix of this entity.
* It is either the prefix defined directly for this entity, or from inheritance.
* @return the effective prefix of this entity.
*/
public String getPrefix() {
return Permissions.resolver.getEffectivePrefix(name, type);
}
/**
* Gets the prefix defined directly for this entity.
* @return the prefix defined directly for this entity.
*/
public String getSelfPrefix() {
return getBackendEntity().getSelfPrefix();
}
/**
* Provides informations on how the effective prefix of this entity is determined.
* @return a {@link ChatTreeNode} providing informations on how the effective prefix of this entity is determined.
*/
public ChatTreeNode debugPrefix() {
return Permissions.resolver.debugPrefix(name, type);
}
/**
* Sets the prefix of this entity.
* @param prefix the prefix for this entity.
*/
public void setSelfPrefix(String prefix) {
Permissions.backendWriter.setSelfPrefix(name, type, prefix);
}
/**
* Gets the effective suffix of this entity.
* It is either the suffix defined directly for this entity, or from inheritance.
* @return the effective suffix of this entity.
*/
public String getSuffix() {
return Permissions.resolver.getEffectiveSuffix(name, type);
}
/**
* Gets the suffix defined directly for this entity.
* @return the suffix defined directly for this entity.
*/
public String getSelfSuffix() {
return getBackendEntity().getSelfSuffix();
}
/**
* Provides informations on how the effective suffix of this entity is determined.
* @return a {@link ChatTreeNode} providing informations on how the effective suffix of this entity is determined.
*/
public ChatTreeNode debugSuffix() {
return Permissions.resolver.debugSuffix(name, type);
}
/**
* Sets the suffix of this entity.
* @param suffix the suffix for this entity.
*/
public void setSelfSuffix(String suffix) {
Permissions.backendWriter.setSelfSuffix(name, type, suffix);
}
/**
* Gets the effective list of permissions that applies to this entity out of a specific server and world.
* It is either the permissions defined directly for this entity, or from inheritance as long as they are not
* overriden.
* @return the effective list of permissions that applies to this entity out of a specific server and world.
*/
public Map<String, Boolean> listEffectivePermissions() {
return listEffectivePermissions(null, null);
}
/**
* Gets the effective list of permissions that applies to this entity on a specific server.
* It is either the permissions defined directly for this entity, or from inheritance as long as they are not
* overriden.
* @param server the server where the returned permissions apply for this entity.
* @return the effective list of permissions that applies to this entity on a specific server.
*/
public Map<String, Boolean> listEffectivePermissions(String server) {
return listEffectivePermissions(server, null);
}
/**
* Gets the effective list of permissions that applies to this entity on a specific server and world.
* It is either the permissions defined directly for this entity, or from inheritance as long as they are not
* overriden.
* @param server the server containing the world where the returned permissions apply for this entity.
* @param world the world in the server where the returned permissions apply for this entity.
* @return the effective list of permissions that applies to this entity on a specific server and world.
*/
public Map<String, Boolean> listEffectivePermissions(String server, String world) {
return Permissions.resolver.getEffectivePermissionList(name, type, server, world);
}
/**
* Gets the effective values of the provided permission range prefix that applies to this entity out of a specific
* server and world.
* It is either the range values defined directly for this entity, or from inheritance as long as they are not
* overriden.
* @param permissionPrefix the permission range prefix.
* @return the effective values of the provided permission range prefix that applies to this entity out of a
* specific server and world.
*/
public LongStream getPermissionRangeValues(String permissionPrefix) {
return getPermissionRangeValues(permissionPrefix, null, null);
}
/**
* Gets the effective values of the provided permission range prefix that applies to this entity on a specific
* server.
* It is either the range values defined directly for this entity, or from inheritance as long as they are not
* overriden.
* @param permissionPrefix the permission range prefix.
* @param server the server where the returned values apply for this entity.
* @return the effective values of the provided permission range prefix that applies to this entity on a specific
* server.
*/
public LongStream getPermissionRangeValues(String permissionPrefix, String server) {
return getPermissionRangeValues(permissionPrefix, server, null);
}
/**
* Gets the effective values of the provided permission range prefix that applies to this entity on a specific
* server and world.
* It is either the range values defined directly for this entity, or from inheritance as long as they are not
* overriden.
* @param permissionPrefix the permission range prefix.
* @param server the server containing the world where the returned values apply for this entity.
* @param world the world in the server where the returned values apply for this entity.
* @return the effective values of the provided permission range prefix that applies to this entity on a specific
* server and world.
*/
public LongStream getPermissionRangeValues(String permissionPrefix, String server, String world) {
String prefixWithEndingDot = permissionPrefix.endsWith(".") ? permissionPrefix : (permissionPrefix + ".");
int prefixLength = prefixWithEndingDot.length();
@@ -120,106 +241,299 @@ public abstract class PermEntity {
.mapToLong(longSuffix -> longSuffix)
.sorted();
}
/**
* Gets the maximum effective value of the provided permission range prefix that applies to this entity out of a
* specific server and world.
* It is either the range values defined directly for this entity, or from inheritance as long as they are not
* overriden.
* @param permissionPrefix the permission range prefix.
* @return the maximum effective value of the provided permission range prefix that applies to this entity out of a
* specific server and world.
*/
public OptionalLong getPermissionRangeMax(String permissionPrefix) {
return getPermissionRangeMax(permissionPrefix, null, null);
}
/**
* Gets the maximum effective value of the provided permission range prefix that applies to this entity on a
* specific server.
* It is either the range values defined directly for this entity, or from inheritance as long as they are not
* overriden.
* @param permissionPrefix the permission range prefix.
* @param server the server where the returned value applies for this entity.
* @return the maximum effective value of the provided permission range prefix that applies to this entity on a
* specific server.
*/
public OptionalLong getPermissionRangeMax(String permissionPrefix, String server) {
return getPermissionRangeMax(permissionPrefix, server, null);
}
/**
* Gets the maximum effective value of the provided permission range prefix that applies to this entity on a
* specific server and world.
* It is either the range values defined directly for this entity, or from inheritance as long as they are not
* overriden.
* @param permissionPrefix the permission range prefix.
* @param server the server containing the world where the returned value applies for this entity.
* @param world the world in the server where the returned value applies for this entity.
* @return the maximum effective value of the provided permission range prefix that applies to this entity on a
* specific server and world.
*/
public OptionalLong getPermissionRangeMax(String permissionPrefix, String server, String world) {
return getPermissionRangeValues(permissionPrefix, server, world).max();
}
/**
* Tells if this entity has the provided permission out of a specific server and world.
* It is either based on the permissions defined directly for this entity, or from inheritance as long as they are
* not overriden.
* @param permission the permission to ckeck on this entity.
* @return true if this entity has the permission, false if it is negated, or null if not known.
*/
public Boolean hasPermission(String permission) {
return hasPermission(permission, null, null);
}
/**
* Tells if this entity has the provided permission on a specitif server.
* It is either based on the permissions defined directly for this entity, or from inheritance as long as they are
* not overriden. It also consider permissions that apply on any server.
* @param permission the permission to ckeck on this entity.
* @param server the server in which to test the permission for this entity.
* @return true if this entity has the permission, false if it is negated, or null if not known.
*/
public Boolean hasPermission(String permission, String server) {
return hasPermission(permission, server, null);
}
/**
* Tells if this entity has the provided permission on a specitif server and world.
* It is either based on the permissions defined directly for this entity, or from inheritance as long as they are
* not overriden. It also consider permissions that apply on any world of that server, and then any server.
* @param permission the permission to ckeck on this entity.
* @param server the server in which to test the permission for this entity.
* @param world the world in which to test the permission for this entity.
* @return true if this entity has the permission, false if it is negated, or null if not known.
*/
public Boolean hasPermission(String permission, String server, String world) {
Boolean ret = Permissions.resolver.getEffectivePermission(name, type, permission, server, world);
Log.debug("[Perm] For " + type.toString().toLowerCase() + " " + getName() + ", '" + permission + "' is " + ret);
return ret;
}
/**
* Tells if this entity has the provided permission on a specitif server and world.
* It is either based on the permissions defined directly for this entity, or from inheritance as long as they are
* not overriden.
* @param permission the permission to ckeck on this entity.
* @param server the server in which to test the permission for this entity.
* @param world the world in which to test the permission for this entity.
* @param deflt the default value is the permission is undefined for this entity.
* @return true if this entity has the permission, false if it is negated, or {@code deflt} if not known.
*/
public boolean hasPermissionOr(String permission, String server, String world, boolean deflt) {
Boolean ret = hasPermission(permission, server, world);
return ret != null ? ret : deflt;
}
/**
* Evaluates the provided permission expression for this entity.
* It uses {@link #hasPermissionOr(String, String, String, boolean)} with {@code false} as a default value, to check
* each permission nodes individualy.
* @param permExpression the permission expression to evaluate on this entity.
* @param server the server in which to test the permission expression for this entity.
* @param world the world in which to test the permission expression for this entity.
* @return true if this the permission expression evaluates to true, false otherwise.
* @see PermissionExpressionParser#evaluate(String, LitteralPermissionTester)
*/
public boolean hasPermissionExpression(String permExpression, String server, String world) {
return PermissionExpressionParser.evaluate(permExpression, p -> hasPermissionOr(p, server, world, false));
}
/**
* Provides informations on how the effective permission of this entity on the provided permission node is
* determined.
* @param permission the permission node to debug on this entity.
* @return a {@link ChatTreeNode} providing informations on how the effective permission is determined.
*/
public ChatTreeNode debugPermission(String permission) {
return debugPermission(permission, null, null);
}
/**
* Provides informations on how the effective permission of this entity on the provided permission node is
* determined.
* @param permission the permission node to debug on this entity.
* @param server the server in which to test the permission for this entity.
* @return a {@link ChatTreeNode} providing informations on how the effective permission is determined.
*/
public ChatTreeNode debugPermission(String permission, String server) {
return debugPermission(permission, server, null);
}
/**
* Provides informations on how the effective permission of this entity on the provided permission node is
* determined.
* @param permission the permission node to debug on this entity.
* @param server the server in which to test the permission for this entity.
* @param world the world in which to test the permission for this entity.
* @return a {@link ChatTreeNode} providing informations on how the effective permission is determined.
*/
public ChatTreeNode debugPermission(String permission, String server, String world) {
return Permissions.resolver.debugPermission(name, type, permission, server, world);
}
/**
* Adds the provided permission node to this entity that apply on any server.
* @param permission the permission node to add.
*/
public void addSelfPermission(String permission) {
addSelfPermission(permission, null, null);
}
/**
* Adds the provided permission node to this entity that apply on the provided server.
* @param permission the permission node to add.
* @param server the server in which to apply the permission.
*/
public void addSelfPermission(String permission, String server) {
addSelfPermission(permission, server, null);
}
/**
* Adds the provided permission node to this entity that apply on the provided server and world.
* @param permission the permission node to add.
* @param server the server in which to apply the permission.
* @param world the world in which to apply the permission.
*/
public void addSelfPermission(String permission, String server, String world) {
Permissions.backendWriter.addSelfPermission(name, type, permission, server, world);
}
/**
* Removes the provided permission node from this entity that applied on any server.
* @param permission the permission node to add.
*/
public void removeSelfPermission(String permission) {
removeSelfPermission(permission, null, null);
}
/**
* Removes the provided permission node from this entity that applied on the provided server.
* @param permission the permission node to remove.
* @param server the server from which to remove the permission.
*/
public void removeSelfPermission(String permission, String server) {
removeSelfPermission(permission, server, null);
}
/**
* Removes the provided permission node from this entity that applied on the provided server and world.
* @param permission the permission node to remove.
* @param server the server from which to remove the permission.
* @param world the world from which to remove the permission.
*/
public void removeSelfPermission(String permission, String server, String world) {
Permissions.backendWriter.removeSelfPermission(name, type, permission, server, world);
}
/**
* Counts the number of self permission nodes for this entity.
* @return the number of self permission nodes for this entity.
*/
public int getSelfPermissionsCount() {
return getSelfPermissionsServerWorldKeys().stream()
.mapToInt(key -> getSelfPermissions(key.server, key.world).size())
.mapToInt(key -> getSelfPermissions(key.server(), key.world()).size())
.sum();
}
/**
* Gets all the server/world attribution that have at least one self permission for this entity.
* @return all the server/world attribution that have at least one self permission for this entity.
*/
public Set<ServerWorldKey> getSelfPermissionsServerWorldKeys() {
return getBackendEntity().getSelfPermissionsServerWorldKeys();
}
/**
* Gets all the self permission nodes that apply everywhere for this entity.
* @return all the self permission nodes that apply everywhere for this entity.
*/
public List<String> getSelfPermissions() {
return getSelfPermissions(null, null);
}
/**
* Gets all the self permission nodes that apply on the provided server for this entity.
* @param server the server from which to get the permissions.
* @return all the self permission nodes that apply on the provided server for this entity.
*/
public List<String> getSelfPermissions(String server) {
return getSelfPermissions(server, null);
}
/**
* Gets all the self permission nodes that apply on the provided server and world for this entity.
* @param server the server from which to get the permissions.
* @param world the world from which to get the permissions.
* @return all the self permission nodes that apply on the provided server and world for this entity.
*/
public List<String> getSelfPermissions(String server, String world) {
return getBackendEntity().getSelfPermissions(server, world);
}
@Override
public boolean equals(Object obj) {

View File

@@ -6,7 +6,10 @@ import java.util.stream.Collectors;
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedGroup;
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
public class PermGroup extends PermEntity {
/**
* Represents an group in the permission system.
*/
public final class PermGroup extends PermEntity {
/* package */ PermGroup(String name) {
super(name, EntityType.Group);
}
@@ -31,27 +34,54 @@ public class PermGroup extends PermEntity {
.map(cg -> cg.name)
.collect(Collectors.toList());
}
/**
* Tells if this group is a default group.
* A player inherits all default groups when they dont explicitely inherit from at least one group.
* @return true if this group is a default group, false otherwise.
*/
public boolean isDefault() {
return getBackendEntity().deflt;
}
/**
* Sets this group as a default group or not.
* All players that dont explicitely inherit from at least one group will either start or stop implicitely
* inheriting from this group.
* @param deflt true to set this group as default, false to set is as not default.
*/
public void setDefault(boolean deflt) {
Permissions.backendWriter.setGroupDefault(name, deflt);
}
/**
* Makes this group inherit the provided group.
* @param group the name of the group to inherit from.
*/
public void addInheritance(String group) {
Permissions.backendWriter.addInheritance(name, type, group);
}
/**
* Makes this group inherit the provided group.
* @param group the group to inherit from.
*/
public void addInheritance(PermGroup group) {
addInheritance(group.name);
}
/**
* Makes this group stop inheriting from the provided group.
* @param group the name of the group to stop inheriting from.
*/
public void removeInheritance(String group) {
Permissions.backendWriter.removeInheritance(name, type, group);
}
/**
* Makes this group stop inheriting from the provided group.
* @param group the group to stop inheriting from.
*/
public void removeInheritance(PermGroup group) {
removeInheritance(group.name);
}

View File

@@ -7,7 +7,10 @@ import java.util.stream.Collectors;
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedPlayer;
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
public class PermPlayer extends PermEntity {
/**
* Represents an player in the permission system.
*/
public final class PermPlayer extends PermEntity {
private final UUID playerId;
/* package */ PermPlayer(UUID id) {
super(id.toString(), EntityType.User);
@@ -29,7 +32,11 @@ public class PermPlayer extends PermEntity {
.map(cg -> cg.name)
.collect(Collectors.toList());
}
/**
* Gets the UUID of this player.
* @return the UUID of this player.
*/
public UUID getPlayerId() {
return playerId;
}
@@ -48,14 +55,18 @@ public class PermPlayer extends PermEntity {
}
/**
* Gets all the groups this player belongs to.
* Alias for {@link #getInheritances()}.
* @return a list of all the groups this player belongs to.
*/
public List<PermGroup> getGroups() {
return getInheritances();
}
/**
* Gets all the group names this player belongs to.
* Alias for {@link #getInheritances()}.
* @return a list of all the group names this player belongs to.
*/
public List<String> getGroupsString() {
return getInheritancesString();
@@ -65,35 +76,67 @@ public class PermPlayer extends PermEntity {
* Tells if the player is directly part of a group.
* This is equivalent to {@link #inheritsFromGroup(String, boolean) inheritsFromGroup(group, false)}
* @param group the group to search for
* @return true if the player is directly part of a group, false otherwise.
*/
public boolean isInGroup(String group) {
return inheritsFromGroup(group, false);
}
/**
* Tells if this player has been assigned to the default groups.
* @return true if this player has been assigned to the default groups, or false if this player belongs explicitely
* to their groups.
*/
public boolean isUsingDefaultGroups() {
return getBackendEntity().usingDefaultGroups;
}
/**
* Sets the group this player will now inheritate, removing all previously inherited groups.
* To keep the other inherited groups, use {@link #addGroup(String)}.
* @param group the name of the group to inherit from.
*/
public void setGroup(String group) {
Permissions.backendWriter.setInheritance(name, type, group);
}
/**
* Sets the group this player will now inheritate, removing all previously inherited groups.
* To keep the other inherited groups, use {@link #addGroup(PermGroup)}.
* @param group the group to inherit from.
*/
public void setGroup(PermGroup group) {
setGroup(group.name);
}
/**
* Makes this player inherit the provided group, keeping the other groups they already inherits from.
* @param group the name of the group to inherit from.
*/
public void addGroup(String group) {
Permissions.backendWriter.addInheritance(name, type, group);
}
/**
* Makes this player inherit the provided group, keeping the other groups they already inherits from.
* @param group the group to inherit from.
*/
public void addGroup(PermGroup group) {
addGroup(group.name);
}
/**
* Makes this player stop inheriting from the provided group.
* @param group the name of the group to stop inheriting from.
*/
public void removeGroup(String group) {
Permissions.backendWriter.removeInheritance(name, type, group);
}
/**
* Makes this player stop inheriting from the provided group.
* @param group the group to stop inheriting from.
*/
public void removeGroup(PermGroup group) {
removeGroup(group.name);
}

View File

@@ -10,10 +10,38 @@ import com.fathzer.soft.javaluator.Operator;
import com.fathzer.soft.javaluator.Operator.Associativity;
import com.fathzer.soft.javaluator.Parameters;
/**
* Class that evaluates a permission string as if it was a boolean expression with permission nodes as variables.
* <p>
* A permission expression contains permission nodes, boolean operators ({@code "||"}, {@code "&&"} and {@code "!"}) and
* literal values {@code "true"} and {@code "false"}.
* Here are some example of permission expressions:
* <pre>{@code
* "p1.cmd"
* "!p1.toto"
* "p1.cmd!"
* "p1.cmd || p1.toto"
* "p1.cmd && p1.toto"
* "p1.cmd && !p1.toto "
* "p1.cmd && true"
* "false || p2.cmd"
* }</pre>
* Notice that spaces around permission nodes and operators does not affect the results of the parsing.
*/
public class PermissionExpressionParser {
private static final PermissionEvaluator PERMISSION_EVALUATOR = new PermissionEvaluator();
/**
* Evaluate the provided permission expression, testing each permission with the provided permTester.
*
* @param permString the permission expression to evaluate.
* @param permTester a function that gives the value of the provided permission node. It is usually a method
* reference to the {@code hasPermission(String)} method the player we want to test the
* permissions.
* @throws IllegalArgumentException if the expression is not correct.
* @return the result of the evaluation of the permission expression.
*/
public static boolean evaluate(String permString, LitteralPermissionTester permTester) {
try {
return PERMISSION_EVALUATOR.evaluate(permString, permTester);
@@ -22,6 +50,9 @@ public class PermissionExpressionParser {
}
}
/**
* Functional interface that converts a string into a boolean.
*/
public interface LitteralPermissionTester extends Function<String, Boolean> { }
@@ -89,41 +120,4 @@ public class PermissionExpressionParser {
}
}
/* TODO move to test code
public static void main(String[] args) {
java.util.List<String> 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();
}
}
}
*/
}

View File

@@ -10,6 +10,13 @@ import fr.pandacube.lib.db.DBConnection;
import fr.pandacube.lib.db.DBException;
import fr.pandacube.lib.util.Log;
/**
* Main class for the Pandalib permission system.
* <p>
* This permission system uses the Pandalib DB API to connect to the database, so the connection to the MySQL must be
* established first, using {@link DB#init(DBConnection, String)}.
* Then, this class must be initialized using {@link #init(Function)}.
*/
public class Permissions {
/* package */ static PermissionsCachedBackendReader backendReader;
@@ -20,6 +27,10 @@ public class Permissions {
/**
* Initialize the permission system.
* The connection to the database needs to be initialized first, using {@link DB#init(DBConnection, String)}.
* @param playerNameGetter a function to get the player name associated with a UUID. It is used for
* and to generate {@link PermPlayer#getName()} and for
* {@link PermEntity#debugPermission(String)}.
* @throws DBException if an error occurs when interacting with the database.
*/
public static void init(Function<UUID, String> playerNameGetter) throws DBException {
Permissions.playerNameGetter = playerNameGetter == null ? UUID::toString : playerNameGetter;
@@ -37,7 +48,18 @@ public class Permissions {
throw e;
}
}
private static void checkInitialized() {
if (backendReader == null) {
throw new IllegalStateException("Permissions system not initialized. Check the server logs to check if there is an error during the startup, and check if the init() method is called properly.");
}
}
/**
* Adds the provided special permissions to this permission system.
* @param specialPermissions the {@link SpecialPermission}s to add.
* @throws IllegalStateException if the permission system was not initialized properly.
*/
public static void addSpecialPermissions(SpecialPermission... specialPermissions) {
checkInitialized();
if (specialPermissions == null)
@@ -45,18 +67,23 @@ public class Permissions {
resolver.specialPermissions.addAll(Arrays.asList(specialPermissions));
}
private static void checkInitialized() {
if (backendReader == null) {
throw new IllegalStateException("Permissions system not initialized. Check the server logs to check if there is an error during the startup, and check if the init() method is called properly.");
}
}
/**
* Clears the cached data of a specific player.
* @param playerId the UUID of the player.
* @throws IllegalStateException if the permission system was not initialized properly.
*/
public static void clearPlayerCache(UUID playerId) {
checkInitialized();
backendReader.clearPlayerCache(playerId);
resolver.clearPlayerFromCache(playerId);
}
/**
* Clears all the cached data (players and groupds) and fetch all the groups data from the database.
* The clearing and fetching of the data is made asynchronously in a new thread.
* @param then the action to perform after the cache has been updated.
* @throws IllegalStateException if the permission system was not initialized properly.
*/
public static void clearCache(Runnable then) {
checkInitialized();
backendReader.clearAndResetCacheAsync(() -> {
@@ -65,13 +92,25 @@ public class Permissions {
then.run();
});
}
/**
* Gets the permission player object.
* @param playerId the UUID of the player.
* @return the permission player object.
* @throws IllegalStateException if the permission system was not initialized properly.
*/
public static PermPlayer getPlayer(UUID playerId) {
checkInitialized();
return new PermPlayer(playerId);
}
/**
* Asks the permission system to preventively and asynchronoulsy cache the data of the provided player.
* This can be called as soon as possible when a player connects, so the permission data of the player are
* accessible as soon as possible when they are needed.
* @param playerId the UUID of the player.
* @throws IllegalStateException if the permission system was not initialized properly.
*/
public static void precachePlayerAsync(UUID playerId) {
checkInitialized();
Thread t = new Thread(() -> {
@@ -84,23 +123,45 @@ public class Permissions {
t.setDaemon(true);
t.start();
}
/**
* Gets the permission group object.
* @param name the name of the group.
* @return the permission group object.
* @throws IllegalStateException if the permission system was not initialized properly.
*/
public static PermGroup getGroup(String name) {
checkInitialized();
return new PermGroup(name);
}
/**
* Gets all the permission group objects.
* @return all the permission group objects.
* @throws IllegalStateException if the permission system was not initialized properly.
*/
public static List<PermGroup> getGroups() {
checkInitialized();
return PermGroup.fromCachedGroups(backendReader.getGroups());
}
/**
* Gets all the default permission group objects.
* @return all the default permission group objects.
* @throws IllegalStateException if the permission system was not initialized properly.
*/
public static List<PermGroup> getDefaultGroups() {
checkInitialized();
return PermGroup.fromCachedGroups(backendReader.getDefaultGroups());
}
/**
* Gets the full permission list.
* @return the full permission list.
* @throws IllegalStateException if the permission system was not initialized properly.
*/
public static List<String> getFullPermissionsList() {
checkInitialized();
return backendReader.getFullPermissionsList();
}

View File

@@ -25,7 +25,7 @@ import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedPlayer;
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
import fr.pandacube.lib.util.Log;
public class PermissionsResolver {
/* package */ class PermissionsResolver {
private final PermissionsCachedBackendReader backendReader;
@@ -400,7 +400,7 @@ public class PermissionsResolver {
ParsedSelfPermission specialPerm = null;
for (SpecialPermission spePerm : specialPermissions) {
if (spePerm.match().match(permission)) {
if (spePerm.matcher().match(permission)) {
boolean res = spePerm.tester().test(permP, permission, server, world);
specialPerm = new ParsedSelfPermission(permission, res, PermType.SPECIAL);
break;

View File

@@ -3,13 +3,19 @@ package fr.pandacube.lib.permissions;
import fr.pandacube.lib.db.SQLElement;
import fr.pandacube.lib.db.SQLField;
/**
* SQL Table to store the permissions data.
*/
public class SQLPermissions extends SQLElement<SQLPermissions> {
/**
* Instanciate a new entry in the table.
*/
public SQLPermissions() {
super();
}
public SQLPermissions(int id) {
private SQLPermissions(int id) {
super(id);
}
@@ -18,22 +24,45 @@ public class SQLPermissions extends SQLElement<SQLPermissions> {
return "permissions";
}
/** The name of the entity (player id or group name). */
public static final SQLField<SQLPermissions, String> name = field(VARCHAR(64), false);
/** The entity type, based on {@link EntityType}. */
public static final SQLField<SQLPermissions, Integer> type = field(TINYINT, false);
/** The key of the data ("permission", "inheritance", …). */
public static final SQLField<SQLPermissions, String> key = field(VARCHAR(256), false);
/** The data value (permission node, name of inherited group, prefix/suffix, …). */
public static final SQLField<SQLPermissions, String> value = field(VARCHAR(256), false);
/** The server in which the permission apply. */
public static final SQLField<SQLPermissions, String> server = field(VARCHAR(64), true);
/** The world in which the permission apply. */
public static final SQLField<SQLPermissions, String> world = field(VARCHAR(64), true);
/**
* All possible type of entity type.
*/
public enum EntityType {
/**
* User entity type.
*/
User,
/**
* Group entity type.
*/
Group;
/**
* Returns the database value of this entity type.
* @return the database value of this entity type.
*/
public int getCode() {
return ordinal();
}
/**
* Gets the {@link EntityType} corresponding to the database value.
* @param code the database value.
* @return the {@link EntityType} corresponding to the database value.
*/
public static EntityType getByCode(int code) {
if (code >= 0 && code < values().length)
return values()[code];

View File

@@ -3,11 +3,14 @@ package fr.pandacube.lib.permissions;
import java.util.Comparator;
import java.util.Objects;
public class ServerWorldKey implements Comparable<ServerWorldKey> {
public final String server, world;
ServerWorldKey(String s, String w) {
server = s; world = w;
}
/**
* A pair of string representing a server and world name, used to organize and filter the permission data of a player or
* group.
* @param server the server name, can be null.
* @param world the world name, can be null.
*/
public record ServerWorldKey(String server, String world) implements Comparable<ServerWorldKey> {
@Override
public boolean equals(Object obj) {
return obj instanceof ServerWorldKey o

View File

@@ -2,14 +2,35 @@ package fr.pandacube.lib.permissions;
/**
* Represents a permission node that is based on an arbitrary player state.
* @param matcher predicate that tell if the provided permission is our special permission.
* @param tester predicate that tell the value of this special permission, based on the parameters.
*/
public record SpecialPermission(PermissionMatcher match, PermissionTester tester) {
public record SpecialPermission(PermissionMatcher matcher, PermissionTester tester) {
/**
* Predicate that tell if the provided permission is our special permission.
*/
public interface PermissionMatcher {
/**
* Tells if the provided permission is our special permission.
* @param permission the permission to test.
* @return true if the provided permission is our special permission, false otherwise.
*/
boolean match(String permission);
}
/**
* Predicate that tell the value of this special permission, based on the parameters.
*/
public interface PermissionTester {
/**
* Tells the value of this special permission, based on the parameters.
* @param player the player to test the permission on.
* @param permission the permission to test.
* @param server the server on which the player is.
* @param world the world in which the player is.
* @return the value of this special permission, based on the parameters.
*/
boolean test(PermPlayer player, String permission, String server, String world);
}

View File

@@ -0,0 +1,109 @@
package fr.pandacube.lib.permissions;
import fr.pandacube.lib.permissions.PermissionExpressionParser.LitteralPermissionTester;
import org.junit.Before;
import org.junit.Test;
import static fr.pandacube.lib.permissions.PermissionExpressionParser.evaluate;
import static org.junit.Assert.*;
public class PermissionExpressionParserTest {
java.util.List<String> pList = java.util.Arrays.asList("p1.cmd", "p1.toto", "p2.lol");
LitteralPermissionTester tester = p -> pList.contains(p);
@Test
public void evaluateTrue() {
assertTrue(evaluate("p1.cmd", tester));
}
@Test
public void evaluateFalse() {
assertFalse(evaluate("p1.notexist", tester));
}
@Test
public void evaluateNegateFalse() {
assertTrue(evaluate("!p1.notexist", tester));
}
@Test
public void evaluateNegateTrue() {
assertFalse(evaluate("!p1.cmd", tester));
}
@Test
public void evaluateRevNegateTrue() {
assertFalse(evaluate("p1.cmd!", tester));
}
@Test
public void evaluateOrBothTrue() {
assertTrue(evaluate("p1.cmd || p1.toto", tester));
}
@Test
public void evaluateOrTrueFalse() {
assertTrue(evaluate("p1.cmd || p1.notexist", tester));
}
@Test
public void evaluateOrFalseTrue() {
assertTrue(evaluate("p1.fefef || p2.lol", tester));
}
@Test
public void evaluateOrBothFalse() {
assertFalse(evaluate("p1.fefef || p2.lolilol", tester));
}
@Test
public void evaluateAndBothTrue() {
assertTrue(evaluate("p1.cmd && p1.toto", tester));
}
@Test
public void evaluateAndTrueFalse() {
assertFalse(evaluate("p1.cmd && p1.notexist", tester));
}
@Test
public void evaluateAndFalseTrue() {
assertFalse(evaluate("p1.fefef && p2.lol", tester));
}
@Test
public void evaluateAndBothFalse() {
assertFalse(evaluate("p1.fefef && p2.lolilol", tester));
}
@Test
public void evaluateAndTrueNegateTrueWithSomeExtraSpaces() {
assertFalse(evaluate("p1.cmd && !p1.toto ", tester));
}
@Test
public void evaluateAndNegateTrueTrueWithLotOfExtraSpaces() {
assertFalse(evaluate(" !p1.cmd && p1.toto ", tester));
}
@Test(expected = IllegalArgumentException.class)
public void evaluateBadSyntax1() {
evaluate("p1.cmd! p2.lol", tester);
}
@Test(expected = IllegalArgumentException.class)
public void evaluateBadSyntax2() {
evaluate("!p1.cmd & p1.toto", tester);
}
@Test(expected = IllegalArgumentException.class)
public void evaluateBadSyntax3() {
evaluate("!p1.cmd | p1.toto", tester);
}
@Test(expected = IllegalArgumentException.class)
public void evaluateBadSyntax4() {
evaluate("p1.not exist", tester);
}
}