364 lines
11 KiB
Java
364 lines
11 KiB
Java
package fr.pandacube.lib.permissions;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
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.Log;
|
|
|
|
/* package */ class PermissionsCachedBackendReader
|
|
{
|
|
/* package */ PermissionsCachedBackendReader() throws DBException {
|
|
clearAndResetCache();
|
|
}
|
|
|
|
|
|
/* package */ static final CachedPlayer DEFAULT_PLAYER = new CachedPlayer(DefaultPlayer.ID, null, null, Map.of());
|
|
static {
|
|
DEFAULT_PLAYER.usingDefaultGroups = true;
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* package */ synchronized List<CachedPlayer> getAllCachedPlayers() {
|
|
return new ArrayList<>(usersCache.asMap().values());
|
|
}
|
|
|
|
/* package */ synchronized void precacheAllPlayers() {
|
|
try {
|
|
DB.getAll(SQLPermissions.class, SQLPermissions.type.eq(EntityType.User.getCode()))
|
|
.stream()
|
|
.collect(Collectors.groupingBy(el -> el.get(SQLPermissions.name))
|
|
)
|
|
.forEach((idStr, pData) -> {
|
|
try {
|
|
UUID pId = UUID.fromString(idStr);
|
|
usersCache.put(pId, initPlayer(pId, pData));
|
|
} catch (Exception e) {
|
|
Log.severe("Error caching player permission data (name=\"" + idStr + "\")", e);
|
|
}
|
|
});
|
|
} catch (DBException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
private CachedPlayer initPlayer(UUID playerId) throws DBException {
|
|
if (playerId.equals(DEFAULT_PLAYER.playerId))
|
|
return DEFAULT_PLAYER;
|
|
|
|
SQLElementList<SQLPermissions> playerData = DB.getAll(SQLPermissions.class,
|
|
SQLPermissions.type.eq(EntityType.User.getCode())
|
|
.and(SQLPermissions.name.like(playerId.toString()))
|
|
);
|
|
|
|
return initPlayer(playerId, playerData);
|
|
}
|
|
|
|
private CachedPlayer initPlayer(UUID playerId, List<SQLPermissions> playerData) {
|
|
|
|
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;
|
|
DEFAULT_PLAYER.groups.clear();
|
|
DEFAULT_PLAYER.groups.addAll(getDefaultGroups());
|
|
}
|
|
} 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 deflt, Map<ServerWorldKey, List<String>> perms) {
|
|
super(n, p, s, perms);
|
|
this.deflt = deflt;
|
|
}
|
|
|
|
|
|
|
|
/* package */ Set<UUID> getPlayersInGroup() throws DBException {
|
|
Set<UUID> ids = new HashSet<>();
|
|
DB.forEach(SQLPermissions.class,
|
|
SQLPermissions.type.eq(EntityType.User.getCode())
|
|
.and(SQLPermissions.key.eq("groups"))
|
|
.and(SQLPermissions.value.eq(name)),
|
|
e -> ids.add(UUID.fromString(e.get(SQLPermissions.name)))
|
|
);
|
|
return ids;
|
|
}
|
|
}
|
|
|
|
}
|