diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/command/PlayerCommandHandler.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/command/PlayerCommandHandler.java index 389bbd839c..4c7ca99029 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/command/PlayerCommandHandler.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/api/command/PlayerCommandHandler.java @@ -1,6 +1,10 @@ package com.bencodez.advancedcore.api.command; -import org.bukkit.command.CommandSender; +import org.bukkit.command.CommandSender; + +import java.util.regex.Pattern; +import java.util.Collections; +import java.util.List; import com.bencodez.advancedcore.AdvancedCorePlugin; @@ -29,17 +33,20 @@ public PlayerCommandHandler(AdvancedCorePlugin plugin, String[] args, String per figureOutPlayerArg(); } - public PlayerCommandHandler(AdvancedCorePlugin plugin, String[] args, String perm, String helpMessage, - boolean allowConsole, boolean forceConsole) { - super(plugin, args, perm, helpMessage, allowConsole); + public PlayerCommandHandler(AdvancedCorePlugin plugin, String[] args, String perm, String helpMessage, + boolean allowConsole, boolean forceConsole) { + super(plugin, args, perm, helpMessage, allowConsole, forceConsole); figureOutPlayerArg(); } @Override public void execute(CommandSender sender, String[] args) { if (playerArg >= 0) { - if (args[playerArg].equalsIgnoreCase("all")) { - executeAll(sender, args); + if (args[playerArg].equalsIgnoreCase("all")) { + if (hasAllPermission(sender)) { + executeAll(sender, args); + } + return; } } executeSinglePlayer(sender, args); @@ -47,7 +54,50 @@ public void execute(CommandSender sender, String[] args) { public abstract void executeAll(CommandSender sender, String[] args); - public abstract void executeSinglePlayer(CommandSender sender, String[] args); + public abstract void executeSinglePlayer(CommandSender sender, String[] args); + + /** + * Checks the stronger permission required for the special {@code all} target. + * The first configured permission is treated as the granular command permission + * and receives an {@code .All} suffix. Any alternative permissions, such as an + * administrator permission, continue to act as overrides. + * + * @param sender command sender + * @return whether bulk execution is authorized + */ + public boolean hasAllPermission(CommandSender sender) { + String permission = getPerm(); + if (permission == null || permission.isEmpty()) { + return false; + } + String[] permissions = permission.split(Pattern.quote("|")); + if (sender.hasPermission(permissions[0] + ".All")) { + return true; + } + if (isAllowMultiplePermissions()) { + for (int i = 1; i < permissions.length; i++) { + if (sender.hasPermission(permissions[i])) { + return true; + } + } + } + return false; + } + + /** + * Returns permissions used by special player targets in addition to the normal + * command permission. Permission-listing commands can use this without granting + * the bulk permission during ordinary command checks. + * + * @return the dedicated permission for the {@code all} target + */ + public List getAdditionalPermissions() { + String permission = getPerm(); + if (permission == null || permission.isEmpty()) { + return Collections.emptyList(); + } + return Collections.singletonList(permission.split(Pattern.quote("|"))[0] + ".All"); + } private void figureOutPlayerArg() { for (int i = 0; i < getArgs().length; i++) { diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/command/PlayerCommandHandlerTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/command/PlayerCommandHandlerTest.java new file mode 100644 index 0000000000..9224d68e91 --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/api/command/PlayerCommandHandlerTest.java @@ -0,0 +1,73 @@ +package com.bencodez.advancedcore.api.command; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.bukkit.command.CommandSender; +import org.junit.jupiter.api.Test; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.AdvancedCoreConfigOptions; + +class PlayerCommandHandlerTest { + + @Test + void forceConsoleConstructorForwardsFlag() { + TestHandler handler = handler(true); + + assertTrue(handler.isForceConsole()); + } + + @Test + void bulkTargetRequiresDedicatedPermission() { + TestHandler handler = handler(false); + CommandSender sender = mock(CommandSender.class); + when(sender.hasPermission("example.command")).thenReturn(true); + + assertFalse(handler.hasAllPermission(sender)); + + when(sender.hasPermission("example.command.All")).thenReturn(true); + assertTrue(handler.hasAllPermission(sender)); + } + + @Test + void administratorAlternativeStillAuthorizesBulkTarget() { + TestHandler handler = handler(false); + CommandSender sender = mock(CommandSender.class); + when(sender.hasPermission("example.admin")).thenReturn(true); + + assertTrue(handler.hasAllPermission(sender)); + } + + @Test + void exposesBulkPermissionForPermissionListings() { + assertEquals(java.util.Collections.singletonList("example.command.All"), + handler(false).getAdditionalPermissions()); + } + + private TestHandler handler(boolean forceConsole) { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + AdvancedCoreConfigOptions options = mock(AdvancedCoreConfigOptions.class); + when(plugin.getOptions()).thenReturn(options); + when(options.isMultiplePermissionChecks()).thenReturn(true); + return new TestHandler(plugin, forceConsole); + } + + private static final class TestHandler extends PlayerCommandHandler { + private TestHandler(AdvancedCorePlugin plugin, boolean forceConsole) { + super(plugin, new String[] { "user", "(player)" }, "example.command|example.admin", "help", true, + forceConsole); + } + + @Override + public void executeAll(CommandSender sender, String[] args) { + } + + @Override + public void executeSinglePlayer(CommandSender sender, String[] args) { + } + } +}