From 2a747c7d0137f1a3d29bdb8726f3a17af5350062 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Fri, 14 Apr 2017 19:54:17 +0200 Subject: [PATCH] #1034 Create permissions for debug sections - Add an individual permission for each debug section (including wildcard perm) - Create test for DebugCommand - Refactor tests for the enum permission nodes to use the same abstract class - Update related project files (plugin.yml, permission docs, command docs) --- docs/commands.md | 9 +- docs/permission_nodes.md | 14 +- .../authme/command/CommandInitializer.java | 9 +- .../authme/debug/CountryLookup.java | 7 + .../authme/debug/DataStatistics.java | 7 + .../executable/authme/debug/DebugCommand.java | 35 +++- .../executable/authme/debug/DebugSection.java | 6 + .../authme/debug/HasPermissionChecker.java | 11 +- .../authme/debug/InputValidator.java | 7 + .../authme/debug/LimboPlayerViewer.java | 7 + .../authme/debug/PermissionGroups.java | 7 + .../authme/debug/PlayerAuthViewer.java | 7 + .../authme/debug/SpawnLocationViewer.java | 7 + .../authme/debug/TestEmailSender.java | 7 + .../permission/DebugSectionPermissions.java | 58 +++++++ .../permission/PlayerStatePermission.java | 7 +- src/main/resources/plugin.yml | 44 ++++- .../authme/debug/DebugCommandTest.java | 154 ++++++++++++++++++ .../debug/HasPermissionCheckerTest.java | 5 +- .../AbstractPermissionsEnumTest.java | 52 ++++++ .../permission/AdminPermissionTest.java | 38 +---- .../DebugSectionPermissionsTest.java | 17 ++ .../permission/PermissionConsistencyTest.java | 2 +- .../permission/PlayerPermissionTest.java | 38 +---- .../permission/PlayerStatePermissionTest.java | 36 ++-- .../permissions/PermissionNodesGatherer.java | 6 +- .../filegeneration/GeneratePluginYml.java | 3 +- 27 files changed, 480 insertions(+), 120 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/permission/DebugSectionPermissions.java create mode 100644 src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugCommandTest.java create mode 100644 src/test/java/fr/xephi/authme/permission/AbstractPermissionsEnumTest.java create mode 100644 src/test/java/fr/xephi/authme/permission/DebugSectionPermissionsTest.java diff --git a/docs/commands.md b/docs/commands.md index 344b643de..b9e127f85 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,5 +1,5 @@ - + ## AuthMe Commands You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` @@ -47,8 +47,8 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
Requires `authme.admin.converter` - **/authme messages**: Adds missing messages to the current messages file.
Requires `authme.admin.updatemessages` -- **/authme debug** [child] [params]: Allows various operations for debugging. -
Requires `authme.debug` +- **/authme debug** [child] [arg] [arg]: Allows various operations for debugging. +
Requires `authme.debug.command` - **/authme help** [query]: View detailed help for /authme commands. - **/login** <password>: Command to log in using AuthMeReloaded.
Requires `authme.player.login` @@ -82,6 +82,7 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
Requires `authme.player.captcha` - **/captcha help** [query]: View detailed help for /captcha commands. + --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Wed Mar 22 23:10:32 CET 2017 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Apr 14 18:52:29 CEST 2017 diff --git a/docs/permission_nodes.md b/docs/permission_nodes.md index fb6408206..ef9df8d8f 100644 --- a/docs/permission_nodes.md +++ b/docs/permission_nodes.md @@ -1,5 +1,5 @@ - + ## AuthMe Permission Nodes The following are the permission nodes that are currently supported by the latest dev builds. @@ -31,6 +31,16 @@ The following are the permission nodes that are currently supported by the lates - **authme.bypassantibot** – Permission node to bypass AntiBot protection. - **authme.bypassforcesurvival** – Permission for users to bypass force-survival mode. - **authme.bypasspurge** – Permission to bypass the purging process. +- **authme.debug.command** – General permission to use the /authme debug command. +- **authme.debug.country** – Permission to use the country lookup section. +- **authme.debug.db** – Permission to view data from the database. +- **authme.debug.group** – Permission to view permission groups. +- **authme.debug.limbo** – Permission to use the limbo data viewer. +- **authme.debug.mail** – Permission to use the test email sender. +- **authme.debug.perm** – Permission to use the permission checker. +- **authme.debug.spawn** – Permission to view spawn information. +- **authme.debug.stats** – Permission to use the stats section. +- **authme.debug.valid** – Permission to use sample validation. - **authme.player.*** – Permission to use all player (non-admin) commands. - **authme.player.canbeforced** – Permission for users a login can be forced to. - **authme.player.captcha** – Command permission to use captcha. @@ -49,4 +59,4 @@ The following are the permission nodes that are currently supported by the lates --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 15:38:58 CEST 2016 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Apr 14 18:52:32 CEST 2017 diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index dd27c2794..841cdf4d8 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -39,8 +39,8 @@ import fr.xephi.authme.command.executable.logout.LogoutCommand; import fr.xephi.authme.command.executable.register.RegisterCommand; import fr.xephi.authme.command.executable.unregister.UnregisterCommand; import fr.xephi.authme.permission.AdminPermission; +import fr.xephi.authme.permission.DebugSectionPermissions; import fr.xephi.authme.permission.PlayerPermission; -import fr.xephi.authme.permission.PlayerStatePermission; import java.util.Arrays; import java.util.Collection; @@ -312,10 +312,9 @@ public class CommandInitializer { .description("Debug features") .detailedDescription("Allows various operations for debugging.") .withArgument("child", "The child to execute", true) - .withArgument(".", "meaning varies", true) - .withArgument(".", "meaning varies", true) - .withArgument(".", "meaning varies", true) - .permission(PlayerStatePermission.DEBUG_COMMAND) + .withArgument("arg", "argument (depends on debug section)", true) + .withArgument("arg", "argument (depends on debug section)", true) + .permission(DebugSectionPermissions.DEBUG_COMMAND) .executableCommand(DebugCommand.class) .register(); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/CountryLookup.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/CountryLookup.java index 05de8ab13..781b8392b 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/CountryLookup.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/CountryLookup.java @@ -2,6 +2,8 @@ package fr.xephi.authme.command.executable.authme.debug; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.permission.DebugSectionPermissions; +import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.service.GeoIpService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.properties.ProtectionSettings; @@ -54,6 +56,11 @@ class CountryLookup implements DebugSection { } } + @Override + public PermissionNode getRequiredPermission() { + return DebugSectionPermissions.COUNTRY_LOOKUP; + } + private void outputInfoForIpAddr(CommandSender sender, String ipAddr) { sender.sendMessage("IP '" + ipAddr + "' maps to country '" + geoIpService.getCountryCode(ipAddr) + "' (" + geoIpService.getCountryName(ipAddr) + ")"); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/DataStatistics.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/DataStatistics.java index 3bf28e057..4afba2def 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/DataStatistics.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/DataStatistics.java @@ -8,6 +8,8 @@ import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.factory.SingletonStore; +import fr.xephi.authme.permission.DebugSectionPermissions; +import fr.xephi.authme.permission.PermissionNode; import org.bukkit.command.CommandSender; import javax.inject.Inject; @@ -52,6 +54,11 @@ class DataStatistics implements DebugSection { outputInjectorStats(sender); } + @Override + public PermissionNode getRequiredPermission() { + return DebugSectionPermissions.DATA_STATISTICS; + } + private void outputDatabaseStats(CommandSender sender) { sender.sendMessage("Total players in DB: " + dataSource.getAccountsRegistered()); sender.sendMessage("Total marked as logged in in DB: " + dataSource.getLoggedPlayers().size()); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugCommand.java index fc7cd4291..e8976287c 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugCommand.java @@ -3,6 +3,8 @@ package fr.xephi.authme.command.executable.authme.debug; import com.google.common.collect.ImmutableSet; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.initialization.factory.Factory; +import fr.xephi.authme.permission.PermissionsManager; +import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import javax.inject.Inject; @@ -24,27 +26,48 @@ public class DebugCommand implements ExecutableCommand { @Inject private Factory debugSectionFactory; + @Inject + private PermissionsManager permissionsManager; + private Map sections; @Override public void executeCommand(CommandSender sender, List arguments) { - DebugSection debugSection = getDebugSection(arguments); + DebugSection debugSection = findDebugSection(arguments); if (debugSection == null) { - sender.sendMessage("Available sections:"); - getSections().values() - .forEach(e -> sender.sendMessage("- " + e.getName() + ": " + e.getDescription())); + sendAvailableSections(sender); } else { - debugSection.execute(sender, arguments.subList(1, arguments.size())); + executeSection(debugSection, sender, arguments); } } - private DebugSection getDebugSection(List arguments) { + private DebugSection findDebugSection(List arguments) { if (arguments.isEmpty()) { return null; } return getSections().get(arguments.get(0).toLowerCase()); } + private void sendAvailableSections(CommandSender sender) { + sender.sendMessage("Sections available to you:"); + long availableSections = getSections().values().stream() + .filter(section -> permissionsManager.hasPermission(sender, section.getRequiredPermission())) + .peek(e -> sender.sendMessage("- " + e.getName() + ": " + e.getDescription())) + .count(); + + if (availableSections == 0) { + sender.sendMessage(ChatColor.RED + "You don't have permission to view any debug section"); + } + } + + private void executeSection(DebugSection section, CommandSender sender, List arguments) { + if (permissionsManager.hasPermission(sender, section.getRequiredPermission())) { + section.execute(sender, arguments.subList(1, arguments.size())); + } else { + sender.sendMessage(ChatColor.RED + "You don't have permission for this section. See /authme debug"); + } + } + // Lazy getter private Map getSections() { if (sections == null) { diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSection.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSection.java index 1f0389833..155520c48 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSection.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSection.java @@ -1,5 +1,6 @@ package fr.xephi.authme.command.executable.authme.debug; +import fr.xephi.authme.permission.PermissionNode; import org.bukkit.command.CommandSender; import java.util.List; @@ -27,4 +28,9 @@ interface DebugSection { */ void execute(CommandSender sender, List arguments); + /** + * @return permission required to run this section + */ + PermissionNode getRequiredPermission(); + } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionChecker.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionChecker.java index 436ef180c..4d2bacf16 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionChecker.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionChecker.java @@ -2,6 +2,7 @@ package fr.xephi.authme.command.executable.authme.debug; import com.google.common.collect.ImmutableList; import fr.xephi.authme.permission.AdminPermission; +import fr.xephi.authme.permission.DebugSectionPermissions; import fr.xephi.authme.permission.DefaultPermission; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsManager; @@ -25,8 +26,8 @@ import java.util.function.BiFunction; */ class HasPermissionChecker implements DebugSection { - static final List> PERMISSION_NODE_CLASSES = - ImmutableList.of(AdminPermission.class, PlayerPermission.class, PlayerStatePermission.class); + static final List> PERMISSION_NODE_CLASSES = ImmutableList.of( + AdminPermission.class, PlayerPermission.class, PlayerStatePermission.class, DebugSectionPermissions.class); @Inject private PermissionsManager permissionsManager; @@ -69,6 +70,11 @@ class HasPermissionChecker implements DebugSection { } } + @Override + public PermissionNode getRequiredPermission() { + return DebugSectionPermissions.HAS_PERMISSION_CHECK; + } + /** * Performs a permission check and informs the given sender of the result. {@code permissionChecker} is the * permission check to perform with the given {@code node} and the {@code player}. @@ -90,7 +96,6 @@ class HasPermissionChecker implements DebugSection { sender.sendMessage(ChatColor.DARK_RED + "Check failed: player '" + player.getName() + "' does NOT have permission '" + node + "'"); } - } /** diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/InputValidator.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/InputValidator.java index 472025060..d1131e0ae 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/InputValidator.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/InputValidator.java @@ -3,6 +3,8 @@ package fr.xephi.authme.command.executable.authme.debug; import fr.xephi.authme.listener.FailedVerificationException; import fr.xephi.authme.listener.OnJoinVerifier; import fr.xephi.authme.message.Messages; +import fr.xephi.authme.permission.DebugSectionPermissions; +import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService.ValidationResult; import org.bukkit.ChatColor; @@ -60,6 +62,11 @@ class InputValidator implements DebugSection { } } + @Override + public PermissionNode getRequiredPermission() { + return DebugSectionPermissions.INPUT_VALIDATOR; + } + private void displayUsageHint(CommandSender sender) { sender.sendMessage("You can define forbidden emails and passwords in your config.yml"); sender.sendMessage("This command allows you to test some of the values:"); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/LimboPlayerViewer.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/LimboPlayerViewer.java index a8c62e683..b017d1938 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/LimboPlayerViewer.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/LimboPlayerViewer.java @@ -3,6 +3,8 @@ package fr.xephi.authme.command.executable.authme.debug; import fr.xephi.authme.data.limbo.LimboPlayer; import fr.xephi.authme.data.limbo.LimboService; import fr.xephi.authme.data.limbo.persistence.LimboPersistence; +import fr.xephi.authme.permission.DebugSectionPermissions; +import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.service.BukkitService; import org.bukkit.ChatColor; @@ -71,6 +73,11 @@ class LimboPlayerViewer implements DebugSection { .sendEntry("Group", LimboPlayer::getGroup, permissionsManager::getPrimaryGroup); } + @Override + public PermissionNode getRequiredPermission() { + return DebugSectionPermissions.LIMBO_PLAYER_VIEWER; + } + /** * Displays the info for the given LimboPlayer and Player to the provided CommandSender. */ diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/PermissionGroups.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/PermissionGroups.java index a7bf19eb1..0fd62d8d3 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/PermissionGroups.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/PermissionGroups.java @@ -1,5 +1,7 @@ package fr.xephi.authme.command.executable.authme.debug; +import fr.xephi.authme.permission.DebugSectionPermissions; +import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsManager; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; @@ -38,4 +40,9 @@ class PermissionGroups implements DebugSection { sender.sendMessage("Primary group is: " + permissionsManager.getGroups(player)); } } + + @Override + public PermissionNode getRequiredPermission() { + return DebugSectionPermissions.PERM_GROUPS; + } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java index a985c8271..f4a26ec87 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java @@ -2,6 +2,8 @@ package fr.xephi.authme.command.executable.authme.debug; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.permission.DebugSectionPermissions; +import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.util.StringUtils; import org.bukkit.ChatColor; @@ -50,6 +52,11 @@ class PlayerAuthViewer implements DebugSection { } } + @Override + public PermissionNode getRequiredPermission() { + return DebugSectionPermissions.PLAYER_AUTH_VIEWER; + } + /** * Outputs the PlayerAuth information to the given sender. * diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/SpawnLocationViewer.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/SpawnLocationViewer.java index 0262055e3..cfb6e0cd0 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/SpawnLocationViewer.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/SpawnLocationViewer.java @@ -1,5 +1,7 @@ package fr.xephi.authme.command.executable.authme.debug; +import fr.xephi.authme.permission.DebugSectionPermissions; +import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.SpawnLoader; @@ -49,6 +51,11 @@ class SpawnLocationViewer implements DebugSection { } } + @Override + public PermissionNode getRequiredPermission() { + return DebugSectionPermissions.SPAWN_LOCATION; + } + private void showGeneralInfo(CommandSender sender) { sender.sendMessage("Spawn priority: " + String.join(", ", settings.getProperty(RestrictionSettings.SPAWN_PRIORITY))); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java index 04ba276a6..e83dfee56 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java @@ -4,6 +4,8 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.mail.SendMailSsl; +import fr.xephi.authme.permission.DebugSectionPermissions; +import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.util.StringUtils; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.HtmlEmail; @@ -60,6 +62,11 @@ class TestEmailSender implements DebugSection { } } + @Override + public PermissionNode getRequiredPermission() { + return DebugSectionPermissions.TEST_EMAIL; + } + private String getEmail(CommandSender sender, List arguments) { if (arguments.isEmpty()) { PlayerAuth auth = dataSource.getAuth(sender.getName()); diff --git a/src/main/java/fr/xephi/authme/permission/DebugSectionPermissions.java b/src/main/java/fr/xephi/authme/permission/DebugSectionPermissions.java new file mode 100644 index 000000000..ee2d0d7fd --- /dev/null +++ b/src/main/java/fr/xephi/authme/permission/DebugSectionPermissions.java @@ -0,0 +1,58 @@ +package fr.xephi.authme.permission; + +/** + * Permissions for the debug sections (/authme debug). + */ +public enum DebugSectionPermissions implements PermissionNode { + + /** General permission to use the /authme debug command. */ + DEBUG_COMMAND("authme.debug.command"), + + /** Permission to use the country lookup section. */ + COUNTRY_LOOKUP("authme.debug.country"), + + /** Permission to use the stats section. */ + DATA_STATISTICS("authme.debug.stats"), + + /** Permission to use the permission checker. */ + HAS_PERMISSION_CHECK("authme.debug.perm"), + + /** Permission to use sample validation. */ + INPUT_VALIDATOR("authme.debug.valid"), + + /** Permission to use the limbo data viewer. */ + LIMBO_PLAYER_VIEWER("authme.debug.limbo"), + + /** Permission to view permission groups. */ + PERM_GROUPS("authme.debug.group"), + + /** Permission to view data from the database. */ + PLAYER_AUTH_VIEWER("authme.debug.db"), + + /** Permission to view spawn information. */ + SPAWN_LOCATION("authme.debug.spawn"), + + /** Permission to use the test email sender. */ + TEST_EMAIL("authme.debug.mail"); + + private final String node; + + /** + * Constructor. + * + * @param node the permission node + */ + DebugSectionPermissions(String node) { + this.node = node; + } + + @Override + public String getNode() { + return node; + } + + @Override + public DefaultPermission getDefaultPermission() { + return DefaultPermission.OP_ONLY; + } +} diff --git a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java b/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java index 2160eea56..aaeb0eea3 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java @@ -29,12 +29,7 @@ public enum PlayerStatePermission implements PermissionNode { /** * Permission to bypass the purging process. */ - BYPASS_PURGE("authme.bypasspurge", DefaultPermission.NOT_ALLOWED), - - /** - * Permission to use the /authme debug command. - */ - DEBUG_COMMAND("authme.debug", DefaultPermission.OP_ONLY); + BYPASS_PURGE("authme.bypasspurge", DefaultPermission.NOT_ALLOWED); /** * The permission node. diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index f04829d41..3ca000fdf 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -45,7 +45,7 @@ commands: - cp email: description: Add email or recover password - usage: /email show|add|change|recover + usage: /email show|add|change|recover|code|setpassword captcha: description: Captcha Command usage: /captcha @@ -154,7 +154,47 @@ permissions: description: Permission to bypass the purging process. default: false authme.debug: - description: Permission to use the /authme debug command. + description: Gives access to /authme debug and all its sections + children: + authme.debug.command: true + authme.debug.country: true + authme.debug.db: true + authme.debug.group: true + authme.debug.limbo: true + authme.debug.mail: true + authme.debug.perm: true + authme.debug.spawn: true + authme.debug.stats: true + authme.debug.valid: true + authme.debug.command: + description: General permission to use the /authme debug command. + default: op + authme.debug.country: + description: Permission to use the country lookup section. + default: op + authme.debug.db: + description: Permission to view data from the database. + default: op + authme.debug.group: + description: Permission to view permission groups. + default: op + authme.debug.limbo: + description: Permission to use the limbo data viewer. + default: op + authme.debug.mail: + description: Permission to use the test email sender. + default: op + authme.debug.perm: + description: Permission to use the permission checker. + default: op + authme.debug.spawn: + description: Permission to view spawn information. + default: op + authme.debug.stats: + description: Permission to use the stats section. + default: op + authme.debug.valid: + description: Permission to use sample validation. default: op authme.player.*: description: Gives access to all player commands diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugCommandTest.java new file mode 100644 index 000000000..a87034441 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugCommandTest.java @@ -0,0 +1,154 @@ +package fr.xephi.authme.command.executable.authme.debug; + +import fr.xephi.authme.initialization.factory.Factory; +import fr.xephi.authme.permission.DebugSectionPermissions; +import fr.xephi.authme.permission.PermissionNode; +import fr.xephi.authme.permission.PermissionsManager; +import org.bukkit.command.CommandSender; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Arrays; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Collections.emptyList; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; + +/** + * Test for {@link DebugCommand}. + */ +@RunWith(MockitoJUnitRunner.class) +public class DebugCommandTest { + + /** + * Number we test against if we expect an action to have been performed for each debug section. + * This is a minimum number so tests don't fail each time a new debug section is added; however, + * it should be close to the total. + */ + private static final int MIN_DEBUG_SECTIONS = 9; + + @InjectMocks + private DebugCommand command; + + @Mock + private Factory debugSectionFactory; + + @Mock + private PermissionsManager permissionsManager; + + @Before + @SuppressWarnings("unchecked") + public void initFactory() { + given(debugSectionFactory.newInstance(any(Class.class))).willAnswer( + invocation -> { + Class classArgument = invocation.getArgument(0); + checkArgument(DebugSection.class.isAssignableFrom(classArgument)); + return spy(classArgument); + }); + } + + @Test + public void shouldListAllAvailableDebugSections() { + // given + CommandSender sender = mock(CommandSender.class); + given(permissionsManager.hasPermission(eq(sender), any(PermissionNode.class))).willReturn(false); + given(permissionsManager.hasPermission(sender, DebugSectionPermissions.INPUT_VALIDATOR)).willReturn(true); + given(permissionsManager.hasPermission(sender, DebugSectionPermissions.DATA_STATISTICS)).willReturn(true); + + // when + command.executeCommand(sender, emptyList()); + + // then + verify(debugSectionFactory, atLeast(MIN_DEBUG_SECTIONS)).newInstance(any(Class.class)); + verify(permissionsManager, atLeast(MIN_DEBUG_SECTIONS)).hasPermission(eq(sender), any(DebugSectionPermissions.class)); + + ArgumentCaptor strCaptor = ArgumentCaptor.forClass(String.class); + verify(sender, times(3)).sendMessage(strCaptor.capture()); + assertThat(strCaptor.getAllValues(), contains( + containsString("Sections available to you"), + containsString("stats: Outputs general data statistics"), + containsString("valid: Check if email / password is valid"))); + } + + @Test + public void shouldNotListAnyDebugSection() { + // given + CommandSender sender = mock(CommandSender.class); + given(permissionsManager.hasPermission(eq(sender), any(PermissionNode.class))).willReturn(false); + + // when + command.executeCommand(sender, emptyList()); + + // then + verify(debugSectionFactory, atLeast(MIN_DEBUG_SECTIONS)).newInstance(any(Class.class)); + verify(permissionsManager, atLeast(MIN_DEBUG_SECTIONS)).hasPermission(eq(sender), any(DebugSectionPermissions.class)); + + ArgumentCaptor strCaptor = ArgumentCaptor.forClass(String.class); + verify(sender, times(2)).sendMessage(strCaptor.capture()); + assertThat(strCaptor.getAllValues(), contains( + equalTo("Sections available to you:"), + containsString("You don't have permission to view any debug section"))); + } + + @Test + public void shouldRunSection() { + // given + DebugSection section = spy(InputValidator.class); + doNothing().when(section).execute(any(CommandSender.class), anyList()); + // Mockito throws a runtime error if below we use the usual "given(factory.newInstance(...)).willReturn(...)" + doReturn(section).when(debugSectionFactory).newInstance(InputValidator.class); + + CommandSender sender = mock(CommandSender.class); + given(permissionsManager.hasPermission(sender, section.getRequiredPermission())).willReturn(true); + List arguments = Arrays.asList(section.getName().toUpperCase(), "test", "toast"); + + // when + command.executeCommand(sender, arguments); + + // then + verify(permissionsManager).hasPermission(sender, section.getRequiredPermission()); + verify(section).execute(sender, Arrays.asList("test", "toast")); + } + + @Test + public void shouldNotRunSectionForMissingPermission() { + // given + DebugSection section = spy(InputValidator.class); + // Mockito throws a runtime error if below we use the usual "given(factory.newInstance(...)).willReturn(...)" + doReturn(section).when(debugSectionFactory).newInstance(InputValidator.class); + + CommandSender sender = mock(CommandSender.class); + given(permissionsManager.hasPermission(sender, section.getRequiredPermission())).willReturn(false); + List arguments = Arrays.asList(section.getName().toUpperCase(), "test"); + + // when + command.executeCommand(sender, arguments); + + // then + verify(permissionsManager).hasPermission(sender, section.getRequiredPermission()); + verify(section, never()).execute(any(CommandSender.class), anyList()); + verify(sender).sendMessage(argThat(containsString("You don't have permission"))); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionCheckerTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionCheckerTest.java index 9f7c6a92f..4bd8eab19 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionCheckerTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionCheckerTest.java @@ -20,9 +20,9 @@ import java.util.stream.Collectors; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.atLeast; @@ -55,8 +55,7 @@ public class HasPermissionCheckerTest { .collect(Collectors.toList()); // when / then - assertThat(HasPermissionChecker.PERMISSION_NODE_CLASSES.containsAll(permissionClasses), equalTo(true)); - assertThat(HasPermissionChecker.PERMISSION_NODE_CLASSES, hasSize(permissionClasses.size())); + assertThat(HasPermissionChecker.PERMISSION_NODE_CLASSES, containsInAnyOrder(permissionClasses.toArray())); } @Test diff --git a/src/test/java/fr/xephi/authme/permission/AbstractPermissionsEnumTest.java b/src/test/java/fr/xephi/authme/permission/AbstractPermissionsEnumTest.java new file mode 100644 index 000000000..4b327ec45 --- /dev/null +++ b/src/test/java/fr/xephi/authme/permission/AbstractPermissionsEnumTest.java @@ -0,0 +1,52 @@ +package fr.xephi.authme.permission; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.fail; + +/** + * Has common tests for enums implementing {@link PermissionNode}. + */ +public abstract class AbstractPermissionsEnumTest { + + @Test + public void shouldAllStartWitRequiredPrefix() { + // given + String requiredPrefix = getRequiredPrefix(); + + // when/then + for (PermissionNode permission : getPermissionNodes()) { + if (!permission.getNode().startsWith(requiredPrefix)) { + fail("The permission '" + permission + "' does not start with the required prefix '" + + requiredPrefix + "'"); + } + } + } + + @Test + public void shouldHaveUniqueNodes() { + // given + Set nodes = new HashSet<>(); + + // when/then + for (PermissionNode permission : getPermissionNodes()) { + if (!nodes.add(permission.getNode())) { + fail("More than one enum value defines the node '" + permission.getNode() + "'"); + } + } + } + + /** + * @return the permission nodes to test + */ + protected abstract PermissionNode[] getPermissionNodes(); + + /** + * @return text with which all permission nodes must start with + */ + protected abstract String getRequiredPrefix(); + +} diff --git a/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java b/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java index 3cfa0e271..f3e4f1fde 100644 --- a/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java +++ b/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java @@ -1,42 +1,18 @@ package fr.xephi.authme.permission; -import org.junit.Test; - -import java.util.HashSet; -import java.util.Set; - -import static org.junit.Assert.fail; - /** * Test for {@link AdminPermission}. */ -public class AdminPermissionTest { +public class AdminPermissionTest extends AbstractPermissionsEnumTest { - @Test - public void shouldStartWithAuthMeAdminPrefix() { - // given - String requiredPrefix = "authme.admin."; - - // when/then - for (AdminPermission permission : AdminPermission.values()) { - if (!permission.getNode().startsWith(requiredPrefix)) { - fail("The permission '" + permission + "' does not start with the required prefix '" - + requiredPrefix + "'"); - } - } + @Override + protected PermissionNode[] getPermissionNodes() { + return AdminPermission.values(); } - @Test - public void shouldHaveUniqueNodes() { - // given - Set nodes = new HashSet<>(); - - // when/then - for (AdminPermission permission : AdminPermission.values()) { - if (!nodes.add(permission.getNode())) { - fail("More than one enum value defines the node '" + permission.getNode() + "'"); - } - } + @Override + protected String getRequiredPrefix() { + return "authme.admin."; } } diff --git a/src/test/java/fr/xephi/authme/permission/DebugSectionPermissionsTest.java b/src/test/java/fr/xephi/authme/permission/DebugSectionPermissionsTest.java new file mode 100644 index 000000000..3e0a9f013 --- /dev/null +++ b/src/test/java/fr/xephi/authme/permission/DebugSectionPermissionsTest.java @@ -0,0 +1,17 @@ +package fr.xephi.authme.permission; + +/** + * Test for {@link DebugSectionPermissions}. + */ +public class DebugSectionPermissionsTest extends AbstractPermissionsEnumTest { + + @Override + protected PermissionNode[] getPermissionNodes() { + return DebugSectionPermissions.values(); + } + + @Override + protected String getRequiredPrefix() { + return "authme.debug."; + } +} diff --git a/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java b/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java index 757319d55..6d7a37e2f 100644 --- a/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java @@ -31,7 +31,7 @@ public class PermissionConsistencyTest { /** Wildcard permissions (present in plugin.yml but not in the codebase). */ private static final Set PLUGIN_YML_PERMISSIONS_WILDCARDS = - ImmutableSet.of("authme.admin.*", "authme.player.*", "authme.player.email"); + ImmutableSet.of("authme.admin.*", "authme.player.*", "authme.player.email", "authme.debug"); /** Name of the fields that make up a permission entry in plugin.yml. */ private static final Set PERMISSION_FIELDS = ImmutableSet.of("description", "default", "children"); diff --git a/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java b/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java index ad8f6fe9e..32a45b98d 100644 --- a/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java +++ b/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java @@ -1,41 +1,17 @@ package fr.xephi.authme.permission; -import org.junit.Test; - -import java.util.HashSet; -import java.util.Set; - -import static org.junit.Assert.fail; - /** * Test for {@link PlayerPermission}. */ -public class PlayerPermissionTest { +public class PlayerPermissionTest extends AbstractPermissionsEnumTest { - @Test - public void shouldStartWithPlayerPrefix() { - // given - String playerBranch = "authme.player."; - - // when/then - for (PlayerPermission permission : PlayerPermission.values()) { - if (!permission.getNode().startsWith(playerBranch)) { - fail("The permission '" + permission + "' should use a node with the player-specific branch '" - + playerBranch + "'"); - } - } + @Override + protected PermissionNode[] getPermissionNodes() { + return PlayerPermission.values(); } - @Test - public void shouldHaveUniqueNodes() { - // given - Set nodes = new HashSet<>(); - - // when/then - for (PlayerPermission permission : PlayerPermission.values()) { - if (!nodes.add(permission.getNode())) { - fail("More than one enum value defines the node '" + permission.getNode() + "'"); - } - } + @Override + protected String getRequiredPrefix() { + return "authme.player."; } } diff --git a/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java b/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java index 6c3e3d163..56e5e1ce1 100644 --- a/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java +++ b/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java @@ -2,7 +2,7 @@ package fr.xephi.authme.permission; import org.junit.Test; -import java.util.HashSet; +import java.util.Collection; import java.util.Set; import static com.google.common.collect.Sets.newHashSet; @@ -11,39 +11,32 @@ import static org.junit.Assert.fail; /** * Test for {@link PlayerStatePermission}. */ -public class PlayerStatePermissionTest { +public class PlayerStatePermissionTest extends AbstractPermissionsEnumTest { @Test - public void shouldStartWithAuthMeAdminPrefix() { + public void shouldNotStartWithOtherPrefixes() { // given - String requiredPrefix = "authme."; - Set forbiddenPrefixes = newHashSet("authme.player", "authme.admin"); + Set forbiddenPrefixes = newHashSet("authme.player", "authme.admin", "authme.debug"); // when/then for (PlayerStatePermission permission : PlayerStatePermission.values()) { - if (!permission.getNode().startsWith(requiredPrefix)) { - fail("The permission '" + permission + "' does not start with the required prefix '" - + requiredPrefix + "'"); - } else if (hasAnyPrefix(permission.getNode(), forbiddenPrefixes)) { + if (startsWithAny(permission.getNode(), forbiddenPrefixes)) { fail("The permission '" + permission + "' should not start with any of " + forbiddenPrefixes); } } } - @Test - public void shouldHaveUniqueNodes() { - // given - Set nodes = new HashSet<>(); - - // when/then - for (PlayerStatePermission permission : PlayerStatePermission.values()) { - if (!nodes.add(permission.getNode())) { - fail("More than one enum value defines the node '" + permission.getNode() + "'"); - } - } + @Override + protected PermissionNode[] getPermissionNodes() { + return PlayerStatePermission.values(); } - private static boolean hasAnyPrefix(String node, Set prefixes) { + @Override + protected String getRequiredPrefix() { + return "authme."; + } + + private static boolean startsWithAny(String node, Collection prefixes) { for (String prefix : prefixes) { if (node.startsWith(prefix)) { return true; @@ -51,5 +44,4 @@ public class PlayerStatePermissionTest { } return false; } - } diff --git a/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java b/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java index e36355121..f177cbc67 100644 --- a/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java +++ b/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java @@ -25,7 +25,7 @@ public class PermissionNodesGatherer { * the second group should contain the enum value. */ private static final Pattern JAVADOC_WITH_ENUM_PATTERN = Pattern.compile( - "/\\*\\*\\s+\\*" // Match starting '/**' and the '*' on the next line + "/\\*\\*(\\s+\\*)?" // Match starting '/**' and optional whitespace with a '*' + "(.*?)\\s+\\*/" // Capture everything until we encounter '*/' + "\\s+([A-Z_]+)\\("); // Match the enum name (e.g. 'LOGIN'), until before the first '(' @@ -87,8 +87,8 @@ public class PermissionNodesGatherer { Map allMatches = new HashMap<>(); Matcher matcher = JAVADOC_WITH_ENUM_PATTERN.matcher(source); while (matcher.find()) { - String description = matcher.group(1); - String enumValue = matcher.group(2); + String description = matcher.group(2); + String enumValue = matcher.group(3); allMatches.put(enumValue, description); } return allMatches; diff --git a/src/test/java/tools/filegeneration/GeneratePluginYml.java b/src/test/java/tools/filegeneration/GeneratePluginYml.java index b4a017c62..8fdc779d7 100644 --- a/src/test/java/tools/filegeneration/GeneratePluginYml.java +++ b/src/test/java/tools/filegeneration/GeneratePluginYml.java @@ -32,8 +32,9 @@ public class GeneratePluginYml implements AutoToolTask { private static final Map WILDCARD_PERMISSIONS = ImmutableMap.of( "authme.player.*", "Gives access to all player commands", + "authme.player.email", "Gives access to all email commands", "authme.admin.*", "Gives access to all admin commands", - "authme.player.email", "Gives access to all email commands"); + "authme.debug", "Gives access to /authme debug and all its sections"); private List permissionNodes;