Merge pull request #220 from AuthMe/1073-email-cooldown

Configurable cooldown for /email recovery
This commit is contained in:
ljacqu 2017-02-26 22:44:39 +01:00 committed by GitHub
commit 57903f1c08
41 changed files with 612 additions and 119 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 Sun Feb 05 13:46:19 CET 2017. See docs/config/config.tpl.md --> <!-- File auto-generated on Sat Feb 25 21:59:18 CET 2017. See docs/config/config.tpl.md -->
## AuthMe Configuration ## AuthMe Configuration
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
@ -422,6 +422,8 @@ Security:
maxLoginTry: 5 maxLoginTry: 5
# Captcha length # Captcha length
captchaLength: 5 captchaLength: 5
# Minutes after which login attempts count is reset for a player
captchaCountReset: 60
tempban: tempban:
# Tempban a user's IP address if they enter the wrong password too many times # Tempban a user's IP address if they enter the wrong password too many times
enableTempban: false enableTempban: false
@ -438,6 +440,10 @@ Security:
length: 8 length: 8
# How many hours is a recovery code valid for? # How many hours is a recovery code valid for?
validForHours: 4 validForHours: 4
emailRecovery:
# 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.
cooldown: 60
BackupSystem: BackupSystem:
# Enable or disable automatic backup # Enable or disable automatic backup
ActivateBackup: false ActivateBackup: false
@ -454,4 +460,4 @@ To change settings on a running server, save your changes to config.yml and use
--- ---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Feb 05 13:46:19 CET 2017 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Feb 25 21:59:18 CET 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 Wed Jan 11 21:24:50 CET 2017. See docs/translations/translations.tpl.md --> <!-- File auto-generated on Sat Feb 25 21:59:17 CET 2017. See docs/translations/translations.tpl.md -->
# AuthMe Translations # AuthMe Translations
The following translations are available in AuthMe. Set `messagesLanguage` to the language code The following translations are available in AuthMe. Set `messagesLanguage` to the language code
@ -8,35 +8,34 @@ in your config.yml to use the language, or use another language code to start a
Code | Language | Translated | &nbsp; Code | Language | Translated | &nbsp;
---- | -------- | ---------: | ------ ---- | -------- | ---------: | ------
[en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 69% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=69&h=5&txtpad=1" alt="bar" /> [bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 61% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=61&h=5&txtpad=1" alt="bar" />
[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 62% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=62&h=5&txtpad=1" alt="bar" /> [eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 55% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=55&h=5&txtpad=1" alt="bar" />
[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 66% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=66&h=5&txtpad=1" alt="bar" /> [fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 59% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=59&h=5&txtpad=1" alt="bar" />
[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 70% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=70&h=5&txtpad=1" alt="bar" /> [gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 63% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=63&h=5&txtpad=1" alt="bar" />
[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 99% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=99&h=5&txtpad=1" alt="bar" /> [hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 88% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=88&h=5&txtpad=1" alt="bar" />
[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 70% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=70&h=5&txtpad=1" alt="bar" /> [id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 63% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=63&h=5&txtpad=1" alt="bar" />
[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 72% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=72&h=5&txtpad=1" alt="bar" /> [ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 64% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=64&h=5&txtpad=1" alt="bar" />
[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 53% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=53&h=5&txtpad=1" alt="bar" /> [lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 47% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa5500&w=47&h=5&txtpad=1" alt="bar" />
[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 77% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb9900&w=77&h=5&txtpad=1" alt="bar" /> [nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 86% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=86&h=5&txtpad=1" alt="bar" /> [pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 77% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb9900&w=77&h=5&txtpad=1" alt="bar" />
[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 99% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=99&h=5&txtpad=1" alt="bar" /> [ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 88% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=88&h=5&txtpad=1" alt="bar" />
[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 99% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=99&h=5&txtpad=1" alt="bar" /> [ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 46% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa5500&w=46&h=5&txtpad=1" alt="bar" /> [sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 41% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa4400&w=41&h=5&txtpad=1" alt="bar" />
[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 81% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=81&h=5&txtpad=1" alt="bar" /> [tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 72% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=72&h=5&txtpad=1" alt="bar" />
[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 93% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=93&h=5&txtpad=1" alt="bar" /> [uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 83% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=83&h=5&txtpad=1" alt="bar" />
[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 81% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=81&h=5&txtpad=1" alt="bar" /> [zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 81% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=81&h=5&txtpad=1" alt="bar" /> [zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 72% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=72&h=5&txtpad=1" alt="bar" />
[zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 96% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=96&h=5&txtpad=1" alt="bar" /> [zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 86% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=86&h=5&txtpad=1" alt="bar" />
[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 81% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=81&h=5&txtpad=1" alt="bar" /> [zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 72% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=72&h=5&txtpad=1" alt="bar" />
--- ---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Wed Jan 11 21:24:50 CET 2017 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Feb 25 21:59:17 CET 2017

View File

@ -5,24 +5,31 @@ import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.mail.EmailService; import fr.xephi.authme.mail.EmailService;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.RecoveryCodeService; import fr.xephi.authme.service.RecoveryCodeService;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.RandomStringUtils; import fr.xephi.authme.util.RandomStringUtils;
import fr.xephi.authme.util.expiring.Duration;
import fr.xephi.authme.util.expiring.ExpiringSet;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import javax.annotation.PostConstruct;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import static fr.xephi.authme.settings.properties.EmailSettings.RECOVERY_PASSWORD_LENGTH; import static fr.xephi.authme.settings.properties.EmailSettings.RECOVERY_PASSWORD_LENGTH;
/** /**
* Command for password recovery by email. * Command for password recovery by email.
*/ */
public class RecoverEmailCommand extends PlayerCommand { public class RecoverEmailCommand extends PlayerCommand implements Reloadable {
@Inject @Inject
private PasswordSecurity passwordSecurity; private PasswordSecurity passwordSecurity;
@ -42,8 +49,19 @@ public class RecoverEmailCommand extends PlayerCommand {
@Inject @Inject
private RecoveryCodeService recoveryCodeService; private RecoveryCodeService recoveryCodeService;
@Inject
private Messages messages;
private ExpiringSet<String> emailCooldown;
@PostConstruct
private void initEmailCooldownSet() {
emailCooldown = new ExpiringSet<>(
commonService.getProperty(SecuritySettings.EMAIL_RECOVERY_COOLDOWN_SECONDS), TimeUnit.SECONDS);
}
@Override @Override
public void runCommand(Player player, List<String> arguments) { protected void runCommand(Player player, List<String> arguments) {
final String playerMail = arguments.get(0); final String playerMail = arguments.get(0);
final String playerName = player.getName(); final String playerName = player.getName();
@ -78,15 +96,29 @@ public class RecoverEmailCommand extends PlayerCommand {
processRecoveryCode(player, arguments.get(1), email); processRecoveryCode(player, arguments.get(1), email);
} }
} else { } else {
generateAndSendNewPassword(player, email); boolean maySendMail = checkEmailCooldown(player);
if (maySendMail) {
generateAndSendNewPassword(player, email);
}
} }
} }
@Override
public void reload() {
emailCooldown.setExpiration(
commonService.getProperty(SecuritySettings.EMAIL_RECOVERY_COOLDOWN_SECONDS), TimeUnit.SECONDS);
}
private void createAndSendRecoveryCode(Player player, String email) { private void createAndSendRecoveryCode(Player player, String email) {
if (!checkEmailCooldown(player)) {
return;
}
String recoveryCode = recoveryCodeService.generateCode(player.getName()); String recoveryCode = recoveryCodeService.generateCode(player.getName());
boolean couldSendMail = emailService.sendRecoveryCode(player.getName(), email, recoveryCode); boolean couldSendMail = emailService.sendRecoveryCode(player.getName(), email, recoveryCode);
if (couldSendMail) { if (couldSendMail) {
commonService.send(player, MessageKey.RECOVERY_CODE_SENT); commonService.send(player, MessageKey.RECOVERY_CODE_SENT);
emailCooldown.add(player.getName().toLowerCase());
} else { } else {
commonService.send(player, MessageKey.EMAIL_SEND_FAILURE); commonService.send(player, MessageKey.EMAIL_SEND_FAILURE);
} }
@ -111,8 +143,19 @@ public class RecoverEmailCommand extends PlayerCommand {
boolean couldSendMail = emailService.sendPasswordMail(name, email, thePass); boolean couldSendMail = emailService.sendPasswordMail(name, email, thePass);
if (couldSendMail) { if (couldSendMail) {
commonService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE); commonService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE);
emailCooldown.add(player.getName().toLowerCase());
} else { } else {
commonService.send(player, MessageKey.EMAIL_SEND_FAILURE); commonService.send(player, MessageKey.EMAIL_SEND_FAILURE);
} }
} }
private boolean checkEmailCooldown(Player player) {
Duration waitDuration = emailCooldown.getExpiration(player.getName().toLowerCase());
if (waitDuration.getDuration() > 0) {
String durationText = messages.formatDuration(waitDuration);
messages.send(player, MessageKey.EMAIL_COOLDOWN_ERROR, durationText);
return false;
}
return true;
}
} }

View File

@ -225,7 +225,35 @@ public enum MessageKey {
RECOVERY_CODE_SENT("recovery_code_sent"), RECOVERY_CODE_SENT("recovery_code_sent"),
/** The recovery code is not correct! Use "/email recovery [email]" to generate a new one */ /** The recovery code is not correct! Use "/email recovery [email]" to generate a new one */
INCORRECT_RECOVERY_CODE("recovery_code_incorrect"); INCORRECT_RECOVERY_CODE("recovery_code_incorrect"),
/** An email was already sent recently. You must wait %time before you can send a new one. */
EMAIL_COOLDOWN_ERROR("email_cooldown_error", "%time"),
/** second */
SECOND("second"),
/** seconds */
SECONDS("seconds"),
/** minute */
MINUTE("minute"),
/** minutes */
MINUTES("minutes"),
/** hour */
HOUR("hour"),
/** hours */
HOURS("hours"),
/** day */
DAY("day"),
/** days */
DAYS("days");
private String key; private String key;
private String[] tags; private String[] tags;

View File

@ -1,11 +1,15 @@
package fr.xephi.authme.message; package fr.xephi.authme.message;
import com.google.common.collect.ImmutableMap;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.util.expiring.Duration;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/** /**
* Class for retrieving and sending translatable messages to players. * Class for retrieving and sending translatable messages to players.
@ -15,6 +19,20 @@ public class Messages implements Reloadable {
// Custom Authme tag replaced to new line // Custom Authme tag replaced to new line
private static final String NEWLINE_TAG = "%nl%"; private static final String NEWLINE_TAG = "%nl%";
/** Contains the keys of the singular messages for time units. */
private static final Map<TimeUnit, MessageKey> TIME_UNIT_SINGULARS = ImmutableMap.<TimeUnit, MessageKey>builder()
.put(TimeUnit.SECONDS, MessageKey.SECOND)
.put(TimeUnit.MINUTES, MessageKey.MINUTE)
.put(TimeUnit.HOURS, MessageKey.HOUR)
.put(TimeUnit.DAYS, MessageKey.DAY).build();
/** Contains the keys of the plural messages for time units. */
private static final Map<TimeUnit, MessageKey> TIME_UNIT_PLURALS = ImmutableMap.<TimeUnit, MessageKey>builder()
.put(TimeUnit.SECONDS, MessageKey.SECONDS)
.put(TimeUnit.MINUTES, MessageKey.MINUTES)
.put(TimeUnit.HOURS, MessageKey.HOURS)
.put(TimeUnit.DAYS, MessageKey.DAYS).build();
private final MessageFileHandlerProvider messageFileHandlerProvider; private final MessageFileHandlerProvider messageFileHandlerProvider;
private MessageFileHandler messageFileHandler; private MessageFileHandler messageFileHandler;
@ -71,6 +89,22 @@ public class Messages implements Reloadable {
return message.split("\n"); return message.split("\n");
} }
/**
* Returns the textual representation for the given duration.
* Note that this class only supports the time units days, hour, minutes and seconds.
*
* @param duration the duration to build a text of
* @return text of the duration
*/
public String formatDuration(Duration duration) {
long value = duration.getDuration();
MessageKey timeUnitKey = value == 1
? TIME_UNIT_SINGULARS.get(duration.getTimeUnit())
: TIME_UNIT_PLURALS.get(duration.getTimeUnit());
return value + " " + retrieveMessage(timeUnitKey);
}
/** /**
* Retrieve the message from the text file. * Retrieve the message from the text file.
* *

View File

@ -114,6 +114,13 @@ public class SecuritySettings implements SettingsHolder {
public static final Property<Integer> RECOVERY_CODE_HOURS_VALID = public static final Property<Integer> RECOVERY_CODE_HOURS_VALID =
newProperty("Security.recoveryCode.validForHours", 4); newProperty("Security.recoveryCode.validForHours", 4);
@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."
})
public static final Property<Integer> EMAIL_RECOVERY_COOLDOWN_SECONDS =
newProperty("Security.emailRecovery.cooldown", 60);
private SecuritySettings() { private SecuritySettings() {
} }

View File

@ -3,7 +3,6 @@ package fr.xephi.authme.util;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
@ -71,36 +70,4 @@ public final class Utils {
return Runtime.getRuntime().availableProcessors(); return Runtime.getRuntime().availableProcessors();
} }
public static Duration convertMillisToSuitableUnit(long duration) {
TimeUnit targetUnit;
if (duration > 1000L * 60L * 60L * 24L) {
targetUnit = TimeUnit.DAYS;
} else if (duration > 1000L * 60L * 60L) {
targetUnit = TimeUnit.HOURS;
} else if (duration > 1000L * 60L) {
targetUnit = TimeUnit.MINUTES;
} else if (duration > 1000L) {
targetUnit = TimeUnit.SECONDS;
} else {
targetUnit = TimeUnit.MILLISECONDS;
}
return new Duration(targetUnit, duration);
}
public static final class Duration {
private final long duration;
private final TimeUnit unit;
Duration(TimeUnit targetUnit, long durationMillis) {
this(targetUnit, durationMillis, TimeUnit.MILLISECONDS);
}
Duration(TimeUnit targetUnit, long sourceDuration, TimeUnit sourceUnit) {
this.duration = targetUnit.convert(sourceDuration, sourceUnit);
this.unit = targetUnit;
}
}
} }

View File

@ -46,7 +46,13 @@ public class ExpiringMap<K, V> {
*/ */
public V get(K key) { public V get(K key) {
ExpiringEntry<V> value = entries.get(key); ExpiringEntry<V> value = entries.get(key);
return value == null ? null : value.getValue(); if (value == null) {
return null;
} else if (System.currentTimeMillis() > value.getExpiration()) {
entries.remove(key);
return null;
}
return value.getValue();
} }
/** /**
@ -115,7 +121,7 @@ public class ExpiringMap<K, V> {
} }
V getValue() { V getValue() {
return System.currentTimeMillis() > expiration ? null : value; return value;
} }
long getExpiration() { long getExpiration() {

View File

@ -83,23 +83,22 @@ public class ExpiringSet<E> {
/** /**
* Returns the duration of the entry until it expires (provided it is not removed or re-added). * Returns the duration of the entry until it expires (provided it is not removed or re-added).
* If the entry does not exist, -1 is returned. * If the entry does not exist, a duration of -1 seconds is returned.
* *
* @param entry the entry whose duration before it expires should be returned * @param entry the entry whose duration before it expires should be returned
* @param unit the unit in which to return the duration
* @return duration the entry will remain in the set (if there are not modifications) * @return duration the entry will remain in the set (if there are not modifications)
*/ */
public long getExpiration(E entry, TimeUnit unit) { public Duration getExpiration(E entry) {
Long expiration = entries.get(entry); Long expiration = entries.get(entry);
if (expiration == null) { if (expiration == null) {
return -1; return new Duration(-1, TimeUnit.SECONDS);
} }
long stillPresentMillis = expiration - System.currentTimeMillis(); long stillPresentMillis = expiration - System.currentTimeMillis();
if (stillPresentMillis < 0) { if (stillPresentMillis < 0) {
entries.remove(entry); entries.remove(entry);
return -1; return new Duration(-1, TimeUnit.SECONDS);
} }
return unit.convert(stillPresentMillis, TimeUnit.MILLISECONDS); return Duration.createWithSuitableUnit(stillPresentMillis, TimeUnit.MILLISECONDS);
} }
/** /**

View File

@ -1,6 +1,5 @@
package fr.xephi.authme.util.expiring; package fr.xephi.authme.util.expiring;
import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -42,9 +41,10 @@ public class TimedCounter<K> extends ExpiringMap<K, Integer> {
* @return the total of all valid entries * @return the total of all valid entries
*/ */
public int total() { public int total() {
long currentTime = System.currentTimeMillis();
return entries.values().stream() return entries.values().stream()
.filter(entry -> currentTime <= entry.getExpiration())
.map(ExpiringEntry::getValue) .map(ExpiringEntry::getValue)
.filter(Objects::nonNull)
.reduce(0, Integer::sum); .reduce(0, Integer::sum);
} }
} }

View File

@ -45,7 +45,7 @@ unregistered: '&cУспешно от-регистриран!'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&fТвоята регистрация не е активирана, моля провери своя Имейл!' vb_nonActiv: '&fТвоята регистрация не е активирана, моля провери своя Имейл!'
usage_unreg: '&cКоманда: /unregister парола' usage_unreg: '&cКоманда: /unregister парола'
pwd_changed: '&cПаролата е променена!' pwd_changed: '&cПаролата е променена!'
@ -87,8 +87,19 @@ email_send: '[AuthMe] Изпраен е имейл !'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&cМоля добави своя имейл с : /email add имейл имейл' add_email: '&cМоля добави своя имейл с : /email add имейл имейл'
recovery_email: '&cЗабравихте своята парола? Моля използвай /email recovery <имейл>' recovery_email: '&cЗабравихте своята парола? Моля използвай /email recovery <имейл>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&cYou need to type a captcha, please type: /captcha <theCaptcha>' usage_captcha: '&cYou need to type a captcha, please type: /captcha <theCaptcha>'
wrong_captcha: '&cГрешен код, използвай : /captcha THE_CAPTCHA' wrong_captcha: '&cГрешен код, използвай : /captcha THE_CAPTCHA'
valid_captcha: '&cТвоя код е валиден!' valid_captcha: '&cТвоя код е валиден!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -90,8 +90,19 @@ email_send_failure: '&cO e-mail não pôde ser enviado, reporte isso a um admini
show_no_email: '&2Você atualmente não têm endereço de e-mail associado a esta conta.' show_no_email: '&2Você atualmente não têm endereço de e-mail associado a esta conta.'
add_email: '&3Por favor, adicione seu e-mail para a sua conta com o comando "/email add <seuEmail> <seuEmail>"' add_email: '&3Por favor, adicione seu e-mail para a sua conta com o comando "/email add <seuEmail> <seuEmail>"'
recovery_email: '&3Esqueceu sua senha? Por favor, use o comando "/email recovery <seuEmail>"' recovery_email: '&3Esqueceu sua senha? Por favor, use o comando "/email recovery <seuEmail>"'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3Para iniciar sessão você tem que resolver um código captcha, utilize o comando "/captcha <theCaptcha>"' usage_captcha: '&3Para iniciar sessão você tem que resolver um código captcha, utilize o comando "/captcha <theCaptcha>"'
wrong_captcha: '&cCaptcha errado, por favor, escreva "/captcha THE_CAPTCHA" no chat!' wrong_captcha: '&cCaptcha errado, por favor, escreva "/captcha THE_CAPTCHA" no chat!'
valid_captcha: '&2Código Captcha resolvido corretamente!' valid_captcha: '&2Código Captcha resolvido corretamente!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -86,8 +86,19 @@ email_send_failure: 'Email nemohl být odeslán. Kontaktujte prosím admina.'
show_no_email: '&2K tomuto účtu nemáte přidanou žádnou emailovou adresu.' show_no_email: '&2K tomuto účtu nemáte přidanou žádnou emailovou adresu.'
add_email: '&cPřidej prosím svůj email pomocí : /email add TvůjEmail TvůjEmail' add_email: '&cPřidej prosím svůj email pomocí : /email add TvůjEmail TvůjEmail'
recovery_email: '&cZapomněl jsi heslo? Napiš: /email recovery <TvůjEmail>' recovery_email: '&cZapomněl jsi heslo? Napiš: /email recovery <TvůjEmail>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&cPoužij: /captcha <theCaptcha>' usage_captcha: '&cPoužij: /captcha <theCaptcha>'
wrong_captcha: '&cŠpatné opsana Captcha, pouzij prosim: /captcha THE_CAPTCHA' wrong_captcha: '&cŠpatné opsana Captcha, pouzij prosim: /captcha THE_CAPTCHA'
valid_captcha: '&cZadaná captcha je v pořádku!' valid_captcha: '&cZadaná captcha je v pořádku!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -86,8 +86,19 @@ email_send_failure: 'Die E-Mail konnte nicht gesendet werden. Bitte kontaktiere
show_no_email: '&2Du hast zur Zeit keine E-Mail-Adresse für deinen Account hinterlegt.' show_no_email: '&2Du hast zur Zeit keine E-Mail-Adresse für deinen Account hinterlegt.'
add_email: '&3Bitte hinterlege deine E-Mail-Adresse: /email add <deineEmail> <emailBestätigen>' add_email: '&3Bitte hinterlege deine E-Mail-Adresse: /email add <deineEmail> <emailBestätigen>'
recovery_email: '&3Passwort vergessen? Nutze "/email recovery <deineEmail>" für ein neues Passwort' recovery_email: '&3Passwort vergessen? Nutze "/email recovery <deineEmail>" für ein neues Passwort'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3Um dich einzuloggen, tippe dieses Captcha so ein: /captcha <theCaptcha>' usage_captcha: '&3Um dich einzuloggen, tippe dieses Captcha so ein: /captcha <theCaptcha>'
wrong_captcha: '&cFalsches Captcha, bitte nutze: /captcha THE_CAPTCHA' wrong_captcha: '&cFalsches Captcha, bitte nutze: /captcha THE_CAPTCHA'
valid_captcha: '&2Das Captcha ist korrekt!' valid_captcha: '&2Das Captcha ist korrekt!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -86,8 +86,19 @@ email_send_failure: 'The email could not be sent. Please contact an administrato
show_no_email: '&2You currently don''t have email address associated with this account.' show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&3Please add your email to your account with the command: /email add <yourEmail> <confirmEmail>' add_email: '&3Please add your email to your account with the command: /email add <yourEmail> <confirmEmail>'
recovery_email: '&3Forgot your password? Please use the command: /email recovery <yourEmail>' recovery_email: '&3Forgot your password? Please use the command: /email recovery <yourEmail>'
email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3To login you have to solve a captcha code, please use the command: /captcha <theCaptcha>' usage_captcha: '&3To login you have to solve a captcha code, please use the command: /captcha <theCaptcha>'
wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'
valid_captcha: '&2Captcha code solved correctly!' valid_captcha: '&2Captcha code solved correctly!'
# Time units
second: 'second'
seconds: 'seconds'
minute: 'minute'
minutes: 'minutes'
hour: 'hour'
hours: 'hours'
day: 'day'
days: 'days'

View File

@ -89,8 +89,19 @@ email_send_failure: 'No se ha podido enviar el correo electrónico. Por favor, c
show_no_email: '&2No tienes ningun E-Mail asociado en esta cuenta.' show_no_email: '&2No tienes ningun E-Mail asociado en esta cuenta.'
add_email: '&cPor favor agrega tu e-mail con: /email add tuEmail confirmarEmail' add_email: '&cPor favor agrega tu e-mail con: /email add tuEmail confirmarEmail'
recovery_email: '&c¿Olvidaste tu contraseña? Por favor usa /email recovery <tuEmail>' recovery_email: '&c¿Olvidaste tu contraseña? Por favor usa /email recovery <tuEmail>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&cUso: /captcha <theCaptcha>' usage_captcha: '&cUso: /captcha <theCaptcha>'
wrong_captcha: '&cCaptcha incorrecto, por favor usa: /captcha THE_CAPTCHA' wrong_captcha: '&cCaptcha incorrecto, por favor usa: /captcha THE_CAPTCHA'
valid_captcha: '&c¡ Captcha ingresado correctamente !' valid_captcha: '&c¡ Captcha ingresado correctamente !'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -45,7 +45,7 @@ unregistered: '&cZure erregistroa ezabatu duzu!'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&fZure kontua aktibatu gabe dago, konfirmatu zure emaila!' vb_nonActiv: '&fZure kontua aktibatu gabe dago, konfirmatu zure emaila!'
usage_unreg: '&cErabili: /unregister password' usage_unreg: '&cErabili: /unregister password'
pwd_changed: '&cPasahitza aldatu duzu!' pwd_changed: '&cPasahitza aldatu duzu!'
@ -87,8 +87,19 @@ email_send: '[AuthMe] Berreskuratze emaila bidalita !'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&cMesedez gehitu zure emaila : /email add yourEmail confirmEmail' add_email: '&cMesedez gehitu zure emaila : /email add yourEmail confirmEmail'
recovery_email: '&cPasahitza ahaztu duzu? Erabili /email recovery <zureemaila>' recovery_email: '&cPasahitza ahaztu duzu? Erabili /email recovery <zureemaila>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
# TODO usage_captcha: '&3To login you have to solve a captcha code, please use the command "/captcha <theCaptcha>"' # TODO usage_captcha: '&3To login you have to solve a captcha code, please use the command: /captcha <theCaptcha>'
# TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' # TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'
# TODO valid_captcha: '&2Captcha code solved correctly!' # TODO valid_captcha: '&2Captcha code solved correctly!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -45,7 +45,7 @@ unregistered: '&cPelaajatili poistettu onnistuneesti!'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&fKäyttäjäsi ei ole vahvistettu!' vb_nonActiv: '&fKäyttäjäsi ei ole vahvistettu!'
usage_unreg: '&cKäyttötapa: /unregister password' usage_unreg: '&cKäyttötapa: /unregister password'
pwd_changed: '&cSalasana vaihdettu!!' pwd_changed: '&cSalasana vaihdettu!!'
@ -87,8 +87,19 @@ email_send: '[AuthMe] Palautus sähköposti lähetetty!'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&cLisää sähköpostisi: /email add sähköpostisi sähköpostisiUudelleen' add_email: '&cLisää sähköpostisi: /email add sähköpostisi sähköpostisiUudelleen'
recovery_email: '&cUnohtuiko salasana? Käytä komentoa: /email recovery <Sähköpostisi>' recovery_email: '&cUnohtuiko salasana? Käytä komentoa: /email recovery <Sähköpostisi>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&cKäyttötapa: /captcha <theCaptcha>' usage_captcha: '&cKäyttötapa: /captcha <theCaptcha>'
wrong_captcha: '&cVäärä varmistus, käytä : /captcha THE_CAPTCHA' wrong_captcha: '&cVäärä varmistus, käytä : /captcha THE_CAPTCHA'
valid_captcha: '&cSinun varmistus onnistui.!' valid_captcha: '&cSinun varmistus onnistui.!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -91,8 +91,19 @@ email_send_failure: '&cL''email n''a pas pu être envoyé. Veuillez contacter un
show_no_email: 'Vous n''avez aucune adresse email enregistré sur votre compte.' show_no_email: 'Vous n''avez aucune adresse email enregistré sur votre compte.'
add_email: '&cMerci d''ajouter votre email : /email add <votreEmail> <confirmerEmail>' add_email: '&cMerci d''ajouter votre email : /email add <votreEmail> <confirmerEmail>'
recovery_email: '&cVous avez oublié votre Mot de Passe? Utilisez /email recovery <votreEmail>' recovery_email: '&cVous avez oublié votre Mot de Passe? Utilisez /email recovery <votreEmail>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&cTrop de tentatives de connexion échouées, utilisez: /captcha <theCaptcha>' usage_captcha: '&cTrop de tentatives de connexion échouées, utilisez: /captcha <theCaptcha>'
wrong_captcha: '&cCaptcha incorrect, écrivez de nouveau : /captcha THE_CAPTCHA' wrong_captcha: '&cCaptcha incorrect, écrivez de nouveau : /captcha THE_CAPTCHA'
valid_captcha: '&aCaptché validé! Veuillez maintenant vous connecter.' valid_captcha: '&aCaptché validé! Veuillez maintenant vous connecter.'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -45,7 +45,7 @@ unregistered: '&cFeito! Xa non estás rexistrado!'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&fA túa conta aínda non está activada, comproba a túa bandexa de correo!!' vb_nonActiv: '&fA túa conta aínda non está activada, comproba a túa bandexa de correo!!'
usage_unreg: '&cUso: /unregister <contrasinal>' usage_unreg: '&cUso: /unregister <contrasinal>'
pwd_changed: '&cCambiouse o contrasinal!' pwd_changed: '&cCambiouse o contrasinal!'
@ -87,8 +87,19 @@ email_send: '[AuthMe] Enviouse o correo de confirmación!'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&cPor favor, engade o teu correo electrónico con: /email add <oTeuCorreo> <confirmarCorreo>' add_email: '&cPor favor, engade o teu correo electrónico con: /email add <oTeuCorreo> <confirmarCorreo>'
recovery_email: '&cOlvidaches o contrasinal? Por favor, usa /email recovery <oTeuCorreo>' recovery_email: '&cOlvidaches o contrasinal? Por favor, usa /email recovery <oTeuCorreo>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&cNecesitas escribir un captcha, por favor escribe: /captcha <theCaptcha>' usage_captcha: '&cNecesitas escribir un captcha, por favor escribe: /captcha <theCaptcha>'
wrong_captcha: '&cCaptcha equivocado, por favor usa: /captcha THE_CAPTCHA' wrong_captcha: '&cCaptcha equivocado, por favor usa: /captcha THE_CAPTCHA'
valid_captcha: '&cO teu captcha é válido !' valid_captcha: '&cO teu captcha é válido !'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -86,8 +86,19 @@ email_already_used: '&4Ez az email cím már használatban van!'
show_no_email: '&2Ehhez a felhasználóhoz jelenleg még nincs email hozzárendelve.' show_no_email: '&2Ehhez a felhasználóhoz jelenleg még nincs email hozzárendelve.'
add_email: '&3Kérlek rendeld hozzá a felhasználódhoz az email címedet "/email add <email címed> <email címed ismét>"' add_email: '&3Kérlek rendeld hozzá a felhasználódhoz az email címedet "/email add <email címed> <email címed ismét>"'
recovery_email: '&3Ha elfelejtetted a jelszavad, használd az: "/email recovery <regisztrált email címed>"' recovery_email: '&3Ha elfelejtetted a jelszavad, használd az: "/email recovery <regisztrált email címed>"'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3A bejelentkezéshez CAPTCHA szükséges, kérlek használd a következő parancsot "/captcha <theCaptcha>"' usage_captcha: '&3A bejelentkezéshez CAPTCHA szükséges, kérlek használd a következő parancsot "/captcha <theCaptcha>"'
wrong_captcha: '&cHibás CAPTCHA, kérlek írd be a következő parancsot: "/captcha THE_CAPTCHA"!' wrong_captcha: '&cHibás CAPTCHA, kérlek írd be a következő parancsot: "/captcha THE_CAPTCHA"!'
valid_captcha: '&2CAPTCHA sikeresen feloldva!' valid_captcha: '&2CAPTCHA sikeresen feloldva!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -45,7 +45,7 @@ unregistered: '&cUnregister berhasil!'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&cAkunmu belum diaktifkan, silahkan periksa email kamu!' vb_nonActiv: '&cAkunmu belum diaktifkan, silahkan periksa email kamu!'
# TODO usage_unreg: '&cUsage: /unregister <password>' # TODO usage_unreg: '&cUsage: /unregister <password>'
pwd_changed: '&2Berhasil mengubah password!' pwd_changed: '&2Berhasil mengubah password!'
@ -87,8 +87,19 @@ email_exists: '&cEmail pemulihan sudah dikirim! kamu bisa membatalkan dan mengir
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&3Silahkan tambahkan email ke akunmu menggunakan command "/email add <emailKamu> <ulangiEmail>"' add_email: '&3Silahkan tambahkan email ke akunmu menggunakan command "/email add <emailKamu> <ulangiEmail>"'
recovery_email: '&3Lupa password? silahkan gunakan command "/email recovery <emailKamu>"' recovery_email: '&3Lupa password? silahkan gunakan command "/email recovery <emailKamu>"'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3Kamu harus menyelesaikan kode captcha untuk login, silahkan gunakan command "/captcha <theCaptcha>"' usage_captcha: '&3Kamu harus menyelesaikan kode captcha untuk login, silahkan gunakan command "/captcha <theCaptcha>"'
wrong_captcha: '&cCaptcha salah, gunakan command "/captcha THE_CAPTCHA" pada chat!' wrong_captcha: '&cCaptcha salah, gunakan command "/captcha THE_CAPTCHA" pada chat!'
valid_captcha: '&2Kode captcha terselesaikan!' valid_captcha: '&2Kode captcha terselesaikan!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -88,8 +88,19 @@ email_send_failure: 'Non è stato possibile inviare l''email contenente la tua n
show_no_email: '&2Al momento non hai nessun indirizzo email associato al tuo account.' show_no_email: '&2Al momento non hai nessun indirizzo email associato al tuo account.'
add_email: '&3Per poter recuperare la password in futuro, aggiungi un indirizzo email al tuo account con il comando: /email add <tuaEmail> <confermaEmail>' add_email: '&3Per poter recuperare la password in futuro, aggiungi un indirizzo email al tuo account con il comando: /email add <tuaEmail> <confermaEmail>'
recovery_email: '&3Hai dimenticato la tua password? Puoi recuperarla eseguendo il comando: /email recovery <email>' recovery_email: '&3Hai dimenticato la tua password? Puoi recuperarla eseguendo il comando: /email recovery <email>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3Per poterti autenticare devi risolvere un captcha, per favore scrivi: /captcha <theCaptcha>' usage_captcha: '&3Per poterti autenticare devi risolvere un captcha, per favore scrivi: /captcha <theCaptcha>'
wrong_captcha: '&cCaptcha sbagliato, per favore riprova scrivendo: "/captcha THE_CAPTCHA" in chat!' wrong_captcha: '&cCaptcha sbagliato, per favore riprova scrivendo: "/captcha THE_CAPTCHA" in chat!'
valid_captcha: '&2Il captcha inserito è valido!' valid_captcha: '&2Il captcha inserito è valido!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -49,7 +49,7 @@ unregistered: '&c성공적으로 탈퇴했습니다!'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&f당신의 계정은 아직 활성화되어있지 않습니다, 당신의 이메일을 확인해보세요!' vb_nonActiv: '&f당신의 계정은 아직 활성화되어있지 않습니다, 당신의 이메일을 확인해보세요!'
usage_unreg: '&c사용법: /unregister 비밀번호' usage_unreg: '&c사용법: /unregister 비밀번호'
pwd_changed: '&c비밀번호를 변경했습니다!' pwd_changed: '&c비밀번호를 변경했습니다!'
@ -91,8 +91,19 @@ email_exists: '[AuthMe] 당신의 계정에 이미 이메일이 존재합니다.
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&c당신의 이메일을 추가해주세요 : /email add 당신의이메일 이메일재입력' add_email: '&c당신의 이메일을 추가해주세요 : /email add 당신의이메일 이메일재입력'
recovery_email: '&c비밀번호를 잊어버리셨다고요? /email recovery <당신의이메일>을 사용하세요' recovery_email: '&c비밀번호를 잊어버리셨다고요? /email recovery <당신의이메일>을 사용하세요'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&c보안문자 입력이 필요합니다, 입력해주세요: /captcha <theCaptcha>' usage_captcha: '&c보안문자 입력이 필요합니다, 입력해주세요: /captcha <theCaptcha>'
wrong_captcha: '&c잘못된 보안문자, 사용해주세요 : /captcha THE_CAPTCHA' wrong_captcha: '&c잘못된 보안문자, 사용해주세요 : /captcha THE_CAPTCHA'
valid_captcha: '&c당신의 보안문자는 적합합니다!' valid_captcha: '&c당신의 보안문자는 적합합니다!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -45,7 +45,7 @@ unregistered: '&aSekmingai issiregistravote!'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&aJusu vartotojas nera patvirtintas, patikrinkite el.pasta.' vb_nonActiv: '&aJusu vartotojas nera patvirtintas, patikrinkite el.pasta.'
usage_unreg: '&ePanaikinti registracija: "/unregister slaptazodis"' usage_unreg: '&ePanaikinti registracija: "/unregister slaptazodis"'
pwd_changed: '&aSlaptazodis pakeistas' pwd_changed: '&aSlaptazodis pakeistas'
@ -87,8 +87,19 @@ same_nick: '&cKazkas situo vardu jau zaidzia.'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&ePrasau pridekite savo el.pasta : /email add Email confirmEmail' add_email: '&ePrasau pridekite savo el.pasta : /email add Email confirmEmail'
recovery_email: '&cPamirsote slaptazodi? Rasykite: /email recovery el.pastas' recovery_email: '&cPamirsote slaptazodi? Rasykite: /email recovery el.pastas'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&cPanaudojimas: /captcha <theCaptcha>' usage_captcha: '&cPanaudojimas: /captcha <theCaptcha>'
wrong_captcha: '&cNeteisinga Captcha, naudokite : /captcha THE_CAPTCHA' wrong_captcha: '&cNeteisinga Captcha, naudokite : /captcha THE_CAPTCHA'
valid_captcha: '&cJusu captcha Teisinga!' valid_captcha: '&cJusu captcha Teisinga!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -86,8 +86,19 @@ email_send_failure: 'De E-mail kon niet verzonden worden. Neem contact op met ee
show_no_email: '&2Je hebt nog geen E-mailadres toegevoegd aan dit account.' show_no_email: '&2Je hebt nog geen E-mailadres toegevoegd aan dit account.'
add_email: '&3Voeg jouw E-mailadres alsjeblieft toe met: /email add <E-mail> <wachtwoord> <herhaalWachtwoord>' add_email: '&3Voeg jouw E-mailadres alsjeblieft toe met: /email add <E-mail> <wachtwoord> <herhaalWachtwoord>'
recovery_email: '&3Wachtwoord vergeten? Gebruik alsjeblieft het commando: /email recovery <E-mail>' recovery_email: '&3Wachtwoord vergeten? Gebruik alsjeblieft het commando: /email recovery <E-mail>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3Om in te loggen moet je een captcha-code oplossen, gebruik het commando: /captcha <theCaptcha>' usage_captcha: '&3Om in te loggen moet je een captcha-code oplossen, gebruik het commando: /captcha <theCaptcha>'
wrong_captcha: '&cVerkeerde captcha-code, typ alsjeblieft "/captcha THE_CAPTCHA" in de chat!' wrong_captcha: '&cVerkeerde captcha-code, typ alsjeblieft "/captcha THE_CAPTCHA" in de chat!'
valid_captcha: '&2De captcha-code is geldig!' valid_captcha: '&2De captcha-code is geldig!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -87,8 +87,19 @@ email_send_failure: 'Nie mozna wyslac emaila. Skontaktuj sie z administracja.'
show_no_email: '&2Nie posiadasz adresu email przypisanego do tego konta.' show_no_email: '&2Nie posiadasz adresu email przypisanego do tego konta.'
add_email: '&cProsze dodac swoj email: /email add twojEmail powtorzEmail' add_email: '&cProsze dodac swoj email: /email add twojEmail powtorzEmail'
recovery_email: '&cZapomniales hasla? Prosze uzyj komendy /email recovery <TwojEmail>' recovery_email: '&cZapomniales hasla? Prosze uzyj komendy /email recovery <TwojEmail>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&cWpisz: /captcha <theCaptcha>' usage_captcha: '&cWpisz: /captcha <theCaptcha>'
wrong_captcha: '&cZly kod, prosze wpisac: /captcha THE_CAPTCHA' wrong_captcha: '&cZly kod, prosze wpisac: /captcha THE_CAPTCHA'
valid_captcha: '&cTwoj kod jest nieprawidlowy!' valid_captcha: '&cTwoj kod jest nieprawidlowy!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -45,7 +45,7 @@ unregistered: '&cRegisto eliminado com sucesso!'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
two_factor_create: '&2O seu código secreto é o %code. Você pode verificá-lo a partir daqui %url' two_factor_create: '&2O seu código secreto é o %code. Você pode verificá-lo a partir daqui %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&fA sua conta não foi ainda activada, verifique o seu email onde irá receber indicações para activação de conta. ' vb_nonActiv: '&fA sua conta não foi ainda activada, verifique o seu email onde irá receber indicações para activação de conta. '
usage_unreg: '&cUse: /unregister password' usage_unreg: '&cUse: /unregister password'
pwd_changed: '&cPassword alterada!' pwd_changed: '&cPassword alterada!'
@ -87,8 +87,19 @@ email_already_used: '&4O endereço de e-mail já está sendo usado'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&cPor favor adicione o seu email com : /email add seuEmail confirmarSeuEmail' add_email: '&cPor favor adicione o seu email com : /email add seuEmail confirmarSeuEmail'
recovery_email: '&cPerdeu a sua password? Para a recuperar escreva /email recovery <seuEmail>' recovery_email: '&cPerdeu a sua password? Para a recuperar escreva /email recovery <seuEmail>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&cPrecisa digitar um captcha, escreva: /captcha <theCaptcha>' usage_captcha: '&cPrecisa digitar um captcha, escreva: /captcha <theCaptcha>'
wrong_captcha: '&cCaptcha errado, por favor escreva: /captcha THE_CAPTCHA' wrong_captcha: '&cCaptcha errado, por favor escreva: /captcha THE_CAPTCHA'
valid_captcha: '&cO seu captcha é válido!' valid_captcha: '&cO seu captcha é válido!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -86,8 +86,19 @@ email_already_used: '&4Email-ul a fost deja folosit'
show_no_email: '&2Nu ai nici-o adresa de email asociat cu acest cont.' show_no_email: '&2Nu ai nici-o adresa de email asociat cu acest cont.'
add_email: '&3Te rugam adaugati email-ul la contul tau folosind comanda "/email add <email> <email>"' add_email: '&3Te rugam adaugati email-ul la contul tau folosind comanda "/email add <email> <email>"'
recovery_email: '&3Ti-ai uitat parola? Te rugam foloseste comanda "/email recovery <email>"' recovery_email: '&3Ti-ai uitat parola? Te rugam foloseste comanda "/email recovery <email>"'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3Pentru a te autentifica trebuie sa folosesti codul de la captcha, te rugam foloseste comanda "/captcha <theCaptcha>"' usage_captcha: '&3Pentru a te autentifica trebuie sa folosesti codul de la captcha, te rugam foloseste comanda "/captcha <theCaptcha>"'
wrong_captcha: '&cCod-ul captcha este gresit, te rugam foloseste comanda "/captcha THE_CAPTCHA"!' wrong_captcha: '&cCod-ul captcha este gresit, te rugam foloseste comanda "/captcha THE_CAPTCHA"!'
valid_captcha: '&2Cod-ul captcha a fost scris corect!' valid_captcha: '&2Cod-ul captcha a fost scris corect!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -86,8 +86,19 @@ email_send_failure: 'Письмо не може быть отправлено.
show_no_email: '&2В данный момент к вашему аккаунте не привязана электронная почта.' show_no_email: '&2В данный момент к вашему аккаунте не привязана электронная почта.'
add_email: '&cДобавьте свой email: &e/email add <Ваш Email> <Ваш Email>' add_email: '&cДобавьте свой email: &e/email add <Ваш Email> <Ваш Email>'
recovery_email: '&cЗабыли пароль? Используйте &e/email recovery <Ваш Email>' recovery_email: '&cЗабыли пароль? Используйте &e/email recovery <Ваш Email>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Каптча # Каптча
usage_captcha: '&cВы должны ввести код, используйте: &e/captcha <theCaptcha>' usage_captcha: '&cВы должны ввести код, используйте: &e/captcha <theCaptcha>'
wrong_captcha: '&cНеверный код, используйте: &e/captcha THE_CAPTCHA' wrong_captcha: '&cНеверный код, используйте: &e/captcha THE_CAPTCHA'
valid_captcha: '&2Вы успешно ввели код!' valid_captcha: '&2Вы успешно ввели код!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -49,7 +49,7 @@ unregistered: '&cUcet bol vymazany!'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&fUcet nie je aktivny. Prezri si svoj e-mail!' vb_nonActiv: '&fUcet nie je aktivny. Prezri si svoj e-mail!'
usage_unreg: '&cPríkaz: /unregister heslo' usage_unreg: '&cPríkaz: /unregister heslo'
pwd_changed: '&cHeslo zmenené!' pwd_changed: '&cHeslo zmenené!'
@ -91,8 +91,19 @@ same_nick: '&fHrác s tymto nickom uz hrá!'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&cPridaj svoj e-mail príkazom "/email add email zopakujEmail"' add_email: '&cPridaj svoj e-mail príkazom "/email add email zopakujEmail"'
recovery_email: '&cZabudol si heslo? Pouzi príkaz /email recovery <tvojEmail>' recovery_email: '&cZabudol si heslo? Pouzi príkaz /email recovery <tvojEmail>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
# TODO usage_captcha: '&3To login you have to solve a captcha code, please use the command "/captcha <theCaptcha>"' # TODO usage_captcha: '&3To login you have to solve a captcha code, please use the command: /captcha <theCaptcha>'
# TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' # TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'
# TODO valid_captcha: '&2Captcha code solved correctly!' # TODO valid_captcha: '&2Captcha code solved correctly!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -44,7 +44,7 @@ unregistered: '&cKayit basariyla kaldirildi!'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
two_factor_create: '&2Gizli kodunuz %code. Buradan test edebilirsin, %url' two_factor_create: '&2Gizli kodunuz %code. Buradan test edebilirsin, %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&cHeabiniz henuz aktif edilmemis, e-postanizi kontrol edin!' vb_nonActiv: '&cHeabiniz henuz aktif edilmemis, e-postanizi kontrol edin!'
usage_unreg: '&cKullanim: /unregister <sifre>' usage_unreg: '&cKullanim: /unregister <sifre>'
pwd_changed: '&2Sifre basariyla degistirildi!' pwd_changed: '&2Sifre basariyla degistirildi!'
@ -86,8 +86,19 @@ email_already_used: '&4Eposta adresi zaten kullaniliyor.'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&3Lutfen hesabinize eposta adresinizi komut ile ekleyin "/email add <eposta> <tekrarEposta>"' add_email: '&3Lutfen hesabinize eposta adresinizi komut ile ekleyin "/email add <eposta> <tekrarEposta>"'
recovery_email: '&3Sifreni mi unuttun ? Komut kullanarak ogrenebilirsin "/email recovery <eposta>"' recovery_email: '&3Sifreni mi unuttun ? Komut kullanarak ogrenebilirsin "/email recovery <eposta>"'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3Giris yapmak icin guvenlik kodunu komut yazarak girin "/captcha <theCaptcha>"' usage_captcha: '&3Giris yapmak icin guvenlik kodunu komut yazarak girin "/captcha <theCaptcha>"'
wrong_captcha: '&cYanlis guvenlik kodu, kullanim sekli "/captcha THE_CAPTCHA" sohbete yazin!' wrong_captcha: '&cYanlis guvenlik kodu, kullanim sekli "/captcha THE_CAPTCHA" sohbete yazin!'
valid_captcha: '&2Guvenlik kodu dogrulandi!' valid_captcha: '&2Guvenlik kodu dogrulandi!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -44,7 +44,7 @@ accounts_owned_self: 'Кількість ваших твінк‒акаунті
accounts_owned_other: 'Кількість твінк‒акаунтів гравця %name: %count' accounts_owned_other: 'Кількість твінк‒акаунтів гравця %name: %count'
two_factor_create: '&2Ваш секретний код — %code %nl%&2Можете зкопіювати його за цим посиланням — %url' two_factor_create: '&2Ваш секретний код — %code %nl%&2Можете зкопіювати його за цим посиланням — %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&cВаш акаунт ще не активовано. Будь ласка, провірте свою електронну пошту!' vb_nonActiv: '&cВаш акаунт ще не активовано. Будь ласка, провірте свою електронну пошту!'
usage_unreg: '&cСинтаксис: /unregister <пароль>' usage_unreg: '&cСинтаксис: /unregister <пароль>'
pwd_changed: '&2Пароль успішно змінено!' pwd_changed: '&2Пароль успішно змінено!'
@ -86,8 +86,19 @@ email_already_used: '&4До цієї електронної пошти прив
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&3Не забудьте прив’язати електронну пошту до свого акаунта, за допомогою команди "/email add <e-mail> <e-mail повторно>"' add_email: '&3Не забудьте прив’язати електронну пошту до свого акаунта, за допомогою команди "/email add <e-mail> <e-mail повторно>"'
recovery_email: 'Забули пароль? Можете скористатись командою &9/email recovery &f<&9ваш e-mail&f>' recovery_email: 'Забули пароль? Можете скористатись командою &9/email recovery &f<&9ваш e-mail&f>'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3Для продовження доведеться ввести капчу — "/captcha <theCaptcha>"' usage_captcha: '&3Для продовження доведеться ввести капчу — "/captcha <theCaptcha>"'
wrong_captcha: '&cНевірно введена капча! Спробуйте ще раз — "/captcha THE_CAPTCHA"' wrong_captcha: '&cНевірно введена капча! Спробуйте ще раз — "/captcha THE_CAPTCHA"'
valid_captcha: '&2Капчу прийнято.' valid_captcha: '&2Капчу прийнято.'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -86,8 +86,19 @@ email_send_failure: 'Không thể gửi thư. Vui lòng liên hệ với ban qu
show_no_email: '&2Hiện tại bạn chưa liên kết bất kỳ email nào với tài khoản này.' show_no_email: '&2Hiện tại bạn chưa liên kết bất kỳ email nào với tài khoản này.'
add_email: '&eVui lòng thêm email của bạn với lệnh "/email add <email> <nhập lại email>"' add_email: '&eVui lòng thêm email của bạn với lệnh "/email add <email> <nhập lại email>"'
recovery_email: '&aBạn quên mật khẩu? Vui lòng gõ lệnh "/email recovery <email>"' recovery_email: '&aBạn quên mật khẩu? Vui lòng gõ lệnh "/email recovery <email>"'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&eĐể đăng nhập vui lòng hãy gõ mã Captcha, gõ lệnh "/captcha <theCaptcha>"' usage_captcha: '&eĐể đăng nhập vui lòng hãy gõ mã Captcha, gõ lệnh "/captcha <theCaptcha>"'
wrong_captcha: '&cSai mã captcha, Vui lòng nhấn "/captcha THE_CAPTCHA" trong kênh chát!' wrong_captcha: '&cSai mã captcha, Vui lòng nhấn "/captcha THE_CAPTCHA" trong kênh chát!'
valid_captcha: '&2Mã captcha đã được xác nhận!' valid_captcha: '&2Mã captcha đã được xác nhận!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -87,8 +87,19 @@ email_send_failure: '邮件发送失败,请联系管理员'
show_no_email: '&2您当前并没有任何邮箱与该账号绑定' show_no_email: '&2您当前并没有任何邮箱与该账号绑定'
add_email: '&8[&6玩家系统&8] &c请输入“/email add <你的邮箱> <再输入一次以确认>”以把你的邮箱添加到此帐号' add_email: '&8[&6玩家系统&8] &c请输入“/email add <你的邮箱> <再输入一次以确认>”以把你的邮箱添加到此帐号'
recovery_email: '&8[&6玩家系统&8] &c忘了你的密码请输入“/email recovery <你的邮箱>”' recovery_email: '&8[&6玩家系统&8] &c忘了你的密码请输入“/email recovery <你的邮箱>”'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&8[&6玩家系统&8] &c正确用法/captcha <theCaptcha>' usage_captcha: '&8[&6玩家系统&8] &c正确用法/captcha <theCaptcha>'
wrong_captcha: '&8[&6玩家系统&8] &c错误的验证码请输入“/captcha THE_CAPTCHA”' wrong_captcha: '&8[&6玩家系统&8] &c错误的验证码请输入“/captcha THE_CAPTCHA”'
valid_captcha: '&8[&6玩家系统&8] &c你的验证码是有效的' valid_captcha: '&8[&6玩家系统&8] &c你的验证码是有效的'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -49,7 +49,7 @@ unregistered: '&8[&6用戶系統&8] &c你已成功刪除會員註冊記錄。'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
two_factor_create: '&8[&6用戶系統 - 兩步驗證碼&8] &b你的登入金鑰為&9「%c%code&9」&b掃描連結為&c %url' two_factor_create: '&8[&6用戶系統 - 兩步驗證碼&8] &b你的登入金鑰為&9「%c%code&9」&b掃描連結為&c %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&8[&6用戶系統&8] &f你的帳戶還沒有經過電郵驗證 ' vb_nonActiv: '&8[&6用戶系統&8] &f你的帳戶還沒有經過電郵驗證 '
usage_unreg: '&8[&6用戶系統&8] &f用法 《 /unregister <密碼> 》' usage_unreg: '&8[&6用戶系統&8] &f用法 《 /unregister <密碼> 》'
pwd_changed: '&8[&6用戶系統&8] &c你成功更換了你的密碼 ' pwd_changed: '&8[&6用戶系統&8] &c你成功更換了你的密碼 '
@ -91,8 +91,19 @@ email_already_used: '&8[&6用戶系統&8] &4這個電郵地址已被使用。'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&8[&6用戶系統&8] &b請為你的帳戶立即添加電郵地址 《 /email add <電郵地址> <重覆電郵地址> 》' add_email: '&8[&6用戶系統&8] &b請為你的帳戶立即添加電郵地址 《 /email add <電郵地址> <重覆電郵地址> 》'
recovery_email: '&8[&6用戶系統&8] &b忘記密碼請使用 /email recovery <電郵地址> 來更新密碼。' recovery_email: '&8[&6用戶系統&8] &b忘記密碼請使用 /email recovery <電郵地址> 來更新密碼。'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&8[&6用戶系統&8] &f用法 《 /captcha <theCaptcha> 》' usage_captcha: '&8[&6用戶系統&8] &f用法 《 /captcha <theCaptcha> 》'
wrong_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效請使用 《 /captcha THE_CAPTCHA 》 再次輸入。' wrong_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效請使用 《 /captcha THE_CAPTCHA 》 再次輸入。'
valid_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效 ' valid_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效 '
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -86,8 +86,19 @@ email_already_used: '&4此電子郵件地址已被使用'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&3請使用命令: /email add [你的電郵地址] [重覆確認你的電郵地址] 將您的電子郵件添加到您的帳戶"' add_email: '&3請使用命令: /email add [你的電郵地址] [重覆確認你的電郵地址] 將您的電子郵件添加到您的帳戶"'
recovery_email: '&3忘記密碼了嗎 請使用命令: "/email recovery [你的電郵地址]"' recovery_email: '&3忘記密碼了嗎 請使用命令: "/email recovery [你的電郵地址]"'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3T要登錄您必須使用captcha驗證碼,請使用命令: "/captcha <theCaptcha>"' usage_captcha: '&3T要登錄您必須使用captcha驗證碼,請使用命令: "/captcha <theCaptcha>"'
wrong_captcha: '&c驗證碼錯誤!請按T在聊天中輸入 "/captcha THE_CAPTCHA"' wrong_captcha: '&c驗證碼錯誤!請按T在聊天中輸入 "/captcha THE_CAPTCHA"'
valid_captcha: '&2驗證碼正確!' valid_captcha: '&2驗證碼正確!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -49,7 +49,7 @@ unregistered: '&b【AuthMe】&6你已經成功取消註冊。'
# TODO accounts_owned_other: 'The player %name has %count accounts:' # TODO accounts_owned_other: 'The player %name has %count accounts:'
two_factor_create: '&b【AuthMe - 兩步驗證碼】&b你的登入金鑰為&9「%c%code&9」&b掃描連結為&c %url' two_factor_create: '&b【AuthMe - 兩步驗證碼】&b你的登入金鑰為&9「%c%code&9」&b掃描連結為&c %url'
# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' # TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.'
# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' # TODO recovery_code_incorrect: 'The recovery code is not correct! Use "/email recovery [email]" to generate a new one'
vb_nonActiv: '&b【AuthMe】&6你的帳號還沒有經過驗證! 檢查看看你的電子信箱 (Email) 吧!' vb_nonActiv: '&b【AuthMe】&6你的帳號還沒有經過驗證! 檢查看看你的電子信箱 (Email) 吧!'
usage_unreg: '&b【AuthMe】&6用法: &c"/unregister <密碼>"' usage_unreg: '&b【AuthMe】&6用法: &c"/unregister <密碼>"'
pwd_changed: '&b【AuthMe】&6密碼變更成功!' pwd_changed: '&b【AuthMe】&6密碼變更成功!'
@ -91,8 +91,19 @@ email_already_used: '&b【AuthMe】&4這個電郵地址已被使用。'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.' # TODO show_no_email: '&2You currently don''t have email address associated with this account.'
add_email: '&b【AuthMe】&6請使用 &c"/email add <你的Email> <再次輸入你的Email>" &6來添加 Email' add_email: '&b【AuthMe】&6請使用 &c"/email add <你的Email> <再次輸入你的Email>" &6來添加 Email'
recovery_email: '&b【AuthMe】&6忘記密碼了嗎? 使用 &c"/email recovery <你的Email>"' recovery_email: '&b【AuthMe】&6忘記密碼了嗎? 使用 &c"/email recovery <你的Email>"'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&b【AuthMe】&6請用 &c"/captcha <theCaptcha>" &6來輸入你的驗證碼' usage_captcha: '&b【AuthMe】&6請用 &c"/captcha <theCaptcha>" &6來輸入你的驗證碼'
wrong_captcha: '&b【AuthMe】&6錯誤的驗證碼請使用 《 /captcha THE_CAPTCHA 》 再試一次吧。' wrong_captcha: '&b【AuthMe】&6錯誤的驗證碼請使用 《 /captcha THE_CAPTCHA 》 再試一次吧。'
valid_captcha: '&b【AuthMe】&6驗證碼無效!' valid_captcha: '&b【AuthMe】&6驗證碼無效!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -1,29 +1,39 @@
package fr.xephi.authme.command.executable.email; package fr.xephi.authme.command.executable.email;
import ch.jalu.injector.testing.BeforeInjecting;
import ch.jalu.injector.testing.DelayedInjectionRunner;
import ch.jalu.injector.testing.InjectDelayed;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
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;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.RecoveryCodeService; import fr.xephi.authme.service.RecoveryCodeService;
import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.expiring.Duration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.Mockito;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.TimeUnit;
import static fr.xephi.authme.AuthMeMatchers.stringWithLength; import static fr.xephi.authme.AuthMeMatchers.stringWithLength;
import static org.hamcrest.Matchers.both;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
@ -38,19 +48,19 @@ import static org.mockito.Mockito.verifyZeroInteractions;
/** /**
* Test for {@link RecoverEmailCommand}. * Test for {@link RecoverEmailCommand}.
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(DelayedInjectionRunner.class)
public class RecoverEmailCommandTest { public class RecoverEmailCommandTest {
private static final String DEFAULT_EMAIL = "your@email.com"; private static final String DEFAULT_EMAIL = "your@email.com";
@InjectMocks @InjectDelayed
private RecoverEmailCommand command; private RecoverEmailCommand command;
@Mock @Mock
private PasswordSecurity passwordSecurity; private PasswordSecurity passwordSecurity;
@Mock @Mock
private CommonService commandService; private CommonService commonService;
@Mock @Mock
private DataSource dataSource; private DataSource dataSource;
@ -64,11 +74,19 @@ public class RecoverEmailCommandTest {
@Mock @Mock
private RecoveryCodeService recoveryCodeService; private RecoveryCodeService recoveryCodeService;
@Mock
private Messages messages;
@BeforeClass @BeforeClass
public static void initLogger() { public static void initLogger() {
TestHelper.setupLogger(); TestHelper.setupLogger();
} }
@BeforeInjecting
public void initSettings() {
given(commonService.getProperty(SecuritySettings.EMAIL_RECOVERY_COOLDOWN_SECONDS)).willReturn(40);
}
@Test @Test
public void shouldHandleMissingMailProperties() { public void shouldHandleMissingMailProperties() {
// given // given
@ -79,7 +97,7 @@ public class RecoverEmailCommandTest {
command.executeCommand(sender, Collections.singletonList("some@email.tld")); command.executeCommand(sender, Collections.singletonList("some@email.tld"));
// then // then
verify(commandService).send(sender, MessageKey.INCOMPLETE_EMAIL_SETTINGS); verify(commonService).send(sender, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
verifyZeroInteractions(dataSource, passwordSecurity); verifyZeroInteractions(dataSource, passwordSecurity);
} }
@ -98,7 +116,7 @@ public class RecoverEmailCommandTest {
// then // then
verify(emailService).hasAllInformation(); verify(emailService).hasAllInformation();
verifyZeroInteractions(dataSource); verifyZeroInteractions(dataSource);
verify(commandService).send(sender, MessageKey.ALREADY_LOGGED_IN_ERROR); verify(commonService).send(sender, MessageKey.ALREADY_LOGGED_IN_ERROR);
} }
@Test @Test
@ -118,7 +136,7 @@ public class RecoverEmailCommandTest {
verify(emailService).hasAllInformation(); verify(emailService).hasAllInformation();
verify(dataSource).getAuth(name); verify(dataSource).getAuth(name);
verifyNoMoreInteractions(dataSource); verifyNoMoreInteractions(dataSource);
verify(commandService).send(sender, MessageKey.USAGE_REGISTER); verify(commonService).send(sender, MessageKey.USAGE_REGISTER);
} }
@Test @Test
@ -138,7 +156,7 @@ public class RecoverEmailCommandTest {
verify(emailService).hasAllInformation(); verify(emailService).hasAllInformation();
verify(dataSource).getAuth(name); verify(dataSource).getAuth(name);
verifyNoMoreInteractions(dataSource); verifyNoMoreInteractions(dataSource);
verify(commandService).send(sender, MessageKey.INVALID_EMAIL); verify(commonService).send(sender, MessageKey.INVALID_EMAIL);
} }
@Test @Test
@ -158,7 +176,7 @@ public class RecoverEmailCommandTest {
verify(emailService).hasAllInformation(); verify(emailService).hasAllInformation();
verify(dataSource).getAuth(name); verify(dataSource).getAuth(name);
verifyNoMoreInteractions(dataSource); verifyNoMoreInteractions(dataSource);
verify(commandService).send(sender, MessageKey.INVALID_EMAIL); verify(commonService).send(sender, MessageKey.INVALID_EMAIL);
} }
@Test @Test
@ -183,7 +201,7 @@ public class RecoverEmailCommandTest {
verify(emailService).hasAllInformation(); verify(emailService).hasAllInformation();
verify(dataSource).getAuth(name); verify(dataSource).getAuth(name);
verify(recoveryCodeService).generateCode(name); verify(recoveryCodeService).generateCode(name);
verify(commandService).send(sender, MessageKey.RECOVERY_CODE_SENT); verify(commonService).send(sender, MessageKey.RECOVERY_CODE_SENT);
verify(emailService).sendRecoveryCode(name, email, code); verify(emailService).sendRecoveryCode(name, email, code);
} }
@ -207,7 +225,7 @@ public class RecoverEmailCommandTest {
// then // then
verify(emailService).hasAllInformation(); verify(emailService).hasAllInformation();
verify(dataSource, only()).getAuth(name); verify(dataSource, only()).getAuth(name);
verify(commandService).send(sender, MessageKey.INCORRECT_RECOVERY_CODE); verify(commonService).send(sender, MessageKey.INCORRECT_RECOVERY_CODE);
verifyNoMoreInteractions(emailService); verifyNoMoreInteractions(emailService);
} }
@ -224,7 +242,7 @@ public class RecoverEmailCommandTest {
String code = "A6EF3AC8"; String code = "A6EF3AC8";
PlayerAuth auth = newAuthWithEmail(email); PlayerAuth auth = newAuthWithEmail(email);
given(dataSource.getAuth(name)).willReturn(auth); given(dataSource.getAuth(name)).willReturn(auth);
given(commandService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH)).willReturn(20); given(commonService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH)).willReturn(20);
given(passwordSecurity.computeHash(anyString(), eq(name))) given(passwordSecurity.computeHash(anyString(), eq(name)))
.willAnswer(invocation -> new HashedPassword(invocation.getArgument(0))); .willAnswer(invocation -> new HashedPassword(invocation.getArgument(0)));
given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(true); given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(true);
@ -243,7 +261,7 @@ public class RecoverEmailCommandTest {
verify(dataSource).updatePassword(eq(name), any(HashedPassword.class)); verify(dataSource).updatePassword(eq(name), any(HashedPassword.class));
verify(recoveryCodeService).removeCode(name); verify(recoveryCodeService).removeCode(name);
verify(emailService).sendPasswordMail(name, email, generatedPassword); verify(emailService).sendPasswordMail(name, email, generatedPassword);
verify(commandService).send(sender, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE); verify(commonService).send(sender, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE);
} }
@Test @Test
@ -258,7 +276,7 @@ public class RecoverEmailCommandTest {
String email = "shark@example.org"; String email = "shark@example.org";
PlayerAuth auth = newAuthWithEmail(email); PlayerAuth auth = newAuthWithEmail(email);
given(dataSource.getAuth(name)).willReturn(auth); given(dataSource.getAuth(name)).willReturn(auth);
given(commandService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH)).willReturn(20); given(commonService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH)).willReturn(20);
given(passwordSecurity.computeHash(anyString(), eq(name))) given(passwordSecurity.computeHash(anyString(), eq(name)))
.willAnswer(invocation -> new HashedPassword(invocation.getArgument(0))); .willAnswer(invocation -> new HashedPassword(invocation.getArgument(0)));
given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(false); given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(false);
@ -275,7 +293,40 @@ public class RecoverEmailCommandTest {
assertThat(generatedPassword, stringWithLength(20)); assertThat(generatedPassword, stringWithLength(20));
verify(dataSource).updatePassword(eq(name), any(HashedPassword.class)); verify(dataSource).updatePassword(eq(name), any(HashedPassword.class));
verify(emailService).sendPasswordMail(name, email, generatedPassword); verify(emailService).sendPasswordMail(name, email, generatedPassword);
verify(commandService).send(sender, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE); verify(commonService).send(sender, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE);
}
@Test
public void shouldNotSendEmailIfCooldownCheckFails() {
// given
String name = "feverRay";
Player sender = mock(Player.class);
given(sender.getName()).willReturn(name);
given(emailService.hasAllInformation()).willReturn(true);
given(emailService.sendRecoveryCode(anyString(), anyString(), anyString())).willReturn(true);
given(playerCache.isAuthenticated(name)).willReturn(false);
String email = "mymail@example.org";
PlayerAuth auth = newAuthWithEmail(email);
given(dataSource.getAuth(name)).willReturn(auth);
given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(true);
given(recoveryCodeService.generateCode(anyString())).willReturn("Code");
// Trigger sending of recovery code
command.executeCommand(sender, Collections.singletonList(email));
Mockito.reset(emailService, commonService);
given(emailService.hasAllInformation()).willReturn(true);
given(messages.formatDuration(any(Duration.class))).willReturn("8 minutes");
// when
command.executeCommand(sender, Collections.singletonList(email));
// then
verify(emailService, only()).hasAllInformation();
ArgumentCaptor<Duration> durationCaptor = ArgumentCaptor.forClass(Duration.class);
verify(messages).formatDuration(durationCaptor.capture());
assertThat(durationCaptor.getValue().getDuration(), both(lessThan(41L)).and(greaterThan(36L)));
assertThat(durationCaptor.getValue().getTimeUnit(), equalTo(TimeUnit.SECONDS));
verify(messages).send(sender, MessageKey.EMAIL_COOLDOWN_ERROR, "8 minutes");
} }

View File

@ -1,7 +1,9 @@
package fr.xephi.authme.message; package fr.xephi.authme.message;
import com.google.common.collect.ImmutableMap;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.util.expiring.Duration;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.junit.Before; import org.junit.Before;
@ -11,6 +13,8 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mockito; import org.mockito.Mockito;
import java.io.File; import java.io.File;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -230,6 +234,26 @@ public class MessagesIntegrationTest {
assertThat(result, equalTo("Use /captcha 24680 to solve the captcha")); assertThat(result, equalTo("Use /captcha 24680 to solve the captcha"));
} }
@Test
public void shouldFormatDurationObjects() {
// given
Map<Duration, String> expectedTexts = ImmutableMap.<Duration, String>builder()
.put(new Duration(1, TimeUnit.SECONDS), "1 second")
.put(new Duration(12, TimeUnit.SECONDS), "12 seconds")
.put(new Duration(1, TimeUnit.MINUTES), "1 minute")
.put(new Duration(0, TimeUnit.MINUTES), "0 minutes")
.put(new Duration(1, TimeUnit.HOURS), "1 hour")
.put(new Duration(-4, TimeUnit.HOURS), "-4 hours")
.put(new Duration(1, TimeUnit.DAYS), "1 day")
.put(new Duration(44, TimeUnit.DAYS), "44 days")
.build();
// when / then
for (Map.Entry<Duration, String> entry : expectedTexts.entrySet()) {
assertThat(messages.formatDuration(entry.getKey()), equalTo(entry.getValue()));
}
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static MessageFileHandlerProvider providerReturning(File file, String defaultFile) { private static MessageFileHandlerProvider providerReturning(File file, String defaultFile) {
MessageFileHandlerProvider handler = mock(MessageFileHandlerProvider.class); MessageFileHandlerProvider handler = mock(MessageFileHandlerProvider.class);

View File

@ -4,7 +4,6 @@ import org.junit.Test;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -97,14 +96,31 @@ public class ExpiringSetTest {
set.add("my entry"); set.add("my entry");
// when // when
long expiresInHours = set.getExpiration("my entry", TimeUnit.HOURS); Duration expiration = set.getExpiration("my entry");
long expiresInMinutes = set.getExpiration("my entry", TimeUnit.MINUTES); Duration unknownExpiration = set.getExpiration("bogus");
long unknownExpires = set.getExpiration("bogus", TimeUnit.SECONDS);
// then // then
assertThat(expiresInHours, equalTo(2L)); assertIsDuration(expiration, 2, TimeUnit.HOURS);
assertThat(expiresInMinutes, either(equalTo(122L)).or(equalTo(123L))); assertIsDuration(unknownExpiration, -1, TimeUnit.SECONDS);
assertThat(unknownExpires, equalTo(-1L)); }
@Test
public void shouldReturnExpirationInSuitableUnits() {
// given
ExpiringSet<Integer> set = new ExpiringSet<>(601, TimeUnit.SECONDS);
set.add(12);
set.setExpiration(49, TimeUnit.HOURS);
set.add(23);
// when
Duration expiration12 = set.getExpiration(12);
Duration expiration23 = set.getExpiration(23);
Duration expectedUnknown = set.getExpiration(-100);
// then
assertIsDuration(expiration12, 10, TimeUnit.MINUTES);
assertIsDuration(expiration23, 2, TimeUnit.DAYS);
assertIsDuration(expectedUnknown, -1, TimeUnit.SECONDS);
} }
@Test @Test
@ -114,9 +130,14 @@ public class ExpiringSetTest {
set.add(23); set.add(23);
// when // when
long expiresInSeconds = set.getExpiration(23, TimeUnit.SECONDS); Duration expiration = set.getExpiration(23);
// then // then
assertThat(expiresInSeconds, equalTo(-1L)); assertIsDuration(expiration, -1, TimeUnit.SECONDS);
}
private static void assertIsDuration(Duration duration, long expectedDuration, TimeUnit expectedUnit) {
assertThat(duration.getTimeUnit(), equalTo(expectedUnit));
assertThat(duration.getDuration(), equalTo(expectedDuration));
} }
} }