#1557 Disallow player from using /email setpassword more than once

This commit is contained in:
ljacqu 2018-05-21 13:29:34 +02:00
parent 768ef9179a
commit b9943675ba
5 changed files with 72 additions and 14 deletions

View File

@ -33,9 +33,9 @@ import fr.xephi.authme.command.executable.changepassword.ChangePasswordCommand;
import fr.xephi.authme.command.executable.email.AddEmailCommand; import fr.xephi.authme.command.executable.email.AddEmailCommand;
import fr.xephi.authme.command.executable.email.ChangeEmailCommand; import fr.xephi.authme.command.executable.email.ChangeEmailCommand;
import fr.xephi.authme.command.executable.email.EmailBaseCommand; import fr.xephi.authme.command.executable.email.EmailBaseCommand;
import fr.xephi.authme.command.executable.email.EmailSetPasswordCommand;
import fr.xephi.authme.command.executable.email.ProcessCodeCommand; import fr.xephi.authme.command.executable.email.ProcessCodeCommand;
import fr.xephi.authme.command.executable.email.RecoverEmailCommand; import fr.xephi.authme.command.executable.email.RecoverEmailCommand;
import fr.xephi.authme.command.executable.email.SetPasswordCommand;
import fr.xephi.authme.command.executable.email.ShowEmailCommand; import fr.xephi.authme.command.executable.email.ShowEmailCommand;
import fr.xephi.authme.command.executable.login.LoginCommand; import fr.xephi.authme.command.executable.login.LoginCommand;
import fr.xephi.authme.command.executable.logout.LogoutCommand; import fr.xephi.authme.command.executable.logout.LogoutCommand;
@ -540,7 +540,7 @@ public class CommandInitializer {
.detailedDescription("Set a new password after successfully recovering your account.") .detailedDescription("Set a new password after successfully recovering your account.")
.withArgument("password", "New password", MANDATORY) .withArgument("password", "New password", MANDATORY)
.permission(PlayerPermission.RECOVER_EMAIL) .permission(PlayerPermission.RECOVER_EMAIL)
.executableCommand(SetPasswordCommand.class) .executableCommand(EmailSetPasswordCommand.class)
.register(); .register();
return emailBase; return emailBase;

View File

@ -18,7 +18,7 @@ import java.util.List;
/** /**
* Command for changing password following successful recovery. * Command for changing password following successful recovery.
*/ */
public class SetPasswordCommand extends PlayerCommand { public class EmailSetPasswordCommand extends PlayerCommand {
@Inject @Inject
private DataSource dataSource; private DataSource dataSource;
@ -45,11 +45,14 @@ public class SetPasswordCommand extends PlayerCommand {
if (!result.hasError()) { if (!result.hasError()) {
HashedPassword hashedPassword = passwordSecurity.computeHash(password, name); HashedPassword hashedPassword = passwordSecurity.computeHash(password, name);
dataSource.updatePassword(name, hashedPassword); dataSource.updatePassword(name, hashedPassword);
recoveryService.removeFromSuccessfulRecovery(player);
ConsoleLogger.info("Player '" + name + "' has changed their password from recovery"); ConsoleLogger.info("Player '" + name + "' has changed their password from recovery");
commonService.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS); commonService.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS);
} else { } else {
commonService.send(player, result.getMessageKey(), result.getArgs()); commonService.send(player, result.getMessageKey(), result.getArgs());
} }
} else {
commonService.send(player, MessageKey.CHANGE_PASSWORD_EXPIRED);
} }
} }
} }

View File

@ -121,6 +121,16 @@ public class PasswordRecoveryService implements Reloadable, HasCleanup {
commonService.send(player, MessageKey.RECOVERY_CHANGE_PASSWORD); commonService.send(player, MessageKey.RECOVERY_CHANGE_PASSWORD);
} }
/**
* Removes a player from the list of successful recovers so that he can
* no longer use the /email setpassword command.
*
* @param player The player to remove.
*/
public void removeFromSuccessfulRecovery(Player player) {
successfulRecovers.remove(player.getName());
}
/** /**
* Check if a player is able to have emails sent. * Check if a player is able to have emails sent.
* *
@ -149,12 +159,7 @@ public class PasswordRecoveryService implements Reloadable, HasCleanup {
String playerAddress = PlayerUtils.getPlayerIp(player); String playerAddress = PlayerUtils.getPlayerIp(player);
String storedAddress = successfulRecovers.get(name); String storedAddress = successfulRecovers.get(name);
if (storedAddress == null || !playerAddress.equals(storedAddress)) { return storedAddress != null && playerAddress.equals(storedAddress);
messages.send(player, MessageKey.CHANGE_PASSWORD_EXPIRED);
return false;
}
return true;
} }
@Override @Override

View File

@ -24,13 +24,13 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
/** /**
* Tests for {@link SetPasswordCommand}. * Tests for {@link EmailSetPasswordCommand}.
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class SetPasswordCommandTest { public class EmailSetPasswordCommandTest {
@InjectMocks @InjectMocks
private SetPasswordCommand command; private EmailSetPasswordCommand command;
@Mock @Mock
private DataSource dataSource; private DataSource dataSource;
@ -70,6 +70,7 @@ public class SetPasswordCommandTest {
// then // then
verify(validationService).validatePassword("abc123", name); verify(validationService).validatePassword("abc123", name);
verify(dataSource).updatePassword(name, hashedPassword); verify(dataSource).updatePassword(name, hashedPassword);
verify(recoveryService).removeFromSuccessfulRecovery(player);
verify(commonService).send(player, MessageKey.PASSWORD_CHANGED_SUCCESS); verify(commonService).send(player, MessageKey.PASSWORD_CHANGED_SUCCESS);
} }
@ -101,7 +102,7 @@ public class SetPasswordCommandTest {
command.runCommand(player, Collections.singletonList("abc123")); command.runCommand(player, Collections.singletonList("abc123"));
// then // then
verifyZeroInteractions(validationService); verifyZeroInteractions(validationService, dataSource);
verifyZeroInteractions(dataSource); verify(commonService).send(player, MessageKey.CHANGE_PASSWORD_EXPIRED);
} }
} }

View File

@ -3,6 +3,7 @@ package fr.xephi.authme.service;
import ch.jalu.injector.testing.BeforeInjecting; import ch.jalu.injector.testing.BeforeInjecting;
import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.DelayedInjectionRunner;
import ch.jalu.injector.testing.InjectDelayed; import ch.jalu.injector.testing.InjectDelayed;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.mail.EmailService; import fr.xephi.authme.mail.EmailService;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
@ -14,6 +15,8 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -70,4 +73,50 @@ public class PasswordRecoveryServiceTest {
verify(emailService).sendRecoveryCode(name, email, code); verify(emailService).sendRecoveryCode(name, email, code);
verify(commonService).send(player, MessageKey.RECOVERY_CODE_SENT); verify(commonService).send(player, MessageKey.RECOVERY_CODE_SENT);
} }
@Test
public void shouldKeepTrackOfSuccessfulRecoversByIp() {
// given
Player bobby = mock(Player.class);
TestHelper.mockPlayerIp(bobby, "192.168.8.8");
given(bobby.getName()).willReturn("bobby");
Player bobby2 = mock(Player.class);
TestHelper.mockPlayerIp(bobby2, "127.0.0.1");
given(bobby2.getName()).willReturn("bobby");
Player other = mock(Player.class);
TestHelper.mockPlayerIp(other, "192.168.8.8");
given(other.getName()).willReturn("other");
// when
recoveryService.addSuccessfulRecovery(bobby);
// then
assertThat(recoveryService.canChangePassword(bobby), equalTo(true));
assertThat(recoveryService.canChangePassword(bobby2), equalTo(false));
assertThat(recoveryService.canChangePassword(other), equalTo(false));
}
@Test
public void shouldRemovePlayerFromSuccessfulRecovers() {
// given
Player bobby = mock(Player.class);
TestHelper.mockPlayerIp(bobby, "192.168.8.8");
given(bobby.getName()).willReturn("bobby");
recoveryService.addSuccessfulRecovery(bobby);
Player other = mock(Player.class);
TestHelper.mockPlayerIp(other, "8.8.8.8");
given(other.getName()).willReturn("other");
recoveryService.addSuccessfulRecovery(other);
// when
recoveryService.removeFromSuccessfulRecovery(other);
// then
assertThat(recoveryService.canChangePassword(bobby), equalTo(true));
assertThat(recoveryService.canChangePassword(other), equalTo(false));
}
} }