From 785e4514efd3b67bb6019a352bce1609c422f83f Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Thu, 5 Dec 2024 16:57:55 +0000 Subject: [PATCH] Permission improvements - The standard command permission is now granted by default (battlearena.command.) - User is informed they do not have permission when attempting to execute a subcommand - If a server admin wishes to fully hide or disable the base command, simply setting the permission value to false for the base permission will now hide it from tab complete, rather than this being the default behavior - Wildcards are now supported (i.e. battlearena.command.arena.*) --- .../org/battleplugins/arena/ArenaLoader.java | 3 ++ .../org/battleplugins/arena/BattleArena.java | 2 + .../arena/command/BaseCommandExecutor.java | 28 +++++----- .../arena/util/CommandInjector.java | 52 +++++++++++++++++++ 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/plugin/src/main/java/org/battleplugins/arena/ArenaLoader.java b/plugin/src/main/java/org/battleplugins/arena/ArenaLoader.java index f898dce0..2ce02ceb 100644 --- a/plugin/src/main/java/org/battleplugins/arena/ArenaLoader.java +++ b/plugin/src/main/java/org/battleplugins/arena/ArenaLoader.java @@ -5,6 +5,7 @@ import org.battleplugins.arena.config.ParseException; import org.battleplugins.arena.event.arena.ArenaCreateExecutorEvent; import org.battleplugins.arena.event.arena.ArenaInitializeEvent; +import org.battleplugins.arena.util.CommandInjector; import org.bukkit.Bukkit; import org.bukkit.command.PluginCommand; import org.bukkit.configuration.Configuration; @@ -46,6 +47,8 @@ public void load() { command.setExecutor(executor); + CommandInjector.registerPermissions(arena.getName().toLowerCase(Locale.ROOT), executor); + new ArenaInitializeEvent(arena).callEvent(); this.battleArena.info("Loaded arena: {}.", arena.getName()); diff --git a/plugin/src/main/java/org/battleplugins/arena/BattleArena.java b/plugin/src/main/java/org/battleplugins/arena/BattleArena.java index 7102dfb7..a4b2b7fd 100644 --- a/plugin/src/main/java/org/battleplugins/arena/BattleArena.java +++ b/plugin/src/main/java/org/battleplugins/arena/BattleArena.java @@ -584,6 +584,8 @@ public Set getFailedModules() { public void registerExecutor(String commandName, BaseCommandExecutor executor, String... aliases) { PluginCommand command = CommandInjector.inject(commandName, commandName.toLowerCase(Locale.ROOT), aliases); command.setExecutor(executor); + + CommandInjector.registerPermissions(commandName.toLowerCase(Locale.ROOT), executor); } /** diff --git a/plugin/src/main/java/org/battleplugins/arena/command/BaseCommandExecutor.java b/plugin/src/main/java/org/battleplugins/arena/command/BaseCommandExecutor.java index b23ccce1..c9a0ec5b 100644 --- a/plugin/src/main/java/org/battleplugins/arena/command/BaseCommandExecutor.java +++ b/plugin/src/main/java/org/battleplugins/arena/command/BaseCommandExecutor.java @@ -47,7 +47,7 @@ public class BaseCommandExecutor implements TabExecutor { protected final String parentCommand; protected final String permissionSubNode; - private final Map> commandMethods = new HashMap<>(); + private final Map> commandWrappers = new HashMap<>(); private final List subCommandExecutors = new ArrayList<>(); @@ -142,20 +142,20 @@ private void registerCommands() { CommandWrapper wrapper = new CommandWrapper(this, method, this.getUsage(method)); for (String cmd : arenaCommand.commands()) { - Set wrappers = this.commandMethods.getOrDefault(cmd, new HashSet<>()); + Set wrappers = this.commandWrappers.getOrDefault(cmd, new HashSet<>()); wrappers.add(wrapper); - this.commandMethods.put(cmd, wrappers); + this.commandWrappers.put(cmd, wrappers); } } } public void injectWrapper(CommandWrapper wrapper) { for (String cmd : wrapper.getCommand().commands()) { - Set wrappers = this.commandMethods.getOrDefault(cmd, new HashSet<>()); + Set wrappers = this.commandWrappers.getOrDefault(cmd, new HashSet<>()); wrappers.add(wrapper); - this.commandMethods.put(cmd, wrappers); + this.commandWrappers.put(cmd, wrappers); } } @@ -317,7 +317,7 @@ public void sendHeader(CommandSender sender) { public void sendHelpMessage(CommandSender sender, int page) { // Compile all the command arguments Map commandWrappers = new HashMap<>(); - for (Map.Entry> entry : this.commandMethods.entrySet()) { + for (Map.Entry> entry : this.commandWrappers.entrySet()) { for (CommandWrapper wrapper : entry.getValue()) { commandWrappers.put(wrapper.usage, wrapper); } @@ -404,8 +404,8 @@ public void sendUsageMessage(CommandSender sender, Method method) { private List getCommandWrappers(String command, String subCommand) { List wrappers = new ArrayList<>(); - for (String cmd : this.commandMethods.keySet()) { - for (CommandWrapper wrapper : this.commandMethods.get(cmd)) { + for (String cmd : this.commandWrappers.keySet()) { + for (CommandWrapper wrapper : this.commandWrappers.get(cmd)) { ArenaCommand arenaCommand = wrapper.getCommand(); if (!Arrays.asList(arenaCommand.commands()).contains(command)) { @@ -658,8 +658,8 @@ public final List onTabComplete(CommandSender sender, Command command, S try { if (args.length == 1) { List commands = new ArrayList<>(); - for (String cmd : this.commandMethods.keySet()) { - CommandWrapper wrapper = this.commandMethods.get(cmd).iterator().next(); + for (String cmd : this.commandWrappers.keySet()) { + CommandWrapper wrapper = this.commandWrappers.get(cmd).iterator().next(); ArenaCommand arenaCommand = wrapper.getCommand(); if (!arenaCommand.permissionNode().isEmpty() && !this.hasPermission(sender, this.getPermissionNode(arenaCommand.permissionNode()))) { @@ -677,7 +677,7 @@ public final List onTabComplete(CommandSender sender, Command command, S } if (args.length > 1) { - Set wrappers = this.commandMethods.get(args[0]); + Set wrappers = this.commandWrappers.get(args[0]); for (CommandWrapper wrapper : wrappers) { ArenaCommand arenaCommand = wrapper.getCommand(); if (!arenaCommand.permissionNode().isEmpty() && !this.hasPermission(sender, this.getPermissionNode(arenaCommand.permissionNode()))) { @@ -714,7 +714,11 @@ public final List onTabComplete(CommandSender sender, Command command, S return completions; } - protected String getPermissionNode(String node) { + public Map> getCommandWrappers() { + return this.commandWrappers; + } + + public String getPermissionNode(String node) { return "battlearena.command." + (this.permissionSubNode == null ? "" : this.permissionSubNode + ".") + node; } diff --git a/plugin/src/main/java/org/battleplugins/arena/util/CommandInjector.java b/plugin/src/main/java/org/battleplugins/arena/util/CommandInjector.java index cc1a644f..e0c161d8 100644 --- a/plugin/src/main/java/org/battleplugins/arena/util/CommandInjector.java +++ b/plugin/src/main/java/org/battleplugins/arena/util/CommandInjector.java @@ -1,13 +1,21 @@ package org.battleplugins.arena.util; import org.battleplugins.arena.BattleArena; +import org.battleplugins.arena.command.BaseCommandExecutor; import org.bukkit.Bukkit; import org.bukkit.command.PluginCommand; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; public class CommandInjector { @@ -31,4 +39,48 @@ public static PluginCommand inject(String headerName, String commandName, String throw new RuntimeException("Failed to construct PluginCommand " + headerName, e); } } + + public static void registerPermissions(String commandName, BaseCommandExecutor executor) { + String rootPermissionNode = "battlearena.command." + commandName.toLowerCase(Locale.ROOT); + String wildcardPermissionNode = rootPermissionNode + ".*"; + + PluginManager pluginManager = Bukkit.getServer().getPluginManager(); + + // Register the root permission so it shows up in things such as LuckPerms UI. + // Set to true by default so the user can see the command, however this is intentionally + // done so server owners can fully revoke the permission, and it will not show up in tab complete. + Permission rootPermission = pluginManager.getPermission(rootPermissionNode); + if (rootPermission == null) { + pluginManager.addPermission(rootPermission = new Permission(rootPermissionNode, PermissionDefault.TRUE)); + } + + Permission wildcardPermission = pluginManager.getPermission(wildcardPermissionNode); + if (wildcardPermission == null) { + pluginManager.addPermission(wildcardPermission = new Permission(wildcardPermissionNode, PermissionDefault.OP)); + } + + Set childPermissions = new HashSet<>(); + for (Map.Entry> entry : executor.getCommandWrappers().entrySet()) { + for (BaseCommandExecutor.CommandWrapper wrapper : entry.getValue()) { + String node = wrapper.getCommand().permissionNode(); + String permissionNode = executor.getPermissionNode(node); + + // Add our permission node to the parent permission + if (permissionNode != null) { + childPermissions.add(permissionNode); + } + } + } + + // Add all child permissions to the parent permission + for (String childPermissionNode : childPermissions) { + Permission childPermission = pluginManager.getPermission(childPermissionNode); + if (childPermission == null) { + pluginManager.addPermission(childPermission = new Permission(childPermissionNode, PermissionDefault.OP)); + } + + // For wildcard permissions, set to true as we want all permissions granted when the wildcard is present + childPermission.addParent(wildcardPermission, true); + } + } }