diff --git a/api/pom.xml b/api/pom.xml index a44da7cd8..c2b8c3cef 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.5-SNAPSHOT + 2.6-SNAPSHOT 4.0.0 diff --git a/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java b/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java index ceb4a9cc9..ebde682bd 100644 --- a/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java +++ b/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java @@ -43,12 +43,16 @@ public interface LPConfiguration { /** * @return the default group, in a node representation + * @deprecated as of 1.6, the default group is always "default" */ + @Deprecated String getDefaultGroupNode(); /** * @return the name of the default group + * @deprecated as of 1.6, the default group is always "default" */ + @Deprecated String getDefaultGroupName(); /** diff --git a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java index b29199b35..4b4cd162f 100644 --- a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java +++ b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java @@ -175,4 +175,12 @@ public interface LuckPermsApi { */ boolean isTrackLoaded(String name); + /** + * Returns a permission builder instance + * @param permission the main permission node to build + * @return a {@link Node.Builder} instance + * @throws IllegalArgumentException if the permission is invalid + */ + Node.Builder buildNode(String permission) throws IllegalArgumentException; + } diff --git a/api/src/main/java/me/lucko/luckperms/api/Node.java b/api/src/main/java/me/lucko/luckperms/api/Node.java new file mode 100644 index 000000000..c7cfd3053 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/Node.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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.api; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Represents an immutable node object + */ +public interface Node extends Map.Entry { + + /** + * @return the actual permission node + */ + String getPermission(); + + /** + * Get what value the permission is set to. A negated node would return false. + * @return the permission's value + */ + @Override + Boolean getValue(); + + /** + * @return true if the node is negated + */ + boolean isNegated(); + + /** + * Gets the server this node applies on, if the node is server specific + * @return an {@link Optional} containing the server, if one is defined + */ + Optional getServer(); + + /** + * Gets the world this node applies on, if the node is world specific + * @return an {@link Optional} containing the world, if one is defined + */ + Optional getWorld(); + + /** + * @return true if this node is server specific + */ + boolean isServerSpecific(); + + /** + * @return true if this node is server specific + */ + boolean isWorldSpecific(); + + /** + * If this node should apply on a specific server + * @param server the name of the server + * @param includeGlobal if global permissions should apply + * @param applyRegex if regex should be applied + * @return true if the node should apply + */ + boolean shouldApplyOnServer(String server, boolean includeGlobal, boolean applyRegex); + + /** + * If this node should apply on a specific world + * @param world the name of the world + * @param includeGlobal if global permissions should apply + * @param applyRegex if regex should be applied + * @return true if the node should apply + */ + boolean shouldApplyOnWorld(String world, boolean includeGlobal, boolean applyRegex); + + /** + * If this node should apply given the specific context + * @param context the context key value pairs + * @return true if the node should apply + */ + boolean shouldApplyWithContext(Map context); + + /** + * Similar to {@link #shouldApplyOnServer(String, boolean, boolean)}, except this method accepts a List + * @param servers the list of servers + * @param includeGlobal if global permissions should apply + * @return true if the node should apply + */ + boolean shouldApplyOnAnyServers(List servers, boolean includeGlobal); + + /** + * Similar to {@link #shouldApplyOnWorld(String, boolean, boolean)}, except this method accepts a List + * @param worlds the list of world + * @param includeGlobal if global permissions should apply + * @return true if the node should apply + */ + boolean shouldApplyOnAnyWorlds(List worlds, boolean includeGlobal); + + /** + * Resolves a list of wildcards that match this node + * @param possibleNodes a list of possible permission nodes + * @return a list of permissions that match this wildcard + */ + List resolveWildcard(List possibleNodes); + + /** + * Resolves any shorthand parts of this node and returns the full list + * @return a list of full nodes + */ + List resolveShorthand(); + + /** + * @return true if this node will expire in the future + */ + boolean isTemporary(); + + /** + * @return true if this node will not expire + */ + boolean isPermanent(); + + /** + * @return the time in Unix time when this node will expire + */ + long getExpiryUnixTime(); + + /** + * @return the {@link Date} when this node will expire + */ + Date getExpiry(); + + /** + * @return the number of seconds until this permission will expire + */ + long getSecondsTilExpiry(); + + /** + * @return true if this node has expired + */ + boolean hasExpired(); + + /** + * @return the extra contexts required for this node to apply + */ + Map getExtraContexts(); + + /** + * Converts this node into a serialized form + * @return a serialized node string + */ + String toSerializedNode(); + + /** + * @return true if this is a group node + */ + boolean isGroupNode(); + + /** + * @return the name of the group + * @throws IllegalStateException if this is not a group node. See {@link #isGroupNode()} + */ + String getGroupName(); + + /** + * @return true is this node is a wildcard node + */ + boolean isWildcard(); + + /** + * Gets the level of this wildcard, higher is more specific + * @return the wildcard level + * @throws IllegalStateException if this is not a wildcard + */ + int getWildcardLevel(); + + /** + * Similar to {@link #equals(Object)}, except doesn't take note of the expiry time or value + * @param node the other node + * @return true if the two nodes are almost equal + */ + boolean almostEquals(Node node); + + interface Builder { + Builder setNegated(boolean negated); + Builder setValue(boolean value); + Builder setExpiry(long expireAt); + Builder setWorld(String world); + Builder setServer(String server) throws IllegalArgumentException; + Builder withExtraContext(String key, String value); + Node build(); + } + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java index d5617a240..7ec60189b 100644 --- a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java +++ b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java @@ -27,33 +27,61 @@ import me.lucko.luckperms.exceptions.ObjectLacksException; import java.util.List; import java.util.Map; +import java.util.Set; /** - * Wrapper interface for internal PermissionHolder (user/group) instances + * Wrapper interface for internal PermissionHolder (object/group) instances */ @SuppressWarnings("unused") public interface PermissionHolder { /** * @return the identifier for this object. either a uuid string or name - * However, you should really use {@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()} + * However, you should really just use {@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()} */ String getObjectName(); + /** + * Gets an immutable Set of the objects permission nodes + * @return an immutable set of permissions + * @since 1.6 + */ + Set getPermissionNodes(); + + /** + * Gets an immutable set of the nodes that this object has and inherits + * @return an immutable set of permissions + * @since 1.6 + */ + Set getAllNodes(); + /** * Gets an immutable Map of the objects permission nodes * @return an immutable map of permissions + * @deprecated in favour of {@link #getPermissionNodes()} */ + @Deprecated Map getNodes(); + /** + * Checks to see if the object has a certain permission + * @param node the node to check for + * @return true if the object has the permission + * @throws NullPointerException if the node is null + * @since 1.6 + */ + boolean hasPermission(Node node); + /** * 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 + * @return true if the object has the permission * @throws NullPointerException if the node is null * @throws IllegalArgumentException if the node is invalid + * @deprecated in favour of {@link #hasPermission(Node)} */ + @Deprecated boolean hasPermission(String node, boolean b); /** @@ -61,10 +89,12 @@ public interface PermissionHolder { * @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 + * @return true if the object has the permission * @throws NullPointerException if the node or server is null * @throws IllegalArgumentException if the node or server is invalid + * @deprecated in favour of {@link #hasPermission(Node)} */ + @Deprecated boolean hasPermission(String node, boolean b, String server); /** @@ -73,10 +103,12 @@ public interface PermissionHolder { * @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 + * @return true if the object has the permission * @throws NullPointerException if the node, server or world is null * @throws IllegalArgumentException if the node, server or world is invalid + * @deprecated in favour of {@link #hasPermission(Node)} */ + @Deprecated boolean hasPermission(String node, boolean b, String server, String world); /** @@ -84,10 +116,12 @@ public interface PermissionHolder { * @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 + * @return true if the object has the permission * @throws NullPointerException if the node is null * @throws IllegalArgumentException if the node is invalid + * @deprecated in favour of {@link #hasPermission(Node)} */ + @Deprecated boolean hasPermission(String node, boolean b, boolean temporary); /** @@ -96,10 +130,12 @@ public interface PermissionHolder { * @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 + * @return true if the object has the permission * @throws NullPointerException if the node or server is null * @throws IllegalArgumentException if the node or server is invalid + * @deprecated in favour of {@link #hasPermission(Node)} */ + @Deprecated boolean hasPermission(String node, boolean b, String server, boolean temporary); /** @@ -109,20 +145,33 @@ public interface PermissionHolder { * @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 + * @return true if the object has the permission * @throws NullPointerException if the node, server or world is null - * @throws IllegalArgumentException if the node, server or world is invalud + * @throws IllegalArgumentException if the node, server or world is invalid + * @deprecated in favour of {@link #hasPermission(Node)} */ + @Deprecated boolean hasPermission(String node, boolean b, String server, String world, boolean temporary); + /** + * Cheks to see if the object inherits a certain permission + * @param node the node to check for + * @return true if the object inherits the permission + * @throws NullPointerException if the node is null + * @since 1.6 + */ + boolean inheritsPermission(Node node); + /** * 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 + * @return true if the object inherits the permission * @throws NullPointerException if the node is null * @throws IllegalArgumentException if the node is invalid + * @deprecated in favour of {@link #inheritsPermission(Node)} */ + @Deprecated boolean inheritsPermission(String node, boolean b); /** @@ -130,10 +179,12 @@ public interface PermissionHolder { * @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 + * @return true if the object inherits the permission * @throws NullPointerException if the node or server is null * @throws IllegalArgumentException if the node or server is invalid + * @deprecated in favour of {@link #inheritsPermission(Node)} */ + @Deprecated boolean inheritsPermission(String node, boolean b, String server); /** @@ -142,10 +193,12 @@ public interface PermissionHolder { * @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 + * @return true if the object inherits the permission * @throws NullPointerException if the node, server or world is null * @throws IllegalArgumentException if the node server or world is invalid + * @deprecated in favour of {@link #inheritsPermission(Node)} */ + @Deprecated boolean inheritsPermission(String node, boolean b, String server, String world); /** @@ -153,10 +206,12 @@ public interface PermissionHolder { * @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 + * @return true if the object inherits the permission * @throws NullPointerException if the node is null * @throws IllegalArgumentException if the node is invalid + * @deprecated in favour of {@link #inheritsPermission(Node)} */ + @Deprecated boolean inheritsPermission(String node, boolean b, boolean temporary); /** @@ -165,10 +220,12 @@ public interface PermissionHolder { * @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 + * @return true if the object inherits the permission * @throws NullPointerException if the node or server is null * @throws IllegalArgumentException if the node or server is invalid + * @deprecated in favour of {@link #inheritsPermission(Node)} */ + @Deprecated boolean inheritsPermission(String node, boolean b, String server, boolean temporary); /** @@ -178,12 +235,23 @@ public interface PermissionHolder { * @param server The server * @param world The world * @param temporary if the permission is temporary - * @return true if the user inherits the permission + * @return true if the object inherits the permission * @throws NullPointerException if the node, server or world is null * @throws IllegalArgumentException if the node, server or world if invalid + * @deprecated in favour of {@link #inheritsPermission(Node)} */ + @Deprecated boolean inheritsPermission(String node, boolean b, String server, String world, boolean temporary); + /** + * Sets a permission for the object + * @param node The node to be set + * @throws ObjectAlreadyHasException if the object already has the permission + * @throws NullPointerException if the node is null + * @since 1.6 + */ + void setPermission(Node node) throws ObjectAlreadyHasException; + /** * Sets a permission for the object * @param node The node to be set @@ -191,7 +259,9 @@ public interface PermissionHolder { * @throws ObjectAlreadyHasException if the object already has the permission * @throws NullPointerException if the node is null * @throws IllegalArgumentException if the node is invalid + * @deprecated in favour of {@link #setPermission(Node)} */ + @Deprecated void setPermission(String node, boolean value) throws ObjectAlreadyHasException; /** @@ -202,7 +272,9 @@ public interface PermissionHolder { * @throws ObjectAlreadyHasException if the object already has the permission * @throws NullPointerException if the node or server is null * @throws IllegalArgumentException if the node or server is invalid + * @deprecated in favour of {@link #setPermission(Node)} */ + @Deprecated void setPermission(String node, boolean value, String server) throws ObjectAlreadyHasException; /** @@ -214,7 +286,9 @@ public interface PermissionHolder { * @throws ObjectAlreadyHasException if the object already has the permission * @throws NullPointerException if the node, server or world is null * @throws IllegalArgumentException if the node, server or world is invalid + * @deprecated in favour of {@link #setPermission(Node)} */ + @Deprecated void setPermission(String node, boolean value, String server, String world) throws ObjectAlreadyHasException; /** @@ -225,7 +299,9 @@ public interface PermissionHolder { * @throws ObjectAlreadyHasException if the object already has the permission * @throws NullPointerException if the node is null * @throws IllegalArgumentException if the node is invalid or if the expiry time is in the past + * @deprecated in favour of {@link #setPermission(Node)} */ + @Deprecated void setPermission(String node, boolean value, long expireAt) throws ObjectAlreadyHasException; /** @@ -237,7 +313,9 @@ public interface PermissionHolder { * @throws ObjectAlreadyHasException if the object already has the permission * @throws NullPointerException if the node or server is null * @throws IllegalArgumentException if the node/server is invalid or if the expiry time is in the past + * @deprecated in favour of {@link #setPermission(Node)} */ + @Deprecated void setPermission(String node, boolean value, String server, long expireAt) throws ObjectAlreadyHasException; /** @@ -250,9 +328,20 @@ public interface PermissionHolder { * @throws ObjectAlreadyHasException if the object already has the permission * @throws NullPointerException if the node, server or world is null * @throws IllegalArgumentException if the node/server/world is invalid, or if the expiry time is in the past + * @deprecated in favour of {@link #setPermission(Node)} */ + @Deprecated void setPermission(String node, boolean value, String server, String world, long expireAt) throws ObjectAlreadyHasException; + /** + * Unsets a permission for the object + * @param node The node to be unset + * @throws ObjectLacksException if the node wasn't already set + * @throws NullPointerException if the node is null + * @since 1.6 + */ + void unsetPermission(Node node) throws ObjectLacksException; + /** * Unsets a permission for the object * @param node The node to be unset @@ -260,7 +349,9 @@ public interface PermissionHolder { * @throws ObjectLacksException if the node wasn't already set * @throws NullPointerException if the node is null * @throws IllegalArgumentException if the node is invalid + * @deprecated in favour of {@link #unsetPermission(Node)} */ + @Deprecated void unsetPermission(String node, boolean temporary) throws ObjectLacksException; /** @@ -269,7 +360,9 @@ public interface PermissionHolder { * @throws ObjectLacksException if the node wasn't already set * @throws NullPointerException if the node is null * @throws IllegalArgumentException if the node is invalid + * @deprecated in favour of {@link #unsetPermission(Node)} */ + @Deprecated void unsetPermission(String node) throws ObjectLacksException; /** @@ -279,7 +372,9 @@ public interface PermissionHolder { * @throws ObjectLacksException if the node wasn't already set * @throws NullPointerException if the node or server is null * @throws IllegalArgumentException if the node or server is invalid + * @deprecated in favour of {@link #unsetPermission(Node)} */ + @Deprecated void unsetPermission(String node, String server) throws ObjectLacksException; /** @@ -290,7 +385,9 @@ public interface PermissionHolder { * @throws ObjectLacksException if the node wasn't already set * @throws NullPointerException if the node, server or world is null * @throws IllegalArgumentException if the node, server or world is invalid + * @deprecated in favour of {@link #unsetPermission(Node)} */ + @Deprecated void unsetPermission(String node, String server, String world) throws ObjectLacksException; /** @@ -301,7 +398,9 @@ public interface PermissionHolder { * @throws ObjectLacksException if the node wasn't already set * @throws NullPointerException if the node or server is null * @throws IllegalArgumentException if the node or server is invalid + * @deprecated in favour of {@link #unsetPermission(Node)} */ + @Deprecated void unsetPermission(String node, String server, boolean temporary) throws ObjectLacksException; /** @@ -313,7 +412,9 @@ public interface PermissionHolder { * @throws ObjectLacksException if the node wasn't already set * @throws NullPointerException if the node, server or world is null * @throws IllegalArgumentException if the node, server or world is invalid + * @deprecated in favour of {@link #unsetPermission(Node)} */ + @Deprecated void unsetPermission(String node, String server, String world, boolean temporary) throws ObjectLacksException; /** @@ -323,7 +424,9 @@ public interface PermissionHolder { * @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 + * @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)} */ + @Deprecated Map getLocalPermissions(String server, String world, List excludedGroups, List possibleNodes); /** @@ -332,7 +435,9 @@ public interface PermissionHolder { * @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 + * @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)} */ + @Deprecated Map getLocalPermissions(String server, String world, List excludedGroups); /** @@ -341,7 +446,9 @@ public interface PermissionHolder { * @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 + * @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)} */ + @Deprecated Map getLocalPermissions(String server, List excludedGroups, List possibleNodes); /** @@ -349,21 +456,54 @@ public interface PermissionHolder { * @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 + * @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)} */ + @Deprecated Map getLocalPermissions(String server, List excludedGroups); + /** + * Convert the holders nodes into a Map of permissions to be applied on the platform + * @param server the server + * @param world the world + * @param extraContext any extra context to filter by + * @param includeGlobal whether to include global nodes + * @param possibleNodes a list of possible permissions for resolving wildcards + * @param applyGroups if inherited group permissions should be included + * @return a map of permissions + * @since 1.6 + */ + Map getPermissions(String server, String world, Map extraContext, boolean includeGlobal, List possibleNodes, boolean applyGroups); + /** * Processes the nodes and returns the temporary ones. * @return a map of temporary nodes + * @deprecated in favour of {@link #getTemporaryPermissionNodes()} */ + @Deprecated Map, Long> getTemporaryNodes(); + /** + * Processes the nodes and returns the temporary ones. + * @return a set of temporary nodes + * @since 1.6 + */ + Set getTemporaryPermissionNodes(); + /** * Processes the nodes and returns the non-temporary ones. * @return a map of permanent nodes + * @deprecated in favour of {@link #getPermanentPermissionNodes()} */ + @Deprecated Map getPermanentNodes(); + /** + * Processes the nodes and returns the non-temporary ones. + * @return a set of permanent nodes + * @since 1.6 + */ + Set getPermanentPermissionNodes(); + /** * Removes temporary permissions that have expired */ diff --git a/api/src/main/java/me/lucko/luckperms/api/event/AbstractPermissionEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/AbstractPermissionEvent.java new file mode 100644 index 000000000..f0240386e --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/event/AbstractPermissionEvent.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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.api.event; + +import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.PermissionHolder; + +/** + * @since 1.6 + */ +public class AbstractPermissionEvent extends TargetedEvent { + + private final Node node; + + protected AbstractPermissionEvent(String eventName, PermissionHolder target, Node node) { + super(eventName, target); + this.node = node; + } + + public Node getNode() { + return node; + } +} diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionExpireEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionExpireEvent.java index 5f1bca90f..7569d0a48 100644 --- a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionExpireEvent.java +++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionExpireEvent.java @@ -27,7 +27,9 @@ import me.lucko.luckperms.api.event.TargetedEvent; /** * Called when a permission expires for an object. + * @deprecated in favour of {@link PermissionNodeExpireEvent} */ +@Deprecated public class PermissionExpireEvent extends TargetedEvent { private final String node; diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeExpireEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeExpireEvent.java new file mode 100644 index 000000000..82456d123 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeExpireEvent.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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.api.event.events; + +import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.PermissionHolder; +import me.lucko.luckperms.api.event.AbstractPermissionEvent; + +/** + * Called when a temporary permission node expires + * @since 1.6 + */ +public class PermissionNodeExpireEvent extends AbstractPermissionEvent { + public PermissionNodeExpireEvent(PermissionHolder target, Node node) { + super("Permission Node Expire Event", target, node); + } +} diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeSetEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeSetEvent.java new file mode 100644 index 000000000..b0473e5b4 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeSetEvent.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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.api.event.events; + +import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.PermissionHolder; +import me.lucko.luckperms.api.event.AbstractPermissionEvent; + +/** + * Called when a permission node is set on a holder + * @since 1.6 + */ +public class PermissionNodeSetEvent extends AbstractPermissionEvent { + public PermissionNodeSetEvent(PermissionHolder target, Node node) { + super("Permission Node Set Event", target, node); + } +} diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeUnsetEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeUnsetEvent.java new file mode 100644 index 000000000..8f6ebdf6a --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeUnsetEvent.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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.api.event.events; + +import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.PermissionHolder; +import me.lucko.luckperms.api.event.AbstractPermissionEvent; + +/** + * Called when a permission node is unset from a holder + * @since 1.6 + */ +public class PermissionNodeUnsetEvent extends AbstractPermissionEvent { + public PermissionNodeUnsetEvent(PermissionHolder target, Node node) { + super("Permission Node Unset Event", target, node); + } +} diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionSetEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionSetEvent.java index 66a30ba59..44f7678ac 100644 --- a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionSetEvent.java +++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionSetEvent.java @@ -30,7 +30,9 @@ import java.util.Map; /** * Called whenever a user or group has a permission set. + * @deprecated in favour of {@link PermissionNodeSetEvent} */ +@Deprecated public class PermissionSetEvent extends AbstractPermissionAddEvent { private final String node; diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionUnsetEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionUnsetEvent.java index 65db10c14..23a50bf74 100644 --- a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionUnsetEvent.java +++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionUnsetEvent.java @@ -27,7 +27,9 @@ import me.lucko.luckperms.api.event.AbstractPermissionRemoveEvent; /** * Called whenever a user or group has a permission unset. + * @deprecated in favour of {@link PermissionNodeUnsetEvent} */ +@Deprecated public class PermissionUnsetEvent extends AbstractPermissionRemoveEvent { private final String node; diff --git a/bukkit-placeholders/pom.xml b/bukkit-placeholders/pom.xml index 366c8ab03..5a200e7a8 100644 --- a/bukkit-placeholders/pom.xml +++ b/bukkit-placeholders/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.5-SNAPSHOT + 2.6-SNAPSHOT 4.0.0 diff --git a/bukkit/pom.xml b/bukkit/pom.xml index 122b936fb..725b58048 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.5-SNAPSHOT + 2.6-SNAPSHOT 4.0.0 diff --git a/bungee/pom.xml b/bungee/pom.xml index 144c7f81f..e89cc19f4 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.5-SNAPSHOT + 2.6-SNAPSHOT 4.0.0 diff --git a/common/pom.xml b/common/pom.xml index 6d484959e..2c8e8fbe2 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.5-SNAPSHOT + 2.6-SNAPSHOT 4.0.0 diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java index 173f5ba54..8a46df6d1 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java @@ -27,6 +27,7 @@ import lombok.AllArgsConstructor; import lombok.NonNull; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.api.*; +import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.event.LPEvent; import me.lucko.luckperms.api.event.LPListener; import me.lucko.luckperms.api.implementation.internal.*; @@ -36,6 +37,8 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import static me.lucko.luckperms.api.implementation.internal.Utils.checkNode; + /** * Provides static access to LuckPerms */ @@ -187,4 +190,9 @@ public class ApiProvider implements LuckPermsApi { public boolean isTrackLoaded(@NonNull String name) { return plugin.getTrackManager().isLoaded(name); } + + @Override + public Node.Builder buildNode(String permission) throws IllegalArgumentException { + return new me.lucko.luckperms.utils.Node.Builder(checkNode(permission)); + } } diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java index 2b21a28c7..16d873dd2 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java @@ -24,15 +24,15 @@ package me.lucko.luckperms.api.implementation.internal; import lombok.AllArgsConstructor; import lombok.NonNull; +import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.PermissionHolder; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectLacksException; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import static me.lucko.luckperms.api.implementation.internal.Utils.*; +import static me.lucko.luckperms.core.PermissionHolder.convertToLegacy; /** * Provides a link between {@link PermissionHolder} and {@link me.lucko.luckperms.core.PermissionHolder} @@ -49,9 +49,24 @@ public class PermissionHolderLink implements PermissionHolder { return master.getObjectName(); } + @Override + public Set getPermissionNodes() { + return Collections.unmodifiableSet(master.getNodes()); + } + + @Override + public Set getAllNodes() { + return Collections.unmodifiableSet(master.getAllNodes(null)); + } + @Override public Map getNodes() { - return Collections.unmodifiableMap(master.getNodes()); + return convertToLegacy(master.getNodes()); + } + + @Override + public boolean hasPermission(@NonNull Node node) { + return master.hasPermission(node); } @Override @@ -84,6 +99,11 @@ public class PermissionHolderLink implements PermissionHolder { return master.hasPermission(node, b, checkServer(server), world, temporary); } + @Override + public boolean inheritsPermission(@NonNull Node node) { + return master.inheritsPermission(node); + } + @Override public boolean inheritsPermission(@NonNull String node, @NonNull boolean b) { return master.inheritsPermission(node, b); @@ -114,6 +134,11 @@ public class PermissionHolderLink implements PermissionHolder { return master.inheritsPermission(node, b, checkServer(server), world, temporary); } + @Override + public void setPermission(@NonNull Node node) throws ObjectAlreadyHasException { + master.setPermission(node); + } + @Override public void setPermission(@NonNull String node, @NonNull boolean value) throws ObjectAlreadyHasException { master.setPermission(checkNode(node), value); @@ -144,6 +169,11 @@ public class PermissionHolderLink implements PermissionHolder { master.setPermission(checkNode(node), value, checkServer(server), world, checkTime(expireAt)); } + @Override + public void unsetPermission(@NonNull Node node) throws ObjectLacksException { + master.unsetPermission(node); + } + @Override public void unsetPermission(@NonNull String node, @NonNull boolean temporary) throws ObjectLacksException { master.unsetPermission(checkNode(node), temporary); @@ -194,13 +224,34 @@ public class PermissionHolderLink implements PermissionHolder { return master.getLocalPermissions(server, excludedGroups); } + @Override + public Map getPermissions(String server, String world, Map extraContext, boolean includeGlobal, List possibleNodes, boolean applyGroups) { + return master.getPermissions(server, world, extraContext, includeGlobal, possibleNodes, applyGroups); + } + @Override public Map, Long> getTemporaryNodes() { + Map, Long> m = new HashMap<>(); + + for (Node node : master.getTemporaryNodes()) { + m.put(new AbstractMap.SimpleEntry<>(node.getKey(), node.getValue()), node.getExpiryUnixTime()); + } + + return m; + } + + @Override + public Set getTemporaryPermissionNodes() { return master.getTemporaryNodes(); } @Override public Map getPermanentNodes() { + return convertToLegacy(master.getPermanentNodes()); + } + + @Override + public Set getPermanentPermissionNodes() { return master.getPermanentNodes(); } diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java index e43a7c171..5b9469670 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java @@ -29,27 +29,27 @@ import me.lucko.luckperms.api.User; import me.lucko.luckperms.utils.ArgumentChecker; @UtilityClass -class Utils { +public class Utils { - static void checkUser(User user) { + public static void checkUser(User user) { if (!(user instanceof UserLink)) { throw new IllegalStateException("User instance cannot be handled by this implementation."); } } - static void checkGroup(Group group) { + public static void checkGroup(Group group) { if (!(group instanceof GroupLink)) { throw new IllegalStateException("Group instance cannot be handled by this implementation."); } } - static void checkTrack(Track track) { + public static void checkTrack(Track track) { if (!(track instanceof TrackLink)) { throw new IllegalStateException("Track instance cannot be handled by this implementation."); } } - static String checkUsername(String s) { + public static String checkUsername(String s) { if (ArgumentChecker.checkUsername(s)) { throw new IllegalArgumentException("Invalid username entry '" + s + "'. Usernames must be less than 16 chars" + " and only contain 'a-z A-Z 1-9 _'."); @@ -57,7 +57,7 @@ class Utils { return s; } - static String checkName(String s) { + public static String checkName(String s) { if (ArgumentChecker.checkName(s)) { throw new IllegalArgumentException("Invalid name entry '" + s + "'. Names must be less than 37 chars" + " and only contain 'a-z A-Z 1-9'."); @@ -65,21 +65,21 @@ class Utils { return s.toLowerCase(); } - static String checkServer(String s) { + public static String checkServer(String s) { if (ArgumentChecker.checkServer(s)) { throw new IllegalArgumentException("Invalid server entry '" + s + "'. Server names can only contain alphanumeric characters."); } return s; } - static String checkNode(String s) { + public static String checkNode(String s) { if (ArgumentChecker.checkNode(s)) { throw new IllegalArgumentException("Invalid node entry '" + s + "'. Nodes cannot contain '/' or '$' characters."); } return s; } - static long checkTime(long l) { + public static long checkTime(long l) { if (ArgumentChecker.checkTime(l)) { throw new IllegalArgumentException("Unix time '" + l + "' is invalid, as it has already passed."); } diff --git a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupInfo.java b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupInfo.java index b317967d0..8da31ef54 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupInfo.java +++ b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupInfo.java @@ -43,8 +43,8 @@ public class GroupInfo extends SubCommand { public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List args, String label) { Message.GROUP_INFO.send(sender, group.getName(), - group.getPermanentNodes().keySet().size(), - group.getTemporaryNodes().keySet().size(), + group.getPermanentNodes().size(), + group.getTemporaryNodes().size(), label, group.getName() ); diff --git a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupListNodes.java b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupListNodes.java index bdefa900e..f4137543a 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupListNodes.java +++ b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupListNodes.java @@ -30,6 +30,8 @@ import me.lucko.luckperms.groups.Group; import java.util.List; +import static me.lucko.luckperms.core.PermissionHolder.convertToLegacy; + public class GroupListNodes extends SubCommand { public GroupListNodes() { super("listnodes", "Lists the permission nodes the group has", "/%s group listnodes", @@ -38,8 +40,8 @@ public class GroupListNodes extends SubCommand { @Override public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List args, String label) { - Message.LISTNODES.send(sender, group.getName(), Util.permNodesToString(group.getPermanentNodes())); - Message.LISTNODES_TEMP.send(sender, group.getName(), Util.tempNodesToString(group.getTemporaryNodes())); + Message.LISTNODES.send(sender, group.getName(), Util.permNodesToString(convertToLegacy(group.getPermanentNodes()))); + Message.LISTNODES_TEMP.send(sender, group.getName(), Util.tempNodesToString(group.getTemporaryNodesLegacy())); return CommandResult.SUCCESS; } } diff --git a/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserInfo.java b/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserInfo.java index feb4b89fc..b692abc62 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserInfo.java +++ b/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserInfo.java @@ -43,8 +43,8 @@ public class UserInfo extends SubCommand { plugin.getPlayerStatus(user.getUuid()), Util.listToCommaSep(user.getGroupNames()), user.getPrimaryGroup(), - user.getPermanentNodes().keySet().size(), - user.getTemporaryNodes().keySet().size(), + user.getPermanentNodes().size(), + user.getTemporaryNodes().size(), label, user.getName() ); diff --git a/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserListNodes.java b/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserListNodes.java index 9d0176f9e..80a15847d 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserListNodes.java +++ b/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserListNodes.java @@ -26,10 +26,13 @@ import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.commands.*; import me.lucko.luckperms.constants.Message; import me.lucko.luckperms.constants.Permission; +import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.users.User; import java.util.List; +import static me.lucko.luckperms.core.PermissionHolder.*; + public class UserListNodes extends SubCommand { public UserListNodes() { super("listnodes", "Lists the permission nodes the user has", "/%s user listnodes", @@ -38,8 +41,8 @@ public class UserListNodes extends SubCommand { @Override public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List args, String label) { - Message.LISTNODES.send(sender, user.getName(), Util.permNodesToString(user.getPermanentNodes())); - Message.LISTNODES_TEMP.send(sender, user.getName(), Util.tempNodesToString(user.getTemporaryNodes())); + Message.LISTNODES.send(sender, user.getName(), Util.permNodesToString(convertToLegacy(user.getPermanentNodes()))); + Message.LISTNODES_TEMP.send(sender, user.getName(), Util.tempNodesToString(user.getTemporaryNodesLegacy())); return CommandResult.SUCCESS; } } diff --git a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java index 03198de49..c652be3eb 100644 --- a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java @@ -26,20 +26,18 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.event.events.GroupRemoveEvent; -import me.lucko.luckperms.api.event.events.PermissionExpireEvent; -import me.lucko.luckperms.api.event.events.PermissionSetEvent; -import me.lucko.luckperms.api.event.events.PermissionUnsetEvent; +import me.lucko.luckperms.api.event.events.PermissionNodeExpireEvent; +import me.lucko.luckperms.api.event.events.PermissionNodeSetEvent; +import me.lucko.luckperms.api.event.events.PermissionNodeUnsetEvent; import me.lucko.luckperms.api.implementation.internal.PermissionHolderLink; -import me.lucko.luckperms.constants.Patterns; import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; import me.lucko.luckperms.exceptions.ObjectLacksException; import me.lucko.luckperms.groups.Group; -import me.lucko.luckperms.utils.DateUtil; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Pattern; import java.util.stream.Collectors; /** @@ -66,31 +64,42 @@ public abstract class PermissionHolder { * The user/group's permissions */ @Getter - private Map nodes = new ConcurrentHashMap<>(); + private Set nodes = ConcurrentHashMap.newKeySet(); - public void setNodes(Map nodes) { + public void setNodes(Set nodes) { this.nodes.clear(); - this.nodes.putAll(nodes); + this.nodes.addAll(nodes); auditTemporaryPermissions(); } - /** - * Utility method for checking if a map has a certain permission. Used by both #hasPermission and #inheritsPermission - */ - private static boolean hasPermission(Map toQuery, String node, boolean b) { - // Not temporary - if (!node.contains("$")) { - return b ? toQuery.containsKey(node) && toQuery.get(node) : toQuery.containsKey(node) && !toQuery.get(node); + @Deprecated + public static Map convertToLegacy(Set nodes) { + Map m = new HashMap<>(); + for (Node node : nodes) { + m.put(node.toSerializedNode(), node.getValue()); } + return Collections.unmodifiableMap(m); + } - node = Patterns.TEMP_DELIMITER.split(node)[0]; + private static Node.Builder buildNode(String permission) { + return new me.lucko.luckperms.utils.Node.Builder(permission); + } - for (Map.Entry 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(); - } + @Deprecated + public void setNodes(Map nodes) { + this.nodes.clear(); + + this.nodes.addAll(nodes.entrySet().stream() + .map(e -> me.lucko.luckperms.utils.Node.fromSerialisedNode(e.getKey(), e.getValue())) + .collect(Collectors.toList())); + + auditTemporaryPermissions(); + } + + private static boolean hasPermission(Set toQuery, Node node) { + for (Node n : toQuery) { + if (n.almostEquals(node)) { + return true; } } @@ -98,436 +107,171 @@ public abstract class PermissionHolder { } /** - * 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 + * Check if the holder has a permission node + * @param node the node to check + * @return true if the holder has the node */ + public boolean hasPermission(Node node) { + return hasPermission(this.nodes, node); + } + public boolean hasPermission(String node, boolean b) { - if (node.startsWith("global/")) node = node.replace("global/", ""); - return hasPermission(this.nodes, node, b); + return hasPermission(buildNode(node).setValue(b).build()); } - /** - * 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); + return hasPermission(buildNode(node).setValue(b).setServer(server).build()); } - /** - * 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); + return hasPermission(buildNode(node).setValue(b).setServer(server).setWorld(world).build()); } - /** - * 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); + return hasPermission(buildNode(node).setValue(b).setExpiry(temporary ? 10L : 0L).build()); } - /** - * 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); + return hasPermission(buildNode(node).setValue(b).setServer(server).setExpiry(temporary ? 10L : 0L).build()); } - /** - * 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); + return hasPermission(buildNode(node).setValue(b).setServer(server).setWorld(world).setExpiry(temporary ? 10L : 0L).build()); } /** - * 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 + * Check if the holder inherits a node + * @param node the node to check + * @return true if the holder inherits the node */ + public boolean inheritsPermission(Node node) { + return hasPermission(getAllNodes(null), node); + } + 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"); + return inheritsPermission(buildNode(node).setValue(b).build()); } - /** - * 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 local = getLocalPermissions(server, null); - return hasPermission(local, node, b); + return inheritsPermission(buildNode(node).setValue(b).setServer(server).build()); } - /** - * 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 local = getLocalPermissions(server, world, null); - return hasPermission(local, node, b); + return inheritsPermission(buildNode(node).setValue(b).setServer(server).setWorld(world).build()); } - /** - * 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); + return inheritsPermission(buildNode(node).setValue(b).setExpiry(temporary ? 10L : 0L).build()); } - /** - * 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); + return inheritsPermission(buildNode(node).setValue(b).setServer(server).setExpiry(temporary ? 10L : 0L).build()); } - /** - * 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); + return inheritsPermission(buildNode(node).setValue(b).setServer(server).setWorld(world).setExpiry(temporary ? 10L : 0L).build()); } - /** - * 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 { - setPermission(node, value, null, null, 0L); - } - - /** - * 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(node, value, server, null, 0L); - } - - /** - * 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(node, value, server, world, 0L); - } - - /** - * 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, value, null, null, expireAt); - } - - /** - * 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, value, server, null, expireAt); - } - - /** - * 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 { - if (node.startsWith("global/")) node = node.replace("global/", ""); - - if (server != null && server.equals("")) server = null; - if (world != null && world.equals("")) world = null; - - StringBuilder builder = new StringBuilder(); - - if (server != null) { - builder.append(server); - - if (world != null) { - builder.append("-").append(world); - } - builder.append("/"); - } else { - if (world != null) { - builder.append("global-").append(world).append("/"); - } - } - - builder.append(node); - - if (expireAt != 0L) { - builder.append("$").append(expireAt); - } - - final String finalNode = builder.toString(); - - - if (hasPermission(finalNode, value)) { + public void setPermission(Node node) throws ObjectAlreadyHasException { + if (hasPermission(node)) { throw new ObjectAlreadyHasException(); } - this.nodes.put(finalNode, value); - plugin.getApiProvider().fireEventAsync(new PermissionSetEvent( - new PermissionHolderLink(this), node, value, server, world, expireAt)); + nodes.add(node); + plugin.getApiProvider().fireEventAsync(new PermissionNodeSetEvent(new PermissionHolderLink(this), node)); + } + + public void setPermission(String node, boolean value) throws ObjectAlreadyHasException { + setPermission(buildNode(node).setValue(value).build()); + } + + public void setPermission(String node, boolean value, String server) throws ObjectAlreadyHasException { + setPermission(buildNode(node).setValue(value).setServer(server).build()); + } + + public void setPermission(String node, boolean value, String server, String world) throws ObjectAlreadyHasException { + setPermission(buildNode(node).setValue(value).setServer(server).setWorld(world).build()); + } + + public void setPermission(String node, boolean value, long expireAt) throws ObjectAlreadyHasException { + setPermission(buildNode(node).setValue(value).setExpiry(expireAt).build()); + } + + public void setPermission(String node, boolean value, String server, long expireAt) throws ObjectAlreadyHasException { + setPermission(buildNode(node).setValue(value).setServer(server).setExpiry(expireAt).build()); + } + + public void setPermission(String node, boolean value, String server, String world, long expireAt) throws ObjectAlreadyHasException { + setPermission(buildNode(node).setValue(value).setServer(server).setWorld(world).setExpiry(expireAt).build()); } /** - * 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 + * Unsets a permission node + * @param node the node to unset + * @throws ObjectLacksException if the holder doesn't have this node already */ - public void unsetPermission(String node, boolean temporary) throws ObjectLacksException { - unsetPermission(node, null, null, temporary); - } - - /** - * 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, null, null, false); - } - - /** - * 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(node, server, null, false); - } - - /** - * 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(node, server, world, false); - } - - /** - * 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(node, server, null, 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 { - if (node.startsWith("global/")) node = node.replace("global/", ""); - - if (server != null && server.equals("")) server = null; - if (world != null && world.equals("")) world = null; - - StringBuilder builder = new StringBuilder(); - - if (server != null) { - builder.append(server); - - if (world != null) { - builder.append("-").append(world); - } - builder.append("/"); - } else { - if (world != null) { - builder.append("global-").append(world).append("/"); - } - } - - builder.append(node); - - final String finalNode = builder.toString(); - Optional match = Optional.empty(); - - if (temporary) { - match = this.nodes.keySet().stream() - .filter(n -> n.contains("$")) - .filter(n -> Patterns.TEMP_DELIMITER.split(n)[0].equalsIgnoreCase(finalNode)) - .findFirst(); - } else { - if (this.nodes.containsKey(finalNode)) { - match = Optional.of(finalNode); - } - } - - if (match.isPresent()) { - this.nodes.remove(match.get()); - plugin.getApiProvider().fireEventAsync(new PermissionUnsetEvent( - new PermissionHolderLink(this), node, server, world, temporary)); - if (node.startsWith("group.")) { - plugin.getApiProvider().fireEventAsync(new GroupRemoveEvent( - new PermissionHolderLink(this), Patterns.DOT.split(node, 2)[1], server, world, temporary)); - } - - } else { + public void unsetPermission(Node node) throws ObjectLacksException { + if (!hasPermission(node)) { throw new ObjectLacksException(); } + + nodes.remove(node); + + if (node.isGroupNode()) { + plugin.getApiProvider().fireEventAsync(new GroupRemoveEvent(new PermissionHolderLink(this), + node.getGroupName(), node.getServer().orElse(null), node.getWorld().orElse(null), node.isTemporary())); + } else { + plugin.getApiProvider().fireEventAsync(new PermissionNodeUnsetEvent(new PermissionHolderLink(this), node)); + } + } + + public void unsetPermission(String node, boolean temporary) throws ObjectLacksException { + unsetPermission(buildNode(node).setExpiry(temporary ? 10L : 0L).build()); + } + + public void unsetPermission(String node) throws ObjectLacksException { + unsetPermission(buildNode(node).build()); + } + + public void unsetPermission(String node, String server) throws ObjectLacksException { + unsetPermission(buildNode(node).setServer(server).build()); + } + + public void unsetPermission(String node, String server, String world) throws ObjectLacksException { + unsetPermission(buildNode(node).setServer(server).setWorld(world).build()); + } + + public void unsetPermission(String node, String server, boolean temporary) throws ObjectLacksException { + unsetPermission(buildNode(node).setServer(server).setExpiry(temporary ? 10L : 0L).build()); + } + + public void unsetPermission(String node, String server, String world, boolean temporary) throws ObjectLacksException { + unsetPermission(buildNode(node).setServer(server).setWorld(world).setExpiry(temporary ? 10L : 0L).build()); } /** - * 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 + * @return The temporary nodes held by the holder */ - public Map getLocalPermissions(String server, String world, List excludedGroups, List possibleNodes) { - return getPermissions(server, world, excludedGroups, plugin.getConfiguration().getIncludeGlobalPerms(), possibleNodes); + public Set getTemporaryNodes() { + return nodes.stream().filter(Node::isTemporary).collect(Collectors.toSet()); + } + + @Deprecated + public Map, Long> getTemporaryNodesLegacy() { + Map, Long> m = new HashMap<>(); + + for (Node node : getTemporaryNodes()) { + m.put(new AbstractMap.SimpleEntry<>(node.getKey(), node.getValue()), node.getExpiryUnixTime()); + } + + return m; } /** - * 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 + * @return The permanent nodes held by the holder */ - public Map getLocalPermissions(String server, String world, List 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 getLocalPermissions(String server, List excludedGroups, List 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 getLocalPermissions(String server, List excludedGroups) { - return getLocalPermissions(server, null, excludedGroups, null); - } - - /** - * Processes the objects and returns the temporary ones. - * @return a map of temporary nodes - */ - public Map, 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, 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 getPermanentNodes() { - return this.nodes.entrySet().stream().filter(e -> !e.getKey().contains("$")) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + public Set getPermanentNodes() { + return nodes.stream().filter(Node::isPermanent).collect(Collectors.toSet()); } /** @@ -535,384 +279,183 @@ public abstract class PermissionHolder { * @return true if permissions had expired and were removed */ public boolean auditTemporaryPermissions() { - List toExpire = this.nodes.keySet().stream() - .filter(s -> s.contains("$")) - .filter(s -> DateUtil.shouldExpire(Long.parseLong(Patterns.TEMP_DELIMITER.split(s)[1]))) - .collect(Collectors.toList()); + boolean work = false; + Iterator iterator = nodes.iterator(); - toExpire.forEach(s -> { - plugin.getApiProvider().fireEventAsync(new PermissionExpireEvent(new PermissionHolderLink(this), s)); - this.nodes.remove(s); - }); - return !toExpire.isEmpty(); - } + while (iterator.hasNext()) { + Node element = iterator.next(); + if (element.hasExpired()) { + iterator.remove(); - public Map convertTemporaryPerms() { - auditTemporaryPermissions(); - - Map nodes = new HashMap<>(); - Map tempNodes = new HashMap<>(); - - for (Map.Entry e : this.nodes.entrySet()) { - if (e.getKey().contains("$")) { - tempNodes.put(e.getKey(), e.getValue()); - } else { - nodes.put(e.getKey(), e.getValue()); + work = true; + plugin.getApiProvider().fireEventAsync(new PermissionNodeExpireEvent(new PermissionHolderLink(this), element)); } } - // temporary permissions override non-temporary permissions - tempNodes.entrySet().forEach(e -> nodes.put(stripTime(e.getKey()), e.getValue())); - return nodes; + return work; } - public Map getPermissions(String server, String world, List excludedGroups, boolean includeGlobal, List possibleNodes) { - return getPermissions(server, world, excludedGroups, includeGlobal, possibleNodes, true); - } + /** + * Gets all of the nodes that this holder has and inherits + * @param excludedGroups a list of groups to exclude + * @return a set of nodes + */ + public Set getAllNodes(List excludedGroups) { + Set all = ConcurrentHashMap.newKeySet(); + all.addAll(nodes); - public Map getPermissions(String server, String world, List excludedGroups, boolean includeGlobal, List possibleNodes, boolean applyGroups) { if (excludedGroups == null) { excludedGroups = new ArrayList<>(); } - excludedGroups.add(getObjectName()); + excludedGroups.add(getObjectName().toLowerCase()); + + Set parents = nodes.stream() + .filter(Node::isGroupNode) + .map(Node::getGroupName) + .collect(Collectors.toSet()); + + for (String parent : parents) { + Group group = plugin.getGroupManager().get(parent); + if (group == null) { + continue; + } + + if (excludedGroups.contains(group.getObjectName())) { + continue; + } + + inherited: + for (Node inherited : group.getAllNodes(excludedGroups)) { + for (Node existing : all) { + if (existing.almostEquals(inherited)) { + continue inherited; + } + } + + all.add(inherited); + } + } + + return all; + } + + /* + * Don't use these methods, only here for compat reasons + */ + public Map getLocalPermissions(String server, String world, List excludedGroups, List possibleNodes) { + return getPermissions(server, world, null, plugin.getConfiguration().getIncludeGlobalPerms(), possibleNodes, true); + } + + public Map getLocalPermissions(String server, String world, List excludedGroups) { + return getPermissions(server, world, null, plugin.getConfiguration().getIncludeGlobalPerms(), null, true); + } + + public Map getLocalPermissions(String server, List excludedGroups, List possibleNodes) { + return getLocalPermissions(server, null, excludedGroups, possibleNodes); + } + + public Map getLocalPermissions(String server, List excludedGroups) { + return getLocalPermissions(server, null, excludedGroups, null); + } + + /** + * Convert the holders nodes into a Map of permissions to be applied on the platform + * @param server the server + * @param world the world + * @param extraContext any extra context to filter by + * @param includeGlobal whether to include global nodes + * @param possibleNodes a list of possible permissions for resolving wildcards + * @param applyGroups if inherited group permissions should be included + * @return a map of permissions + */ + public Map getPermissions(String server, String world, Map extraContext, boolean includeGlobal, List possibleNodes, boolean applyGroups) { Map 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 serverWorldSpecificNodes = new HashMap<>(); - final Map serverSpecificNodes = new HashMap<>(); - final Map userNodes = new HashMap<>(); - final Map serverWorldSpecificGroups = new HashMap<>(); - final Map serverSpecificGroups = new HashMap<>(); - final Map groupNodes = new HashMap<>(); - - // Sorts the permissions and puts them into a priority order - for (Map.Entry 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]); - }); + SortedSet allNodes; if (applyGroups) { - // Apply lowest priority: groupNodes - for (Map.Entry 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().get(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 m : Arrays.asList(serverSpecificGroups, serverWorldSpecificGroups)) { - for (Map.Entry 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().get(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); - } + allNodes = sort(getAllNodes(null), true); + } else { + allNodes = sort(nodes, true); } - // Apply next priority: userNodes - perms.putAll(userNodes); - applyShorthandIfEnabled(perms); - - // Apply final priorities: serverSpecificNodes and then serverWorldSpecificNodes - for (Map m : Arrays.asList(serverSpecificNodes, serverWorldSpecificNodes)) { - for (Map.Entry node : m.entrySet()) { - final String rawNode = Patterns.SERVER_DELIMITER.split(node.getKey())[1]; - perms.put(rawNode, node.getValue()); + for (Node node : allNodes) { + if (!node.shouldApplyOnServer(server, includeGlobal, plugin.getConfiguration().getApplyRegex())) { + continue; } - applyShorthandIfEnabled(perms); - } - if (plugin.getConfiguration().getApplyRegex()) { - if (possibleNodes != null && !possibleNodes.isEmpty()) { - perms = applyRegex(perms, possibleNodes); - } else { - perms = applyRegex(perms, plugin.getPossiblePermissions()); + if (!node.shouldApplyOnWorld(world, includeGlobal, plugin.getConfiguration().getApplyRegex())) { + continue; } - } - applyShorthandIfEnabled(perms); + if (!node.shouldApplyWithContext(extraContext)) { + continue; + } - if (plugin.getConfiguration().getApplyWildcards()) { - if (possibleNodes != null && !possibleNodes.isEmpty()) { - perms = applyWildcards(perms, possibleNodes); - } else { - perms = applyWildcards(perms, plugin.getPossiblePermissions()); + perms.put(node.getPermission(), node.getValue()); + + if (plugin.getConfiguration().getApplyShorthand()) { + node.resolveShorthand().stream() + .filter(s -> !perms.containsKey(s)) + .forEach(s -> perms.put(s, node.getValue())); + } + + if (plugin.getConfiguration().getApplyWildcards()) { + node.resolveWildcard(possibleNodes).stream() + .filter(s -> !perms.containsKey(s)) + .forEach(s -> perms.put(s, node.getValue())); } } return perms; } - private boolean matches(String entry, String possibleRegex) { - if (possibleRegex.toLowerCase().startsWith("r=") && plugin.getConfiguration().getApplyRegex()) { - Pattern p = Patterns.compile(possibleRegex.substring(2)); - if (p == null) { - return false; + public static SortedSet sort(Set toSort, boolean reversed) { + TreeSet set = new TreeSet<>(reversed ? PRIORITY_COMPARATOR.reversed() : PRIORITY_COMPARATOR); + set.addAll(toSort); + return set; + } + + private static final PriorityComparator PRIORITY_COMPARATOR = new PriorityComparator(); + private static class PriorityComparator implements Comparator { + + @Override + public int compare(Node o1, Node o2) { + if (takesPriority(o1, o2)) { + return 1; } - return p.matcher(entry).matches(); + + if (takesPriority(o2, o1)) { + return -1; + } + + return 1; } - if (possibleRegex.startsWith("(") && possibleRegex.endsWith(")") && possibleRegex.contains("|")) { - final String bits = possibleRegex.substring(1, possibleRegex.length() - 1); - String[] parts = Patterns.VERTICAL_BAR.split(bits); + public boolean takesPriority(Node target, Node over) { + if (target.isTemporary() && !over.isTemporary()) { + return true; + } - for (String s : parts) { - if (s.equalsIgnoreCase(entry)) { + if (target.isWorldSpecific() && !over.isWorldSpecific()) { + return true; + } + + if (target.isServerSpecific() && !over.isServerSpecific()) { + return true; + } + + if (!target.isWildcard() && over.isWildcard()) { + return true; + } + + if (target.isWildcard() && over.isWildcard()) { + if (target.getWildcardLevel() > over.getWildcardLevel()) { return true; } } return false; } - - return entry.equalsIgnoreCase(possibleRegex); - } - - private Map applyRegex(Map input, List possibleNodes) { - for (Map.Entry 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; - } - - // TODO Support the "Sponge way" of doing wildcards - private static Map applyWildcards(Map input, List possibleNodes) { - SortedMap> wildcards = new TreeMap<>(Collections.reverseOrder()); - for (Map.Entry 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> 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 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 map) { - if (plugin.getConfiguration().getApplyShorthand()) { - applyShorthand(map); - } - } - - private static Map applyShorthand(Map input) { - for (Map.Entry 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> 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 nodes = new HashSet<>(); - for (Set set : nodeParts) { - final Set 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; } } diff --git a/common/src/main/java/me/lucko/luckperms/groups/Group.java b/common/src/main/java/me/lucko/luckperms/groups/Group.java index 286e0bc90..a613062c0 100644 --- a/common/src/main/java/me/lucko/luckperms/groups/Group.java +++ b/common/src/main/java/me/lucko/luckperms/groups/Group.java @@ -321,7 +321,7 @@ public class Group extends PermissionHolder implements Identifiable { */ private List getGroups(String server, String world, boolean includeGlobal) { // Call super #getPermissions method, and just sort through those - Map perms = getPermissions(server, world, null, includeGlobal, null); + Map perms = getPermissions(server, world, null, includeGlobal, null, true); return perms.keySet().stream() .filter(s -> Patterns.GROUP_MATCH.matcher(s).matches()) .map(s -> Patterns.DOT.split(s, 2)[1]) diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java index 926084af5..c20c0b652 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java @@ -28,11 +28,13 @@ import lombok.Cleanup; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.api.LogEntry; import me.lucko.luckperms.constants.Constants; +import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.data.Log; import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.storage.Datastore; import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.users.User; +import me.lucko.luckperms.utils.Node; import java.io.*; import java.util.*; @@ -41,6 +43,8 @@ import java.util.logging.*; import java.util.logging.Formatter; import java.util.stream.Collectors; +import static me.lucko.luckperms.core.PermissionHolder.*; + @SuppressWarnings({"ResultOfMethodCallIgnored", "UnnecessaryLocalVariable"}) public class FlatfileDatastore extends Datastore { private static final String LOG_FORMAT = "%s(%s): [%s] %s(%s) --> %s"; @@ -184,7 +188,7 @@ public class FlatfileDatastore extends Datastore { writer.name("primaryGroup").value(user.getPrimaryGroup()); writer.name("perms"); writer.beginObject(); - for (Map.Entry e : user.getNodes().entrySet()) { + for (Map.Entry e : convertToLegacy(user.getNodes()).entrySet()) { writer.name(e.getKey()).value(e.getValue().booleanValue()); } writer.endObject(); @@ -209,7 +213,7 @@ public class FlatfileDatastore extends Datastore { while (reader.hasNext()) { String node = reader.nextName(); boolean b = reader.nextBoolean(); - user.getNodes().put(node, b); + user.getNodes().add(Node.fromSerialisedNode(node, b)); } reader.endObject(); @@ -225,7 +229,7 @@ public class FlatfileDatastore extends Datastore { writer.name("primaryGroup").value(user.getPrimaryGroup()); writer.name("perms"); writer.beginObject(); - for (Map.Entry e : user.getNodes().entrySet()) { + for (Map.Entry e : convertToLegacy(user.getNodes()).entrySet()) { writer.name(e.getKey()).value(e.getValue().booleanValue()); } writer.endObject(); @@ -260,7 +264,7 @@ public class FlatfileDatastore extends Datastore { while (reader.hasNext()) { String node = reader.nextName(); boolean b = reader.nextBoolean(); - user.getNodes().put(node, b); + user.getNodes().add(Node.fromSerialisedNode(node, b)); } reader.endObject(); @@ -291,7 +295,7 @@ public class FlatfileDatastore extends Datastore { writer.name("primaryGroup").value(user.getPrimaryGroup()); writer.name("perms"); writer.beginObject(); - for (Map.Entry e : user.getNodes().entrySet()) { + for (Map.Entry e : convertToLegacy(user.getNodes()).entrySet()) { writer.name(e.getKey()).value(e.getValue().booleanValue()); } writer.endObject(); @@ -319,7 +323,7 @@ public class FlatfileDatastore extends Datastore { writer.name("name").value(group.getName()); writer.name("perms"); writer.beginObject(); - for (Map.Entry e : group.getNodes().entrySet()) { + for (Map.Entry e : convertToLegacy(group.getNodes()).entrySet()) { writer.name(e.getKey()).value(e.getValue().booleanValue()); } writer.endObject(); @@ -339,7 +343,7 @@ public class FlatfileDatastore extends Datastore { while (reader.hasNext()) { String node = reader.nextName(); boolean b = reader.nextBoolean(); - group.getNodes().put(node, b); + group.getNodes().add(Node.fromSerialisedNode(node, b)); } reader.endObject(); @@ -369,7 +373,7 @@ public class FlatfileDatastore extends Datastore { while (reader.hasNext()) { String node = reader.nextName(); boolean b = reader.nextBoolean(); - group.getNodes().put(node, b); + group.getNodes().add(Node.fromSerialisedNode(node, b)); } reader.endObject(); @@ -411,7 +415,7 @@ public class FlatfileDatastore extends Datastore { writer.name("name").value(group.getName()); writer.name("perms"); writer.beginObject(); - for (Map.Entry e : group.getNodes().entrySet()) { + for (Map.Entry e : convertToLegacy(group.getNodes()).entrySet()) { writer.name(e.getKey()).value(e.getValue().booleanValue()); } writer.endObject(); diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java index 8fb0ba102..f4ea4b62a 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java @@ -31,6 +31,7 @@ import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.InsertOneOptions; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.api.LogEntry; +import me.lucko.luckperms.core.PermissionHolder; import me.lucko.luckperms.data.Log; import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.groups.GroupManager; @@ -45,6 +46,8 @@ import java.util.*; import java.util.concurrent.Callable; import java.util.stream.Collectors; +import static me.lucko.luckperms.core.PermissionHolder.convertToLegacy; + @SuppressWarnings("unchecked") public class MongoDBDatastore extends Datastore { @@ -427,7 +430,7 @@ public class MongoDBDatastore extends Datastore { .append("primaryGroup", user.getPrimaryGroup()); Document perms = new Document(); - for (Map.Entry e : convert(user.getNodes()).entrySet()) { + for (Map.Entry e : convert(convertToLegacy(user.getNodes())).entrySet()) { perms.append(e.getKey(), e.getValue()); } @@ -439,7 +442,7 @@ public class MongoDBDatastore extends Datastore { Document main = new Document("_id", group.getName()); Document perms = new Document(); - for (Map.Entry e : convert(group.getNodes()).entrySet()) { + for (Map.Entry e : convert(convertToLegacy(group.getNodes())).entrySet()) { perms.append(e.getKey(), e.getValue()); } diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java index 497a346ff..42d675e55 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java @@ -109,7 +109,8 @@ abstract class SQLDatastore extends Datastore { boolean onResult(ResultSet resultSet) throws SQLException { if (resultSet.next()) { user.setName(resultSet.getString("name")); - user.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); + Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); + user.setNodes(nodes); user.setPrimaryGroup(resultSet.getString("primary_group")); return true; } @@ -193,7 +194,8 @@ abstract class SQLDatastore extends Datastore { } }); } else { - user.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); + Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); + user.setNodes(nodes); user.setPrimaryGroup(resultSet.getString("primary_group")); if (!resultSet.getString("name").equals(user.getName())) { @@ -251,7 +253,8 @@ abstract class SQLDatastore extends Datastore { } }); } else { - group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); + Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); + group.setNodes(nodes); } return success; } @@ -273,7 +276,8 @@ abstract class SQLDatastore extends Datastore { @Override boolean onResult(ResultSet resultSet) throws SQLException { if (resultSet.next()) { - group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); + Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); + group.setNodes(nodes); return true; } return false; @@ -297,7 +301,8 @@ abstract class SQLDatastore extends Datastore { boolean onResult(ResultSet resultSet) throws SQLException { while (resultSet.next()) { Group group = plugin.getGroupManager().make(resultSet.getString("name")); - group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); + Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); + group.setNodes(nodes); groups.add(group); } return true; diff --git a/common/src/main/java/me/lucko/luckperms/users/User.java b/common/src/main/java/me/lucko/luckperms/users/User.java index 26fdcc508..8e57f4175 100644 --- a/common/src/main/java/me/lucko/luckperms/users/User.java +++ b/common/src/main/java/me/lucko/luckperms/users/User.java @@ -288,7 +288,9 @@ public abstract class User extends PermissionHolder implements Identifiable getGroups(String server, String world, boolean includeGlobal) { // Call super #getPermissions method, and just sort through those - Map perms = getPermissions(server, world, null, includeGlobal, null); + Map perms = getPermissions(server, world, null, includeGlobal, null, true); return perms.keySet().stream() .filter(s -> Patterns.GROUP_MATCH.matcher(s).matches()) .map(s -> Patterns.DOT.split(s, 2)[1]) diff --git a/common/src/main/java/me/lucko/luckperms/utils/Node.java b/common/src/main/java/me/lucko/luckperms/utils/Node.java new file mode 100644 index 000000000..b7c24e445 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/utils/Node.java @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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 com.google.common.collect.ImmutableMap; +import lombok.*; +import me.lucko.luckperms.constants.Patterns; + +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * An immutable permission node + */ +@SuppressWarnings({"WeakerAccess", "unused"}) +@ToString +@EqualsAndHashCode +public class Node implements me.lucko.luckperms.api.Node { + public static me.lucko.luckperms.api.Node fromSerialisedNode(String s, Boolean b) { + return builderFromSerialisedNode(s, b).build(); + } + + public static me.lucko.luckperms.api.Node.Builder builderFromSerialisedNode(String s, Boolean b) { + if (s.contains("/")) { + String[] parts = Patterns.SERVER_DELIMITER.split(s, 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 (parts[1].contains("$")) { + String[] tempParts = Patterns.TEMP_DELIMITER.split(parts[1], 2); + return new Node.Builder(tempParts[0], true).setServerRaw(serverParts[0]).setWorld(serverParts[1]) + .setExpiry(Long.parseLong(tempParts[1])).setValue(b); + } else { + return new Node.Builder(parts[1], true).setServerRaw(serverParts[0]).setWorld(serverParts[1]).setValue(b); + } + + } else { + // SERVER BUT NOT WORLD SPECIFIC + if (parts[1].contains("$")) { + String[] tempParts = Patterns.TEMP_DELIMITER.split(parts[1], 2); + return new Node.Builder(tempParts[0], true).setServerRaw(parts[0]).setExpiry(Long.parseLong(tempParts[1])).setValue(b); + } else { + return new Node.Builder(parts[1], true).setServerRaw(parts[0]).setValue(b); + } + } + } else { + // NOT SERVER SPECIFIC + if (s.contains("$")) { + String[] tempParts = Patterns.TEMP_DELIMITER.split(s, 2); + return new Node.Builder(tempParts[0], true).setExpiry(Long.parseLong(tempParts[1])).setValue(b); + } else { + return new Node.Builder(s, true).setValue(b); + } + } + } + + @Getter + private final String permission; + + @Getter + private Boolean value; + + private String server = null; + private String world = null; + + private long expireAt = 0L; + + private final Map extraContexts = new HashMap<>(); + + /** + * Make an immutable node instance + * @param permission the actual permission node + * @param value the value (if it's *not* negated) + * @param expireAt the time when the node will expire + * @param server the server this node applies on + * @param world the world this node applies on + * @param extraContexts any additional contexts applying to this node + */ + public Node(String permission, boolean value, long expireAt, String server, String world, Map extraContexts) { + if (permission == null || permission.equals("")) { + throw new IllegalArgumentException("Empty permission"); + } + + if (server != null && (server.equalsIgnoreCase("global") || server.equals(""))) { + server = null; + } + + if (world != null && world.equals("")) { + world = null; + } + + if (world != null && server == null) { + server = "global"; + } + + this.permission = permission; + this.value = value; + this.expireAt = expireAt; + this.server = server; + this.world = world; + + if (extraContexts != null) { + this.extraContexts.putAll(extraContexts); + } + } + + public boolean isNegated() { + return !value; + } + + public Optional getServer() { + return Optional.ofNullable(server); + } + + public Optional getWorld() { + return Optional.ofNullable(world); + } + + public boolean isServerSpecific() { + return getServer().isPresent(); + } + + public boolean isWorldSpecific() { + return getWorld().isPresent(); + } + + @Override + public boolean shouldApplyOnServer(String server, boolean includeGlobal, boolean applyRegex) { + if (server == null || server.equals("")) { + return true; + } + + if (isServerSpecific()) { + if (server.toLowerCase().startsWith("r=") && applyRegex) { + Pattern p = Patterns.compile(server.substring(2)); + if (p == null) { + return false; + } + return p.matcher(this.server).matches(); + } + + if (server.startsWith("(") && server.endsWith(")") && server.contains("|")) { + final String bits = server.substring(1, server.length() - 1); + String[] parts = Patterns.VERTICAL_BAR.split(bits); + + for (String s : parts) { + if (s.equalsIgnoreCase(this.server)) { + return true; + } + } + + return false; + } + + return this.server.equalsIgnoreCase(server); + } else { + return includeGlobal; + } + } + + @Override + public boolean shouldApplyOnWorld(String world, boolean includeGlobal, boolean applyRegex) { + if (world == null || world.equals("")) { + return true; + } + + if (isWorldSpecific()) { + if (world.toLowerCase().startsWith("r=") && applyRegex) { + Pattern p = Patterns.compile(world.substring(2)); + if (p == null) { + return false; + } + return p.matcher(this.world).matches(); + } + + if (world.startsWith("(") && world.endsWith(")") && world.contains("|")) { + final String bits = world.substring(1, world.length() - 1); + String[] parts = Patterns.VERTICAL_BAR.split(bits); + + for (String s : parts) { + if (s.equalsIgnoreCase(this.world)) { + return true; + } + } + + return false; + } + + return this.world.equalsIgnoreCase(world); + } else { + return includeGlobal; + } + } + + @Override + public boolean shouldApplyWithContext(Map context) { + if (context == null || context.isEmpty()) { + return true; + } + + for (Map.Entry c : context.entrySet()) { + if (!getExtraContexts().containsKey(c.getKey())) { + return false; + } + + if (!getExtraContexts().get(c.getKey()).equalsIgnoreCase(c.getValue())) { + return false; + } + } + + return true; + } + + @Override + public boolean shouldApplyOnAnyServers(List servers, boolean includeGlobal) { + for (String s : servers) { + if (shouldApplyOnServer(s, includeGlobal, false)) { + return true; + } + } + + return false; + } + + @Override + public boolean shouldApplyOnAnyWorlds(List worlds, boolean includeGlobal) { + for (String s : worlds) { + if (shouldApplyOnWorld(s, includeGlobal, false)) { + return true; + } + } + + return false; + } + + @Override + public List resolveWildcard(List possibleNodes) { + if (!isWildcard() || possibleNodes == null) { + return Collections.emptyList(); + } + + String match = getPermission().substring(0, getPermission().length() - 2); + return possibleNodes.stream().filter(pn -> pn.startsWith(match)).collect(Collectors.toList()); + } + + @Override + public List resolveShorthand() { + if (!Patterns.SHORTHAND_NODE.matcher(getPermission()).find()) { + return Collections.emptyList(); + } + + if (!getPermission().contains(".")) { + return Collections.emptyList(); + } + + String[] parts = Patterns.DOT.split(getPermission()); + List> 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 nodes = new HashSet<>(); + for (Set set : nodeParts) { + final Set 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; + } + + return new ArrayList<>(nodes); + } + + public boolean isTemporary() { + return expireAt != 0L; + } + + public boolean isPermanent() { + return !isTemporary(); + } + + public long getExpiryUnixTime(){ + return expireAt; + } + + public Date getExpiry() { + return new Date(expireAt * 1000L); + } + + public long getSecondsTilExpiry() { + return expireAt - (System.currentTimeMillis() / 1000L); + } + + public boolean hasExpired() { + return expireAt < (System.currentTimeMillis() / 1000L); + } + + public Map getExtraContexts() { + return ImmutableMap.copyOf(extraContexts); + } + + public String toSerializedNode() { + StringBuilder builder = new StringBuilder(); + + if (server != null) { + builder.append(server); + + if (world != null) { + builder.append("-").append(world); + } + builder.append("/"); + } else { + if (world != null) { + builder.append("global-").append(world).append("/"); + } + } + + if (!extraContexts.isEmpty()) { + builder.append("("); + for (Map.Entry entry : extraContexts.entrySet()) { + builder.append(entry.getKey()).append("=").append(entry.getValue()).append(","); + } + + builder.deleteCharAt(builder.length() - 1); + builder.append(")"); + } + + builder.append(permission); + + if (expireAt != 0L) { + builder.append("$").append(expireAt); + } + + return builder.toString(); + } + + @Override + public boolean isGroupNode() { + return Patterns.GROUP_MATCH.matcher(getPermission()).matches(); + } + + @Override + public String getGroupName() { + if (!isGroupNode()) { + throw new IllegalStateException("This is not a group node"); + } + + return getPermission().substring("group.".length()); + } + + @Override + public boolean isWildcard() { + return getPermission().endsWith(".*"); + } + + @Override + public int getWildcardLevel() { + return (int) getPermission().chars().filter(num -> num == Character.getNumericValue('.')).count(); + } + + @Override + public boolean almostEquals(me.lucko.luckperms.api.Node other) { + if (!other.getPermission().equalsIgnoreCase(this.getPermission())) { + return false; + } + + if (other.getServer().isPresent() != this.getServer().isPresent()) { + if (other.getServer().isPresent()) { + if (!other.getServer().get().equalsIgnoreCase(this.getServer().get())) { + return false; + } + } + } else { + return false; + } + + if (other.getWorld().isPresent() != this.getWorld().isPresent()) { + if (other.getWorld().isPresent()) { + if (!other.getWorld().get().equalsIgnoreCase(this.getWorld().get())) { + return false; + } + } + } else { + return false; + } + + if (!other.getExtraContexts().equals(this.getExtraContexts())) { + return false; + } + + if (other.isTemporary() != this.isTemporary()) { + return false; + } + + return true; + } + + @Override + public Boolean setValue(Boolean value) { + throw new UnsupportedOperationException(); + } + + @Override + public String getKey() { + return getPermission(); + } + + @RequiredArgsConstructor + public static class Builder implements me.lucko.luckperms.api.Node.Builder { + private final String permission; + private Boolean value = true; + private String server = null; + private String world = null; + private long expireAt = 0L; + + private final Map extraContexts = new HashMap<>(); + + Builder(String permission, boolean shouldConvertContexts) { + if (!shouldConvertContexts) { + this.permission = permission; + } else { + if (!Patterns.NODE_CONTEXTS.matcher(permission).matches()) { + this.permission = permission; + } else { + String[] contextParts = permission.substring(1).split("\\)", 2); + // 0 = context, 1 = node + this.permission = contextParts[1]; + + for (String s : contextParts[0].split("\\,")) { + if (!s.contains("=")) { + // Not valid + continue; + } + + String[] context = s.split("\\=", 2); + extraContexts.put(context[0], context[1]); + } + } + } + } + + @Override + public me.lucko.luckperms.api.Node.Builder setNegated(boolean negated) { + value = !negated; + return this; + } + + @Override + public me.lucko.luckperms.api.Node.Builder setValue(boolean value) { + this.value = value; + return this; + } + + @Override + public me.lucko.luckperms.api.Node.Builder setExpiry(long expireAt) { + this.expireAt = expireAt; + return this; + } + + @Override + public me.lucko.luckperms.api.Node.Builder setWorld(String world) { + this.world = world; + return this; + } + + @Override + public me.lucko.luckperms.api.Node.Builder setServer(String server) { + if (server != null && ArgumentChecker.checkServer(server)) { + throw new IllegalArgumentException("Server name invalid."); + } + + this.server = server; + return this; + } + + public me.lucko.luckperms.api.Node.Builder setServerRaw(String server) { + this.server = server; + return this; + } + + @Override + public me.lucko.luckperms.api.Node.Builder withExtraContext(@NonNull String key, @NonNull String value) { + this.extraContexts.put(key, value); + return this; + } + + @Override + public me.lucko.luckperms.api.Node build() { + return new Node(permission, value, expireAt, server, world, extraContexts); + } + } + +} diff --git a/pom.xml b/pom.xml index f4a108835..d75a8ca30 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.lucko.luckperms luckperms - 2.5-SNAPSHOT + 2.6-SNAPSHOT common api diff --git a/sponge/pom.xml b/sponge/pom.xml index 9770299c8..44c49e3b8 100644 --- a/sponge/pom.xml +++ b/sponge/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.5-SNAPSHOT + 2.6-SNAPSHOT 4.0.0 diff --git a/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java b/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java index a30dd1d5b..7f2043385 100644 --- a/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java @@ -153,6 +153,10 @@ public class LuckPermsSubject implements Subject { @Override public Map, Map> getAllPermissions() { + return null; + // TODO + + /* Map nodes = holder.convertTemporaryPerms(); Map, Map> permissions = new HashMap<>(); @@ -286,6 +290,7 @@ public class LuckPermsSubject implements Subject { } return permissions; + */ } @Override