#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)
This commit is contained in:
ljacqu 2017-04-14 19:54:17 +02:00
parent cbec5427f2
commit 2a747c7d01
27 changed files with 480 additions and 120 deletions

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Wed Mar 22 23:10:32 CET 2017. See docs/commands/commands.tpl.md --> <!-- File auto-generated on Fri Apr 14 18:52:29 CEST 2017. See docs/commands/commands.tpl.md -->
## AuthMe Commands ## AuthMe Commands
You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` 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 (`[ ]`).
<br />Requires `authme.admin.converter` <br />Requires `authme.admin.converter`
- **/authme messages**: Adds missing messages to the current messages file. - **/authme messages**: Adds missing messages to the current messages file.
<br />Requires `authme.admin.updatemessages` <br />Requires `authme.admin.updatemessages`
- **/authme debug** [child] [params]: Allows various operations for debugging. - **/authme debug** [child] [arg] [arg]: Allows various operations for debugging.
<br />Requires `authme.debug` <br />Requires `authme.debug.command`
- **/authme help** [query]: View detailed help for /authme commands. - **/authme help** [query]: View detailed help for /authme commands.
- **/login** &lt;password>: Command to log in using AuthMeReloaded. - **/login** &lt;password>: Command to log in using AuthMeReloaded.
<br />Requires `authme.player.login` <br />Requires `authme.player.login`
@ -82,6 +82,7 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
<br />Requires `authme.player.captcha` <br />Requires `authme.player.captcha`
- **/captcha help** [query]: View detailed help for /captcha commands. - **/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

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Sun Oct 23 15:38:58 CEST 2016. See permissions/permission_nodes.tpl.md --> <!-- File auto-generated on Fri Apr 14 18:52:32 CEST 2017. See docs/permissions/permission_nodes.tpl.md -->
## AuthMe Permission Nodes ## AuthMe Permission Nodes
The following are the permission nodes that are currently supported by the latest dev builds. 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.bypassantibot** Permission node to bypass AntiBot protection.
- **authme.bypassforcesurvival** Permission for users to bypass force-survival mode. - **authme.bypassforcesurvival** Permission for users to bypass force-survival mode.
- **authme.bypasspurge** Permission to bypass the purging process. - **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.*** Permission to use all player (non-admin) commands.
- **authme.player.canbeforced** Permission for users a login can be forced to. - **authme.player.canbeforced** Permission for users a login can be forced to.
- **authme.player.captcha** Command permission to use captcha. - **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

View File

@ -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.register.RegisterCommand;
import fr.xephi.authme.command.executable.unregister.UnregisterCommand; import fr.xephi.authme.command.executable.unregister.UnregisterCommand;
import fr.xephi.authme.permission.AdminPermission; import fr.xephi.authme.permission.AdminPermission;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PlayerPermission; import fr.xephi.authme.permission.PlayerPermission;
import fr.xephi.authme.permission.PlayerStatePermission;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -312,10 +312,9 @@ public class CommandInitializer {
.description("Debug features") .description("Debug features")
.detailedDescription("Allows various operations for debugging.") .detailedDescription("Allows various operations for debugging.")
.withArgument("child", "The child to execute", true) .withArgument("child", "The child to execute", true)
.withArgument(".", "meaning varies", true) .withArgument("arg", "argument (depends on debug section)", true)
.withArgument(".", "meaning varies", true) .withArgument("arg", "argument (depends on debug section)", true)
.withArgument(".", "meaning varies", true) .permission(DebugSectionPermissions.DEBUG_COMMAND)
.permission(PlayerStatePermission.DEBUG_COMMAND)
.executableCommand(DebugCommand.class) .executableCommand(DebugCommand.class)
.register(); .register();

View File

@ -2,6 +2,8 @@ package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource; 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.GeoIpService;
import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.settings.properties.ProtectionSettings; 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) { private void outputInfoForIpAddr(CommandSender sender, String ipAddr) {
sender.sendMessage("IP '" + ipAddr + "' maps to country '" + geoIpService.getCountryCode(ipAddr) sender.sendMessage("IP '" + ipAddr + "' maps to country '" + geoIpService.getCountryCode(ipAddr)
+ "' (" + geoIpService.getCountryName(ipAddr) + ")"); + "' (" + geoIpService.getCountryName(ipAddr) + ")");

View File

@ -8,6 +8,8 @@ import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.initialization.factory.SingletonStore; 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 org.bukkit.command.CommandSender;
import javax.inject.Inject; import javax.inject.Inject;
@ -52,6 +54,11 @@ class DataStatistics implements DebugSection {
outputInjectorStats(sender); outputInjectorStats(sender);
} }
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.DATA_STATISTICS;
}
private void outputDatabaseStats(CommandSender sender) { private void outputDatabaseStats(CommandSender sender) {
sender.sendMessage("Total players in DB: " + dataSource.getAccountsRegistered()); sender.sendMessage("Total players in DB: " + dataSource.getAccountsRegistered());
sender.sendMessage("Total marked as logged in in DB: " + dataSource.getLoggedPlayers().size()); sender.sendMessage("Total marked as logged in in DB: " + dataSource.getLoggedPlayers().size());

View File

@ -3,6 +3,8 @@ package fr.xephi.authme.command.executable.authme.debug;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.initialization.factory.Factory; import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.permission.PermissionsManager;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import javax.inject.Inject; import javax.inject.Inject;
@ -24,27 +26,48 @@ public class DebugCommand implements ExecutableCommand {
@Inject @Inject
private Factory<DebugSection> debugSectionFactory; private Factory<DebugSection> debugSectionFactory;
@Inject
private PermissionsManager permissionsManager;
private Map<String, DebugSection> sections; private Map<String, DebugSection> sections;
@Override @Override
public void executeCommand(CommandSender sender, List<String> arguments) { public void executeCommand(CommandSender sender, List<String> arguments) {
DebugSection debugSection = getDebugSection(arguments); DebugSection debugSection = findDebugSection(arguments);
if (debugSection == null) { if (debugSection == null) {
sender.sendMessage("Available sections:"); sendAvailableSections(sender);
getSections().values()
.forEach(e -> sender.sendMessage("- " + e.getName() + ": " + e.getDescription()));
} else { } else {
debugSection.execute(sender, arguments.subList(1, arguments.size())); executeSection(debugSection, sender, arguments);
} }
} }
private DebugSection getDebugSection(List<String> arguments) { private DebugSection findDebugSection(List<String> arguments) {
if (arguments.isEmpty()) { if (arguments.isEmpty()) {
return null; return null;
} }
return getSections().get(arguments.get(0).toLowerCase()); 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<String> 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 // Lazy getter
private Map<String, DebugSection> getSections() { private Map<String, DebugSection> getSections() {
if (sections == null) { if (sections == null) {

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.authme.debug; package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.permission.PermissionNode;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.List; import java.util.List;
@ -27,4 +28,9 @@ interface DebugSection {
*/ */
void execute(CommandSender sender, List<String> arguments); void execute(CommandSender sender, List<String> arguments);
/**
* @return permission required to run this section
*/
PermissionNode getRequiredPermission();
} }

View File

@ -2,6 +2,7 @@ package fr.xephi.authme.command.executable.authme.debug;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import fr.xephi.authme.permission.AdminPermission; import fr.xephi.authme.permission.AdminPermission;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.DefaultPermission; import fr.xephi.authme.permission.DefaultPermission;
import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
@ -25,8 +26,8 @@ import java.util.function.BiFunction;
*/ */
class HasPermissionChecker implements DebugSection { class HasPermissionChecker implements DebugSection {
static final List<Class<? extends PermissionNode>> PERMISSION_NODE_CLASSES = static final List<Class<? extends PermissionNode>> PERMISSION_NODE_CLASSES = ImmutableList.of(
ImmutableList.of(AdminPermission.class, PlayerPermission.class, PlayerStatePermission.class); AdminPermission.class, PlayerPermission.class, PlayerStatePermission.class, DebugSectionPermissions.class);
@Inject @Inject
private PermissionsManager permissionsManager; 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 * 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}. * 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() sender.sendMessage(ChatColor.DARK_RED + "Check failed: player '" + player.getName()
+ "' does NOT have permission '" + node + "'"); + "' does NOT have permission '" + node + "'");
} }
} }
/** /**

View File

@ -3,6 +3,8 @@ package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.listener.FailedVerificationException; import fr.xephi.authme.listener.FailedVerificationException;
import fr.xephi.authme.listener.OnJoinVerifier; import fr.xephi.authme.listener.OnJoinVerifier;
import fr.xephi.authme.message.Messages; 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;
import fr.xephi.authme.service.ValidationService.ValidationResult; import fr.xephi.authme.service.ValidationService.ValidationResult;
import org.bukkit.ChatColor; 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) { private void displayUsageHint(CommandSender sender) {
sender.sendMessage("You can define forbidden emails and passwords in your config.yml"); 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:"); sender.sendMessage("This command allows you to test some of the values:");

View File

@ -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.LimboPlayer;
import fr.xephi.authme.data.limbo.LimboService; import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.data.limbo.persistence.LimboPersistence; 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.permission.PermissionsManager;
import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.BukkitService;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -71,6 +73,11 @@ class LimboPlayerViewer implements DebugSection {
.sendEntry("Group", LimboPlayer::getGroup, permissionsManager::getPrimaryGroup); .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. * Displays the info for the given LimboPlayer and Player to the provided CommandSender.
*/ */

View File

@ -1,5 +1,7 @@
package fr.xephi.authme.command.executable.authme.debug; 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 fr.xephi.authme.permission.PermissionsManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -38,4 +40,9 @@ class PermissionGroups implements DebugSection {
sender.sendMessage("Primary group is: " + permissionsManager.getGroups(player)); sender.sendMessage("Primary group is: " + permissionsManager.getGroups(player));
} }
} }
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.PERM_GROUPS;
}
} }

View File

@ -2,6 +2,8 @@ package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource; 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.security.crypts.HashedPassword;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import org.bukkit.ChatColor; 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. * Outputs the PlayerAuth information to the given sender.
* *

View File

@ -1,5 +1,7 @@
package fr.xephi.authme.command.executable.authme.debug; 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.service.BukkitService;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader; 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) { private void showGeneralInfo(CommandSender sender) {
sender.sendMessage("Spawn priority: " sender.sendMessage("Spawn priority: "
+ String.join(", ", settings.getProperty(RestrictionSettings.SPAWN_PRIORITY))); + String.join(", ", settings.getProperty(RestrictionSettings.SPAWN_PRIORITY)));

View File

@ -4,6 +4,8 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.mail.SendMailSsl; 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 fr.xephi.authme.util.StringUtils;
import org.apache.commons.mail.EmailException; import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail; 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<String> arguments) { private String getEmail(CommandSender sender, List<String> arguments) {
if (arguments.isEmpty()) { if (arguments.isEmpty()) {
PlayerAuth auth = dataSource.getAuth(sender.getName()); PlayerAuth auth = dataSource.getAuth(sender.getName());

View File

@ -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;
}
}

View File

@ -29,12 +29,7 @@ public enum PlayerStatePermission implements PermissionNode {
/** /**
* Permission to bypass the purging process. * Permission to bypass the purging process.
*/ */
BYPASS_PURGE("authme.bypasspurge", DefaultPermission.NOT_ALLOWED), BYPASS_PURGE("authme.bypasspurge", DefaultPermission.NOT_ALLOWED);
/**
* Permission to use the /authme debug command.
*/
DEBUG_COMMAND("authme.debug", DefaultPermission.OP_ONLY);
/** /**
* The permission node. * The permission node.

View File

@ -45,7 +45,7 @@ commands:
- cp - cp
email: email:
description: Add email or recover password description: Add email or recover password
usage: /email show|add|change|recover usage: /email show|add|change|recover|code|setpassword
captcha: captcha:
description: Captcha Command description: Captcha Command
usage: /captcha <captcha> usage: /captcha <captcha>
@ -154,7 +154,47 @@ permissions:
description: Permission to bypass the purging process. description: Permission to bypass the purging process.
default: false default: false
authme.debug: 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 default: op
authme.player.*: authme.player.*:
description: Gives access to all player commands description: Gives access to all player commands

View File

@ -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<DebugSection> 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<String> 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<String> 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<String> 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<String> 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")));
}
}

View File

@ -20,9 +20,9 @@ import java.util.stream.Collectors;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeast;
@ -55,8 +55,7 @@ public class HasPermissionCheckerTest {
.collect(Collectors.toList()); .collect(Collectors.toList());
// when / then // when / then
assertThat(HasPermissionChecker.PERMISSION_NODE_CLASSES.containsAll(permissionClasses), equalTo(true)); assertThat(HasPermissionChecker.PERMISSION_NODE_CLASSES, containsInAnyOrder(permissionClasses.toArray()));
assertThat(HasPermissionChecker.PERMISSION_NODE_CLASSES, hasSize(permissionClasses.size()));
} }
@Test @Test

View File

@ -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<String> 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();
}

View File

@ -1,42 +1,18 @@
package fr.xephi.authme.permission; 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}. * Test for {@link AdminPermission}.
*/ */
public class AdminPermissionTest { public class AdminPermissionTest extends AbstractPermissionsEnumTest {
@Test @Override
public void shouldStartWithAuthMeAdminPrefix() { protected PermissionNode[] getPermissionNodes() {
// given return AdminPermission.values();
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 + "'");
}
}
} }
@Test @Override
public void shouldHaveUniqueNodes() { protected String getRequiredPrefix() {
// given return "authme.admin.";
Set<String> 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() + "'");
}
}
} }
} }

View File

@ -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.";
}
}

View File

@ -31,7 +31,7 @@ public class PermissionConsistencyTest {
/** Wildcard permissions (present in plugin.yml but not in the codebase). */ /** Wildcard permissions (present in plugin.yml but not in the codebase). */
private static final Set<String> PLUGIN_YML_PERMISSIONS_WILDCARDS = private static final Set<String> 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. */ /** Name of the fields that make up a permission entry in plugin.yml. */
private static final Set<String> PERMISSION_FIELDS = ImmutableSet.of("description", "default", "children"); private static final Set<String> PERMISSION_FIELDS = ImmutableSet.of("description", "default", "children");

View File

@ -1,41 +1,17 @@
package fr.xephi.authme.permission; 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}. * Test for {@link PlayerPermission}.
*/ */
public class PlayerPermissionTest { public class PlayerPermissionTest extends AbstractPermissionsEnumTest {
@Test @Override
public void shouldStartWithPlayerPrefix() { protected PermissionNode[] getPermissionNodes() {
// given return PlayerPermission.values();
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 + "'");
}
}
} }
@Test @Override
public void shouldHaveUniqueNodes() { protected String getRequiredPrefix() {
// given return "authme.player.";
Set<String> 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() + "'");
}
}
} }
} }

View File

@ -2,7 +2,7 @@ package fr.xephi.authme.permission;
import org.junit.Test; import org.junit.Test;
import java.util.HashSet; import java.util.Collection;
import java.util.Set; import java.util.Set;
import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.Sets.newHashSet;
@ -11,39 +11,32 @@ import static org.junit.Assert.fail;
/** /**
* Test for {@link PlayerStatePermission}. * Test for {@link PlayerStatePermission}.
*/ */
public class PlayerStatePermissionTest { public class PlayerStatePermissionTest extends AbstractPermissionsEnumTest {
@Test @Test
public void shouldStartWithAuthMeAdminPrefix() { public void shouldNotStartWithOtherPrefixes() {
// given // given
String requiredPrefix = "authme."; Set<String> forbiddenPrefixes = newHashSet("authme.player", "authme.admin", "authme.debug");
Set<String> forbiddenPrefixes = newHashSet("authme.player", "authme.admin");
// when/then // when/then
for (PlayerStatePermission permission : PlayerStatePermission.values()) { for (PlayerStatePermission permission : PlayerStatePermission.values()) {
if (!permission.getNode().startsWith(requiredPrefix)) { if (startsWithAny(permission.getNode(), forbiddenPrefixes)) {
fail("The permission '" + permission + "' does not start with the required prefix '"
+ requiredPrefix + "'");
} else if (hasAnyPrefix(permission.getNode(), forbiddenPrefixes)) {
fail("The permission '" + permission + "' should not start with any of " + forbiddenPrefixes); fail("The permission '" + permission + "' should not start with any of " + forbiddenPrefixes);
} }
} }
} }
@Test @Override
public void shouldHaveUniqueNodes() { protected PermissionNode[] getPermissionNodes() {
// given return PlayerStatePermission.values();
Set<String> 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() + "'");
}
}
} }
private static boolean hasAnyPrefix(String node, Set<String> prefixes) { @Override
protected String getRequiredPrefix() {
return "authme.";
}
private static boolean startsWithAny(String node, Collection<String> prefixes) {
for (String prefix : prefixes) { for (String prefix : prefixes) {
if (node.startsWith(prefix)) { if (node.startsWith(prefix)) {
return true; return true;
@ -51,5 +44,4 @@ public class PlayerStatePermissionTest {
} }
return false; return false;
} }
} }

View File

@ -25,7 +25,7 @@ public class PermissionNodesGatherer {
* the second group should contain the enum value. * the second group should contain the enum value.
*/ */
private static final Pattern JAVADOC_WITH_ENUM_PATTERN = Pattern.compile( 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+\\*/" // Capture everything until we encounter '*/'
+ "\\s+([A-Z_]+)\\("); // Match the enum name (e.g. 'LOGIN'), until before the first '(' + "\\s+([A-Z_]+)\\("); // Match the enum name (e.g. 'LOGIN'), until before the first '('
@ -87,8 +87,8 @@ public class PermissionNodesGatherer {
Map<String, String> allMatches = new HashMap<>(); Map<String, String> allMatches = new HashMap<>();
Matcher matcher = JAVADOC_WITH_ENUM_PATTERN.matcher(source); Matcher matcher = JAVADOC_WITH_ENUM_PATTERN.matcher(source);
while (matcher.find()) { while (matcher.find()) {
String description = matcher.group(1); String description = matcher.group(2);
String enumValue = matcher.group(2); String enumValue = matcher.group(3);
allMatches.put(enumValue, description); allMatches.put(enumValue, description);
} }
return allMatches; return allMatches;

View File

@ -32,8 +32,9 @@ public class GeneratePluginYml implements AutoToolTask {
private static final Map<String, String> WILDCARD_PERMISSIONS = ImmutableMap.of( private static final Map<String, String> WILDCARD_PERMISSIONS = ImmutableMap.of(
"authme.player.*", "Gives access to all player commands", "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.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<PermissionNode> permissionNodes; private List<PermissionNode> permissionNodes;