From e14ea7362666bd846878f37432a244ad6db11e79 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 7 Aug 2016 01:16:05 +0200 Subject: [PATCH] Add wildcard permissions --- README.md | 25 +++-- api/pom.xml | 2 +- .../lucko/luckperms/api/LPConfiguration.java | 5 + .../lucko/luckperms/api/PermissionHolder.java | 23 ++++- bukkit/pom.xml | 2 +- .../me/lucko/luckperms/LPBukkitPlugin.java | 20 +++- bukkit/src/main/resources/config.yml | 5 + bungee/pom.xml | 2 +- .../me/lucko/luckperms/BungeeListener.java | 28 +++++- .../me/lucko/luckperms/LPBungeePlugin.java | 7 ++ .../me/lucko/luckperms/users/BungeeUser.java | 20 +--- bungee/src/main/resources/config.yml | 5 + common/pom.xml | 2 +- .../me/lucko/luckperms/LuckPermsPlugin.java | 6 ++ .../internal/LPConfigurationLink.java | 5 + .../internal/PermissionHolderLink.java | 10 ++ .../lucko/luckperms/constants/Permission.java | 99 ++++++++----------- .../java/me/lucko/luckperms/groups/Group.java | 2 +- .../java/me/lucko/luckperms/users/User.java | 2 +- .../luckperms/utils/LPConfiguration.java | 4 + .../luckperms/utils/PermissionHolder.java | 81 ++++++++++++++- pom.xml | 2 +- sponge/pom.xml | 2 +- .../me/lucko/luckperms/LPSpongePlugin.java | 8 ++ sponge/src/main/resources/luckperms.conf | 5 + 25 files changed, 265 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 5ad5ee6da..8626b677f 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ A permissions implementation for Bukkit/Spigot, BungeeCord and Sponge. ## Features * **Group inheritance** - users can be members of multiple groups, groups can inherit other groups * **Temporary permissions** - users/groups can be given permissions that expire after a given time +* **Wildcard permissions** - users/groups can be given wildcard permissions (e.g. "minecraft.command.*"), even when plugins haven't implemented their own wildcards. * **Temporary groups** - users/groups can be added to/inherit other groups temporarily * **Multi-server support** - data is synced across all servers/platforms * **Full offline-mode/mixed-mode support** - player permissions are synced properly over offline-mode or mixed online/offline-mode networks. @@ -34,23 +35,31 @@ You can define the settings for per-server permissions, the storage method and c ## Info ### Permission Calculation -Permissions are calculated based on a priority system as follows. +#### Permissions are calculated based on a priority system as follows. -* Temporary permissions will override non-temporary permissions. +* **Non wildcard permissions will override wildcard permissions** + +Example: if a user has a true permission set for "luckperms.\*", and a false permission set for "luckperms.something", the non-wildcard permission will override the wildcard, and "luckperms.something" will be set to false, despite the wildcard. + +* **More specific wildcards override less specific ones** + +Example: if a user has "luckperms.\*" set to true, but "luckperms.user.\*" set to false, all of the user permissions will be set to false, despite the more generic wildcard for "luckperms.*". + +* **Temporary permissions will override non-temporary permissions.** Example: if a user has a false permission set for "test.node", and a temporary true permission set for "test.node", the temporary permission will override the permanent one, and the user will be granted the true node. -* World specific permissions will override generic permissions. +* **World specific permissions will override generic permissions.** Example: if a user has a global "fly.use" permission, and then has a negated "fly.use" permission in the "world_nether" world, the world specific permission will override the globally defined one, and the user will be granted the negated node (provided they're in that world, of course.). -* Server specific permissions will override generic/global permissions. +* **Server specific permissions will override generic/global permissions.** -Example: if a user has a global "fly.use" permission, and then has a negated "fly.use" permission on the "factions" server, the server specific permission will override the globally defined one, and the user will be granted the negated node. +Example: if a user has a global "fly.use" permission, and then has a negated "fly.use" permission on the "factions" server, the server specific permission will override the globally defined one, and the user will be granted the negated node (provided they're on that server). -* Inherited permissions will be overridden by an objects own permissions. +* **Inherited permissions will be overridden by an objects own permissions.** -Example: A user is a member of the default group, which grants "some.thing.perm", but the users own permissions has "some.thing.perm" set to false. The inherited permission will be overridden by the users own permissions, and the user will be granted the negative node (provided they're on that server). +Example: A user is a member of the default group, which grants "some.thing.perm", but the users own permissions has "some.thing.perm" set to false. The inherited permission will be overridden by the users own permissions, and the user will be granted the negative node. ### Temporary Permissions Temporary permissions are checked each time a user/group is loaded, and when the sync task runs. This means if you set a temporary permission to expire after 30 seconds, it won't actually be removed until the sync task runs. @@ -102,7 +111,7 @@ You can add LuckPerms as a Maven dependency by adding the following to your proj me.lucko.luckperms luckperms-api - 2.0 + 2.1 ```` diff --git a/api/pom.xml b/api/pom.xml index bbd16cab5..2b7b1a2e4 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.0 + 2.1 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 e6c300a07..05b9ae1d4 100644 --- a/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java +++ b/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java @@ -59,6 +59,11 @@ public interface LPConfiguration { */ boolean getOnlineMode(); + /** + * @return if LuckPerms is applying wildcard permissions + */ + boolean getApplyWildcards(); + /** * @return the database values set in the configuration */ 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 6d52cb6d0..0f5ae3e89 100644 --- a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java +++ b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java @@ -262,6 +262,16 @@ public interface PermissionHolder { */ void unsetPermission(String node, String server, String world, boolean temporary) throws ObjectLacksException; + /** + * 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 + */ + Map getLocalPermissions(String server, String world, List excludedGroups, List possibleNodes); + /** * Gets the permissions and inherited permissions that apply to a specific server * @param server The server to get nodes for @@ -273,8 +283,17 @@ public interface PermissionHolder { /** * Gets the permissions and inherited permissions that apply to a specific server - * @param server The server to get nodes for (can be null) - * @param excludedGroups Groups that shouldn't be inherited, can be null (to prevent circular inheritance issues) + * @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 + */ + Map getLocalPermissions(String server, List excludedGroups, List 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 */ Map getLocalPermissions(String server, List excludedGroups); diff --git a/bukkit/pom.xml b/bukkit/pom.xml index 9547cb0d2..84a9e3a23 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.0 + 2.1 4.0.0 diff --git a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java index 3bf2d9250..111984060 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java @@ -40,7 +40,6 @@ import me.lucko.luckperms.users.UserManager; import me.lucko.luckperms.utils.LPConfiguration; import me.lucko.luckperms.utils.LogUtil; import me.lucko.luckperms.utils.UuidCache; -import org.bukkit.Bukkit; import org.bukkit.command.PluginCommand; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; @@ -48,6 +47,7 @@ import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -71,7 +71,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { configuration = new BukkitConfig(this); // register events - PluginManager pm = Bukkit.getPluginManager(); + PluginManager pm = getServer().getPluginManager(); pm.registerEvents(new BukkitListener(this), this); // register commands @@ -155,12 +155,12 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { @Override public void doAsync(Runnable r) { - Bukkit.getScheduler().runTaskAsynchronously(this, r); + getServer().getScheduler().runTaskAsynchronously(this, r); } @Override public void doSync(Runnable r) { - Bukkit.getScheduler().runTask(this, r); + getServer().getScheduler().runTask(this, r); } @Override @@ -183,6 +183,18 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { return getServer().getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList()); } + @Override + public List getPossiblePermissions() { + final List perms = new ArrayList<>(); + + getServer().getPluginManager().getPermissions().forEach(p -> { + perms.add(p.getName()); + p.getChildren().keySet().forEach(perms::add); + }); + + return perms; + } + @Override public void runUpdateTask() { getServer().getScheduler().runTaskAsynchronously(this, new UpdateTask(this)); diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index 1d36f2173..3da9ad1b8 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -22,6 +22,11 @@ include-global: true # In this case, set online-mode to true no matter what is set in server.properties. (we can just fallback to the servers uuid cache) online-mode: true +# If the plugin should apply wildcard permissions. +# If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered permissions matching +# the wildcard. This will only work for plugins that define all of their permissions to the server. +apply-wildcards: true + # Which storage method the plugin should use. # Currently supported: mysql, sqlite, flatfile # Fill out connection info below if you're using MySQL diff --git a/bungee/pom.xml b/bungee/pom.xml index 76e58ddab..192490588 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.0 + 2.1 4.0.0 diff --git a/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java b/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java index 9dcafada3..c0ac23b77 100644 --- a/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java +++ b/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java @@ -29,14 +29,13 @@ import me.lucko.luckperms.utils.UuidCache; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.LoginEvent; -import net.md_5.bungee.api.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PostLoginEvent; -import net.md_5.bungee.api.event.ServerSwitchEvent; +import net.md_5.bungee.api.event.*; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.event.EventHandler; import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -49,6 +48,27 @@ class BungeeListener extends AbstractListener implements Listener { this.plugin = plugin; } + @EventHandler + public void onPlayerPermissionCheck(PermissionCheckEvent e) { + if (!(e.getSender() instanceof ProxiedPlayer)) { + return; + } + + final ProxiedPlayer player = ((ProxiedPlayer) e.getSender()); + final User user = plugin.getUserManager().getUser(plugin.getUuidCache().getUUID(player.getUniqueId())); + if (user == null) return; + + + final String server = player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName()); + Map local = user.getLocalPermissions(plugin.getConfiguration().getServer(), server, null, Collections.singletonList(e.getPermission())); + for (Map.Entry en : local.entrySet()) { + if (en.getKey().equalsIgnoreCase(e.getPermission())) { + e.setHasPermission(en.getValue()); + return; + } + } + } + @EventHandler public void onPlayerLogin(LoginEvent e) { /* Delay the login here, as we want to cache UUID data before the player is connected to a backend bukkit server. diff --git a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java index 233caa395..6378ff542 100644 --- a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java @@ -41,6 +41,7 @@ import me.lucko.luckperms.utils.UuidCache; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Plugin; +import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -143,6 +144,12 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { return getProxy().getPlayers().stream().map(ProxiedPlayer::getName).collect(Collectors.toList()); } + @Override + public List getPossiblePermissions() { + // No such thing on Bungee. Wildcards are processed in the listener instead. + return Collections.emptyList(); + } + @Override public void runUpdateTask() { doAsync(new UpdateTask(this)); diff --git a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java index e05edd852..762aa6e02 100644 --- a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java +++ b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java @@ -23,38 +23,20 @@ package me.lucko.luckperms.users; import me.lucko.luckperms.LPBungeePlugin; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; import java.util.UUID; public class BungeeUser extends User { - private final LPBungeePlugin plugin; - BungeeUser(UUID uuid, LPBungeePlugin plugin) { super(uuid, plugin); - this.plugin = plugin; } BungeeUser(UUID uuid, String username, LPBungeePlugin plugin) { super(uuid, username, plugin); - this.plugin = plugin; } @Override public void refreshPermissions() { - ProxiedPlayer player = plugin.getProxy().getPlayer(plugin.getUuidCache().getExternalUUID(getUuid())); - if (player == null) return; - - // Clear existing permissions - Collection perms = new ArrayList<>(player.getPermissions()); - perms.forEach(p -> player.setPermission(p, false)); - - // Re-add all defined permissions for the user - final String server = player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName()); - Map local = getLocalPermissions(getPlugin().getConfiguration().getServer(), server, null); - local.entrySet().forEach(e -> player.setPermission(e.getKey(), e.getValue())); + // Do nothing. Permissions are applied when needed in a listener. } } diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index e1d8a6eea..97cf85b09 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -22,6 +22,11 @@ include-global: false # In this case, set online-mode to true no matter what is set in server.properties. (we can just fallback to the servers uuid cache) online-mode: true +# If the plugin should apply wildcard permissions. +# If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered permissions matching +# the wildcard. This will only work for plugins that define all of their permissions to the server. +apply-wildcards: true + # Which storage method the plugin should use. # Currently supported: mysql & flatfile # Fill out connection info below if you're using MySQL diff --git a/common/pom.xml b/common/pom.xml index 29c6a407f..796301bf9 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.0 + 2.1 4.0.0 diff --git a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java index 20cf930ba..9662aec47 100644 --- a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java @@ -106,6 +106,12 @@ public interface LuckPermsPlugin { */ List getPlayerList(); + /** + * Gets all possible permission nodes, used for resolving wildcards + * @return a {@link List} of permission nodes + */ + List getPossiblePermissions(); + /** * Runs an update task */ diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java index ac33dfdc0..a8d028e78 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java @@ -63,6 +63,11 @@ public class LPConfigurationLink implements LPConfiguration { return master.getOnlineMode(); } + @Override + public boolean getApplyWildcards() { + return master.getApplyWildcards(); + } + @Override public MySQLConfiguration getDatabaseValues() { return master.getDatabaseValues(); 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 8df55992f..a8dbd67d4 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 @@ -175,11 +175,21 @@ class PermissionHolderLink implements PermissionHolder { master.unsetPermission(checkNode(node), checkServer(server), world, temporary); } + @Override + public Map getLocalPermissions(String server, String world, List excludedGroups, List possibleNodes) { + return master.getLocalPermissions(server, world, excludedGroups, possibleNodes); + } + @Override public Map getLocalPermissions(String server, String world, List excludedGroups) { return master.getLocalPermissions(server, world, excludedGroups); } + @Override + public Map getLocalPermissions(String server, List excludedGroups, List possibleNodes) { + return master.getLocalPermissions(server, excludedGroups, possibleNodes); + } + @Override public Map getLocalPermissions(String server, List excludedGroups) { return master.getLocalPermissions(server, excludedGroups); diff --git a/common/src/main/java/me/lucko/luckperms/constants/Permission.java b/common/src/main/java/me/lucko/luckperms/constants/Permission.java index cc356244d..a8506b2b9 100644 --- a/common/src/main/java/me/lucko/luckperms/constants/Permission.java +++ b/common/src/main/java/me/lucko/luckperms/constants/Permission.java @@ -23,7 +23,6 @@ package me.lucko.luckperms.constants; import lombok.AllArgsConstructor; -import lombok.Getter; import me.lucko.luckperms.commands.Sender; @SuppressWarnings("SpellCheckingInspection") @@ -42,74 +41,56 @@ public enum Permission { DELETE_TRACK("deletetrack", null), LIST_TRACKS("listtracks", null), - USER_INFO("info", PermissionGroup.USER), - USER_GETUUID("getuuid", PermissionGroup.USER), - USER_LISTNODES("listnodes", PermissionGroup.USER), - USER_HASPERMISSION("haspermission", PermissionGroup.USER), - USER_INHERITSPERMISSION("inheritspermission", PermissionGroup.USER), - USER_SETPERMISSION("setpermission", PermissionGroup.USER), - USER_UNSETPERMISSION("unsetpermission", PermissionGroup.USER), - USER_ADDGROUP("addgroup", PermissionGroup.USER), - USER_REMOVEGROUP("removegroup", PermissionGroup.USER), - USER_SET_TEMP_PERMISSION("settemppermission", PermissionGroup.USER), - USER_UNSET_TEMP_PERMISSION("unsettemppermission", PermissionGroup.USER), - USER_ADDTEMPGROUP("addtempgroup", PermissionGroup.USER), - USER_REMOVETEMPGROUP("removetempgroup", PermissionGroup.USER), - USER_SETPRIMARYGROUP("setprimarygroup", PermissionGroup.USER), - USER_SHOWTRACKS("showtracks", PermissionGroup.USER), - USER_PROMOTE("promote", PermissionGroup.USER), - USER_DEMOTE("demote", PermissionGroup.USER), - USER_SHOWPOS("showpos", PermissionGroup.USER), - USER_CLEAR("clear", PermissionGroup.USER), + USER_INFO("info", "user"), + USER_GETUUID("getuuid", "user"), + USER_LISTNODES("listnodes", "user"), + USER_HASPERMISSION("haspermission", "user"), + USER_INHERITSPERMISSION("inheritspermission", "user"), + USER_SETPERMISSION("setpermission", "user"), + USER_UNSETPERMISSION("unsetpermission", "user"), + USER_ADDGROUP("addgroup", "user"), + USER_REMOVEGROUP("removegroup", "user"), + USER_SET_TEMP_PERMISSION("settemppermission", "user"), + USER_UNSET_TEMP_PERMISSION("unsettemppermission", "user"), + USER_ADDTEMPGROUP("addtempgroup", "user"), + USER_REMOVETEMPGROUP("removetempgroup", "user"), + USER_SETPRIMARYGROUP("setprimarygroup", "user"), + USER_SHOWTRACKS("showtracks", "user"), + USER_PROMOTE("promote", "user"), + USER_DEMOTE("demote", "user"), + USER_SHOWPOS("showpos", "user"), + USER_CLEAR("clear", "user"), - GROUP_INFO("info", PermissionGroup.GROUP), - GROUP_LISTNODES("listnodes", PermissionGroup.GROUP), - GROUP_HASPERMISSION("haspermission", PermissionGroup.GROUP), - GROUP_INHERITSPERMISSION("inheritspermission", PermissionGroup.GROUP), - GROUP_SETPERMISSION("setpermission", PermissionGroup.GROUP), - GROUP_UNSETPERMISSION("unsetpermission", PermissionGroup.GROUP), - GROUP_SETINHERIT("setinherit", PermissionGroup.GROUP), - GROUP_UNSETINHERIT("unsetinherit", PermissionGroup.GROUP), - GROUP_SET_TEMP_PERMISSION("settemppermission", PermissionGroup.GROUP), - GROUP_UNSET_TEMP_PERMISSION("unsettemppermission", PermissionGroup.GROUP), - GROUP_SET_TEMP_INHERIT("settempinherit", PermissionGroup.GROUP), - GROUP_UNSET_TEMP_INHERIT("unsettempinherit", PermissionGroup.GROUP), - GROUP_SHOWTRACKS("showtracks", PermissionGroup.GROUP), - GROUP_CLEAR("clear", PermissionGroup.GROUP), + GROUP_INFO("info", "group"), + GROUP_LISTNODES("listnodes", "group"), + GROUP_HASPERMISSION("haspermission", "group"), + GROUP_INHERITSPERMISSION("inheritspermission", "group"), + GROUP_SETPERMISSION("setpermission", "group"), + GROUP_UNSETPERMISSION("unsetpermission", "group"), + GROUP_SETINHERIT("setinherit", "group"), + GROUP_UNSETINHERIT("unsetinherit", "group"), + GROUP_SET_TEMP_PERMISSION("settemppermission", "group"), + GROUP_UNSET_TEMP_PERMISSION("unsettemppermission", "group"), + GROUP_SET_TEMP_INHERIT("settempinherit", "group"), + GROUP_UNSET_TEMP_INHERIT("unsettempinherit", "group"), + GROUP_SHOWTRACKS("showtracks", "group"), + GROUP_CLEAR("clear", "group"), - TRACK_INFO("info", PermissionGroup.TRACK), - TRACK_APPEND("append", PermissionGroup.TRACK), - TRACK_INSERT("insert", PermissionGroup.TRACK), - TRACK_REMOVE("remove", PermissionGroup.TRACK), - TRACK_CLEAR("clear", PermissionGroup.TRACK); + TRACK_INFO("info", "track"), + TRACK_APPEND("append", "track"), + TRACK_INSERT("insert", "track"), + TRACK_REMOVE("remove", "track"), + TRACK_CLEAR("clear", "track"); private String node; - private PermissionGroup group; + private String group; public boolean isAuthorized(Sender sender) { - if (sender.hasPermission("luckperms.*")) { - return true; - } - if (group != null) { - return group.isAuthorized(sender) || sender.hasPermission("luckperms." + group.getNode() + "." + node); + return sender.hasPermission("luckperms." + group + "." + node); } return sender.hasPermission("luckperms." + node); } - @Getter - @AllArgsConstructor - private enum PermissionGroup { - USER("user"), - GROUP("group"), - TRACK("track"); - - private String node; - - private boolean isAuthorized(Sender sender) { - return sender.hasPermission("luckperms." + node + ".*"); - } - } - } 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 5005202c3..aa47a6597 100644 --- a/common/src/main/java/me/lucko/luckperms/groups/Group.java +++ b/common/src/main/java/me/lucko/luckperms/groups/Group.java @@ -282,7 +282,7 @@ public class Group extends PermissionHolder { */ 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); + Map perms = getPermissions(server, world, null, includeGlobal, null); 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/users/User.java b/common/src/main/java/me/lucko/luckperms/users/User.java index 8ef229e95..fb225df5e 100644 --- a/common/src/main/java/me/lucko/luckperms/users/User.java +++ b/common/src/main/java/me/lucko/luckperms/users/User.java @@ -313,7 +313,7 @@ public abstract class User extends PermissionHolder { */ 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); + Map perms = getPermissions(server, world, null, includeGlobal, null); 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/LPConfiguration.java b/common/src/main/java/me/lucko/luckperms/utils/LPConfiguration.java index 74a276008..9c95b8d03 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/LPConfiguration.java +++ b/common/src/main/java/me/lucko/luckperms/utils/LPConfiguration.java @@ -88,6 +88,10 @@ public abstract class LPConfiguration { return getBoolean("online-mode", true); } + public boolean getApplyWildcards() { + return getBoolean("apply-wildcards", true); + } + public MySQLConfiguration getDatabaseValues() { return new MySQLConfiguration( getString("sql.address", null), diff --git a/common/src/main/java/me/lucko/luckperms/utils/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/utils/PermissionHolder.java index d8f411c9e..74d55f537 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/utils/PermissionHolder.java @@ -396,6 +396,18 @@ public abstract class PermissionHolder { unsetPermission(server + "-" + world + "/" + node, temporary); } + /** + * Gets the permissions and inherited permissions that apply to a specific server + * @param server The server to get nodes for + * @param world The world to get nodes for + * @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues) + * @param possibleNodes A list of possible permission nodes for wildcard permission handling + * @return a {@link Map} of the permissions + */ + public Map getLocalPermissions(String server, String world, List excludedGroups, List possibleNodes) { + return getPermissions(server, world, excludedGroups, plugin.getConfiguration().getIncludeGlobalPerms(), possibleNodes); + } + /** * Gets the permissions and inherited permissions that apply to a specific server * @param server The server to get nodes for @@ -404,7 +416,18 @@ public abstract class PermissionHolder { * @return a {@link Map} of the permissions */ public Map getLocalPermissions(String server, String world, List excludedGroups) { - return getPermissions(server, world, excludedGroups, plugin.getConfiguration().getIncludeGlobalPerms()); + 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); } /** @@ -414,7 +437,7 @@ public abstract class PermissionHolder { * @return a {@link Map} of the permissions */ public Map getLocalPermissions(String server, List excludedGroups) { - return getLocalPermissions(server, null, excludedGroups); + return getLocalPermissions(server, null, excludedGroups, null); } /** @@ -468,7 +491,7 @@ public abstract class PermissionHolder { return nodes; } - protected Map getPermissions(String server, String world, List excludedGroups, boolean includeGlobal) { + protected Map getPermissions(String server, String world, List excludedGroups, boolean includeGlobal, List possibleNodes) { if (excludedGroups == null) { excludedGroups = new ArrayList<>(); } @@ -643,9 +666,61 @@ public abstract class PermissionHolder { } } + if (plugin.getConfiguration().getApplyWildcards()) { + if (possibleNodes != null && !possibleNodes.isEmpty()) { + return applyWildcards(perms, possibleNodes); + } + return applyWildcards(perms, plugin.getPossiblePermissions()); + } + return perms; } + private Map applyWildcards(Map input, List possibleNodes) { + // Add all group nodes, so wildcard group.* and '*' can apply. + plugin.getGroupManager().getGroups().keySet().forEach(s -> possibleNodes.add("group." + s)); + + 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 static String stripTime(String s) { if (s.contains("$")) { return Patterns.TEMP_DELIMITER.split(s)[0]; diff --git a/pom.xml b/pom.xml index 766425688..1721d80de 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.lucko.luckperms luckperms - 2.0 + 2.1 common api diff --git a/sponge/pom.xml b/sponge/pom.xml index 435942f85..805570d40 100644 --- a/sponge/pom.xml +++ b/sponge/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.0 + 2.1 4.0.0 diff --git a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java index 383a67290..6f0cb55d0 100644 --- a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java @@ -50,6 +50,8 @@ import org.spongepowered.api.event.game.state.GamePreInitializationEvent; import org.spongepowered.api.event.game.state.GameStoppingServerEvent; import org.spongepowered.api.plugin.Plugin; import org.spongepowered.api.scheduler.Scheduler; +import org.spongepowered.api.service.permission.PermissionDescription; +import org.spongepowered.api.service.permission.PermissionService; import java.io.File; import java.nio.file.Path; @@ -181,6 +183,12 @@ public class LPSpongePlugin implements LuckPermsPlugin { return game.getServer().getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList()); } + @Override + public List getPossiblePermissions() { + PermissionService p = game.getServiceManager().provideUnchecked(PermissionService.class); + return p.getDescriptions().stream().map(PermissionDescription::getId).collect(Collectors.toList()); + } + @Override public void runUpdateTask() { scheduler.createTaskBuilder().async().execute(new UpdateTask(this)).submit(LPSpongePlugin.this); diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 8c422bd19..6eb0e64b2 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -22,6 +22,11 @@ include-global=true # matter what is set in server.properties. (we can just fallback to the servers uuid cache) online-mode=true +# If the plugin should apply wildcard permissions. +# If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered permissions matching +# the wildcard. This will only work for plugins that define all of their permissions to the server. +apply-wildcards=true + # Which storage method the plugin should use. # Currently supported: mysql, sqlite, flatfile # Fill out connection info below if you're using MySQL