Email verification code (#1382)
* Base verification code implementation, must define command, manager, events * VerificationManager, verification command and messages, handled some sensible commands, configuration values * Improved manager and sensible commands trigger * Updated messages * Updated verification code manager, fixed tests * Switched to a permission based command * Verification manager and command improved and added tests * Edited messages
This commit is contained in:
@@ -40,6 +40,7 @@ import fr.xephi.authme.command.executable.login.LoginCommand;
|
||||
import fr.xephi.authme.command.executable.logout.LogoutCommand;
|
||||
import fr.xephi.authme.command.executable.register.RegisterCommand;
|
||||
import fr.xephi.authme.command.executable.unregister.UnregisterCommand;
|
||||
import fr.xephi.authme.command.executable.verification.VerificationCommand;
|
||||
import fr.xephi.authme.permission.AdminPermission;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PlayerPermission;
|
||||
@@ -136,13 +137,24 @@ public class CommandInitializer {
|
||||
CommandDescription captchaBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("captcha")
|
||||
.description("Captcha Command")
|
||||
.description("Captcha command")
|
||||
.detailedDescription("Captcha command for AuthMeReloaded.")
|
||||
.withArgument("captcha", "The Captcha", false)
|
||||
.permission(PlayerPermission.CAPTCHA)
|
||||
.executableCommand(CaptchaCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base verification code command
|
||||
CommandDescription verificationBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("verification")
|
||||
.description("Verification command")
|
||||
.detailedDescription("Command to complete the verification process for AuthMeReloaded.")
|
||||
.withArgument("code", "The code", false)
|
||||
.permission(PlayerPermission.VERIFICATION_CODE)
|
||||
.executableCommand(VerificationCommand.class)
|
||||
.register();
|
||||
|
||||
List<CommandDescription> baseCommands = ImmutableList.of(
|
||||
authMeBase,
|
||||
emailBase,
|
||||
@@ -151,7 +163,8 @@ public class CommandInitializer {
|
||||
registerBase,
|
||||
unregisterBase,
|
||||
changePasswordBase,
|
||||
captchaBase);
|
||||
captchaBase,
|
||||
verificationBase);
|
||||
|
||||
setHelpOnAllBases(baseCommands);
|
||||
commands = baseCommands;
|
||||
|
||||
+15
-3
@@ -1,6 +1,7 @@
|
||||
package fr.xephi.authme.command.executable.changepassword;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.VerificationCodeManager;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
@@ -29,17 +30,28 @@ public class ChangePasswordCommand extends PlayerCommand {
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private VerificationCodeManager codeManager;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
String oldPassword = arguments.get(0);
|
||||
String newPassword = arguments.get(1);
|
||||
|
||||
String name = player.getName().toLowerCase();
|
||||
|
||||
if (!playerCache.isAuthenticated(name)) {
|
||||
commonService.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the user has been verified or not
|
||||
if (codeManager.isVerificationRequired(player)) {
|
||||
codeManager.codeExistOrGenerateNew(name);
|
||||
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
|
||||
return;
|
||||
}
|
||||
|
||||
String oldPassword = arguments.get(0);
|
||||
String newPassword = arguments.get(1);
|
||||
|
||||
// Make sure the password is allowed
|
||||
ValidationResult passwordValidation = validationService.validatePassword(newPassword, name);
|
||||
if (passwordValidation.hasError()) {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.VerificationCodeManager;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -16,11 +18,24 @@ public class ChangeEmailCommand extends PlayerCommand {
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private VerificationCodeManager codeManager;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
final String playerName = player.getName();
|
||||
// Check if the user has been verified or not
|
||||
if (codeManager.isVerificationRequired(player)) {
|
||||
codeManager.codeExistOrGenerateNew(playerName);
|
||||
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
|
||||
return;
|
||||
}
|
||||
|
||||
String playerMailOld = arguments.get(0);
|
||||
String playerMailNew = arguments.get(1);
|
||||
|
||||
management.performChangeEmail(player, playerMailOld, playerMailNew);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package fr.xephi.authme.command.executable.unregister;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.VerificationCodeManager;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
@@ -24,6 +25,9 @@ public class UnregisterCommand extends PlayerCommand {
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private VerificationCodeManager codeManager;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
String playerPass = arguments.get(0);
|
||||
@@ -35,6 +39,13 @@ public class UnregisterCommand extends PlayerCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the user has been verified or not
|
||||
if (codeManager.isVerificationRequired(player)) {
|
||||
codeManager.codeExistOrGenerateNew(playerName);
|
||||
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unregister the player
|
||||
management.performUnregister(player, playerPass);
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package fr.xephi.authme.command.executable.verification;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.VerificationCodeManager;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used to complete the email verification process.
|
||||
*/
|
||||
public class VerificationCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private VerificationCodeManager codeManager;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
final String playerName = player.getName();
|
||||
|
||||
if (!codeManager.canSendMail()) {
|
||||
ConsoleLogger.warning("Mail API is not set");
|
||||
commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (codeManager.isVerificationRequired(player)) {
|
||||
if (codeManager.isCodeRequired(playerName)) {
|
||||
if (codeManager.checkCode(playerName, arguments.get(0))) {
|
||||
commonService.send(player, MessageKey.VERIFICATION_CODE_VERIFIED);
|
||||
} else {
|
||||
commonService.send(player, MessageKey.INCORRECT_VERIFICATION_CODE);
|
||||
}
|
||||
} else {
|
||||
commonService.send(player, MessageKey.VERIFICATION_CODE_EXPIRED);
|
||||
}
|
||||
} else {
|
||||
if (codeManager.hasEmail(playerName)) {
|
||||
commonService.send(player, MessageKey.VERIFICATION_CODE_ALREADY_VERIFIED);
|
||||
} else {
|
||||
commonService.send(player, MessageKey.VERIFICATION_CODE_EMAIL_NEEDED);
|
||||
commonService.send(player, MessageKey.ADD_EMAIL_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_VERIFICATION_CODE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package fr.xephi.authme.data;
|
||||
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.datasource.DataSourceResult;
|
||||
import fr.xephi.authme.initialization.HasCleanup;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.mail.EmailService;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.permission.PlayerPermission;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import fr.xephi.authme.util.RandomStringUtils;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import fr.xephi.authme.util.expiring.ExpiringMap;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class VerificationCodeManager implements SettingsDependent, HasCleanup {
|
||||
|
||||
private final EmailService emailService;
|
||||
private final DataSource dataSource;
|
||||
private final PermissionsManager permissionsManager;
|
||||
|
||||
private final ExpiringMap<String, String> verificationCodes;
|
||||
private final Set<String> verifiedPlayers;
|
||||
|
||||
private boolean canSendMail;
|
||||
|
||||
@Inject
|
||||
VerificationCodeManager(Settings settings, DataSource dataSource, EmailService emailService,
|
||||
PermissionsManager permissionsManager) {
|
||||
this.emailService = emailService;
|
||||
this.dataSource = dataSource;
|
||||
this.permissionsManager = permissionsManager;
|
||||
verifiedPlayers = new HashSet<>();
|
||||
long countTimeout = settings.getProperty(SecuritySettings.VERIFICATION_CODE_EXPIRATION_MINUTES);
|
||||
verificationCodes = new ExpiringMap<>(countTimeout, TimeUnit.MINUTES);
|
||||
reload(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if it is possible to send emails
|
||||
*
|
||||
* @return true if the service is enabled, false otherwise
|
||||
*/
|
||||
public boolean canSendMail() {
|
||||
return canSendMail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given player is able to verify his identity
|
||||
*
|
||||
* @param player the player to verify
|
||||
* @return true if the player has not been verified yet, false otherwise
|
||||
*/
|
||||
public boolean isVerificationRequired(Player player) {
|
||||
final String name = player.getName();
|
||||
return canSendMail
|
||||
&& !isPlayerVerified(name)
|
||||
&& permissionsManager.hasPermission(player, PlayerPermission.VERIFICATION_CODE)
|
||||
&& hasEmail(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given player is required to verify his identity through a command
|
||||
*
|
||||
* @param name the name of the player to verify
|
||||
* @return true if the player has an existing code and has not been verified yet, false otherwise
|
||||
*/
|
||||
public boolean isCodeRequired(String name) {
|
||||
return canSendMail && hasCode(name) && !isPlayerVerified(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given player has been verified or not
|
||||
*
|
||||
* @param name the name of the player to verify
|
||||
* @return true if the player has been verified, false otherwise
|
||||
*/
|
||||
private boolean isPlayerVerified(String name) {
|
||||
return verifiedPlayers.contains(name.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a code exists for the player
|
||||
*
|
||||
* @param name the name of the player to verify
|
||||
* @return true if the code exists, false otherwise
|
||||
*/
|
||||
public boolean hasCode(String name) {
|
||||
return (verificationCodes.get(name.toLowerCase()) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given player is able to receive emails
|
||||
*
|
||||
* @param name the name of the player to verify
|
||||
* @return true if the player is able to receive emails, false otherwise
|
||||
*/
|
||||
public boolean hasEmail(String name) {
|
||||
boolean result = false;
|
||||
DataSourceResult<String> emailResult = dataSource.getEmail(name);
|
||||
if (emailResult.playerExists()) {
|
||||
final String email = emailResult.getValue();
|
||||
if (!Utils.isEmailEmpty(email)) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a code exists for the player or generates and saves a new one.
|
||||
*
|
||||
* @param name the player's name
|
||||
*/
|
||||
public void codeExistOrGenerateNew(String name) {
|
||||
if (!hasCode(name)) {
|
||||
generateCode(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a code for the player and returns it.
|
||||
*
|
||||
* @param name the name of the player to generate a code for
|
||||
*/
|
||||
private void generateCode(String name) {
|
||||
DataSourceResult<String> emailResult = dataSource.getEmail(name);
|
||||
if (emailResult.playerExists()) {
|
||||
final String email = emailResult.getValue();
|
||||
if (!Utils.isEmailEmpty(email)) {
|
||||
String code = RandomStringUtils.generateNum(6); // 6 digits code
|
||||
verificationCodes.put(name.toLowerCase(), code);
|
||||
emailService.sendVerificationMail(name, email, code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given code against the existing one.
|
||||
*
|
||||
* @param name the name of the player to check
|
||||
* @param code the supplied code
|
||||
* @return true if the code matches, false otherwise
|
||||
*/
|
||||
public boolean checkCode(String name, String code) {
|
||||
boolean correct = false;
|
||||
if (code.equals(verificationCodes.get(name.toLowerCase()))) {
|
||||
verify(name);
|
||||
correct = true;
|
||||
}
|
||||
return correct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the user to the set of verified users
|
||||
*
|
||||
* @param name the name of the player to generate a code for
|
||||
*/
|
||||
public void verify(String name){
|
||||
verifiedPlayers.add(name.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the user from the set of verified users
|
||||
*
|
||||
* @param name the name of the player to generate a code for
|
||||
*/
|
||||
public void unverify(String name){
|
||||
verifiedPlayers.remove(name.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(Settings settings) {
|
||||
canSendMail = emailService.hasAllInformation();
|
||||
long countTimeout = settings.getProperty(SecuritySettings.VERIFICATION_CODE_EXPIRATION_MINUTES);
|
||||
verificationCodes.setExpiration(countTimeout, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performCleanup() {
|
||||
verificationCodes.removeExpiredEntries();
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public class EmailService {
|
||||
File file = null;
|
||||
if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) {
|
||||
try {
|
||||
file = generateImage(name, newPass);
|
||||
file = generatePasswordImage(name, newPass);
|
||||
mailText = embedImageIntoEmailContent(file, email, mailText);
|
||||
} catch (IOException | EmailException e) {
|
||||
ConsoleLogger.logException(
|
||||
@@ -80,6 +80,33 @@ public class EmailService {
|
||||
return couldSendEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an email to the user with the temporary verification code.
|
||||
*
|
||||
* @param name the name of the player
|
||||
* @param mailAddress the player's email
|
||||
* @param code the verification code
|
||||
* @return true if email could be sent, false otherwise
|
||||
*/
|
||||
public boolean sendVerificationMail(String name, String mailAddress, String code) {
|
||||
if (!hasAllInformation()) {
|
||||
ConsoleLogger.warning("Cannot send verification email: not all email settings are complete");
|
||||
return false;
|
||||
}
|
||||
|
||||
HtmlEmail email;
|
||||
try {
|
||||
email = sendMailSsl.initializeMail(mailAddress);
|
||||
} catch (EmailException e) {
|
||||
ConsoleLogger.logException("Failed to create verification email with the given settings:", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
String mailText = replaceTagsForVerificationEmail(settings.getVerificationEmailMessage(), name, code,
|
||||
settings.getProperty(SecuritySettings.VERIFICATION_CODE_EXPIRATION_MINUTES));
|
||||
return sendMailSsl.sendEmail(mailText, email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an email to the user with a recovery code for the password recovery process.
|
||||
*
|
||||
@@ -102,7 +129,7 @@ public class EmailService {
|
||||
return sendMailSsl.sendEmail(message, htmlEmail);
|
||||
}
|
||||
|
||||
private File generateImage(String name, String newPass) throws IOException {
|
||||
private File generatePasswordImage(String name, String newPass) throws IOException {
|
||||
ImageGenerator gen = new ImageGenerator(newPass);
|
||||
File file = new File(dataFolder, name + "_new_pass.jpg");
|
||||
ImageIO.write(gen.generateImage(), "jpg", file);
|
||||
@@ -123,6 +150,14 @@ public class EmailService {
|
||||
.replace("<generatedpass />", newPass);
|
||||
}
|
||||
|
||||
private String replaceTagsForVerificationEmail(String mailText, String name, String code, int minutesValid) {
|
||||
return mailText
|
||||
.replace("<playername />", name)
|
||||
.replace("<servername />", serverName)
|
||||
.replace("<generatedcode />", code)
|
||||
.replace("<minutesvalid />", String.valueOf(minutesValid));
|
||||
}
|
||||
|
||||
private String replaceTagsForRecoveryCodeMail(String mailText, String name, String code, int hoursValid) {
|
||||
return mailText
|
||||
.replace("<playername />", name)
|
||||
|
||||
@@ -239,6 +239,36 @@ public enum MessageKey {
|
||||
/** An email was already sent recently. You must wait %time before you can send a new one. */
|
||||
EMAIL_COOLDOWN_ERROR("email_cooldown_error", "%time"),
|
||||
|
||||
/**
|
||||
* The command you are trying to execute is sensitive and requires a verification!
|
||||
* A verification code has been sent to your email,
|
||||
* run the command "/verification [code]" to verify your identity.
|
||||
*/
|
||||
VERIFICATION_CODE_REQUIRED("verification_code_required"),
|
||||
|
||||
/** Usage: /verification <code> */
|
||||
USAGE_VERIFICATION_CODE("usage_verification_code"),
|
||||
|
||||
/** Incorrect code, please type "/verification <code>" into the chat! */
|
||||
INCORRECT_VERIFICATION_CODE("incorrect_verification_code"),
|
||||
|
||||
/**
|
||||
* Your identity has been verified!
|
||||
* You can now execute every sensitive command within the current session!
|
||||
*/
|
||||
VERIFICATION_CODE_VERIFIED("verification_code_verified"),
|
||||
|
||||
/**
|
||||
* You can already execute every sensitive command within the current session!
|
||||
*/
|
||||
VERIFICATION_CODE_ALREADY_VERIFIED("verification_code_already_verified"),
|
||||
|
||||
/** Your code has expired! Execute another sensitive command to get a new code! */
|
||||
VERIFICATION_CODE_EXPIRED("verification_code_expired"),
|
||||
|
||||
/** To verify your identity you need to link an email address with your account! */
|
||||
VERIFICATION_CODE_EMAIL_NEEDED("verification_code_email_needed"),
|
||||
|
||||
/** second */
|
||||
SECOND("second"),
|
||||
|
||||
|
||||
@@ -63,7 +63,12 @@ public enum PlayerPermission implements PermissionNode {
|
||||
/**
|
||||
* Permission to use to see own other accounts.
|
||||
*/
|
||||
SEE_OWN_ACCOUNTS("authme.player.seeownaccounts");
|
||||
SEE_OWN_ACCOUNTS("authme.player.seeownaccounts"),
|
||||
|
||||
/**
|
||||
* Permission to use the email verification codes feature.
|
||||
*/
|
||||
VERIFICATION_CODE("authme.player.security.verificationcode");
|
||||
|
||||
/**
|
||||
* The permission node.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.xephi.authme.process.logout;
|
||||
|
||||
import fr.xephi.authme.data.VerificationCodeManager;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
@@ -26,6 +27,9 @@ public class AsynchronousLogout implements AsynchronousProcess {
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private VerificationCodeManager codeManager;
|
||||
|
||||
@Inject
|
||||
private SyncProcessManager syncProcessManager;
|
||||
|
||||
@@ -52,6 +56,7 @@ public class AsynchronousLogout implements AsynchronousProcess {
|
||||
}
|
||||
|
||||
playerCache.removePlayer(name);
|
||||
codeManager.unverify(name);
|
||||
database.setUnlogged(name);
|
||||
database.revokeSession(name);
|
||||
syncProcessManager.processSyncPlayerLogout(player);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package fr.xephi.authme.process.quit;
|
||||
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.data.VerificationCodeManager;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.CacheDataSource;
|
||||
@@ -44,6 +45,9 @@ public class AsynchronousQuit implements AsynchronousProcess {
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Inject
|
||||
private VerificationCodeManager codeManager;
|
||||
|
||||
AsynchronousQuit() {
|
||||
}
|
||||
|
||||
@@ -80,6 +84,7 @@ public class AsynchronousQuit implements AsynchronousProcess {
|
||||
|
||||
//always unauthenticate the player - use session only for auto logins on the same ip
|
||||
playerCache.removePlayer(name);
|
||||
codeManager.unverify(name);
|
||||
|
||||
//always update the database when the player quit the game (if sessions are disabled)
|
||||
if (wasLoggedIn) {
|
||||
|
||||
@@ -20,6 +20,7 @@ public class Settings extends SettingsManager {
|
||||
|
||||
private final File pluginFolder;
|
||||
private String passwordEmailMessage;
|
||||
private String verificationEmailMessage;
|
||||
private String recoveryCodeEmailMessage;
|
||||
|
||||
/**
|
||||
@@ -46,6 +47,15 @@ public class Settings extends SettingsManager {
|
||||
return passwordEmailMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text for verification emails (before sensitive commands can be used).
|
||||
*
|
||||
* @return The email message
|
||||
*/
|
||||
public String getVerificationEmailMessage() {
|
||||
return verificationEmailMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text to use when someone requests to receive a recovery code.
|
||||
*
|
||||
@@ -57,6 +67,7 @@ public class Settings extends SettingsManager {
|
||||
|
||||
private void loadSettingsFromFiles() {
|
||||
passwordEmailMessage = readFile("email.html");
|
||||
verificationEmailMessage = readFile("verification_code_email.html");
|
||||
recoveryCodeEmailMessage = readFile("recovery_code_email.html");
|
||||
}
|
||||
|
||||
|
||||
@@ -147,6 +147,10 @@ public final class SecuritySettings implements SettingsHolder {
|
||||
public static final Property<Boolean> USE_EMAIL_MASKING =
|
||||
newProperty("Security.privacy.enableEmailMasking", false);
|
||||
|
||||
@Comment("Minutes after which a verification code will expire")
|
||||
public static final Property<Integer> VERIFICATION_CODE_EXPIRATION_MINUTES =
|
||||
newProperty("Security.privacy.verificationCodeExpiration", 10);
|
||||
|
||||
private SecuritySettings() {
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ public final class RandomStringUtils {
|
||||
|
||||
private static final char[] CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
|
||||
private static final Random RANDOM = new SecureRandom();
|
||||
private static final int HEX_MAX_INDEX = 16;
|
||||
private static final int NUM_INDEX = 10;
|
||||
private static final int LOWER_ALPHANUMERIC_INDEX = 36;
|
||||
private static final int HEX_MAX_INDEX = 16;
|
||||
|
||||
// Utility class
|
||||
private RandomStringUtils() {
|
||||
@@ -38,6 +39,17 @@ public final class RandomStringUtils {
|
||||
return generateString(length, HEX_MAX_INDEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random numbers string of the given length. In other words, the generated string
|
||||
* contains characters only within the range [0-9].
|
||||
*
|
||||
* @param length The length of the random string to generate
|
||||
* @return The random numbers string
|
||||
*/
|
||||
public static String generateNum(int length) {
|
||||
return generateString(length, NUM_INDEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random string with digits and lowercase and uppercase letters. The result of this
|
||||
* method matches the pattern [0-9a-zA-Z].
|
||||
|
||||
Reference in New Issue
Block a user