mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-12-24 01:27:35 +01:00
#1141 Move TOTP code during login as separate step: /2fa code
Rough version. - Introduces a limbo player state on the LimboPlayer, allowing us to add further mandatory actions between successful (password) authentication and the ability to play on the server
This commit is contained in:
parent
af6bee59bd
commit
495cfc69a9
@ -44,6 +44,7 @@ import fr.xephi.authme.command.executable.totp.AddTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.ConfirmTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.RemoveTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.TotpBaseCommand;
|
||||
import fr.xephi.authme.command.executable.totp.TotpCodeCommand;
|
||||
import fr.xephi.authme.command.executable.unregister.UnregisterCommand;
|
||||
import fr.xephi.authme.command.executable.verification.VerificationCommand;
|
||||
import fr.xephi.authme.permission.AdminPermission;
|
||||
@ -92,7 +93,6 @@ public class CommandInitializer {
|
||||
.description("Login command")
|
||||
.detailedDescription("Command to log in using AuthMeReloaded.")
|
||||
.withArgument("password", "Login password", MANDATORY)
|
||||
.withArgument("2facode", "TOTP code", OPTIONAL)
|
||||
.permission(PlayerPermission.LOGIN)
|
||||
.executableCommand(LoginCommand.class)
|
||||
.register();
|
||||
@ -561,6 +561,15 @@ public class CommandInitializer {
|
||||
.executableCommand(TotpBaseCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base totp code
|
||||
CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("code", "c")
|
||||
.description("Command for logging in")
|
||||
.detailedDescription("Processes the two-factor authentication code during login.")
|
||||
.executableCommand(TotpCodeCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp add
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
|
@ -4,6 +4,7 @@ import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.data.captcha.LoginCaptchaManager;
|
||||
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||
import fr.xephi.authme.data.limbo.LimboMessageType;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
@ -80,6 +81,6 @@ public class CaptchaCommand extends PlayerCommand {
|
||||
String newCode = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
|
||||
commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
|
||||
}
|
||||
limboService.resetMessageTask(player, false);
|
||||
limboService.resetMessageTask(player, LimboMessageType.REGISTER);
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,7 @@ public class LoginCommand extends PlayerCommand {
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
String password = arguments.get(0);
|
||||
String totpCode = arguments.size() > 1 ? arguments.get(1) : null;
|
||||
management.performLogin(player, password, totpCode);
|
||||
management.performLogin(player, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,72 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.data.limbo.LimboPlayer;
|
||||
import fr.xephi.authme.data.limbo.LimboPlayerState;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.process.login.AsynchronousLogin;
|
||||
import fr.xephi.authme.security.totp.TotpService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TOTP code command for processing the 2FA code during the login process.
|
||||
*/
|
||||
public class TotpCodeCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private LimboService limboService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Inject
|
||||
private TotpService totpService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private AsynchronousLogin asynchronousLogin;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
if (playerCache.isAuthenticated(player.getName())) {
|
||||
messages.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.REGISTER_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
LimboPlayer limbo = limboService.getLimboPlayer(player.getName());
|
||||
if (limbo.getState() == LimboPlayerState.TOTP_REQUIRED) {
|
||||
processCode(player, limbo, auth, arguments.get(0));
|
||||
} else {
|
||||
messages.send(player, MessageKey.LOGIN_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void processCode(Player player, LimboPlayer limbo, PlayerAuth auth, String inputCode) {
|
||||
boolean isCodeValid = totpService.verifyCode(auth, inputCode);
|
||||
if (isCodeValid) {
|
||||
limbo.setState(LimboPlayerState.FINISHED);
|
||||
asynchronousLogin.performLogin(player, auth);
|
||||
} else {
|
||||
player.sendMessage("Invalid code!");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package fr.xephi.authme.data.limbo;
|
||||
|
||||
public enum LimboMessageType {
|
||||
|
||||
REGISTER,
|
||||
|
||||
LOG_IN,
|
||||
|
||||
TOTP_CODE
|
||||
|
||||
}
|
@ -23,6 +23,7 @@ public class LimboPlayer {
|
||||
private final float flySpeed;
|
||||
private BukkitTask timeoutTask = null;
|
||||
private MessageTask messageTask = null;
|
||||
private LimboPlayerState state = LimboPlayerState.PASSWORD_REQUIRED;
|
||||
|
||||
public LimboPlayer(Location loc, boolean operator, Collection<String> groups, boolean fly, float walkSpeed,
|
||||
float flySpeed) {
|
||||
@ -124,4 +125,12 @@ public class LimboPlayer {
|
||||
setMessageTask(null);
|
||||
setTimeoutTask(null);
|
||||
}
|
||||
|
||||
public LimboPlayerState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(LimboPlayerState state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package fr.xephi.authme.data.limbo;
|
||||
|
||||
public enum LimboPlayerState {
|
||||
|
||||
PASSWORD_REQUIRED,
|
||||
|
||||
TOTP_REQUIRED,
|
||||
|
||||
FINISHED
|
||||
|
||||
}
|
@ -45,11 +45,11 @@ class LimboPlayerTaskManager {
|
||||
*
|
||||
* @param player the player
|
||||
* @param limbo the associated limbo player of the player
|
||||
* @param isRegistered whether the player is registered or not (needed to determine the message in the task)
|
||||
* @param messageType message type
|
||||
*/
|
||||
void registerMessageTask(Player player, LimboPlayer limbo, boolean isRegistered) {
|
||||
void registerMessageTask(Player player, LimboPlayer limbo, LimboMessageType messageType) {
|
||||
int interval = settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL);
|
||||
MessageResult result = getMessageKey(player.getName(), isRegistered);
|
||||
MessageResult result = getMessageKey(player.getName(), messageType);
|
||||
if (interval > 0) {
|
||||
String[] joinMessage = messages.retrieveSingle(player, result.messageKey, result.args).split("\n");
|
||||
MessageTask messageTask = new MessageTask(player, joinMessage);
|
||||
@ -89,12 +89,14 @@ class LimboPlayerTaskManager {
|
||||
* Returns the appropriate message key according to the registration status and settings.
|
||||
*
|
||||
* @param name the player's name
|
||||
* @param isRegistered whether or not the username is registered
|
||||
* @param messageType the message to show
|
||||
* @return the message key to display to the user
|
||||
*/
|
||||
private MessageResult getMessageKey(String name, boolean isRegistered) {
|
||||
if (isRegistered) {
|
||||
private MessageResult getMessageKey(String name, LimboMessageType messageType) {
|
||||
if (messageType == LimboMessageType.LOG_IN) {
|
||||
return new MessageResult(MessageKey.LOGIN_MESSAGE);
|
||||
} else if (messageType == LimboMessageType.TOTP_CODE) {
|
||||
return new MessageResult(MessageKey.TWO_FACTOR_CODE_REQUIRED);
|
||||
} else if (registrationCaptchaManager.isCaptchaRequired(name)) {
|
||||
final String captchaCode = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(name);
|
||||
return new MessageResult(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captchaCode);
|
||||
|
@ -69,7 +69,8 @@ public class LimboService {
|
||||
LimboPlayer limboPlayer = helper.merge(existingLimbo, limboFromDisk);
|
||||
limboPlayer = helper.merge(helper.createLimboPlayer(player, isRegistered, location), limboPlayer);
|
||||
|
||||
taskManager.registerMessageTask(player, limboPlayer, isRegistered);
|
||||
taskManager.registerMessageTask(player, limboPlayer,
|
||||
isRegistered ? LimboMessageType.LOG_IN : LimboMessageType.REGISTER);
|
||||
taskManager.registerTimeoutTask(player, limboPlayer);
|
||||
helper.revokeLimboStates(player);
|
||||
authGroupHandler.setGroup(player, limboPlayer,
|
||||
@ -134,7 +135,7 @@ public class LimboService {
|
||||
Optional<LimboPlayer> limboPlayer = getLimboOrLogError(player, "reset tasks");
|
||||
limboPlayer.ifPresent(limbo -> {
|
||||
taskManager.registerTimeoutTask(player, limbo);
|
||||
taskManager.registerMessageTask(player, limbo, true);
|
||||
taskManager.registerMessageTask(player, limbo, LimboMessageType.LOG_IN);
|
||||
});
|
||||
authGroupHandler.setGroup(player, limboPlayer.orElse(null), AuthGroupType.REGISTERED_UNAUTHENTICATED);
|
||||
}
|
||||
@ -143,11 +144,11 @@ public class LimboService {
|
||||
* Resets the message task associated with the player's LimboPlayer.
|
||||
*
|
||||
* @param player the player to set a new message task for
|
||||
* @param isRegistered whether or not the player is registered
|
||||
* @param messageType the message to show for the limbo player
|
||||
*/
|
||||
public void resetMessageTask(Player player, boolean isRegistered) {
|
||||
public void resetMessageTask(Player player, LimboMessageType messageType) {
|
||||
getLimboOrLogError(player, "reset message task")
|
||||
.ifPresent(limbo -> taskManager.registerMessageTask(player, limbo, isRegistered));
|
||||
.ifPresent(limbo -> taskManager.registerMessageTask(player, limbo, messageType));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,6 +197,9 @@ public enum MessageKey {
|
||||
/** Your secret code is %code. You can scan it from here %url */
|
||||
TWO_FACTOR_CREATE("misc.two_factor_create", "%code", "%url"),
|
||||
|
||||
/** Please submit your two-factor authentication code with /2fa code <code>. */
|
||||
TWO_FACTOR_CODE_REQUIRED("two_factor.code_required"),
|
||||
|
||||
/** You are not the owner of this account. Please choose another name! */
|
||||
NOT_OWNER_ERROR("on_join_validation.not_owner_error"),
|
||||
|
||||
|
@ -131,6 +131,7 @@ public class MessageUpdater {
|
||||
.put("captcha", new String[]{"Captcha"})
|
||||
.put("verification", new String[]{"Verification code"})
|
||||
.put("time", new String[]{"Time units"})
|
||||
.put("two_factor", new String[]{"Two-factor authentication"})
|
||||
.build();
|
||||
|
||||
Set<String> addedKeys = new HashSet<>();
|
||||
|
@ -49,8 +49,8 @@ public class Management {
|
||||
}
|
||||
|
||||
|
||||
public void performLogin(Player player, String password, String totpCode) {
|
||||
runTask(() -> asynchronousLogin.login(player, password, totpCode));
|
||||
public void performLogin(Player player, String password) {
|
||||
runTask(() -> asynchronousLogin.login(player, password));
|
||||
}
|
||||
|
||||
public void forceLogin(Player player) {
|
||||
|
@ -6,6 +6,8 @@ import fr.xephi.authme.data.TempbanManager;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.data.captcha.LoginCaptchaManager;
|
||||
import fr.xephi.authme.data.limbo.LimboMessageType;
|
||||
import fr.xephi.authme.data.limbo.LimboPlayerState;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.events.AuthMeAsyncPreLoginEvent;
|
||||
@ -18,7 +20,6 @@ import fr.xephi.authme.permission.PlayerStatePermission;
|
||||
import fr.xephi.authme.process.AsynchronousProcess;
|
||||
import fr.xephi.authme.process.SyncProcessManager;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.totp.TotpService;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.SessionService;
|
||||
@ -79,9 +80,6 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
@Inject
|
||||
private BungeeSender bungeeSender;
|
||||
|
||||
@Inject
|
||||
private TotpService totpService;
|
||||
|
||||
AsynchronousLogin() {
|
||||
}
|
||||
|
||||
@ -90,12 +88,17 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
*
|
||||
* @param player the player to log in
|
||||
* @param password the password to log in with
|
||||
* @param totpCode the totp code (nullable)
|
||||
*/
|
||||
public void login(Player player, String password, String totpCode) {
|
||||
public void login(Player player, String password) {
|
||||
PlayerAuth auth = getPlayerAuth(player);
|
||||
if (auth != null && checkPlayerInfo(player, auth, password, totpCode)) {
|
||||
performLogin(player, auth);
|
||||
if (auth != null && checkPlayerInfo(player, auth, password)) {
|
||||
if (auth.getTotpKey() != null) {
|
||||
limboService.resetMessageTask(player, LimboMessageType.TOTP_CODE);
|
||||
limboService.getLimboPlayer(player.getName()).setState(LimboPlayerState.TOTP_REQUIRED);
|
||||
// TODO #1141: Check if we should check limbo state before processing password
|
||||
} else {
|
||||
performLogin(player, auth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,7 +133,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
if (auth == null) {
|
||||
service.send(player, MessageKey.UNKNOWN_USER);
|
||||
// Recreate the message task to immediately send the message again as response
|
||||
limboService.resetMessageTask(player, false);
|
||||
limboService.resetMessageTask(player, LimboMessageType.REGISTER);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -161,11 +164,10 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
* @param player the player requesting to log in
|
||||
* @param auth the PlayerAuth object of the player
|
||||
* @param password the password supplied by the player
|
||||
* @param totpCode the input totp code (nullable)
|
||||
* @return true if the password matches and all other conditions are met (e.g. no captcha required),
|
||||
* false otherwise
|
||||
*/
|
||||
private boolean checkPlayerInfo(Player player, PlayerAuth auth, String password, String totpCode) {
|
||||
private boolean checkPlayerInfo(Player player, PlayerAuth auth, String password) {
|
||||
final String name = player.getName().toLowerCase();
|
||||
|
||||
// If captcha is required send a message to the player and deny to log in
|
||||
@ -180,17 +182,6 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
loginCaptchaManager.increaseLoginFailureCount(name);
|
||||
tempbanManager.increaseCount(ip, name);
|
||||
|
||||
if (auth.getTotpKey() != null) {
|
||||
if (totpCode == null) {
|
||||
player.sendMessage(
|
||||
"You have two-factor authentication enabled. Please provide it: /login <password> <2faCode>");
|
||||
return false;
|
||||
} else if (!totpService.verifyCode(auth, totpCode)) {
|
||||
player.sendMessage("Invalid code for two-factor authentication. Please try again");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (passwordSecurity.comparePassword(password, auth.getPassword(), player.getName())) {
|
||||
return true;
|
||||
} else {
|
||||
@ -235,7 +226,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
* @param player the player to log in
|
||||
* @param auth the associated PlayerAuth object
|
||||
*/
|
||||
private void performLogin(Player player, PlayerAuth auth) {
|
||||
public void performLogin(Player player, PlayerAuth auth) {
|
||||
if (player.isOnline()) {
|
||||
final boolean isFirstLogin = (auth.getLastLogin() == null);
|
||||
|
||||
|
@ -26,7 +26,7 @@ public final class RestrictionSettings implements SettingsHolder {
|
||||
@Comment("Allowed commands for unauthenticated players")
|
||||
public static final Property<List<String>> ALLOW_COMMANDS =
|
||||
newLowercaseListProperty("settings.restrictions.allowCommands",
|
||||
"/login", "/register", "/l", "/reg", "/email", "/captcha");
|
||||
"/login", "/register", "/l", "/reg", "/email", "/captcha", "/2fa", "/totp");
|
||||
|
||||
@Comment({
|
||||
"Max number of allowed registrations per IP",
|
||||
|
@ -128,6 +128,9 @@ verification:
|
||||
code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!'
|
||||
email_needed: '&3To verify your identity you need to link an email address with your account!!'
|
||||
|
||||
two_factor:
|
||||
code_required: 'Please submit your two-factor authentication code with /2fa code <code>'
|
||||
|
||||
# Time units
|
||||
time:
|
||||
second: 'second'
|
||||
|
@ -11,7 +11,6 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
@ -57,19 +56,7 @@ public class LoginCommandTest {
|
||||
command.executeCommand(sender, Collections.singletonList("password"));
|
||||
|
||||
// then
|
||||
verify(management).performLogin(sender, "password", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCallManagementForPasswordAndTotpCode() {
|
||||
// given
|
||||
Player sender = mock(Player.class);
|
||||
|
||||
// when
|
||||
command.executeCommand(sender, Arrays.asList("pwd", "12345"));
|
||||
|
||||
// then
|
||||
verify(management).performLogin(sender, "pwd", "12345");
|
||||
verify(management).performLogin(sender, "password" );
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -76,7 +76,7 @@ public class LimboPlayerTaskManagerTest {
|
||||
given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(interval);
|
||||
|
||||
// when
|
||||
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false);
|
||||
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, LimboMessageType.REGISTER);
|
||||
|
||||
// then
|
||||
verify(limboPlayer).setMessageTask(any(MessageTask.class));
|
||||
@ -94,7 +94,7 @@ public class LimboPlayerTaskManagerTest {
|
||||
given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(0);
|
||||
|
||||
// when
|
||||
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, true);
|
||||
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, LimboMessageType.LOG_IN);
|
||||
|
||||
// then
|
||||
verifyZeroInteractions(limboPlayer, bukkitService);
|
||||
@ -113,7 +113,7 @@ public class LimboPlayerTaskManagerTest {
|
||||
given(messages.retrieveSingle(player, MessageKey.REGISTER_MESSAGE)).willReturn("Please register!");
|
||||
|
||||
// when
|
||||
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false);
|
||||
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, LimboMessageType.REGISTER);
|
||||
|
||||
// then
|
||||
assertThat(limboPlayer.getMessageTask(), not(nullValue()));
|
||||
@ -137,7 +137,7 @@ public class LimboPlayerTaskManagerTest {
|
||||
given(messages.retrieveSingle(player, MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captcha)).willReturn("Need to use captcha");
|
||||
|
||||
// when
|
||||
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false);
|
||||
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, LimboMessageType.REGISTER);
|
||||
|
||||
// then
|
||||
assertThat(limboPlayer.getMessageTask(), not(nullValue()));
|
||||
|
@ -90,7 +90,7 @@ public class LimboServiceTest {
|
||||
limboService.createLimboPlayer(player, true);
|
||||
|
||||
// then
|
||||
verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(true));
|
||||
verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(LimboMessageType.LOG_IN));
|
||||
verify(taskManager).registerTimeoutTask(eq(player), any(LimboPlayer.class));
|
||||
verify(player).setAllowFlight(false);
|
||||
verify(player).setFlySpeed(0.0f);
|
||||
@ -121,7 +121,7 @@ public class LimboServiceTest {
|
||||
limboService.createLimboPlayer(player, false);
|
||||
|
||||
// then
|
||||
verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(false));
|
||||
verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(LimboMessageType.REGISTER));
|
||||
verify(taskManager).registerTimeoutTask(eq(player), any(LimboPlayer.class));
|
||||
verify(permissionsManager, only()).hasGroupSupport();
|
||||
verify(player).setAllowFlight(false);
|
||||
@ -209,7 +209,7 @@ public class LimboServiceTest {
|
||||
|
||||
// then
|
||||
verify(taskManager).registerTimeoutTask(player, limbo);
|
||||
verify(taskManager).registerMessageTask(player, limbo, true);
|
||||
verify(taskManager).registerMessageTask(player, limbo, LimboMessageType.LOG_IN);
|
||||
verify(authGroupHandler).setGroup(player, limbo, AuthGroupType.REGISTERED_UNAUTHENTICATED);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user