Implement /email setpassword
This commit is contained in:
@@ -32,6 +32,7 @@ import fr.xephi.authme.command.executable.email.ChangeEmailCommand;
|
||||
import fr.xephi.authme.command.executable.email.EmailBaseCommand;
|
||||
import fr.xephi.authme.command.executable.email.ProcessCodeCommand;
|
||||
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.login.LoginCommand;
|
||||
import fr.xephi.authme.command.executable.logout.LogoutCommand;
|
||||
@@ -435,6 +436,17 @@ public class CommandInitializer {
|
||||
.executableCommand(ProcessCodeCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the change password after recovery command
|
||||
CommandDescription.builder()
|
||||
.parent(EMAIL_BASE)
|
||||
.labels("setpassword")
|
||||
.description("Set new password after recovery")
|
||||
.detailedDescription("Set a new password after successfully recovering your account.")
|
||||
.withArgument("password", "New password", false)
|
||||
.permission(PlayerPermission.RECOVER_EMAIL)
|
||||
.executableCommand(SetPasswordCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base captcha command
|
||||
CommandDescription CAPTCHA_BASE = CommandDescription.builder()
|
||||
.parent(null)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.PasswordRecoveryService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for changing password following successful recovery.
|
||||
*/
|
||||
public class SetPasswordCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private PasswordRecoveryService recoveryService;
|
||||
|
||||
@Inject
|
||||
private PasswordSecurity passwordSecurity;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
if (recoveryService.canChangePassword(player)) {
|
||||
String name = player.getName();
|
||||
String password = arguments.get(0);
|
||||
|
||||
ValidationResult result = validationService.validatePassword(password, name);
|
||||
if (!result.hasError()) {
|
||||
HashedPassword hashedPassword = passwordSecurity.computeHash(password, name);
|
||||
dataSource.updatePassword(name, hashedPassword);
|
||||
commonService.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS);
|
||||
} else {
|
||||
commonService.send(player, result.getMessageKey(), result.getArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,6 +230,12 @@ public enum MessageKey {
|
||||
/** You have exceeded the maximum number of attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one. */
|
||||
RECOVERY_TRIES_EXCEEDED("recovery_tries_exceeded"),
|
||||
|
||||
/** Please use the command /email setpassword to change your password immediately. */
|
||||
RECOVERY_CHANGE_PASSWORD("recovery_change_password"),
|
||||
|
||||
/** You cannot change your password using this command anymore. */
|
||||
CHANGE_PASSWORD_EXPIRED("change_password_expired"),
|
||||
|
||||
/** An email was already sent recently. You must wait %time before you can send a new one. */
|
||||
EMAIL_COOLDOWN_ERROR("email_cooldown_error", "%time"),
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import fr.xephi.authme.util.PlayerUtils;
|
||||
import fr.xephi.authme.util.RandomStringUtils;
|
||||
import fr.xephi.authme.util.expiring.Duration;
|
||||
import fr.xephi.authme.util.expiring.ExpiringSet;
|
||||
@@ -46,11 +47,14 @@ public class PasswordRecoveryService implements Reloadable {
|
||||
private Messages messages;
|
||||
|
||||
private ExpiringSet<String> emailCooldown;
|
||||
private ExpiringSet<String> successfulRecovers;
|
||||
|
||||
@PostConstruct
|
||||
private void initEmailCooldownSet() {
|
||||
emailCooldown = new ExpiringSet<>(
|
||||
commonService.getProperty(SecuritySettings.EMAIL_RECOVERY_COOLDOWN_SECONDS), TimeUnit.SECONDS);
|
||||
successfulRecovers = new ExpiringSet<>(
|
||||
commonService.getProperty(SecuritySettings.PASSWORD_CHANGE_TIMEOUT), TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,6 +100,11 @@ public class PasswordRecoveryService implements Reloadable {
|
||||
if (couldSendMail) {
|
||||
commonService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE);
|
||||
emailCooldown.add(player.getName().toLowerCase());
|
||||
|
||||
String address = PlayerUtils.getPlayerIp(player);
|
||||
|
||||
successfulRecovers.add(address);
|
||||
commonService.send(player, MessageKey.RECOVERY_CHANGE_PASSWORD);
|
||||
} else {
|
||||
commonService.send(player, MessageKey.EMAIL_SEND_FAILURE);
|
||||
}
|
||||
@@ -117,6 +126,23 @@ public class PasswordRecoveryService implements Reloadable {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player can change their password after recovery
|
||||
* using the /email setpassword command.
|
||||
*
|
||||
* @param player The player to check.
|
||||
* @return True if the player can change their password.
|
||||
*/
|
||||
public boolean canChangePassword(Player player) {
|
||||
String address = PlayerUtils.getPlayerIp(player);
|
||||
Duration waitDuration = successfulRecovers.getExpiration(address);
|
||||
if (waitDuration.getDuration() > 0) {
|
||||
messages.send(player, MessageKey.EMAIL_COOLDOWN_ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
emailCooldown.setExpiration(
|
||||
|
||||
@@ -118,6 +118,12 @@ public class SecuritySettings implements SettingsHolder {
|
||||
public static final Property<Integer> RECOVERY_CODE_MAX_TRIES =
|
||||
newProperty("Security.recoveryCode.maxTries", 3);
|
||||
|
||||
@Comment({"How long a player has after password recovery to change their password",
|
||||
"without logging in. This is in minutes.",
|
||||
"Default: 2 minutes"})
|
||||
public static final Property<Integer> PASSWORD_CHANGE_TIMEOUT =
|
||||
newProperty("Security.recoveryCode.passwordChangeTimeout", 2);
|
||||
|
||||
@Comment({
|
||||
"Seconds a user has to wait for before a password recovery mail may be sent again",
|
||||
"This prevents an attacker from abusing AuthMe's email feature."
|
||||
|
||||
Reference in New Issue
Block a user