renamed modules dir
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
package fr.pandacube.lib.permissions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Set;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
import fr.pandacube.lib.chat.ChatUtil.DisplayTreeNode;
|
||||
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) {
|
||||
name = n; type = t;
|
||||
}
|
||||
|
||||
protected abstract CachedEntity getBackendEntity();
|
||||
public abstract List<PermGroup> getInheritances();
|
||||
public abstract List<String> getInheritancesString();
|
||||
public abstract String getName();
|
||||
|
||||
public String getInternalName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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)));
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return Permissions.resolver.getEffectivePrefix(name, type);
|
||||
}
|
||||
|
||||
|
||||
public String getSelfPrefix() {
|
||||
return getBackendEntity().getSelfPrefix();
|
||||
}
|
||||
|
||||
|
||||
public DisplayTreeNode debugPrefix() {
|
||||
return Permissions.resolver.debugPrefix(name, type);
|
||||
}
|
||||
|
||||
|
||||
public void setSelfPrefix(String prefix) {
|
||||
Permissions.backendWriter.setSelfPrefix(name, type, prefix);
|
||||
}
|
||||
|
||||
|
||||
public String getSuffix() {
|
||||
return Permissions.resolver.getEffectiveSuffix(name, type);
|
||||
}
|
||||
|
||||
public String getSelfSuffix() {
|
||||
return getBackendEntity().getSelfSuffix();
|
||||
}
|
||||
|
||||
|
||||
public DisplayTreeNode debugSuffix() {
|
||||
return Permissions.resolver.debugSuffix(name, type);
|
||||
}
|
||||
|
||||
|
||||
public void setSelfSuffix(String suffix) {
|
||||
Permissions.backendWriter.setSelfSuffix(name, type, suffix);
|
||||
}
|
||||
|
||||
|
||||
public Map<String, Boolean> listEffectivePermissions() {
|
||||
return listEffectivePermissions(null, null);
|
||||
}
|
||||
|
||||
public Map<String, Boolean> listEffectivePermissions(String server) {
|
||||
return listEffectivePermissions(server, null);
|
||||
}
|
||||
|
||||
public Map<String, Boolean> listEffectivePermissions(String server, String world) {
|
||||
return Permissions.resolver.getEffectivePermissionList(name, type, server, world);
|
||||
}
|
||||
|
||||
|
||||
public LongStream getPermissionRangeValues(String permissionPrefix) {
|
||||
return getPermissionRangeValues(permissionPrefix, null, null);
|
||||
}
|
||||
|
||||
public LongStream getPermissionRangeValues(String permissionPrefix, String server) {
|
||||
return getPermissionRangeValues(permissionPrefix, server, null);
|
||||
}
|
||||
|
||||
public LongStream getPermissionRangeValues(String permissionPrefix, String server, String world) {
|
||||
String prefixWithEndingDot = permissionPrefix.endsWith(".") ? permissionPrefix : (permissionPrefix + ".");
|
||||
int prefixLength = prefixWithEndingDot.length();
|
||||
return listEffectivePermissions(server, world).entrySet().stream()
|
||||
.filter(Map.Entry::getValue) // permission must be positive
|
||||
.map(Map.Entry::getKey) // keep only the permission node (key), since the value is always true
|
||||
.filter(p -> p.startsWith(prefixWithEndingDot)) // keep only relevant permissions
|
||||
.map(p -> p.substring(prefixLength)) // keep only what is after the prefix
|
||||
.map(suffix -> { // convert to long
|
||||
try {
|
||||
return Long.parseLong(suffix);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.mapToLong(longSuffix -> longSuffix)
|
||||
.sorted();
|
||||
}
|
||||
|
||||
|
||||
public OptionalLong getPermissionRangeMax(String permissionPrefix) {
|
||||
return getPermissionRangeMax(permissionPrefix, null, null);
|
||||
}
|
||||
|
||||
public OptionalLong getPermissionRangeMax(String permissionPrefix, String server) {
|
||||
return getPermissionRangeMax(permissionPrefix, server, null);
|
||||
}
|
||||
|
||||
public OptionalLong getPermissionRangeMax(String permissionPrefix, String server, String world) {
|
||||
return getPermissionRangeValues(permissionPrefix, server, world).max();
|
||||
}
|
||||
|
||||
|
||||
public Boolean hasPermission(String permission) {
|
||||
return hasPermission(permission, null, null);
|
||||
}
|
||||
|
||||
public Boolean hasPermission(String permission, String server) {
|
||||
return hasPermission(permission, server, null);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public DisplayTreeNode debugPermission(String permission, String server) {
|
||||
return debugPermission(permission, server, null);
|
||||
}
|
||||
|
||||
public DisplayTreeNode debugPermission(String permission, String server, String world) {
|
||||
return Permissions.resolver.debugPermission(name, type, permission, server, world);
|
||||
}
|
||||
|
||||
|
||||
public void addSelfPermission(String permission) {
|
||||
addSelfPermission(permission, null, null);
|
||||
}
|
||||
|
||||
public void addSelfPermission(String permission, String server) {
|
||||
addSelfPermission(permission, server, null);
|
||||
}
|
||||
|
||||
public void addSelfPermission(String permission, String server, String world) {
|
||||
Permissions.backendWriter.addSelfPermission(name, type, permission, server, world);
|
||||
}
|
||||
|
||||
|
||||
public void removeSelfPermission(String permission) {
|
||||
removeSelfPermission(permission, null, null);
|
||||
}
|
||||
|
||||
public void removeSelfPermission(String permission, String server) {
|
||||
removeSelfPermission(permission, server, null);
|
||||
}
|
||||
|
||||
public void removeSelfPermission(String permission, String server, String world) {
|
||||
Permissions.backendWriter.removeSelfPermission(name, type, permission, server, world);
|
||||
}
|
||||
|
||||
public int getSelfPermissionsCount() {
|
||||
return getSelfPermissionsServerWorldKeys().stream()
|
||||
.mapToInt(key -> getSelfPermissions(key.server, key.world).size())
|
||||
.sum();
|
||||
}
|
||||
|
||||
public Set<ServerWorldKey> getSelfPermissionsServerWorldKeys() {
|
||||
return getBackendEntity().getSelfPermissionsServerWorldKeys();
|
||||
}
|
||||
|
||||
public List<String> getSelfPermissions() {
|
||||
return getSelfPermissions(null, null);
|
||||
}
|
||||
|
||||
public List<String> getSelfPermissions(String server) {
|
||||
return getSelfPermissions(server, null);
|
||||
}
|
||||
|
||||
public List<String> getSelfPermissions(String server, String world) {
|
||||
return getBackendEntity().getSelfPermissions(server, world);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof PermEntity o
|
||||
&& Objects.equals(name, o.name)
|
||||
&& type == o.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, type);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package fr.pandacube.lib.permissions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedGroup;
|
||||
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
|
||||
|
||||
public class PermGroup extends PermEntity {
|
||||
/* package */ PermGroup(String name) {
|
||||
super(name, EntityType.Group);
|
||||
}
|
||||
@Override
|
||||
protected CachedGroup getBackendEntity() {
|
||||
return Permissions.backendReader.getCachedGroup(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getInternalName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermGroup> getInheritances() {
|
||||
return fromCachedGroups(getBackendEntity().inheritances);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getInheritancesString() {
|
||||
return getBackendEntity().inheritances.stream()
|
||||
.map(cg -> cg.name)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return getBackendEntity().deflt;
|
||||
}
|
||||
|
||||
public void setDefault(boolean deflt) {
|
||||
Permissions.backendWriter.setGroupDefault(name, deflt);
|
||||
}
|
||||
|
||||
public void addInheritance(String group) {
|
||||
Permissions.backendWriter.addInheritance(name, type, group);
|
||||
}
|
||||
|
||||
public void addInheritance(PermGroup group) {
|
||||
addInheritance(group.name);
|
||||
}
|
||||
|
||||
public void removeInheritance(String group) {
|
||||
Permissions.backendWriter.removeInheritance(name, type, group);
|
||||
}
|
||||
|
||||
public void removeInheritance(PermGroup group) {
|
||||
removeInheritance(group.name);
|
||||
}
|
||||
|
||||
/* package */ static List<PermGroup> fromCachedGroups(List<CachedGroup> in) {
|
||||
return in.stream()
|
||||
.map(cg -> Permissions.getGroup(cg.name))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@@ -0,0 +1,100 @@
|
||||
package fr.pandacube.lib.permissions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedPlayer;
|
||||
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
|
||||
|
||||
public class PermPlayer extends PermEntity {
|
||||
private final UUID playerId;
|
||||
/* package */ PermPlayer(UUID id) {
|
||||
super(id.toString(), EntityType.User);
|
||||
playerId = id;
|
||||
}
|
||||
@Override
|
||||
protected CachedPlayer getBackendEntity() {
|
||||
return Permissions.backendReader.getCachedPlayer(playerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermGroup> getInheritances() {
|
||||
return PermGroup.fromCachedGroups(getBackendEntity().groups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getInheritancesString() {
|
||||
return getBackendEntity().groups.stream()
|
||||
.map(cg -> cg.name)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public UUID getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
private String cachedPlayerName;
|
||||
@Override
|
||||
public synchronized String getName() {
|
||||
if (cachedPlayerName == null)
|
||||
cachePlayerName();
|
||||
return cachedPlayerName;
|
||||
}
|
||||
private void cachePlayerName() {
|
||||
cachedPlayerName = Permissions.playerNameGetter.apply(playerId);
|
||||
if (cachedPlayerName == null)
|
||||
cachedPlayerName = playerId.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for {@link #getInheritances()}.
|
||||
*/
|
||||
public List<PermGroup> getGroups() {
|
||||
return getInheritances();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for {@link #getInheritances()}.
|
||||
*/
|
||||
public List<String> getGroupsString() {
|
||||
return getInheritancesString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public boolean isInGroup(String group) {
|
||||
return inheritsFromGroup(group, false);
|
||||
}
|
||||
|
||||
public boolean isUsingDefaultGroups() {
|
||||
return getBackendEntity().usingDefaultGroups;
|
||||
}
|
||||
|
||||
public void setGroup(String group) {
|
||||
Permissions.backendWriter.setInheritance(name, type, group);
|
||||
}
|
||||
|
||||
public void setGroup(PermGroup group) {
|
||||
setGroup(group.name);
|
||||
}
|
||||
|
||||
public void addGroup(String group) {
|
||||
Permissions.backendWriter.addInheritance(name, type, group);
|
||||
}
|
||||
|
||||
public void addGroup(PermGroup group) {
|
||||
addGroup(group.name);
|
||||
}
|
||||
|
||||
public void removeGroup(String group) {
|
||||
Permissions.backendWriter.removeInheritance(name, type, group);
|
||||
}
|
||||
|
||||
public void removeGroup(PermGroup group) {
|
||||
removeGroup(group.name);
|
||||
}
|
||||
}
|
@@ -0,0 +1,129 @@
|
||||
package fr.pandacube.lib.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.Constant;
|
||||
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<String, Boolean> { }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static class PermissionEvaluator extends AbstractEvaluator<Boolean> {
|
||||
|
||||
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 Constant TRUE = new Constant("true");
|
||||
private static final Constant FALSE = new Constant("false");
|
||||
|
||||
|
||||
private static final Parameters PARAMETERS;
|
||||
|
||||
static {
|
||||
PARAMETERS = new Parameters();
|
||||
PARAMETERS.add(NOT);
|
||||
PARAMETERS.add(AND);
|
||||
PARAMETERS.add(OR);
|
||||
PARAMETERS.add(TRUE);
|
||||
PARAMETERS.add(FALSE);
|
||||
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<Boolean> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean evaluate(Constant constant, Object evaluationContext) {
|
||||
if (constant == TRUE)
|
||||
return true;
|
||||
if (constant == FALSE)
|
||||
return false;
|
||||
return super.evaluate(constant, evaluationContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
package fr.pandacube.lib.permissions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
import fr.pandacube.lib.db.DB;
|
||||
import fr.pandacube.lib.db.DBConnection;
|
||||
import fr.pandacube.lib.db.DBException;
|
||||
import fr.pandacube.lib.util.Log;
|
||||
|
||||
public class Permissions {
|
||||
|
||||
/* package */ static PermissionsCachedBackendReader backendReader;
|
||||
/* package */ static PermissionsResolver resolver;
|
||||
/* package */ static PermissionsBackendWriter backendWriter;
|
||||
/* package */ static Function<UUID, String> playerNameGetter = UUID::toString;
|
||||
|
||||
/**
|
||||
* Initialize the permission system.
|
||||
* The connection to the database needs to be initialized first, using {@link DB#init(DBConnection, String)}.
|
||||
*/
|
||||
public static void init(Function<UUID, String> playerNameGetter) throws DBException {
|
||||
Permissions.playerNameGetter = playerNameGetter == null ? UUID::toString : playerNameGetter;
|
||||
if (backendReader != null)
|
||||
return;
|
||||
try {
|
||||
DB.initTable(SQLPermissions.class);
|
||||
backendReader = new PermissionsCachedBackendReader();
|
||||
resolver = new PermissionsResolver(backendReader);
|
||||
backendWriter = new PermissionsBackendWriter();
|
||||
} catch (Exception e) {
|
||||
backendReader = null;
|
||||
resolver = null;
|
||||
backendWriter = null;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static void addSpecialPermissions(SpecialPermission... specialPermissions) {
|
||||
checkInitialized();
|
||||
if (specialPermissions == null)
|
||||
return;
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearPlayerCache(UUID playerId) {
|
||||
checkInitialized();
|
||||
backendReader.clearPlayerCache(playerId);
|
||||
resolver.clearPlayerFromCache(playerId);
|
||||
}
|
||||
|
||||
public static void clearCache(Runnable then) {
|
||||
checkInitialized();
|
||||
backendReader.clearAndResetCacheAsync(() -> {
|
||||
resolver.clearCache();
|
||||
if (then != null)
|
||||
then.run();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static PermPlayer getPlayer(UUID playerId) {
|
||||
checkInitialized();
|
||||
return new PermPlayer(playerId);
|
||||
}
|
||||
|
||||
public static void precachePlayerAsync(UUID playerId) {
|
||||
checkInitialized();
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
backendReader.getCachedPlayer(playerId);
|
||||
} catch (RuntimeException e) {
|
||||
Log.warning("Can’t init player cache asynchronously: " + e.getMessage());
|
||||
}
|
||||
}, "Async permissions player cache loader");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
public static PermGroup getGroup(String name) {
|
||||
checkInitialized();
|
||||
return new PermGroup(name);
|
||||
}
|
||||
|
||||
public static List<PermGroup> getGroups() {
|
||||
checkInitialized();
|
||||
return PermGroup.fromCachedGroups(backendReader.getGroups());
|
||||
}
|
||||
|
||||
public static List<PermGroup> getDefaultGroups() {
|
||||
checkInitialized();
|
||||
return PermGroup.fromCachedGroups(backendReader.getDefaultGroups());
|
||||
}
|
||||
|
||||
public static List<String> getFullPermissionsList() {
|
||||
return backendReader.getFullPermissionsList();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,289 @@
|
||||
package fr.pandacube.lib.permissions;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import fr.pandacube.lib.db.DB;
|
||||
import fr.pandacube.lib.db.DBException;
|
||||
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
|
||||
|
||||
/* package */ class PermissionsBackendWriter {
|
||||
|
||||
|
||||
/* package */ void addSelfPermission(String name, EntityType type, String permission, String server, String world) {
|
||||
Objects.requireNonNull(name, "name cannot be null");
|
||||
Objects.requireNonNull(type, "type cannot be null");
|
||||
Objects.requireNonNull(permission, "permission cannot be null");
|
||||
Preconditions.checkArgument(world == null || server != null, "world not null but server is null");
|
||||
name = name.toLowerCase();
|
||||
permission = permission.toLowerCase();
|
||||
if (server != null) server = server.toLowerCase();
|
||||
if (world != null) world = world.toLowerCase();
|
||||
|
||||
if (hasEntry(name, type, "permissions", permission, server, world))
|
||||
throw new IllegalStateException("Permission already set");
|
||||
addEntry(name, type, "permissions", permission, server, world);
|
||||
}
|
||||
|
||||
/* package */ void removeSelfPermission(String name, EntityType type, String permission, String server, String world) {
|
||||
Objects.requireNonNull(name, "name cannot be null");
|
||||
Objects.requireNonNull(type, "type cannot be null");
|
||||
Objects.requireNonNull(permission, "permission cannot be null");
|
||||
Preconditions.checkArgument(world == null || server != null, "world not null but server is null");
|
||||
name = name.toLowerCase();
|
||||
permission = permission.toLowerCase();
|
||||
if (server != null) server = server.toLowerCase();
|
||||
if (world != null) world = world.toLowerCase();
|
||||
|
||||
if (!deleteEntry(name, type, "permissions", permission, server, world))
|
||||
throw new IllegalStateException("Permission was not set");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* package */ void setGroupDefault(String name, boolean deflt) {
|
||||
Objects.requireNonNull(name, "name cannot be null");
|
||||
name = name.toLowerCase();
|
||||
try {
|
||||
SQLPermissions entry = DB.getFirst(SQLPermissions.class,
|
||||
SQLPermissions.name.like(name)
|
||||
.and(SQLPermissions.type.eq(EntityType.Group.getCode()))
|
||||
.and(SQLPermissions.key.like("default"))
|
||||
);
|
||||
if (entry != null) {
|
||||
if (deflt) {
|
||||
// update just in case
|
||||
if ("true".equals(entry.get(SQLPermissions.value)))
|
||||
return;
|
||||
entry.set(SQLPermissions.value, "true");
|
||||
entry.save();
|
||||
}
|
||||
else {
|
||||
// delete
|
||||
entry.delete();
|
||||
}
|
||||
}
|
||||
else if (deflt) {
|
||||
// insert
|
||||
addEntry(name, EntityType.Group, "default", "true", null, null);
|
||||
}
|
||||
} catch (DBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* package */ void setSelfPrefix(String name, EntityType type, String prefix) {
|
||||
Objects.requireNonNull(name, "name cannot be null");
|
||||
Objects.requireNonNull(type, "type cannot be null");
|
||||
name = name.toLowerCase();
|
||||
|
||||
try {
|
||||
SQLPermissions entry = DB.getFirst(SQLPermissions.class,
|
||||
SQLPermissions.name.like(name)
|
||||
.and(SQLPermissions.type.eq(type.getCode()))
|
||||
.and(SQLPermissions.key.like("prefix"))
|
||||
);
|
||||
if (entry != null) {
|
||||
if (prefix != null) {
|
||||
// update
|
||||
entry.set(SQLPermissions.value, prefix);
|
||||
entry.save();
|
||||
}
|
||||
else {
|
||||
// delete
|
||||
entry.delete();
|
||||
}
|
||||
}
|
||||
else if (prefix != null) {
|
||||
// insert
|
||||
addEntry(name, type, "prefix", prefix, null, null);
|
||||
}
|
||||
} catch (DBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void setSelfSuffix(String name, EntityType type, String suffix) {
|
||||
Objects.requireNonNull(name, "name cannot be null");
|
||||
Objects.requireNonNull(type, "type cannot be null");
|
||||
name = name.toLowerCase();
|
||||
|
||||
try {
|
||||
SQLPermissions entry = DB.getFirst(SQLPermissions.class,
|
||||
SQLPermissions.name.like(name)
|
||||
.and(SQLPermissions.type.eq(type.getCode()))
|
||||
.and(SQLPermissions.key.like("suffix"))
|
||||
);
|
||||
if (entry != null) {
|
||||
if (suffix != null) {
|
||||
// update
|
||||
entry.set(SQLPermissions.value, suffix);
|
||||
entry.save();
|
||||
}
|
||||
else {
|
||||
// delete
|
||||
entry.delete();
|
||||
}
|
||||
}
|
||||
else if (suffix != null) {
|
||||
// insert
|
||||
addEntry(name, type, "suffix", suffix, null, null);
|
||||
}
|
||||
} catch (DBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* package */ void addInheritance(String name, EntityType type, String inheritance) {
|
||||
Objects.requireNonNull(name, "name cannot be null");
|
||||
Objects.requireNonNull(type, "type cannot be null");
|
||||
Objects.requireNonNull(inheritance, "inheritance cannot be null");
|
||||
name = name.toLowerCase();
|
||||
inheritance = inheritance.toLowerCase();
|
||||
String key = type == EntityType.Group ? "inheritances" : "groups";
|
||||
|
||||
try {
|
||||
SQLPermissions entry = DB.getFirst(SQLPermissions.class,
|
||||
SQLPermissions.name.like(name)
|
||||
.and(SQLPermissions.type.eq(type.getCode()))
|
||||
.and(SQLPermissions.key.like(key))
|
||||
.and(SQLPermissions.value.like(inheritance))
|
||||
);
|
||||
if (entry != null)
|
||||
throw new IllegalStateException("Inheritance already set");
|
||||
addEntry(name, type, key, inheritance, null, null);
|
||||
} catch (DBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* package */ void removeInheritance(String name, EntityType type, String inheritance) {
|
||||
Objects.requireNonNull(name, "name cannot be null");
|
||||
Objects.requireNonNull(type, "type cannot be null");
|
||||
Objects.requireNonNull(inheritance, "inheritance cannot be null");
|
||||
name = name.toLowerCase();
|
||||
inheritance = inheritance.toLowerCase();
|
||||
String key = type == EntityType.Group ? "inheritances" : "groups";
|
||||
|
||||
try {
|
||||
int deleted = DB.delete(SQLPermissions.class,
|
||||
SQLPermissions.name.like(name)
|
||||
.and(SQLPermissions.type.eq(type.getCode()))
|
||||
.and(SQLPermissions.key.like(key))
|
||||
.and(SQLPermissions.value.like(inheritance))
|
||||
);
|
||||
if (deleted == 0)
|
||||
throw new IllegalStateException("Inheritance was not set");
|
||||
} catch (DBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void setInheritance(String name, EntityType type, String inheritance) {
|
||||
Objects.requireNonNull(name, "name cannot be null");
|
||||
Objects.requireNonNull(type, "type cannot be null");
|
||||
Objects.requireNonNull(inheritance, "inheritance cannot be null");
|
||||
name = name.toLowerCase();
|
||||
inheritance = inheritance.toLowerCase();
|
||||
String key = type == EntityType.Group ? "inheritances" : "groups";
|
||||
|
||||
try {
|
||||
DB.delete(SQLPermissions.class,
|
||||
SQLPermissions.name.like(name)
|
||||
.and(SQLPermissions.type.eq(type.getCode()))
|
||||
.and(SQLPermissions.key.like(key))
|
||||
);
|
||||
addEntry(name, type, key, inheritance, null, null);
|
||||
} catch (DBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean deleteEntry(String name, EntityType type,
|
||||
String key, String value,
|
||||
String server, String world) {
|
||||
try {
|
||||
return DB.delete(SQLPermissions.class,
|
||||
SQLPermissions.name.like(name)
|
||||
.and(SQLPermissions.type.eq(type.getCode()))
|
||||
.and(SQLPermissions.key.like(key))
|
||||
.and(SQLPermissions.value.like(value))
|
||||
.and(server == null ? SQLPermissions.server.isNull() : SQLPermissions.server.like(server))
|
||||
.and(world == null ? SQLPermissions.world.isNull() : SQLPermissions.world.like(world))
|
||||
) >= 1;
|
||||
} catch (DBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasEntry(String name, EntityType type,
|
||||
String key, String value,
|
||||
String server, String world) {
|
||||
return getEntry(name, type, key, value, server, world) != null;
|
||||
}
|
||||
|
||||
private SQLPermissions getEntry(String name, EntityType type,
|
||||
String key, String value,
|
||||
String server, String world) {
|
||||
try {
|
||||
return DB.getFirst(SQLPermissions.class,
|
||||
SQLPermissions.name.like(name)
|
||||
.and(SQLPermissions.type.eq(type.getCode()))
|
||||
.and(SQLPermissions.key.like(key))
|
||||
.and(SQLPermissions.value.like(value))
|
||||
.and(server == null ? SQLPermissions.server.isNull() : SQLPermissions.server.like(server))
|
||||
.and(world == null ? SQLPermissions.world.isNull() : SQLPermissions.world.like(world))
|
||||
);
|
||||
} catch (DBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addEntry(String name, EntityType type,
|
||||
String key, String value,
|
||||
String server, String world) {
|
||||
SQLPermissions entry = new SQLPermissions()
|
||||
.set(SQLPermissions.name, name)
|
||||
.set(SQLPermissions.type, type.getCode())
|
||||
.set(SQLPermissions.key, key)
|
||||
.set(SQLPermissions.value, value)
|
||||
.set(SQLPermissions.server, server)
|
||||
.set(SQLPermissions.world, world);
|
||||
try {
|
||||
entry.save();
|
||||
} catch (DBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,304 @@
|
||||
package fr.pandacube.lib.permissions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
import fr.pandacube.lib.db.DB;
|
||||
import fr.pandacube.lib.db.DBException;
|
||||
import fr.pandacube.lib.db.SQLElementList;
|
||||
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
|
||||
import fr.pandacube.lib.util.Log;
|
||||
|
||||
/* package */ class PermissionsCachedBackendReader
|
||||
{
|
||||
/* package */ PermissionsCachedBackendReader() throws DBException {
|
||||
clearAndResetCache();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private final Cache<UUID, CachedPlayer> usersCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
private Set<String> fullPermissionsList = new TreeSet<>();
|
||||
|
||||
/* package */ synchronized List<String> getFullPermissionsList() {
|
||||
return new ArrayList<>(fullPermissionsList);
|
||||
}
|
||||
|
||||
/* package */ synchronized void clearPlayerCache(UUID playerId) {
|
||||
usersCache.invalidate(playerId);
|
||||
}
|
||||
|
||||
/* package */ synchronized CachedPlayer getCachedPlayer(UUID playerId) {
|
||||
try {
|
||||
return usersCache.get(playerId, () -> {
|
||||
try {
|
||||
return initPlayer(playerId);
|
||||
} catch (DBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private CachedPlayer initPlayer(UUID playerId) throws DBException {
|
||||
|
||||
SQLElementList<SQLPermissions> playerData = DB.getAll(SQLPermissions.class,
|
||||
SQLPermissions.type.eq(EntityType.User.getCode())
|
||||
.and(SQLPermissions.name.like(playerId.toString()))
|
||||
);
|
||||
|
||||
Map<String, List<SQLPermissions>> playerRawData = playerData.stream()
|
||||
.collect(
|
||||
Collectors.groupingBy(e -> e.get(SQLPermissions.key),
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList())
|
||||
);
|
||||
|
||||
String playerSelfPrefix = null;
|
||||
if (playerRawData.containsKey("prefix")) {
|
||||
playerSelfPrefix = playerRawData.get("prefix").stream()
|
||||
.map(e -> e.get(SQLPermissions.value))
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
String playerSelfSuffix = null;
|
||||
if (playerRawData.containsKey("suffix")) {
|
||||
playerSelfSuffix = playerRawData.get("suffix").stream()
|
||||
.map(e -> e.get(SQLPermissions.value))
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
Map<ServerWorldKey, List<String>> playerSelfPerms = new LinkedHashMap<>();
|
||||
if (playerRawData.containsKey("permissions")) {
|
||||
playerSelfPerms = playerRawData.get("permissions").stream()
|
||||
.peek(e -> {
|
||||
String value = e.get(SQLPermissions.value);
|
||||
fullPermissionsList.add(value.substring(value.startsWith("-") ? 1 : 0).toLowerCase());
|
||||
})
|
||||
.collect(Collectors.groupingBy(e -> new ServerWorldKey(e.get(SQLPermissions.server), e.get(SQLPermissions.world)),
|
||||
LinkedHashMap::new,
|
||||
Collectors.mapping(e -> e.get(SQLPermissions.value),
|
||||
Collectors.toList()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
CachedPlayer player = new CachedPlayer(playerId, playerSelfPrefix, playerSelfSuffix, playerSelfPerms);
|
||||
|
||||
if (playerRawData.containsKey("groups")) {
|
||||
playerRawData.get("groups").stream()
|
||||
.map(e -> e.get(SQLPermissions.value))
|
||||
.forEach(g -> player.groups.add(getCachedGroup(g)));
|
||||
}
|
||||
|
||||
if (player.groups.isEmpty()) {
|
||||
player.usingDefaultGroups = true;
|
||||
player.groups.addAll(getDefaultGroups());
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private final Map<String, CachedGroup> groupsCache = new LinkedHashMap<>();
|
||||
private boolean cacheIsUpdating = false;
|
||||
|
||||
|
||||
/* package */ synchronized CachedGroup getCachedGroup(String group) {
|
||||
return groupsCache.getOrDefault(group, new CachedGroup(group, null, null, false, new LinkedHashMap<>()));
|
||||
}
|
||||
|
||||
/* package */ synchronized List<CachedGroup> getDefaultGroups() {
|
||||
return groupsCache.values().stream()
|
||||
.filter(g -> g.deflt)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<CachedGroup> getGroups() {
|
||||
return new ArrayList<>(groupsCache.values());
|
||||
}
|
||||
|
||||
/* package */ void clearAndResetCacheAsync(Runnable then) {
|
||||
synchronized (this) {
|
||||
if (cacheIsUpdating)
|
||||
return;
|
||||
}
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
clearAndResetCache();
|
||||
} catch (Throwable e) {
|
||||
Log.severe(e);
|
||||
}
|
||||
if (then != null)
|
||||
then.run();
|
||||
}, "Permissions Backend Group Cache Updater");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void clearAndResetCache() throws DBException {
|
||||
synchronized (this) {
|
||||
if (cacheIsUpdating)
|
||||
return;
|
||||
cacheIsUpdating = true;
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, CachedGroup> newData = new LinkedHashMap<>();
|
||||
Set<String> newFullPermissionsList = new TreeSet<>();
|
||||
|
||||
SQLElementList<SQLPermissions> groupData = DB.getAll(SQLPermissions.class, SQLPermissions.type.eq(EntityType.Group.getCode()));
|
||||
|
||||
Map<String, Map<String, List<SQLPermissions>>> groupsRawData = groupData.stream()
|
||||
.collect(
|
||||
Collectors.groupingBy(e -> e.get(SQLPermissions.name),
|
||||
LinkedHashMap::new,
|
||||
Collectors.groupingBy(e -> e.get(SQLPermissions.key),
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList())
|
||||
)
|
||||
);
|
||||
|
||||
for (String groupName : groupsRawData.keySet()) {
|
||||
initGroup(groupName, groupsRawData, newData, newFullPermissionsList);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
groupsCache.clear();
|
||||
groupsCache.putAll(newData);
|
||||
cacheIsUpdating = false;
|
||||
usersCache.invalidateAll();
|
||||
fullPermissionsList = newFullPermissionsList;
|
||||
}
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
cacheIsUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void initGroup(String groupName, Map<String, Map<String, List<SQLPermissions>>> groupsRawData, Map<String, CachedGroup> newData, Set<String> newFullPermissionsList) {
|
||||
if (newData.containsKey(groupName))
|
||||
return;
|
||||
|
||||
Map<String, List<SQLPermissions>> groupRawData = groupsRawData.getOrDefault(groupName, new LinkedHashMap<>());
|
||||
|
||||
boolean groupDefault = groupRawData.containsKey("default")
|
||||
&& "true".equals(groupRawData.get("default").get(0).get(SQLPermissions.value));
|
||||
|
||||
String groupSelfPrefix = null;
|
||||
if (groupRawData.containsKey("prefix")) {
|
||||
groupSelfPrefix = groupRawData.get("prefix").stream()
|
||||
.map(e -> e.get(SQLPermissions.value))
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
String groupSelfSuffix = null;
|
||||
if (groupRawData.containsKey("suffix")) {
|
||||
groupSelfSuffix = groupRawData.get("suffix").stream()
|
||||
.map(e -> e.get(SQLPermissions.value))
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
Map<ServerWorldKey, List<String>> groupSelfPerms = new LinkedHashMap<>();
|
||||
if (groupRawData.containsKey("permissions")) {
|
||||
groupSelfPerms = groupRawData.get("permissions").stream()
|
||||
.peek(e -> {
|
||||
String value = e.get(SQLPermissions.value);
|
||||
newFullPermissionsList.add(value.substring(value.startsWith("-") ? 1 : 0));
|
||||
})
|
||||
.collect(Collectors.groupingBy(e -> new ServerWorldKey(e.get(SQLPermissions.server), e.get(SQLPermissions.world)),
|
||||
LinkedHashMap::new,
|
||||
Collectors.mapping(e -> e.get(SQLPermissions.value),
|
||||
Collectors.toList()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
CachedGroup group = new CachedGroup(groupName, groupSelfPrefix, groupSelfSuffix, groupDefault, groupSelfPerms);
|
||||
|
||||
newData.put(groupName, group);
|
||||
|
||||
|
||||
if (groupRawData.containsKey("inheritances")) {
|
||||
groupRawData.get("inheritances").stream()
|
||||
.map(e -> e.get(SQLPermissions.value))
|
||||
.forEach(g -> {
|
||||
initGroup(g, groupsRawData, newData, newFullPermissionsList);
|
||||
group.inheritances.add(newData.get(g));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* package */ static abstract class CachedEntity {
|
||||
public final String name;
|
||||
private final String selfPrefix, selfSuffix;
|
||||
private final Map<ServerWorldKey, List<String>> selfPermissions;
|
||||
|
||||
private CachedEntity(String n, String p, String s,
|
||||
Map<ServerWorldKey, List<String>> perms) {
|
||||
name = n; selfPrefix = p; selfSuffix = s; selfPermissions = perms;
|
||||
}
|
||||
|
||||
/* package */ List<String> getSelfPermissions(String server, String world) {
|
||||
return selfPermissions.getOrDefault(new ServerWorldKey(server, world), new ArrayList<>());
|
||||
}
|
||||
|
||||
/* package */ Set<ServerWorldKey> getSelfPermissionsServerWorldKeys() {
|
||||
return new TreeSet<>(selfPermissions.keySet());
|
||||
}
|
||||
|
||||
/* package */ String getSelfPrefix() {
|
||||
return selfPrefix;
|
||||
}
|
||||
|
||||
/* package */ String getSelfSuffix() {
|
||||
return selfSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ static class CachedPlayer extends CachedEntity {
|
||||
public final UUID playerId;
|
||||
public final List<CachedGroup> groups = new ArrayList<>();
|
||||
public boolean usingDefaultGroups = false;
|
||||
private CachedPlayer(UUID pl, String p, String s,
|
||||
Map<ServerWorldKey, List<String>> perms) {
|
||||
super(pl.toString(), p, s, perms);
|
||||
playerId = pl;
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ static class CachedGroup extends CachedEntity {
|
||||
public final boolean deflt;
|
||||
public final List<CachedGroup> inheritances = new ArrayList<>();
|
||||
private CachedGroup(String n, String p, String s,
|
||||
boolean dflt, Map<ServerWorldKey, List<String>> perms) {
|
||||
super(n, p, s, perms);
|
||||
deflt = dflt;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,564 @@
|
||||
package fr.pandacube.lib.permissions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
import fr.pandacube.lib.chat.Chat;
|
||||
import fr.pandacube.lib.chat.ChatUtil;
|
||||
import fr.pandacube.lib.chat.ChatUtil.DisplayTreeNode;
|
||||
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedEntity;
|
||||
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedGroup;
|
||||
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedPlayer;
|
||||
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
|
||||
import fr.pandacube.lib.util.Log;
|
||||
|
||||
public class PermissionsResolver {
|
||||
|
||||
private final PermissionsCachedBackendReader backendReader;
|
||||
|
||||
/* package */ PermissionsResolver(PermissionsCachedBackendReader b) {
|
||||
backendReader = b;
|
||||
}
|
||||
|
||||
/* package */ void clearPlayerFromCache(UUID player) {
|
||||
String playerId = player.toString();
|
||||
synchronized (effectivePermissionsCache) {
|
||||
effectivePermissionsCache.asMap().keySet().removeIf(k -> k.type == EntityType.User && playerId.equals(k.name));
|
||||
}
|
||||
synchronized (effectivePermissionsListCache) {
|
||||
effectivePermissionsListCache.asMap().keySet().removeIf(k -> k.type == EntityType.User && playerId.equals(k.name));
|
||||
}
|
||||
synchronized (effectiveDataCache) {
|
||||
effectiveDataCache.asMap().keySet().removeIf(k -> k.type == EntityType.User && playerId.equals(k.name));
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void clearCache() {
|
||||
effectivePermissionsCache.invalidateAll();
|
||||
effectivePermissionsListCache.invalidateAll();
|
||||
effectiveDataCache.invalidateAll();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* package */ String getEffectivePrefix(String name, EntityType type) {
|
||||
return getEffectiveData(name, type, DataType.PREFIX);
|
||||
}
|
||||
/* package */ String getEffectiveSuffix(String name, EntityType type) {
|
||||
return getEffectiveData(name, type, DataType.SUFFIX);
|
||||
}
|
||||
|
||||
/* package */ DisplayTreeNode debugPrefix(String name, EntityType type) {
|
||||
return debugData(name, type, DataType.PREFIX);
|
||||
}
|
||||
/* package */ DisplayTreeNode debugSuffix(String name, EntityType type) {
|
||||
return debugData(name, type, DataType.SUFFIX);
|
||||
}
|
||||
|
||||
private final Cache<DataCacheKey, String> effectiveDataCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
private String getEffectiveData(String name, EntityType type, DataType dataType) {
|
||||
Objects.requireNonNull(name, "name can’t be null");
|
||||
Objects.requireNonNull(type, "type can’t be null");
|
||||
|
||||
try {
|
||||
return effectiveDataCache.get(new DataCacheKey(name, type, dataType), () -> resolveData(name, type, dataType));
|
||||
} catch (ExecutionException e) {
|
||||
Log.severe(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private DisplayTreeNode debugData(String name, EntityType type, DataType dataType) {
|
||||
CachedEntity entity = (type == EntityType.User)
|
||||
? backendReader.getCachedPlayer(UUID.fromString(name))
|
||||
: backendReader.getCachedGroup(name);
|
||||
return resolveData(entity, dataType).toDisplayTreeNode();
|
||||
}
|
||||
|
||||
private String resolveData(String name, EntityType type, DataType dataType) {
|
||||
CachedEntity entity = (type == EntityType.User)
|
||||
? backendReader.getCachedPlayer(UUID.fromString(name))
|
||||
: backendReader.getCachedGroup(name);
|
||||
DataResolutionNode resolutionResult = resolveData(entity, dataType);
|
||||
|
||||
if (resolutionResult.conflict) {
|
||||
Log.warning("For data " + dataType + ":\n"
|
||||
+ ChatUtil.treeView(resolutionResult.toDisplayTreeNode(), true).stream()
|
||||
.map(Chat::getLegacyText)
|
||||
.collect(Collectors.joining(ChatColor.RESET + "\n")));
|
||||
}
|
||||
|
||||
return resolutionResult.result != null ? resolutionResult.result : "";
|
||||
}
|
||||
|
||||
private DataResolutionNode resolveData(CachedEntity entity, DataType dataType) {
|
||||
// self data
|
||||
DataResolutionNode resolutionNode = new DataResolutionNode(entity, dataType.getter.apply(entity), null);
|
||||
if (resolutionNode.result != null) {
|
||||
return resolutionNode;
|
||||
}
|
||||
|
||||
// check inheritances data
|
||||
List<CachedGroup> inheritances = resolutionNode.entity instanceof CachedPlayer
|
||||
? ((CachedPlayer)resolutionNode.entity).groups
|
||||
: ((CachedGroup)resolutionNode.entity).inheritances;
|
||||
|
||||
List<DataResolutionNode> inheritedResults = new ArrayList<>(inheritances.size());
|
||||
|
||||
for (CachedGroup inherited : inheritances) {
|
||||
inheritedResults.add(resolveData(inherited, dataType));
|
||||
}
|
||||
|
||||
resolutionNode.inheritances.addAll(inheritedResults);
|
||||
|
||||
if (inheritedResults.stream().anyMatch(g -> g.conflict))
|
||||
resolutionNode.conflict = true;
|
||||
|
||||
Set<String> inheritedPermissions = inheritedResults.stream()
|
||||
.map(g -> g.result)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (inheritedPermissions.size() == 1)
|
||||
resolutionNode.result = inheritedPermissions.iterator().next();
|
||||
else if (inheritedPermissions.size() > 1) {
|
||||
resolutionNode.conflictMessage = (resolutionNode.conflictMessage == null ? "" : (resolutionNode.conflictMessage + " ; "))
|
||||
+ "Unsolvable conflict between inherited groups";
|
||||
resolutionNode.conflict = true;
|
||||
}
|
||||
|
||||
return resolutionNode;
|
||||
}
|
||||
|
||||
|
||||
private static class DataResolutionNode {
|
||||
final CachedEntity entity;
|
||||
String result;
|
||||
String conflictMessage;
|
||||
boolean conflict;
|
||||
final List<DataResolutionNode> inheritances = new ArrayList<>();
|
||||
public DataResolutionNode(CachedEntity e, String r, String c) {
|
||||
entity = e; result = r; conflictMessage = c;
|
||||
conflict = c != null;
|
||||
}
|
||||
|
||||
public DisplayTreeNode toDisplayTreeNode() {
|
||||
Chat c = Chat.text(entity.name);
|
||||
if (result == null)
|
||||
c.then(Chat.text(" (non défini)").gray());
|
||||
else
|
||||
c.thenLegacyText(" \"" + ChatColor.RESET + result + ChatColor.RESET + "\"");
|
||||
if (conflictMessage != null)
|
||||
c.thenFailure(" " + conflictMessage);
|
||||
DisplayTreeNode node = new DisplayTreeNode(c);
|
||||
|
||||
if (result == null && !conflict && !inheritances.isEmpty()) {
|
||||
// there is nothing interesting to show on current or subnode
|
||||
node.children.add(new DisplayTreeNode(Chat.text("(Inheritances hidden for brevety)").darkGray().italic()));
|
||||
return node;
|
||||
}
|
||||
|
||||
inheritances.forEach(n -> node.children.add(n.toDisplayTreeNode()));
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DataCacheKey {
|
||||
final String name;
|
||||
final EntityType type;
|
||||
final DataType dataType;
|
||||
DataCacheKey(String n, EntityType t, DataType d) {
|
||||
name = n; type = t; dataType = d;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof DataCacheKey o
|
||||
&& Objects.equals(name, o.name)
|
||||
&& Objects.equals(type, o.type)
|
||||
&& dataType == o.dataType;
|
||||
}
|
||||
}
|
||||
|
||||
private enum DataType {
|
||||
PREFIX(CachedEntity::getSelfPrefix),
|
||||
SUFFIX(CachedEntity::getSelfSuffix);
|
||||
|
||||
private final CachedEntityGetter<String> getter;
|
||||
DataType(CachedEntityGetter<String> g) {
|
||||
getter = g;
|
||||
}
|
||||
}
|
||||
|
||||
private interface CachedEntityGetter<R> {
|
||||
R apply(CachedEntity a);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private final Cache<PermCacheKey, Map<String, Boolean>> effectivePermissionsListCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
/* package */ Map<String, Boolean> getEffectivePermissionList(String name, EntityType type, String server, String world) {
|
||||
Objects.requireNonNull(name, "name can’t be null");
|
||||
Objects.requireNonNull(type, "type can’t be null");
|
||||
Preconditions.checkArgument(world == null || server != null, "world not null but server is null");
|
||||
|
||||
String fServer = server == null ? null : server.toLowerCase();
|
||||
String fWorld = world == null ? null : world.toLowerCase();
|
||||
|
||||
try {
|
||||
return effectivePermissionsListCache.get(new PermCacheKey(name, type, null, fServer, fWorld), () -> {
|
||||
Map<String, Boolean> permList = new LinkedHashMap<>();
|
||||
|
||||
for (String perm : backendReader.getFullPermissionsList()) {
|
||||
Boolean has = getEffectivePermission(name, type, perm, fServer, fWorld);
|
||||
if (has == null)
|
||||
continue;
|
||||
permList.put(perm.toLowerCase(), has);
|
||||
}
|
||||
|
||||
return permList;
|
||||
});
|
||||
} catch (ExecutionException e) {
|
||||
Log.severe(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Cache<PermCacheKey, PermState> effectivePermissionsCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
/* package */ Boolean getEffectivePermission(String name, EntityType type, String permission, String server, String world) {
|
||||
Objects.requireNonNull(name, "name can’t be null");
|
||||
Objects.requireNonNull(type, "type can’t be null");
|
||||
Objects.requireNonNull(permission, "permission can’t be null");
|
||||
Preconditions.checkArgument(world == null || server != null, "world not null but server is null");
|
||||
|
||||
boolean reversed = false;
|
||||
if (permission.startsWith("-")) {
|
||||
permission = permission.substring(1);
|
||||
reversed = true;
|
||||
}
|
||||
|
||||
String fPermission = permission.toLowerCase();
|
||||
String fServer = server == null ? null : server.toLowerCase();
|
||||
String fWorld = world == null ? null : world.toLowerCase();
|
||||
try {
|
||||
Boolean resolved = effectivePermissionsCache.get(new PermCacheKey(name, type, fPermission, fServer, fWorld),
|
||||
() -> resolvePermission(name, type, fPermission, fServer, fWorld)
|
||||
).value;
|
||||
return resolved == null ? null : (reversed != resolved);
|
||||
} catch (ExecutionException e) {
|
||||
Log.severe(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ DisplayTreeNode debugPermission(String name, EntityType type, String permission, String server, String world) {
|
||||
CachedEntity entity = (type == EntityType.User)
|
||||
? backendReader.getCachedPlayer(UUID.fromString(name))
|
||||
: backendReader.getCachedGroup(name);
|
||||
return resolvePermission(entity, permission, server, world, true).toDisplayTreeNode();
|
||||
}
|
||||
|
||||
private PermState resolvePermission(String name, EntityType type, String permission, String server, String world) {
|
||||
|
||||
CachedEntity entity = (type == EntityType.User)
|
||||
? backendReader.getCachedPlayer(UUID.fromString(name))
|
||||
: backendReader.getCachedGroup(name);
|
||||
PermResolutionNode resolutionResult = resolvePermission(entity, permission, server, world, true);
|
||||
|
||||
if (resolutionResult.conflict) {
|
||||
Log.warning("For permission " + permission + ":\n"
|
||||
+ ChatUtil.treeView(resolutionResult.toDisplayTreeNode(), true).stream()
|
||||
.map(Chat::getLegacyText)
|
||||
.collect(Collectors.joining(ChatColor.RESET + "\n")));
|
||||
}
|
||||
|
||||
return resolutionResult.result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private PermResolutionNode resolvePermission(CachedEntity entity, String permission, String server, String world, boolean checkInheritance) {
|
||||
|
||||
// self and special permissions
|
||||
PermResolutionNode resolutionNode = resolveSelfPermission(entity, permission, server, world);
|
||||
if (resolutionNode.result != PermState.UNDEFINED) {
|
||||
return resolutionNode;
|
||||
}
|
||||
|
||||
List<CachedGroup> inheritances = resolutionNode.entity instanceof CachedPlayer
|
||||
? ((CachedPlayer)resolutionNode.entity).groups
|
||||
: ((CachedGroup)resolutionNode.entity).inheritances;
|
||||
|
||||
|
||||
// check no-world/no-server permissions
|
||||
if (server != null) {
|
||||
PermResolutionNode noWNoSNode = resolvePermission(entity, permission, world != null ? server : null, null, false);
|
||||
resolutionNode.inheritances.add(noWNoSNode);
|
||||
if (noWNoSNode.conflict)
|
||||
resolutionNode.conflict = true;
|
||||
if (noWNoSNode.result != PermState.UNDEFINED) {
|
||||
resolutionNode.result = noWNoSNode.result;
|
||||
return resolutionNode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!checkInheritance)
|
||||
return resolutionNode;
|
||||
|
||||
// check inheritances permissions
|
||||
List<PermResolutionNode> inheritedResults = new ArrayList<>(inheritances.size() + 1);
|
||||
|
||||
for (CachedGroup inherited : inheritances) {
|
||||
inheritedResults.add(resolvePermission(inherited, permission, server, world, true));
|
||||
}
|
||||
|
||||
resolutionNode.inheritances.addAll(inheritedResults);
|
||||
|
||||
if (inheritedResults.stream().anyMatch(g -> g.conflict))
|
||||
resolutionNode.conflict = true;
|
||||
|
||||
Set<PermState> inheritedPermissions = inheritedResults.stream()
|
||||
.map(g -> g.result)
|
||||
.collect(Collectors.toCollection(() -> EnumSet.noneOf(PermState.class)));
|
||||
|
||||
boolean inheritancesGranted = inheritedPermissions.contains(PermState.GRANTED);
|
||||
boolean inheritancesRevoqued = inheritedPermissions.contains(PermState.REVOQUED);
|
||||
if (inheritancesGranted != inheritancesRevoqued) {
|
||||
resolutionNode.result = inheritancesGranted ? PermState.GRANTED : PermState.REVOQUED;
|
||||
}
|
||||
else if (inheritancesGranted) {
|
||||
resolutionNode.conflictMessage = (resolutionNode.conflictMessage == null ? "" : (resolutionNode.conflictMessage + " ; "))
|
||||
+ "Unsolvable conflict between inheritances";
|
||||
resolutionNode.conflict = true;
|
||||
}
|
||||
|
||||
return resolutionNode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* package */ final List<SpecialPermission> specialPermissions = new ArrayList<>();
|
||||
|
||||
|
||||
|
||||
private PermResolutionNode resolveSelfPermission(CachedEntity entity, String permission, String server, String world) {
|
||||
// special permissions
|
||||
PermState result = PermState.UNDEFINED;
|
||||
String conflict = null;
|
||||
List<ParsedSelfPermission> foundPerms = null;
|
||||
|
||||
/*
|
||||
* Check for special permissions
|
||||
*/
|
||||
if (entity instanceof CachedPlayer) {
|
||||
PermPlayer permP = Permissions.getPlayer(((CachedPlayer) entity).playerId);
|
||||
ParsedSelfPermission specialPerm = null;
|
||||
|
||||
for (SpecialPermission spePerm : specialPermissions) {
|
||||
if (spePerm.match().match(permission)) {
|
||||
boolean res = spePerm.tester().test(permP, permission, server, world);
|
||||
specialPerm = new ParsedSelfPermission(permission, res, PermType.SPECIAL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (specialPerm != null) {
|
||||
result = PermState.of(specialPerm.result);
|
||||
foundPerms = new ArrayList<>();
|
||||
foundPerms.add(specialPerm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (result == PermState.UNDEFINED) {
|
||||
foundPerms = entity.getSelfPermissions(server, world).stream()
|
||||
.map(p -> {
|
||||
ParsedSelfPermission resNode = null;
|
||||
if (p.equalsIgnoreCase(permission))
|
||||
resNode = new ParsedSelfPermission(p, true, PermType.EXPLICIT);
|
||||
else if (p.equalsIgnoreCase("-" + permission))
|
||||
resNode = new ParsedSelfPermission(p, false, PermType.EXPLICIT);
|
||||
else if (p.endsWith("*") && permission.startsWith(p.substring(0, p.length() - 1)))
|
||||
resNode = new ParsedSelfPermission(p, true, PermType.WILDCARD);
|
||||
else if (p.endsWith("*") && p.startsWith("-") && permission.startsWith(p.substring(1, p.length() - 1)))
|
||||
resNode = new ParsedSelfPermission(p, false, PermType.WILDCARD);
|
||||
return resNode;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
boolean explicitGranted = foundPerms.stream()
|
||||
.anyMatch(n -> n.type == PermType.EXPLICIT && n.result == Boolean.TRUE);
|
||||
boolean explicitRevoqued = foundPerms.stream()
|
||||
.anyMatch(n -> n.type == PermType.EXPLICIT && n.result == Boolean.FALSE);
|
||||
|
||||
boolean wildcardGranted = foundPerms.stream()
|
||||
.anyMatch(n -> n.type == PermType.WILDCARD && n.result == Boolean.TRUE);
|
||||
boolean wildcardRevoqued = foundPerms.stream()
|
||||
.anyMatch(n -> n.type == PermType.WILDCARD && n.result == Boolean.FALSE);
|
||||
|
||||
if (explicitGranted != explicitRevoqued) {
|
||||
result = PermState.of(explicitGranted);
|
||||
if (!wildcardGranted && !wildcardRevoqued) { }
|
||||
else if (wildcardGranted && wildcardRevoqued) {
|
||||
conflict = "Self explicit permission defined but conflict between self wildcard permissions";
|
||||
}
|
||||
else if (explicitGranted == wildcardGranted) {
|
||||
conflict = "Unnecessary explicit permission already granted by self wildcard permissions"; // redundent explicit perm
|
||||
}
|
||||
}
|
||||
else if (explicitGranted) {
|
||||
conflict = "Unsolvable conflit between explicit permissions";
|
||||
}
|
||||
else if (wildcardGranted != wildcardRevoqued) {
|
||||
result = PermState.of(wildcardGranted);
|
||||
}
|
||||
else if (wildcardGranted) {
|
||||
conflict = "Unsolvable conflit between wildcard permissions";
|
||||
}
|
||||
}
|
||||
|
||||
PermResolutionNode node = new PermResolutionNode(entity, server, world, result, conflict);
|
||||
node.selfPermissions = foundPerms;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static class PermResolutionNode {
|
||||
final CachedEntity entity;
|
||||
final String server, world;
|
||||
PermState result;
|
||||
String conflictMessage;
|
||||
boolean conflict;
|
||||
List<ParsedSelfPermission> selfPermissions = new ArrayList<>();
|
||||
final List<PermResolutionNode> inheritances = new ArrayList<>();
|
||||
public PermResolutionNode(CachedEntity e, String s, String w, PermState r, String c) {
|
||||
entity = e; server = s; world = w; result = r; conflictMessage = c;
|
||||
conflict = c != null;
|
||||
}
|
||||
|
||||
public DisplayTreeNode toDisplayTreeNode() {
|
||||
Chat c = Chat.chat()
|
||||
.then(result == PermState.UNDEFINED ? Chat.dataText("■") : result == PermState.GRANTED ? Chat.successText("✔") : Chat.failureText("✘"))
|
||||
.then(Chat.text(entity instanceof CachedPlayer cp ? Permissions.playerNameGetter.apply(cp.playerId) : entity.name)
|
||||
.color(entity instanceof CachedPlayer ? ChatColor.GOLD : ChatColor.DARK_AQUA)
|
||||
);
|
||||
if (server != null)
|
||||
c.thenData(" s=" + server);
|
||||
if (world != null)
|
||||
c.thenData(" w=" + world);
|
||||
if (conflictMessage != null)
|
||||
c.then(Chat.failureText(" " + conflictMessage));
|
||||
DisplayTreeNode node = new DisplayTreeNode(c);
|
||||
|
||||
selfPermissions.forEach(p -> node.children.add(p.toDisplayTreeNode()));
|
||||
|
||||
if (result == PermState.UNDEFINED && !conflict && !inheritances.isEmpty()) {
|
||||
// there is nothing interesting to show on current or subnode
|
||||
node.children.add(new DisplayTreeNode(Chat.text("(Inheritances hidden for brevety)").darkGray().italic()));
|
||||
return node;
|
||||
}
|
||||
|
||||
inheritances.forEach(n -> node.children.add(n.toDisplayTreeNode()));
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ParsedSelfPermission {
|
||||
final String permission;
|
||||
final boolean result;
|
||||
final PermType type;
|
||||
public ParsedSelfPermission(String p, boolean r, PermType t) {
|
||||
permission = p;
|
||||
result = r;
|
||||
type = t;
|
||||
}
|
||||
public DisplayTreeNode toDisplayTreeNode() {
|
||||
return new DisplayTreeNode(Chat.chat()
|
||||
.then(result ? Chat.successText("✔") : Chat.failureText("✘"))
|
||||
.then(Chat.text(permission).color(type == PermType.WILDCARD ? ChatColor.YELLOW : type == PermType.SPECIAL ? ChatColor.LIGHT_PURPLE : ChatColor.WHITE)));
|
||||
}
|
||||
}
|
||||
|
||||
private static class PermCacheKey {
|
||||
final String name;
|
||||
final EntityType type;
|
||||
final String permission, server, world;
|
||||
public PermCacheKey(String n, EntityType t, String p, String s, String w) {
|
||||
name = n; type = t; permission = p; server = s; world = w;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof PermCacheKey o
|
||||
&& Objects.equals(name, o.name)
|
||||
&& Objects.equals(type, o.type)
|
||||
&& Objects.equals(permission, o.permission)
|
||||
&& Objects.equals(server, o.server)
|
||||
&& Objects.equals(world, o.world);
|
||||
}
|
||||
}
|
||||
|
||||
private enum PermType {
|
||||
EXPLICIT, WILDCARD, SPECIAL
|
||||
}
|
||||
|
||||
private enum PermState {
|
||||
GRANTED(true),
|
||||
REVOQUED(false),
|
||||
UNDEFINED(null);
|
||||
final Boolean value;
|
||||
PermState(Boolean v) { value = v; }
|
||||
private static PermState of(Boolean v) {
|
||||
return v == null ? UNDEFINED : v ? GRANTED : REVOQUED;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package fr.pandacube.lib.permissions;
|
||||
|
||||
import fr.pandacube.lib.db.SQLElement;
|
||||
import fr.pandacube.lib.db.SQLField;
|
||||
|
||||
public class SQLPermissions extends SQLElement<SQLPermissions> {
|
||||
|
||||
public SQLPermissions() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLPermissions(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "permissions";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLPermissions, String> name = field(VARCHAR(64), false);
|
||||
public static final SQLField<SQLPermissions, Integer> type = field(TINYINT, false);
|
||||
public static final SQLField<SQLPermissions, String> key = field(VARCHAR(256), false);
|
||||
public static final SQLField<SQLPermissions, String> value = field(VARCHAR(256), false);
|
||||
public static final SQLField<SQLPermissions, String> server = field(VARCHAR(64), true);
|
||||
public static final SQLField<SQLPermissions, String> world = field(VARCHAR(64), true);
|
||||
|
||||
|
||||
public enum EntityType {
|
||||
User,
|
||||
Group;
|
||||
|
||||
public int getCode() {
|
||||
return ordinal();
|
||||
}
|
||||
|
||||
public static EntityType getByCode(int code) {
|
||||
if (code >= 0 && code < values().length)
|
||||
return values()[code];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
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;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof ServerWorldKey o
|
||||
&& Objects.equals(server, o.server)
|
||||
&& Objects.equals(world, o.world);
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(world, server);
|
||||
}
|
||||
|
||||
private static final Comparator<String> STR_NULL_FIRST_COMPARATOR = Comparator.nullsFirst(String::compareToIgnoreCase);
|
||||
@Override
|
||||
public int compareTo(ServerWorldKey o) {
|
||||
return Comparator.comparing((ServerWorldKey k) -> k.server, STR_NULL_FIRST_COMPARATOR)
|
||||
.thenComparing(k -> k.world, STR_NULL_FIRST_COMPARATOR)
|
||||
.compare(this, o);
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package fr.pandacube.lib.permissions;
|
||||
|
||||
/**
|
||||
* Represents a permission node that is based on an arbitrary player state.
|
||||
*/
|
||||
public record SpecialPermission(PermissionMatcher match, PermissionTester tester) {
|
||||
|
||||
public interface PermissionMatcher {
|
||||
boolean match(String permission);
|
||||
}
|
||||
|
||||
public interface PermissionTester {
|
||||
boolean test(PermPlayer player, String permission, String server, String world);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user