#1523 Create admin commands to handle players' 2FA data (#1876)

* #1523 Create admin commands to handle players' 2FA data
- Create admin command to view if a player has enabled 2FA
- Create admin command to disable 2FA for a specified player
This commit is contained in:
ljacqu 2019-08-05 19:31:59 +02:00 committed by GitHub
parent f0d3d085c6
commit 7bede2528f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 362 additions and 5 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 Fri Apr 19 17:16:04 CEST 2019. See docs/commands/commands.tpl.md --> <!-- File auto-generated on Fri Aug 02 16:25:51 CEST 2019. 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 `< >`
@ -24,6 +24,10 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
<br />Requires `authme.admin.changemail` <br />Requires `authme.admin.changemail`
- **/authme getip** &lt;player>: Get the IP address of the specified online player. - **/authme getip** &lt;player>: Get the IP address of the specified online player.
<br />Requires `authme.admin.getip` <br />Requires `authme.admin.getip`
- **/authme totp** &lt;player>: Returns whether the specified player has enabled two-factor authentication
<br />Requires `authme.admin.totpviewstatus`
- **/authme disabletotp** &lt;player>: Disable two-factor authentication for a player
<br />Requires `authme.admin.totpdisable`
- **/authme spawn**: Teleport to the spawn. - **/authme spawn**: Teleport to the spawn.
<br />Requires `authme.admin.spawn` <br />Requires `authme.admin.spawn`
- **/authme setspawn**: Change the player's spawn to your current position. - **/authme setspawn**: Change the player's spawn to your current position.
@ -104,4 +108,4 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
--- ---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Apr 19 17:16:04 CEST 2019 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Aug 02 16:25:51 CEST 2019

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 Fri Apr 19 17:16:06 CEST 2019. See docs/permissions/permission_nodes.tpl.md --> <!-- File auto-generated on Fri Aug 02 16:25:54 CEST 2019. 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.
@ -28,6 +28,8 @@ The following are the permission nodes that are currently supported by the lates
- **authme.admin.setspawn** Administrator command to set the AuthMe spawn. - **authme.admin.setspawn** Administrator command to set the AuthMe spawn.
- **authme.admin.spawn** Administrator command to teleport to the AuthMe spawn. - **authme.admin.spawn** Administrator command to teleport to the AuthMe spawn.
- **authme.admin.switchantibot** Administrator command to toggle the AntiBot protection status. - **authme.admin.switchantibot** Administrator command to toggle the AntiBot protection status.
- **authme.admin.totpdisable** Administrator command to disable the two-factor auth of a user.
- **authme.admin.totpviewstatus** Administrator command to see whether a player has enabled two-factor authentication.
- **authme.admin.unregister** Administrator command to unregister an existing user. - **authme.admin.unregister** Administrator command to unregister an existing user.
- **authme.admin.updatemessages** Permission to use the update messages command. - **authme.admin.updatemessages** Permission to use the update messages command.
- **authme.allowchatbeforelogin** Permission to send chat messages before being logged in. - **authme.allowchatbeforelogin** Permission to send chat messages before being logged in.
@ -71,4 +73,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 Fri Apr 19 17:16:06 CEST 2019 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Aug 02 16:25:54 CEST 2019

View File

@ -24,6 +24,8 @@ import fr.xephi.authme.command.executable.authme.SetFirstSpawnCommand;
import fr.xephi.authme.command.executable.authme.SetSpawnCommand; import fr.xephi.authme.command.executable.authme.SetSpawnCommand;
import fr.xephi.authme.command.executable.authme.SpawnCommand; import fr.xephi.authme.command.executable.authme.SpawnCommand;
import fr.xephi.authme.command.executable.authme.SwitchAntiBotCommand; import fr.xephi.authme.command.executable.authme.SwitchAntiBotCommand;
import fr.xephi.authme.command.executable.authme.TotpDisableAdminCommand;
import fr.xephi.authme.command.executable.authme.TotpViewStatusCommand;
import fr.xephi.authme.command.executable.authme.UnregisterAdminCommand; import fr.xephi.authme.command.executable.authme.UnregisterAdminCommand;
import fr.xephi.authme.command.executable.authme.UpdateHelpMessagesCommand; import fr.xephi.authme.command.executable.authme.UpdateHelpMessagesCommand;
import fr.xephi.authme.command.executable.authme.VersionCommand; import fr.xephi.authme.command.executable.authme.VersionCommand;
@ -290,6 +292,28 @@ public class CommandInitializer {
.executableCommand(GetIpCommand.class) .executableCommand(GetIpCommand.class)
.register(); .register();
// Register totp command
CommandDescription.builder()
.parent(authmeBase)
.labels("totp", "2fa")
.description("See if a player has enabled TOTP")
.detailedDescription("Returns whether the specified player has enabled two-factor authentication.")
.withArgument("player", "Player name", MANDATORY)
.permission(AdminPermission.VIEW_TOTP_STATUS)
.executableCommand(TotpViewStatusCommand.class)
.register();
// Register disable totp command
CommandDescription.builder()
.parent(authmeBase)
.labels("disabletotp", "disable2fa", "deletetotp", "delete2fa")
.description("Delete TOTP token of a player")
.detailedDescription("Disable two-factor authentication for a player.")
.withArgument("player", "Player name", MANDATORY)
.permission(AdminPermission.DISABLE_TOTP)
.executableCommand(TotpDisableAdminCommand.class)
.register();
// Register the spawn command // Register the spawn command
CommandDescription.builder() CommandDescription.builder()
.parent(authmeBase) .parent(authmeBase)

View File

@ -0,0 +1,58 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command to disable two-factor authentication for a user.
*/
public class TotpDisableAdminCommand implements ExecutableCommand {
@Inject
private DataSource dataSource;
@Inject
private Messages messages;
@Inject
private BukkitService bukkitService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
String player = arguments.get(0);
PlayerAuth auth = dataSource.getAuth(player);
if (auth == null) {
messages.send(sender, MessageKey.UNKNOWN_USER);
} else if (auth.getTotpKey() == null) {
sender.sendMessage(ChatColor.RED + "Player '" + player + "' does not have two-factor auth enabled");
} else {
removeTotpKey(sender, player);
}
}
private void removeTotpKey(CommandSender sender, String player) {
if (dataSource.removeTotpKey(player)) {
sender.sendMessage("Disabled two-factor authentication successfully for '" + player + "'");
ConsoleLogger.info(sender.getName() + " disable two-factor authentication for '" + player + "'");
Player onlinePlayer = bukkitService.getPlayerExact(player);
if (onlinePlayer != null) {
messages.send(onlinePlayer, MessageKey.TWO_FACTOR_REMOVED_SUCCESS);
}
} else {
messages.send(sender, MessageKey.ERROR);
}
}
}

View File

@ -0,0 +1,38 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
/**
* Command to see whether a user has enabled two-factor authentication.
*/
public class TotpViewStatusCommand implements ExecutableCommand {
@Inject
private DataSource dataSource;
@Inject
private Messages messages;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
String player = arguments.get(0);
PlayerAuth auth = dataSource.getAuth(player);
if (auth == null) {
messages.send(sender, MessageKey.UNKNOWN_USER);
} else if (auth.getTotpKey() == null) {
sender.sendMessage(ChatColor.RED + "Player '" + player + "' does NOT have two-factor auth enabled");
} else {
sender.sendMessage(ChatColor.DARK_GREEN + "Player '" + player + "' has enabled two-factor authentication");
}
}
}

View File

@ -45,6 +45,16 @@ public enum AdminPermission implements PermissionNode {
*/ */
CHANGE_EMAIL("authme.admin.changemail"), CHANGE_EMAIL("authme.admin.changemail"),
/**
* Administrator command to see whether a player has enabled two-factor authentication.
*/
VIEW_TOTP_STATUS("authme.admin.totpviewstatus"),
/**
* Administrator command to disable the two-factor auth of a user.
*/
DISABLE_TOTP("authme.admin.totpdisable"),
/** /**
* Administrator command to get the last known IP of a user. * Administrator command to get the last known IP of a user.
*/ */

View File

@ -18,7 +18,7 @@ softdepend:
commands: commands:
authme: authme:
description: AuthMe op commands description: AuthMe op commands
usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|recent|debug usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|totp|disabletotp|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|recent|debug
email: email:
description: Add email or recover password description: Add email or recover password
usage: /email show|add|change|recover|code|setpassword usage: /email show|add|change|recover|code|setpassword
@ -85,6 +85,8 @@ permissions:
authme.admin.setspawn: true authme.admin.setspawn: true
authme.admin.spawn: true authme.admin.spawn: true
authme.admin.switchantibot: true authme.admin.switchantibot: true
authme.admin.totpdisable: true
authme.admin.totpviewstatus: true
authme.admin.unregister: true authme.admin.unregister: true
authme.admin.updatemessages: true authme.admin.updatemessages: true
authme.admin.accounts: authme.admin.accounts:
@ -156,6 +158,13 @@ permissions:
authme.admin.switchantibot: authme.admin.switchantibot:
description: Administrator command to toggle the AntiBot protection status. description: Administrator command to toggle the AntiBot protection status.
default: op default: op
authme.admin.totpdisable:
description: Administrator command to disable the two-factor auth of a user.
default: op
authme.admin.totpviewstatus:
description: Administrator command to see whether a player has enabled two-factor
authentication.
default: op
authme.admin.unregister: authme.admin.unregister:
description: Administrator command to unregister an existing user. description: Administrator command to unregister an existing user.
default: op default: op

View File

@ -0,0 +1,120 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Collections;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
/**
* Test for {@link TotpDisableAdminCommand}.
*/
@RunWith(MockitoJUnitRunner.class)
public class TotpDisableAdminCommandTest {
@InjectMocks
private TotpDisableAdminCommand command;
@Mock
private DataSource dataSource;
@Mock
private Messages messages;
@Mock
private BukkitService bukkitService;
@BeforeClass
public static void initLogger() {
TestHelper.setupLogger();
}
@Test
public void shouldHandleUnknownUser() {
// given
CommandSender sender = mock(CommandSender.class);
given(dataSource.getAuth("user")).willReturn(null);
// when
command.executeCommand(sender, Collections.singletonList("user"));
// then
verify(messages).send(sender, MessageKey.UNKNOWN_USER);
verify(dataSource, only()).getAuth("user");
}
@Test
public void shouldHandleUserWithNoTotpEnabled() {
// given
CommandSender sender = mock(CommandSender.class);
PlayerAuth auth = PlayerAuth.builder()
.name("billy")
.totpKey(null)
.build();
given(dataSource.getAuth("Billy")).willReturn(auth);
// when
command.executeCommand(sender, Collections.singletonList("Billy"));
// then
verify(sender).sendMessage(argThat(containsString("'Billy' does not have two-factor auth enabled")));
verify(dataSource, only()).getAuth("Billy");
}
@Test
public void shouldRemoveTotpFromUser() {
// given
CommandSender sender = mock(CommandSender.class);
PlayerAuth auth = PlayerAuth.builder()
.name("Bobby")
.totpKey("56484998")
.build();
given(dataSource.getAuth("Bobby")).willReturn(auth);
given(dataSource.removeTotpKey("Bobby")).willReturn(true);
Player player = mock(Player.class);
given(bukkitService.getPlayerExact("Bobby")).willReturn(player);
// when
command.executeCommand(sender, Collections.singletonList("Bobby"));
// then
verify(sender).sendMessage(argThat(containsString("Disabled two-factor authentication successfully")));
verify(messages).send(player, MessageKey.TWO_FACTOR_REMOVED_SUCCESS);
}
@Test
public void shouldHandleErrorWhileRemovingTotp() {
// given
CommandSender sender = mock(CommandSender.class);
PlayerAuth auth = PlayerAuth.builder()
.name("Bobby")
.totpKey("321654")
.build();
given(dataSource.getAuth("Bobby")).willReturn(auth);
given(dataSource.removeTotpKey("Bobby")).willReturn(false);
// when
command.executeCommand(sender, Collections.singletonList("Bobby"));
// then
verify(messages).send(sender, MessageKey.ERROR);
}
}

View File

@ -0,0 +1,92 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import org.bukkit.command.CommandSender;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Collections;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
/**
* Test for {@link TotpViewStatusCommand}.
*/
@RunWith(MockitoJUnitRunner.class)
public class TotpViewStatusCommandTest {
@InjectMocks
private TotpViewStatusCommand command;
@Mock
private DataSource dataSource;
@Mock
private Messages messages;
@BeforeClass
public static void initLogger() {
TestHelper.setupLogger();
}
@Test
public void shouldHandleUnknownUser() {
// given
CommandSender sender = mock(CommandSender.class);
given(dataSource.getAuth("user")).willReturn(null);
// when
command.executeCommand(sender, Collections.singletonList("user"));
// then
verify(messages).send(sender, MessageKey.UNKNOWN_USER);
verify(dataSource, only()).getAuth("user");
}
@Test
public void shouldInformForUserWithoutTotp() {
// given
CommandSender sender = mock(CommandSender.class);
PlayerAuth auth = PlayerAuth.builder()
.name("billy")
.totpKey(null)
.build();
given(dataSource.getAuth("Billy")).willReturn(auth);
// when
command.executeCommand(sender, Collections.singletonList("Billy"));
// then
verify(sender).sendMessage(argThat(containsString("'Billy' does NOT have two-factor auth enabled")));
}
@Test
public void shouldInformForUserWithTotpEnabled() {
// given
CommandSender sender = mock(CommandSender.class);
PlayerAuth auth = PlayerAuth.builder()
.name("billy")
.totpKey("92841575")
.build();
given(dataSource.getAuth("Billy")).willReturn(auth);
// when
command.executeCommand(sender, Collections.singletonList("Billy"));
// then
verify(sender).sendMessage(argThat(containsString("'Billy' has enabled two-factor authentication")));
}
}