Add logging, prepare for import/export system

This commit is contained in:
Luck
2016-08-15 01:51:36 +02:00
Unverified
parent fbf062933f
commit e216c235ce
139 changed files with 4240 additions and 1439 deletions
@@ -24,6 +24,8 @@ package me.lucko.luckperms.utils;
import lombok.AllArgsConstructor;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.core.UuidCache;
import me.lucko.luckperms.users.User;
import java.util.UUID;
@@ -43,11 +45,11 @@ public class AbstractListener {
} else {
// No previous data for this player
cache.addToCache(u, u);
plugin.getDatastore().saveUUIDData(username, u, b -> {});
plugin.getDatastore().saveUUIDData(username, u, Callback.empty());
}
} else {
// Online mode, no cache needed. This is just for name -> uuid lookup.
plugin.getDatastore().saveUUIDData(username, u, b -> {});
plugin.getDatastore().saveUUIDData(username, u, Callback.empty());
}
plugin.getDatastore().loadOrCreateUser(cache.getUUID(u), username);
@@ -63,12 +65,12 @@ public class AbstractListener {
// Unload the user from memory when they disconnect;
cache.clearCache(uuid);
final User user = plugin.getUserManager().getUser(cache.getUUID(uuid));
plugin.getUserManager().unloadUser(user);
final User user = plugin.getUserManager().get(cache.getUUID(uuid));
plugin.getUserManager().unload(user);
}
protected void refreshPlayer(UUID uuid) {
final User user = plugin.getUserManager().getUser(plugin.getUuidCache().getUUID(uuid));
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(uuid));
if (user != null) {
user.refreshPermissions();
}
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.utils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* An abstract manager class
* @param <I> the class used to identify each object held in this manager
* @param <T> the class this manager is "managing"
*/
public abstract class AbstractManager<I, T extends Identifiable<I>> {
protected final Map<I, T> objects = new ConcurrentHashMap<>();
public Map<I, T> getAll() {
return objects;
}
/**
* Get an object by id
* @param id The id to search by
* @return a {@link T} object if the object is loaded, returns null if the object is not loaded
*/
public T get(I id) {
return objects.get(id);
}
/**
* Add a object to the loaded objects map
* @param t The object to add
*/
public void set(T t) {
objects.put(t.getId(), t);
}
/**
* Updates (or sets if the object wasn't already loaded) an object in the objects map
* @param t The object to update or set
*/
public void updateOrSet(T t) {
if (!isLoaded(t.getId())) {
// The object isn't already loaded
objects.put(t.getId(), t);
} else {
copy(t, objects.get(t.getId()));
}
}
protected abstract void copy(T from, T to);
/**
* Check to see if a object is loaded or not
* @param id The id of the object
* @return true if the object is loaded
*/
public boolean isLoaded(I id) {
return objects.containsKey(id);
}
/**
* Removes and unloads the object from the manager
* @param t The object to unload
*/
public void unload(T t) {
if (t != null) {
objects.remove(t.getId());
}
}
/**
* Unloads all objects from the manager
*/
public void unloadAll() {
objects.clear();
}
/**
* Makes a new object
* @param id the id of the object
* @return a new {@link T} object
*/
public abstract T make(I id);
}
@@ -23,29 +23,29 @@
package me.lucko.luckperms.utils;
import lombok.experimental.UtilityClass;
import me.lucko.luckperms.constants.Patterns;
@UtilityClass
public class ArgumentChecker {
public static boolean checkUsername(String s) {
return !(s.length() > 16 || Patterns.NON_USERNAME.matcher(s).find());
return (s.length() > 16 || Patterns.NON_USERNAME.matcher(s).find());
}
public static boolean checkName(String s) {
return !(s.length() > 36 || Patterns.NON_ALPHA_NUMERIC.matcher(s).find());
return (s.length() > 36 || Patterns.NON_ALPHA_NUMERIC.matcher(s).find());
}
public static boolean checkServer(String s) {
return !s.toLowerCase().startsWith("r=") && !Patterns.NON_ALPHA_NUMERIC.matcher(s).find();
return s.toLowerCase().startsWith("r=") || Patterns.NON_ALPHA_NUMERIC.matcher(s).find();
}
public static boolean checkNode(String s) {
return !(s.contains("/") || s.contains("$"));
return (s.contains("/") || s.contains("$"));
}
public static boolean checkTime(long l) {
return !DateUtil.shouldExpire(l);
return DateUtil.shouldExpire(l);
}
}
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.utils;
public interface Identifiable<T> {
T getId();
}
@@ -1,115 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.utils;
import lombok.AccessLevel;
import lombok.Getter;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.data.MySQLConfiguration;
public abstract class LPConfiguration<T extends LuckPermsPlugin> {
@Getter(AccessLevel.PROTECTED)
private final T plugin;
private final String defaultServerName;
private final boolean defaultIncludeGlobal;
private final String defaultStorage;
public LPConfiguration(T plugin, String defaultServerName, boolean defaultIncludeGlobal, String defaultStorage) {
this.plugin = plugin;
this.defaultServerName = defaultServerName;
this.defaultIncludeGlobal = defaultIncludeGlobal;
this.defaultStorage = defaultStorage;
init();
if (Patterns.NON_ALPHA_NUMERIC.matcher(getServer()).find()) {
plugin.getLog().severe("Server name defined in config.yml contains invalid characters. Server names can " +
"only contain alphanumeric characters.\nDefined server name '" + getServer() + "' will be replaced with '" +
defaultServerName + "' (the default)");
set("server", defaultServerName);
}
if (Patterns.NON_ALPHA_NUMERIC.matcher(getDefaultGroupName()).find()) {
plugin.getLog().severe("Default group defined in config.yml contains invalid characters. Group names can " +
"only contain alphanumeric characters.\nDefined default group name '" + getDefaultGroupName() +
"' will be replaced with 'default' (the default)");
set("default-group", "default");
}
}
protected abstract void init();
protected abstract void set(String path, Object value);
protected abstract String getString(String path, String def);
protected abstract int getInt(String path, int def);
protected abstract boolean getBoolean(String path, boolean def);
public String getServer() {
return getString("server", defaultServerName);
}
public int getSyncTime() {
return getInt("sql.sync-minutes", 3);
}
public String getDefaultGroupNode() {
return "group." + getDefaultGroupName();
}
public String getDefaultGroupName() {
return getString("default-group", "default");
}
public boolean getIncludeGlobalPerms() {
return getBoolean("include-global", defaultIncludeGlobal);
}
public boolean getOnlineMode() {
return getBoolean("online-mode", true);
}
public boolean getApplyWildcards() {
return getBoolean("apply-wildcards", true);
}
public boolean getApplyRegex() {
return getBoolean("apply-regex", true);
}
public boolean getApplyShorthand() {
return getBoolean("apply-shorthand", true);
}
public MySQLConfiguration getDatabaseValues() {
return new MySQLConfiguration(
getString("sql.address", null),
getString("sql.database", null),
getString("sql.username", null),
getString("sql.password", null)
);
}
public String getStorageMethod() {
return getString("storage-method", defaultStorage);
}
}
@@ -26,7 +26,7 @@ import lombok.experimental.UtilityClass;
import me.lucko.luckperms.api.Logger;
@UtilityClass
public class LogUtil {
public class LogFactory {
public static Logger wrap(org.slf4j.Logger l) {
return new Logger() {
private final org.slf4j.Logger logger = l;
@@ -1,62 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.utils;
import lombok.experimental.UtilityClass;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@UtilityClass
public class Patterns {
private static final Map<String, Pattern> CACHE = new ConcurrentHashMap<>();
public static final Pattern SPACE = Pattern.compile(" ");
public static final Pattern SERVER_DELIMITER = Pattern.compile("\\/");
public static final Pattern WORLD_DELIMITER = Pattern.compile("\\-");
public static final Pattern TEMP_DELIMITER = Pattern.compile("\\$");
public static final Pattern DOT = Pattern.compile("\\.");
public static final Pattern VERTICAL_BAR = Pattern.compile("\\|");
public static final Pattern GROUP_MATCH = Pattern.compile("group\\..*");
public static final Pattern NON_ALPHA_NUMERIC = Pattern.compile("[^A-Za-z0-9]");
public static final Pattern NON_USERNAME = Pattern.compile("[^A-Za-z0-9_]");
public static final Pattern SHORTHAND_NODE = Pattern.compile("\\.\\([^.]+\\)");
public static Pattern compile(String regex) throws PatternSyntaxException {
if (!CACHE.containsKey(regex)) {
Pattern p;
try {
p = Pattern.compile(regex);
} catch (PatternSyntaxException e) {
return null;
}
CACHE.put(regex, p);
}
return CACHE.get(regex);
}
}
@@ -1,824 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.utils;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Represents an object that can hold permissions
* For example a User or a Group
*/
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class PermissionHolder {
/**
* The UUID of the user / name of the group.
* Used to prevent circular inheritance issues
*/
@Getter
private final String objectName;
/**
* Reference to the main plugin instance
*/
@Getter(AccessLevel.PROTECTED)
private final LuckPermsPlugin plugin;
/**
* The user/group's permissions
*/
@Getter
private Map<String, Boolean> nodes = new ConcurrentHashMap<>();
public void setNodes(Map<String, Boolean> nodes) {
this.nodes.clear();
this.nodes.putAll(nodes);
auditTemporaryPermissions();
}
/**
* Utility method for checking if a map has a certain permission. Used by both #hasPermission and #inheritsPermission
*/
private static boolean hasPermission(Map<String, Boolean> toQuery, String node, boolean b) {
// Not temporary
if (!node.contains("$")) {
return b ? toQuery.containsKey(node) && toQuery.get(node) : toQuery.containsKey(node) && !toQuery.get(node);
}
node = Patterns.TEMP_DELIMITER.split(node)[0];
for (Map.Entry<String, Boolean> e : toQuery.entrySet()) {
if (e.getKey().contains("$")) {
String[] parts = Patterns.TEMP_DELIMITER.split(e.getKey());
if (parts[0].equalsIgnoreCase(node)) {
return b ? e.getValue() : !e.getValue();
}
}
}
return false;
}
/**
* Checks to see if the object has a certain permission
* @param node The permission node
* @param b If the node is true/false(negated)
* @return true if the user has the permission
*/
public boolean hasPermission(String node, boolean b) {
if (node.startsWith("global/")) node = node.replace("global/", "");
return hasPermission(this.nodes, node, b);
}
/**
* Checks to see the the object has a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @return true if the user has the permission
*/
public boolean hasPermission(String node, boolean b, String server) {
return hasPermission(server + "/" + node, b);
}
/**
* Checks to see the the object has a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @param world The world
* @return true if the user has the permission
*/
public boolean hasPermission(String node, boolean b, String server, String world) {
return hasPermission(server + "-" + world + "/" + node, b);
}
/**
* Checks to see the the object has a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
* @param temporary if the permission is temporary
* @return true if the user has the permission
*/
public boolean hasPermission(String node, boolean b, boolean temporary) {
return hasPermission(node + (temporary ? "$a" : ""), b);
}
/**
* Checks to see the the object has a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server to check on
* @param temporary if the permission is temporary
* @return true if the user has the permission
*/
public boolean hasPermission(String node, boolean b, String server, boolean temporary) {
return hasPermission(server + "/" + node + (temporary ? "$a" : ""), b);
}
/**
* Checks to see the the object has a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server to check on
* @param world The world to check on
* @param temporary if the permission is temporary
* @return true if the user has the permission
*/
public boolean hasPermission(String node, boolean b, String server, String world, boolean temporary) {
return hasPermission(server + "-" + world + "/" + node + (temporary ? "$a" : ""), b);
}
/**
* Checks to see if the object inherits a certain permission
* @param node The permission node
* @param b If the node is true/false(negated)
* @return true if the user inherits the permission
*/
public boolean inheritsPermission(String node, boolean b) {
if (node.contains("/")) {
// Use other method
final String[] parts = Patterns.SERVER_DELIMITER.split(node, 2);
return inheritsPermission(parts[1], b, parts[0]);
}
return inheritsPermission(node, b, "global");
}
/**
* Checks to see the the object inherits a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @return true if the user inherits the permission
*/
public boolean inheritsPermission(String node, boolean b, String server) {
if (server.contains("-")) {
// Use other method
final String[] parts = Patterns.WORLD_DELIMITER.split(server, 2);
return inheritsPermission(node, b, parts[0], parts[1]);
}
final Map<String, Boolean> local = getLocalPermissions(server, null);
return hasPermission(local, node, b);
}
/**
* Checks to see the the object inherits a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @param world The world
* @return true if the user inherits the permission
*/
public boolean inheritsPermission(String node, boolean b, String server, String world) {
final Map<String, Boolean> local = getLocalPermissions(server, world, null);
return hasPermission(local, node, b);
}
/**
* Checks to see if the object inherits a certain permission
* @param node The permission node
* @param b If the node is true/false(negated)
* @param temporary if the permission is temporary
* @return true if the user inherits the permission
*/
public boolean inheritsPermission(String node, boolean b, boolean temporary) {
return inheritsPermission(node + (temporary ? "$a" : ""), b);
}
/**
* Checks to see if the object inherits a certain permission
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @param temporary if the permission is temporary
* @return true if the user inherits the permission
*/
public boolean inheritsPermission(String node, boolean b, String server, boolean temporary) {
return inheritsPermission(server + "/" + node + (temporary ? "$a" : ""), b);
}
/**
* Checks to see if the object inherits a certain permission
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @param world The world
* @param temporary if the permission is temporary
* @return true if the user inherits the permission
*/
public boolean inheritsPermission(String node, boolean b, String server, String world, boolean temporary) {
return inheritsPermission(server + "-" + world + "/" + node + (temporary ? "$a" : ""), b);
}
/**
* Sets a permission for the object
* @param node The node to be set
* @param value What to set the node to - true/false(negated)
* @throws ObjectAlreadyHasException if the object already has the permission
*/
public void setPermission(String node, boolean value) throws ObjectAlreadyHasException {
if (node.startsWith("global/")) node = node.replace("global/", "");
if (hasPermission(node, value)) {
throw new ObjectAlreadyHasException();
}
this.nodes.put(node, value);
}
/**
* Sets a permission for the object
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param server The server to set the permission on
* @throws ObjectAlreadyHasException if the object already has the permission
*/
public void setPermission(String node, boolean value, String server) throws ObjectAlreadyHasException {
setPermission(server + "/" + node, value);
}
/**
* Sets a permission for the object
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param server The server to set the permission on
* @param world The world to set the permission on
* @throws ObjectAlreadyHasException if the object already has the permission
*/
public void setPermission(String node, boolean value, String server, String world) throws ObjectAlreadyHasException {
setPermission(server + "-" + world + "/" + node, value);
}
/**
* Sets a permission for the object
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param expireAt The time in unixtime when the permission will expire
* @throws ObjectAlreadyHasException if the object already has the permission
*/
public void setPermission(String node, boolean value, long expireAt) throws ObjectAlreadyHasException {
setPermission(node + "$" + expireAt, value);
}
/**
* Sets a permission for the object
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param server The server to set the permission on
* @param expireAt The time in unixtime when the permission will expire
* @throws ObjectAlreadyHasException if the object already has the permission
*/
public void setPermission(String node, boolean value, String server, long expireAt) throws ObjectAlreadyHasException {
setPermission(node + "$" + expireAt, value, server);
}
/**
* Sets a permission for the object
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param server The server to set the permission on
* @param world The world to set the permission on
* @param expireAt The time in unixtime when the permission will expire
* @throws ObjectAlreadyHasException if the object already has the permission
*/
public void setPermission(String node, boolean value, String server, String world, long expireAt) throws ObjectAlreadyHasException {
setPermission(node + "$" + expireAt, value, server, world);
}
/**
* Unsets a permission for the object
* @param node The node to be unset
* @param temporary if the permission being removed is temporary
* @throws ObjectLacksException if the node wasn't already set
*/
public void unsetPermission(String node, boolean temporary) throws ObjectLacksException {
if (node.startsWith("global/")) node = node.replace("global/", "");
final String fNode = node;
Optional<String> match = Optional.empty();
if (temporary) {
match = this.nodes.keySet().stream()
.filter(n -> n.contains("$")).filter(n -> Patterns.TEMP_DELIMITER.split(n)[0].equalsIgnoreCase(fNode))
.findFirst();
} else {
if (this.nodes.containsKey(fNode)) {
match = Optional.of(fNode);
}
}
if (match.isPresent()) {
this.nodes.remove(match.get());
} else {
throw new ObjectLacksException();
}
}
/**
* Unsets a permission for the object
* @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set
*/
public void unsetPermission(String node) throws ObjectLacksException {
unsetPermission(node, node.contains("$"));
}
/**
* Unsets a permission for the object
* @param node The node to be unset
* @param server The server to unset the node on
* @throws ObjectLacksException if the node wasn't already set
*/
public void unsetPermission(String node, String server) throws ObjectLacksException {
unsetPermission(server + "/" + node);
}
/**
* Unsets a permission for the object
* @param node The node to be unset
* @param server The server to unset the node on
* @param world The world to unset the node on
* @throws ObjectLacksException if the node wasn't already set
*/
public void unsetPermission(String node, String server, String world) throws ObjectLacksException {
unsetPermission(server + "-" + world + "/" + node);
}
/**
* Unsets a permission for the object
* @param node The node to be unset
* @param server The server to unset the node on
* @param temporary if the permission being unset is temporary
* @throws ObjectLacksException if the node wasn't already set
*/
public void unsetPermission(String node, String server, boolean temporary) throws ObjectLacksException {
unsetPermission(server + "/" + node, temporary);
}
/**
* Unsets a permission for the object
* @param node The node to be unset
* @param server The server to unset the node on
* @param world The world to unset the node on
* @param temporary if the permission being unset is temporary
* @throws ObjectLacksException if the node wasn't already set
*/
public void unsetPermission(String node, String server, String world, boolean temporary) throws ObjectLacksException {
unsetPermission(server + "-" + world + "/" + node, temporary);
}
/**
* Gets the permissions and inherited permissions that apply to a specific server
* @param server The server to get nodes for
* @param world The world to get nodes for
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @param possibleNodes A list of possible permission nodes for wildcard permission handling
* @return a {@link Map} of the permissions
*/
public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups, List<String> possibleNodes) {
return getPermissions(server, world, excludedGroups, plugin.getConfiguration().getIncludeGlobalPerms(), possibleNodes);
}
/**
* Gets the permissions and inherited permissions that apply to a specific server
* @param server The server to get nodes for
* @param world The world to get nodes for
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @return a {@link Map} of the permissions
*/
public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups) {
return getPermissions(server, world, excludedGroups, plugin.getConfiguration().getIncludeGlobalPerms(), null);
}
/**
* Gets the permissions and inherited permissions that apply to a specific server
* @param server The server to get nodes for
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @param possibleNodes A list of possible permission nodes for wildcard permission handling
* @return a {@link Map} of the permissions
*/
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups, List<String> possibleNodes) {
return getLocalPermissions(server, null, excludedGroups, possibleNodes);
}
/**
* Gets the permissions and inherited permissions that apply to a specific server
* @param server The server to get nodes for
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @return a {@link Map} of the permissions
*/
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups) {
return getLocalPermissions(server, null, excludedGroups, null);
}
/**
* Processes the objects and returns the temporary ones.
* @return a map of temporary nodes
*/
public Map<Map.Entry<String, Boolean>, Long> getTemporaryNodes() {
return this.nodes.entrySet().stream().filter(e -> e.getKey().contains("$")).map(e -> {
final String[] parts = Patterns.TEMP_DELIMITER.split(e.getKey());
final long expiry = Long.parseLong(parts[1]);
return new AbstractMap.SimpleEntry<Map.Entry<String, Boolean>, Long>(new AbstractMap.SimpleEntry<>(parts[0], e.getValue()), expiry);
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
/**
* Processes the objects and returns the non-temporary ones.
* @return a map of permanent nodes
*/
public Map<String, Boolean> getPermanentNodes() {
return this.nodes.entrySet().stream().filter(e -> !e.getKey().contains("$"))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
/**
* Removes temporary permissions that have expired
*/
public void auditTemporaryPermissions() {
this.nodes.keySet().stream()
.filter(s -> s.contains("$"))
.filter(s -> DateUtil.shouldExpire(Long.parseLong(Patterns.TEMP_DELIMITER.split(s)[1])))
.forEach(s -> this.nodes.remove(s));
}
private Map<String, Boolean> convertTemporaryPerms() {
auditTemporaryPermissions();
Map<String, Boolean> nodes = new HashMap<>();
Map<String, Boolean> tempNodes = new HashMap<>();
for (Map.Entry<String, Boolean> e : this.nodes.entrySet()) {
if (e.getKey().contains("$")) {
tempNodes.put(e.getKey(), e.getValue());
} else {
nodes.put(e.getKey(), e.getValue());
}
}
// temporary permissions override non-temporary permissions
tempNodes.entrySet().forEach(e -> nodes.put(stripTime(e.getKey()), e.getValue()));
return nodes;
}
protected Map<String, Boolean> getPermissions(String server, String world, List<String> excludedGroups, boolean includeGlobal, List<String> possibleNodes) {
if (excludedGroups == null) {
excludedGroups = new ArrayList<>();
}
excludedGroups.add(getObjectName());
Map<String, Boolean> perms = new HashMap<>();
if (server == null || server.equals("")) {
server = "global";
}
if (world != null && world.equalsIgnoreCase("")) {
world = null;
}
/*
Priority:
1. server+world specific nodes
2. server specific nodes
3. user nodes
4. server+world specific group nodes
5. server specific group nodes
6. group nodes
*/
final Map<String, Boolean> serverWorldSpecificNodes = new HashMap<>();
final Map<String, Boolean> serverSpecificNodes = new HashMap<>();
final Map<String, Boolean> userNodes = new HashMap<>();
final Map<String, Boolean> serverWorldSpecificGroups = new HashMap<>();
final Map<String, Boolean> serverSpecificGroups = new HashMap<>();
final Map<String, Boolean> groupNodes = new HashMap<>();
// Sorts the permissions and puts them into a priority order
for (Map.Entry<String, Boolean> node : convertTemporaryPerms().entrySet()) {
serverSpecific:
if (node.getKey().contains("/")) {
String[] parts = Patterns.SERVER_DELIMITER.split(node.getKey(), 2);
// 0=server(+world) 1=node
// WORLD SPECIFIC
if (parts[0].contains("-")) {
String[] serverParts = Patterns.WORLD_DELIMITER.split(parts[0], 2);
// 0=server 1=world
if ((!serverParts[0].equalsIgnoreCase("global") || !includeGlobal) && (!matches(server, serverParts[0]))) {
// GLOBAL AND UNWANTED OR SERVER SPECIFIC BUT DOES NOT APPLY :(((
continue;
}
if (world != null && !matches(world, serverParts[1])) {
// WORLD SPECIFIC BUT DOES NOT APPLY
continue;
}
if (Patterns.GROUP_MATCH.matcher(parts[1]).matches()) {
// SERVER+WORLD SPECIFIC AND GROUP
serverWorldSpecificGroups.put(node.getKey(), node.getValue());
continue;
}
// SERVER+WORLD SPECIFIC
serverWorldSpecificNodes.put(node.getKey(), node.getValue());
continue;
}
if (parts[0].equalsIgnoreCase("global")) {
// REGULAR
break serverSpecific;
}
if (!matches(server, parts[0])) {
// SERVER SPECIFIC BUT DOES NOT APPLY
continue;
}
if (Patterns.GROUP_MATCH.matcher(parts[1]).matches()) {
// SERVER SPECIFIC AND GROUP
serverSpecificGroups.put(node.getKey(), node.getValue());
continue;
}
// SERVER SPECIFIC
serverSpecificNodes.put(node.getKey(), node.getValue());
continue;
}
// Skip adding global permissions if they are not requested
if (!includeGlobal) continue;
// Could be here if the server was set to global.
String n = node.getKey();
if (n.contains("/")) {
n = Patterns.SERVER_DELIMITER.split(n, 2)[1];
}
if (Patterns.GROUP_MATCH.matcher(n).matches()) {
// GROUP
groupNodes.put(n, node.getValue());
continue;
}
// JUST NORMAL
userNodes.put(n, node.getValue());
}
// If a group is negated at a higher priority, the group should not then be applied at a lower priority
serverWorldSpecificGroups.entrySet().stream().filter(node -> !node.getValue()).forEach(node -> {
groupNodes.remove(node.getKey());
groupNodes.remove(Patterns.SERVER_DELIMITER.split(node.getKey(), 2)[1]);
serverSpecificGroups.remove(node.getKey());
serverSpecificGroups.remove(Patterns.SERVER_DELIMITER.split(node.getKey(), 2)[1]);
serverSpecificGroups.remove(Patterns.WORLD_DELIMITER.split(node.getKey(), 2)[0] + "/" + Patterns.SERVER_DELIMITER.split(node.getKey(), 2)[1]);
});
serverSpecificGroups.entrySet().stream().filter(node -> !node.getValue()).forEach(node -> {
groupNodes.remove(node.getKey());
groupNodes.remove(Patterns.SERVER_DELIMITER.split(node.getKey(), 2)[1]);
});
// Apply lowest priority: groupNodes
for (Map.Entry<String, Boolean> groupNode : groupNodes.entrySet()) {
// Add the actual group perm node, so other plugins can hook
perms.put(groupNode.getKey(), groupNode.getValue());
// Don't add negated groups
if (!groupNode.getValue()) continue;
String groupName = Patterns.DOT.split(groupNode.getKey(), 2)[1];
if (!excludedGroups.contains(groupName)) {
Group group = plugin.getGroupManager().getGroup(groupName);
if (group != null) {
perms.putAll(group.getLocalPermissions(server, excludedGroups));
} else {
plugin.getLog().warn("Error whilst refreshing the permissions of '" + objectName + "'." +
"\n The group '" + groupName + "' is not loaded.");
}
}
}
applyShorthandIfEnabled(perms);
// Apply next priorities: serverSpecificGroups and then serverWorldSpecificGroups
for (Map<String, Boolean> m : Arrays.asList(serverSpecificGroups, serverWorldSpecificGroups)) {
for (Map.Entry<String, Boolean> groupNode : m.entrySet()) {
final String rawNode = Patterns.SERVER_DELIMITER.split(groupNode.getKey())[1];
// Add the actual group perm node, so other plugins can hook
perms.put(rawNode, groupNode.getValue());
// Don't add negated groups
if (!groupNode.getValue()) continue;
String groupName = Patterns.DOT.split(rawNode, 2)[1];
if (!excludedGroups.contains(groupName)) {
Group group = plugin.getGroupManager().getGroup(groupName);
if (group != null) {
perms.putAll(group.getLocalPermissions(server, excludedGroups));
} else {
plugin.getLog().warn("Error whilst refreshing the permissions of '" + objectName + "'." +
"\n The group '" + groupName + "' is not loaded.");
}
}
}
applyShorthandIfEnabled(perms);
}
// Apply next priority: userNodes
perms.putAll(userNodes);
applyShorthandIfEnabled(perms);
// Apply final priorities: serverSpecificNodes and then serverWorldSpecificNodes
for (Map<String, Boolean> m : Arrays.asList(serverSpecificNodes, serverWorldSpecificNodes)) {
for (Map.Entry<String, Boolean> node : m.entrySet()) {
final String rawNode = Patterns.SERVER_DELIMITER.split(node.getKey())[1];
perms.put(rawNode, node.getValue());
}
applyShorthandIfEnabled(perms);
}
if (plugin.getConfiguration().getApplyRegex()) {
if (possibleNodes != null && !possibleNodes.isEmpty()) {
perms = applyRegex(perms, possibleNodes);
} else {
perms = applyRegex(perms, plugin.getPossiblePermissions());
}
}
applyShorthandIfEnabled(perms);
if (plugin.getConfiguration().getApplyWildcards()) {
if (possibleNodes != null && !possibleNodes.isEmpty()) {
perms = applyWildcards(perms, possibleNodes);
} else {
perms = applyWildcards(perms, plugin.getPossiblePermissions());
}
}
return perms;
}
private boolean matches(String entry, String possibleRegex) {
if (!possibleRegex.toLowerCase().startsWith("r=") || !plugin.getConfiguration().getApplyRegex()) {
return entry.equalsIgnoreCase(possibleRegex);
}
Pattern p = Patterns.compile(possibleRegex.substring(2));
if (p == null) {
return false;
}
return p.matcher(entry).matches();
}
private Map<String, Boolean> applyRegex(Map<String, Boolean> input, List<String> possibleNodes) {
for (Map.Entry<String, Boolean> e : input.entrySet()) {
if (!e.getKey().startsWith("r=") && !e.getKey().startsWith("R=")) {
continue;
}
final Pattern node = Patterns.compile(e.getKey().substring(2));
if (node == null) continue;
possibleNodes.stream()
.filter(n -> node.matcher(n).matches())
.filter(n -> !input.containsKey(n))
.forEach(n -> input.put(n, e.getValue()));
}
return input;
}
private static Map<String, Boolean> applyWildcards(Map<String, Boolean> input, List<String> possibleNodes) {
SortedMap<Integer, Map<String, Boolean>> wildcards = new TreeMap<>(Collections.reverseOrder());
for (Map.Entry<String, Boolean> e : input.entrySet()) {
if (e.getKey().equals("*") || e.getKey().equals("'*'")) {
wildcards.put(0, Collections.singletonMap("*", e.getValue()));
continue;
}
if (!e.getKey().endsWith(".*")) {
continue;
}
final String node = e.getKey().substring(0, e.getKey().length() - 2);
final String[] parts = Patterns.DOT.split(node);
if (!wildcards.containsKey(parts.length)) {
wildcards.put(parts.length, new HashMap<>());
}
wildcards.get(parts.length).put(node, e.getValue());
}
for (Map.Entry<Integer, Map<String, Boolean>> e : wildcards.entrySet()) {
if (e.getKey() == 0) {
// Apply all permissions
possibleNodes.stream()
.filter(n -> !input.containsKey(n)) // Don't override existing nodes
.forEach(n -> input.put(n, e.getValue().get("*")));
break;
}
for (Map.Entry<String, Boolean> wc : e.getValue().entrySet()) {
possibleNodes.stream()
.filter(n -> n.startsWith(wc.getKey() + ".")) // Only nodes that match the wildcard are applied
.filter(n -> !input.containsKey(n)) // Don't override existing nodes
.forEach(n -> input.put(n, wc.getValue()));
}
}
return input;
}
private void applyShorthandIfEnabled(Map<String, Boolean> map) {
if (plugin.getConfiguration().getApplyShorthand()) {
applyShorthand(map);
}
}
private static Map<String, Boolean> applyShorthand(Map<String, Boolean> input) {
for (Map.Entry<String, Boolean> e : input.entrySet()) {
if (!Patterns.SHORTHAND_NODE.matcher(e.getKey()).find()) {
continue;
}
if (!e.getKey().contains(".")) {
continue;
}
String[] parts = Patterns.DOT.split(e.getKey());
List<Set<String>> nodeParts = new ArrayList<>();
for (String s : parts) {
if ((!s.startsWith("(") || !s.endsWith(")")) || !s.contains("|")) {
nodeParts.add(Collections.singleton(s));
continue;
}
final String bits = s.substring(1, s.length() - 1);
nodeParts.add(new HashSet<>(Arrays.asList(Patterns.VERTICAL_BAR.split(bits))));
}
Set<String> nodes = new HashSet<>();
for (Set<String> set : nodeParts) {
final Set<String> newNodes = new HashSet<>();
if (nodes.isEmpty()) {
newNodes.addAll(set);
} else {
nodes.forEach(str -> newNodes.addAll(set.stream()
.map(add -> str + "." + add)
.collect(Collectors.toList()))
);
}
nodes = newNodes;
}
nodes.stream()
.filter(n -> !input.containsKey(n)) // Don't override existing nodes
.forEach(n -> input.put(n, e.getValue()));
}
return input;
}
private static String stripTime(String s) {
if (s.contains("$")) {
return Patterns.TEMP_DELIMITER.split(s)[0];
}
return s;
}
}
@@ -1,78 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.utils;
import lombok.Getter;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* This UuidCache is a means of allowing users to have the same internal UUID across a network of offline mode servers
* or mixed offline mode and online mode servers. Platforms running in offline mode generate a random UUID for a user when
* they first join the server, but this UUID will then not be consistent across the network. LuckPerms will instead check
* the datastore cache, to get a UUID for a user that is consistent across an entire network.
*
* If you want to get a user object from the datastore using the api on a server in offline mode, you will need to use this cache,
* OR use Datastore#getUUID, for users that are not online.
*/
public class UuidCache {
// External UUID --> Internal UUID
private Map<UUID, UUID> cache;
@Getter
private final boolean onlineMode;
public UuidCache(boolean onlineMode) {
this.onlineMode = onlineMode;
if (!onlineMode) {
cache = new ConcurrentHashMap<>();
}
}
public UUID getUUID(UUID external) {
return onlineMode ? external : (cache.containsKey(external) ? cache.get(external) : external);
}
public UUID getExternalUUID(UUID internal) {
if (onlineMode) return internal;
Optional<UUID> external = cache.entrySet().stream().filter(e -> e.getValue().equals(internal)).map(Map.Entry::getKey).findFirst();
return external.isPresent() ? external.get() : internal;
}
public void addToCache(UUID external, UUID internal) {
if (onlineMode) return;
cache.put(external, internal);
}
public void clearCache(UUID external) {
if (onlineMode) return;
cache.remove(external);
}
}