mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-12-19 23:28:59 +01:00
#930 Refactor captcha managers to have a crude captcha storage class instead of inheritance
- Remove abstract captcha manager in favor of a primitive captcha code storage (composition over inheritance) - Supply player when checking captcha code for further usage (fixes open point from previous commit)
This commit is contained in:
parent
0494886518
commit
180bbbf0be
@ -1,9 +1,9 @@
|
|||||||
package fr.xephi.authme.command.executable.captcha;
|
package fr.xephi.authme.command.executable.captcha;
|
||||||
|
|
||||||
import fr.xephi.authme.command.PlayerCommand;
|
import fr.xephi.authme.command.PlayerCommand;
|
||||||
import fr.xephi.authme.data.LoginCaptchaManager;
|
|
||||||
import fr.xephi.authme.data.RegistrationCaptchaManager;
|
|
||||||
import fr.xephi.authme.data.auth.PlayerCache;
|
import fr.xephi.authme.data.auth.PlayerCache;
|
||||||
|
import fr.xephi.authme.data.captcha.LoginCaptchaManager;
|
||||||
|
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||||
import fr.xephi.authme.data.limbo.LimboService;
|
import fr.xephi.authme.data.limbo.LimboService;
|
||||||
import fr.xephi.authme.message.MessageKey;
|
import fr.xephi.authme.message.MessageKey;
|
||||||
import fr.xephi.authme.service.CommonService;
|
import fr.xephi.authme.service.CommonService;
|
||||||
@ -47,7 +47,7 @@ public class CaptchaCommand extends PlayerCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkLoginCaptcha(Player player, String captchaCode) {
|
private void checkLoginCaptcha(Player player, String captchaCode) {
|
||||||
final boolean isCorrectCode = loginCaptchaManager.checkCode(player.getName(), captchaCode);
|
final boolean isCorrectCode = loginCaptchaManager.checkCode(player, captchaCode);
|
||||||
if (isCorrectCode) {
|
if (isCorrectCode) {
|
||||||
commonService.send(player, MessageKey.CAPTCHA_SUCCESS);
|
commonService.send(player, MessageKey.CAPTCHA_SUCCESS);
|
||||||
commonService.send(player, MessageKey.LOGIN_MESSAGE);
|
commonService.send(player, MessageKey.LOGIN_MESSAGE);
|
||||||
@ -59,7 +59,7 @@ public class CaptchaCommand extends PlayerCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkRegisterCaptcha(Player player, String captchaCode) {
|
private void checkRegisterCaptcha(Player player, String captchaCode) {
|
||||||
final boolean isCorrectCode = registrationCaptchaManager.checkCode(player.getName(), captchaCode);
|
final boolean isCorrectCode = registrationCaptchaManager.checkCode(player, captchaCode);
|
||||||
if (isCorrectCode) {
|
if (isCorrectCode) {
|
||||||
commonService.send(player, MessageKey.CAPTCHA_SUCCESS);
|
commonService.send(player, MessageKey.CAPTCHA_SUCCESS);
|
||||||
commonService.send(player, MessageKey.REGISTER_MESSAGE);
|
commonService.send(player, MessageKey.REGISTER_MESSAGE);
|
||||||
|
@ -2,7 +2,7 @@ package fr.xephi.authme.command.executable.register;
|
|||||||
|
|
||||||
import fr.xephi.authme.ConsoleLogger;
|
import fr.xephi.authme.ConsoleLogger;
|
||||||
import fr.xephi.authme.command.PlayerCommand;
|
import fr.xephi.authme.command.PlayerCommand;
|
||||||
import fr.xephi.authme.data.RegistrationCaptchaManager;
|
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||||
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.process.Management;
|
import fr.xephi.authme.process.Management;
|
||||||
|
@ -1,126 +0,0 @@
|
|||||||
package fr.xephi.authme.data;
|
|
||||||
|
|
||||||
import fr.xephi.authme.initialization.HasCleanup;
|
|
||||||
import fr.xephi.authme.initialization.SettingsDependent;
|
|
||||||
import fr.xephi.authme.settings.Settings;
|
|
||||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
|
||||||
import fr.xephi.authme.util.RandomStringUtils;
|
|
||||||
import fr.xephi.authme.util.expiring.ExpiringMap;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages captcha codes.
|
|
||||||
*/
|
|
||||||
public abstract class AbstractCaptchaManager implements SettingsDependent, HasCleanup {
|
|
||||||
|
|
||||||
// Note: Proper expiration is set in reload(), which is also called on initialization
|
|
||||||
private final ExpiringMap<String, String> captchaCodes = new ExpiringMap<>(0, TimeUnit.MINUTES);
|
|
||||||
private int captchaLength;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param settings the settings instance
|
|
||||||
*/
|
|
||||||
public AbstractCaptchaManager(Settings settings) {
|
|
||||||
initialize(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the given player is required to solve a captcha.
|
|
||||||
*
|
|
||||||
* @param name the name of the player to verify
|
|
||||||
* @return true if the player has to solve a captcha, false otherwise
|
|
||||||
*/
|
|
||||||
public abstract boolean isCaptchaRequired(String name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the stored captcha for the player or generates and saves a new one.
|
|
||||||
*
|
|
||||||
* @param name the player's name
|
|
||||||
* @return the code the player is required to enter
|
|
||||||
*/
|
|
||||||
public String getCaptchaCodeOrGenerateNew(String name) {
|
|
||||||
String code = captchaCodes.get(name.toLowerCase());
|
|
||||||
return code == null ? generateCode(name) : code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a code for the player and returns it.
|
|
||||||
*
|
|
||||||
* @param name the name of the player to generate a code for
|
|
||||||
* @return the generated code
|
|
||||||
*/
|
|
||||||
public String generateCode(String name) {
|
|
||||||
String code = RandomStringUtils.generate(captchaLength);
|
|
||||||
captchaCodes.put(name.toLowerCase(), code);
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the given code against the existing one and resets the player's auth failure count upon success.
|
|
||||||
*
|
|
||||||
* @param name the name of the player to check
|
|
||||||
* @param code the supplied code
|
|
||||||
* @return true if the code matches or if no captcha is required for the player, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean checkCode(String name, String code) {
|
|
||||||
final String nameLowerCase = name.toLowerCase();
|
|
||||||
String savedCode = captchaCodes.get(nameLowerCase);
|
|
||||||
if (savedCode != null && savedCode.equalsIgnoreCase(code)) {
|
|
||||||
captchaCodes.remove(nameLowerCase);
|
|
||||||
processSuccessfulCode(nameLowerCase);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
processUnsuccessfulCode(nameLowerCase);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialize(Settings settings) {
|
|
||||||
captchaLength = settings.getProperty(SecuritySettings.CAPTCHA_LENGTH);
|
|
||||||
captchaCodes.setExpiration(minutesBeforeCodeExpires(settings), TimeUnit.MINUTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called on initialization and on reload.
|
|
||||||
*
|
|
||||||
* @param settings the settings instance
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void reload(Settings settings) {
|
|
||||||
// Note ljacqu 20171201: Use initialize() as an in-between method so that we can call it in the constructor
|
|
||||||
// without causing any trouble to a child that may extend reload -> at the point of calling, the child's fields
|
|
||||||
// would not yet be initialized.
|
|
||||||
initialize(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void performCleanup() {
|
|
||||||
captchaCodes.removeExpiredEntries();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a player has successfully solved the captcha.
|
|
||||||
*
|
|
||||||
* @param nameLower the player's name (all lowercase)
|
|
||||||
*/
|
|
||||||
protected abstract void processSuccessfulCode(String nameLower);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a player has failed the captcha code.
|
|
||||||
*
|
|
||||||
* @param nameLower the player's name (all lowercase)
|
|
||||||
*/
|
|
||||||
protected void processUnsuccessfulCode(String nameLower) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of minutes a generated captcha code should live for before it may expire.
|
|
||||||
*
|
|
||||||
* @param settings the settings instance
|
|
||||||
* @return number of minutes that the code is valid for
|
|
||||||
*/
|
|
||||||
protected abstract int minutesBeforeCodeExpires(Settings settings);
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
package fr.xephi.authme.data;
|
|
||||||
|
|
||||||
import fr.xephi.authme.settings.Settings;
|
|
||||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
|
||||||
import fr.xephi.authme.util.expiring.TimedCounter;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manager for the handling of captchas after too many failed login attempts.
|
|
||||||
*/
|
|
||||||
public class LoginCaptchaManager extends AbstractCaptchaManager {
|
|
||||||
|
|
||||||
// Note: proper expiration is set in reload(), which is also called on initialization by the parent
|
|
||||||
private final TimedCounter<String> playerCounts = new TimedCounter<>(0, TimeUnit.MINUTES);
|
|
||||||
|
|
||||||
private boolean isEnabled;
|
|
||||||
private int threshold;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
LoginCaptchaManager(Settings settings) {
|
|
||||||
super(settings);
|
|
||||||
reload(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increases the failure count for the given player.
|
|
||||||
*
|
|
||||||
* @param name the player's name
|
|
||||||
*/
|
|
||||||
public void increaseLoginFailureCount(String name) {
|
|
||||||
if (isEnabled) {
|
|
||||||
String playerLower = name.toLowerCase();
|
|
||||||
playerCounts.increment(playerLower);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCaptchaRequired(String playerName) {
|
|
||||||
return isEnabled && playerCounts.get(playerName.toLowerCase()) >= threshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the login count of the given player to 0.
|
|
||||||
*
|
|
||||||
* @param name the player's name
|
|
||||||
*/
|
|
||||||
public void resetLoginFailureCount(String name) {
|
|
||||||
if (isEnabled) {
|
|
||||||
playerCounts.remove(name.toLowerCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reload(Settings settings) {
|
|
||||||
super.reload(settings);
|
|
||||||
|
|
||||||
this.isEnabled = settings.getProperty(SecuritySettings.ENABLE_LOGIN_FAILURE_CAPTCHA);
|
|
||||||
this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TRIES_BEFORE_CAPTCHA);
|
|
||||||
long countTimeout = settings.getProperty(SecuritySettings.CAPTCHA_COUNT_MINUTES_BEFORE_RESET);
|
|
||||||
playerCounts.setExpiration(countTimeout, TimeUnit.MINUTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void performCleanup() {
|
|
||||||
super.performCleanup();
|
|
||||||
playerCounts.removeExpiredEntries();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void processSuccessfulCode(String nameLower) {
|
|
||||||
playerCounts.remove(nameLower);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int minutesBeforeCodeExpires(Settings settings) {
|
|
||||||
return settings.getProperty(SecuritySettings.CAPTCHA_COUNT_MINUTES_BEFORE_RESET);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
package fr.xephi.authme.data;
|
|
||||||
|
|
||||||
import fr.xephi.authme.data.limbo.LimboService;
|
|
||||||
import fr.xephi.authme.initialization.circulardependency.HasCircularDependency;
|
|
||||||
import fr.xephi.authme.initialization.circulardependency.InjectAfterInitialization;
|
|
||||||
import fr.xephi.authme.settings.Settings;
|
|
||||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
|
||||||
import fr.xephi.authme.util.expiring.ExpiringSet;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Captcha manager for registration.
|
|
||||||
*/
|
|
||||||
public class RegistrationCaptchaManager extends AbstractCaptchaManager implements HasCircularDependency {
|
|
||||||
|
|
||||||
private static final int MINUTES_VALID_FOR_REGISTRATION = 30;
|
|
||||||
|
|
||||||
private LimboService limboService;
|
|
||||||
|
|
||||||
private final ExpiringSet<String> verifiedNamesForRegistration =
|
|
||||||
new ExpiringSet<>(MINUTES_VALID_FOR_REGISTRATION, TimeUnit.MINUTES);
|
|
||||||
private boolean isEnabled;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
RegistrationCaptchaManager(Settings settings) {
|
|
||||||
super(settings);
|
|
||||||
reload(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCaptchaRequired(String name) {
|
|
||||||
return isEnabled && !verifiedNamesForRegistration.contains(name.toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reload(Settings settings) {
|
|
||||||
super.reload(settings);
|
|
||||||
this.isEnabled = settings.getProperty(SecuritySettings.ENABLE_CAPTCHA_FOR_REGISTRATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void performCleanup() {
|
|
||||||
super.performCleanup();
|
|
||||||
verifiedNamesForRegistration.removeExpiredEntries();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void processSuccessfulCode(String nameLower) {
|
|
||||||
verifiedNamesForRegistration.add(nameLower);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void processUnsuccessfulCode(String nameLower) {
|
|
||||||
final Player player = Bukkit.getPlayerExact(nameLower); // TODO #930: Pass in player!
|
|
||||||
limboService.resetMessageTask(player, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int minutesBeforeCodeExpires(Settings settings) {
|
|
||||||
return MINUTES_VALID_FOR_REGISTRATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
@InjectAfterInitialization
|
|
||||||
public void setLimboService(LimboService limboService) {
|
|
||||||
this.limboService = limboService;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,90 @@
|
|||||||
|
package fr.xephi.authme.data.captcha;
|
||||||
|
|
||||||
|
import fr.xephi.authme.util.RandomStringUtils;
|
||||||
|
import fr.xephi.authme.util.expiring.ExpiringMap;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primitive service for storing captcha codes.
|
||||||
|
*/
|
||||||
|
public class CaptchaCodeStorage {
|
||||||
|
|
||||||
|
/** Map of captcha codes (with player name as key, case-insensitive). */
|
||||||
|
private ExpiringMap<String, String> captchaCodes;
|
||||||
|
/** Number of characters newly generated captcha codes should have. */
|
||||||
|
private int captchaLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param expirationInMinutes minutes after which a saved captcha code expires
|
||||||
|
* @param captchaLength the number of characters a captcha code should have
|
||||||
|
*/
|
||||||
|
public CaptchaCodeStorage(long expirationInMinutes, int captchaLength) {
|
||||||
|
this.captchaCodes = new ExpiringMap<>(expirationInMinutes, TimeUnit.MINUTES);
|
||||||
|
this.captchaLength = captchaLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the expiration of captcha codes.
|
||||||
|
*
|
||||||
|
* @param expirationInMinutes minutes after which a saved captcha code expires
|
||||||
|
*/
|
||||||
|
public void setExpirationInMinutes(long expirationInMinutes) {
|
||||||
|
captchaCodes.setExpiration(expirationInMinutes, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the captcha length.
|
||||||
|
*
|
||||||
|
* @param captchaLength number of characters a captcha code should have
|
||||||
|
*/
|
||||||
|
public void setCaptchaLength(int captchaLength) {
|
||||||
|
this.captchaLength = captchaLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the stored captcha for the player or generates and saves a new one.
|
||||||
|
*
|
||||||
|
* @param name the player's name
|
||||||
|
* @return the code the player is required to enter
|
||||||
|
*/
|
||||||
|
public String getCodeOrGenerateNew(String name) {
|
||||||
|
String code = captchaCodes.get(name.toLowerCase());
|
||||||
|
return code == null ? generateCode(name) : code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a code for the player and returns it.
|
||||||
|
*
|
||||||
|
* @param name the name of the player to generate a code for
|
||||||
|
* @return the generated code
|
||||||
|
*/
|
||||||
|
public String generateCode(String name) {
|
||||||
|
String code = RandomStringUtils.generate(captchaLength);
|
||||||
|
captchaCodes.put(name.toLowerCase(), code);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the given code against the existing one. Upon success, the saved captcha code is removed from storage.
|
||||||
|
*
|
||||||
|
* @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) {
|
||||||
|
String nameLowerCase = name.toLowerCase();
|
||||||
|
String savedCode = captchaCodes.get(nameLowerCase);
|
||||||
|
if (savedCode != null && savedCode.equalsIgnoreCase(code)) {
|
||||||
|
captchaCodes.remove(nameLowerCase);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeExpiredEntries() {
|
||||||
|
captchaCodes.removeExpiredEntries();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package fr.xephi.authme.data.captcha;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages captcha codes.
|
||||||
|
*/
|
||||||
|
public interface CaptchaManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given player is required to solve a captcha.
|
||||||
|
*
|
||||||
|
* @param name the name of the player to verify
|
||||||
|
* @return true if the player has to solve a captcha, false otherwise
|
||||||
|
*/
|
||||||
|
boolean isCaptchaRequired(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the stored captcha for the player or generates and saves a new one.
|
||||||
|
*
|
||||||
|
* @param name the player's name
|
||||||
|
* @return the code the player is required to enter
|
||||||
|
*/
|
||||||
|
String getCaptchaCodeOrGenerateNew(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a code for the player and returns it.
|
||||||
|
*
|
||||||
|
* @param name the name of the player to generate a code for
|
||||||
|
* @return the generated code
|
||||||
|
*/
|
||||||
|
String generateCode(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the given code against the existing one. This method may perform additional state changes
|
||||||
|
* on success or failure, such as modifying some counter or setting a player as verified.
|
||||||
|
*
|
||||||
|
* @param player the player to check
|
||||||
|
* @param code the supplied code
|
||||||
|
* @return true if the code matches, false otherwise
|
||||||
|
*/
|
||||||
|
boolean checkCode(Player player, String code);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package fr.xephi.authme.data.captcha;
|
||||||
|
|
||||||
|
import fr.xephi.authme.initialization.HasCleanup;
|
||||||
|
import fr.xephi.authme.initialization.SettingsDependent;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||||
|
import fr.xephi.authme.util.expiring.TimedCounter;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for the handling of captchas after too many failed login attempts.
|
||||||
|
*/
|
||||||
|
public class LoginCaptchaManager implements CaptchaManager, SettingsDependent, HasCleanup {
|
||||||
|
|
||||||
|
private final TimedCounter<String> playerCounts;
|
||||||
|
private final CaptchaCodeStorage captchaCodeStorage;
|
||||||
|
|
||||||
|
private boolean isEnabled;
|
||||||
|
private int threshold;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LoginCaptchaManager(Settings settings) {
|
||||||
|
// Note: Proper values are set in reload()
|
||||||
|
this.captchaCodeStorage = new CaptchaCodeStorage(30, 4);
|
||||||
|
this.playerCounts = new TimedCounter<>(9, TimeUnit.MINUTES);
|
||||||
|
reload(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increases the failure count for the given player.
|
||||||
|
*
|
||||||
|
* @param name the player's name
|
||||||
|
*/
|
||||||
|
public void increaseLoginFailureCount(String name) {
|
||||||
|
if (isEnabled) {
|
||||||
|
String playerLower = name.toLowerCase();
|
||||||
|
playerCounts.increment(playerLower);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCaptchaRequired(String playerName) {
|
||||||
|
return isEnabled && playerCounts.get(playerName.toLowerCase()) >= threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCaptchaCodeOrGenerateNew(String name) {
|
||||||
|
return captchaCodeStorage.getCodeOrGenerateNew(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateCode(String name) {
|
||||||
|
return captchaCodeStorage.generateCode(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkCode(Player player, String code) {
|
||||||
|
String nameLower = player.getName().toLowerCase();
|
||||||
|
boolean isCodeCorrect = captchaCodeStorage.checkCode(nameLower, code);
|
||||||
|
if (isCodeCorrect) {
|
||||||
|
playerCounts.remove(nameLower);
|
||||||
|
}
|
||||||
|
return isCodeCorrect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the login count of the given player to 0.
|
||||||
|
*
|
||||||
|
* @param name the player's name
|
||||||
|
*/
|
||||||
|
public void resetLoginFailureCount(String name) {
|
||||||
|
if (isEnabled) {
|
||||||
|
playerCounts.remove(name.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reload(Settings settings) {
|
||||||
|
int expirationInMinutes = settings.getProperty(SecuritySettings.CAPTCHA_COUNT_MINUTES_BEFORE_RESET);
|
||||||
|
captchaCodeStorage.setExpirationInMinutes(expirationInMinutes);
|
||||||
|
int captchaLength = settings.getProperty(SecuritySettings.CAPTCHA_LENGTH);
|
||||||
|
captchaCodeStorage.setCaptchaLength(captchaLength);
|
||||||
|
|
||||||
|
int countTimeout = settings.getProperty(SecuritySettings.CAPTCHA_COUNT_MINUTES_BEFORE_RESET);
|
||||||
|
playerCounts.setExpiration(countTimeout, TimeUnit.MINUTES);
|
||||||
|
|
||||||
|
isEnabled = settings.getProperty(SecuritySettings.ENABLE_LOGIN_FAILURE_CAPTCHA);
|
||||||
|
threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TRIES_BEFORE_CAPTCHA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performCleanup() {
|
||||||
|
playerCounts.removeExpiredEntries();
|
||||||
|
captchaCodeStorage.removeExpiredEntries();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package fr.xephi.authme.data.captcha;
|
||||||
|
|
||||||
|
import fr.xephi.authme.data.limbo.LimboService;
|
||||||
|
import fr.xephi.authme.initialization.HasCleanup;
|
||||||
|
import fr.xephi.authme.initialization.SettingsDependent;
|
||||||
|
import fr.xephi.authme.initialization.circulardependency.HasCircularDependency;
|
||||||
|
import fr.xephi.authme.initialization.circulardependency.InjectAfterInitialization;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||||
|
import fr.xephi.authme.util.expiring.ExpiringSet;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Captcha manager for registration.
|
||||||
|
*/
|
||||||
|
public class RegistrationCaptchaManager
|
||||||
|
implements CaptchaManager, SettingsDependent, HasCleanup, HasCircularDependency {
|
||||||
|
|
||||||
|
private static final int MINUTES_VALID_FOR_REGISTRATION = 30;
|
||||||
|
|
||||||
|
private LimboService limboService;
|
||||||
|
|
||||||
|
private final ExpiringSet<String> verifiedNamesForRegistration;
|
||||||
|
private final CaptchaCodeStorage captchaCodeStorage;
|
||||||
|
private boolean isEnabled;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RegistrationCaptchaManager(Settings settings) {
|
||||||
|
// NOTE: proper captcha length is set in reload()
|
||||||
|
this.captchaCodeStorage = new CaptchaCodeStorage(MINUTES_VALID_FOR_REGISTRATION, 4);
|
||||||
|
this.verifiedNamesForRegistration = new ExpiringSet<>(MINUTES_VALID_FOR_REGISTRATION, TimeUnit.MINUTES);
|
||||||
|
reload(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCaptchaRequired(String name) {
|
||||||
|
return isEnabled && !verifiedNamesForRegistration.contains(name.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCaptchaCodeOrGenerateNew(String name) {
|
||||||
|
return captchaCodeStorage.getCodeOrGenerateNew(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateCode(String name) {
|
||||||
|
return captchaCodeStorage.generateCode(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkCode(Player player, String code) {
|
||||||
|
String nameLower = player.getName().toLowerCase();
|
||||||
|
boolean isCodeCorrect = captchaCodeStorage.checkCode(nameLower, code);
|
||||||
|
if (isCodeCorrect) {
|
||||||
|
verifiedNamesForRegistration.add(nameLower);
|
||||||
|
} else {
|
||||||
|
limboService.resetMessageTask(player, false);
|
||||||
|
}
|
||||||
|
return isCodeCorrect;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reload(Settings settings) {
|
||||||
|
int captchaLength = settings.getProperty(SecuritySettings.CAPTCHA_LENGTH);
|
||||||
|
captchaCodeStorage.setCaptchaLength(captchaLength);
|
||||||
|
|
||||||
|
isEnabled = settings.getProperty(SecuritySettings.ENABLE_CAPTCHA_FOR_REGISTRATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performCleanup() {
|
||||||
|
verifiedNamesForRegistration.removeExpiredEntries();
|
||||||
|
captchaCodeStorage.removeExpiredEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectAfterInitialization
|
||||||
|
public void setLimboService(LimboService limboService) {
|
||||||
|
this.limboService = limboService;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package fr.xephi.authme.data.limbo;
|
package fr.xephi.authme.data.limbo;
|
||||||
|
|
||||||
import fr.xephi.authme.data.RegistrationCaptchaManager;
|
|
||||||
import fr.xephi.authme.data.auth.PlayerCache;
|
import fr.xephi.authme.data.auth.PlayerCache;
|
||||||
|
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||||
import fr.xephi.authme.message.MessageKey;
|
import fr.xephi.authme.message.MessageKey;
|
||||||
import fr.xephi.authme.message.Messages;
|
import fr.xephi.authme.message.Messages;
|
||||||
import fr.xephi.authme.service.BukkitService;
|
import fr.xephi.authme.service.BukkitService;
|
||||||
|
@ -2,7 +2,7 @@ package fr.xephi.authme.process.login;
|
|||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import fr.xephi.authme.ConsoleLogger;
|
import fr.xephi.authme.ConsoleLogger;
|
||||||
import fr.xephi.authme.data.LoginCaptchaManager;
|
import fr.xephi.authme.data.captcha.LoginCaptchaManager;
|
||||||
import fr.xephi.authme.data.TempbanManager;
|
import fr.xephi.authme.data.TempbanManager;
|
||||||
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;
|
||||||
|
@ -6,7 +6,7 @@ import ch.jalu.injector.InjectorBuilder;
|
|||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import fr.xephi.authme.api.v3.AuthMeApi;
|
import fr.xephi.authme.api.v3.AuthMeApi;
|
||||||
import fr.xephi.authme.command.CommandHandler;
|
import fr.xephi.authme.command.CommandHandler;
|
||||||
import fr.xephi.authme.data.RegistrationCaptchaManager;
|
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||||
import fr.xephi.authme.data.limbo.LimboService;
|
import fr.xephi.authme.data.limbo.LimboService;
|
||||||
import fr.xephi.authme.datasource.DataSource;
|
import fr.xephi.authme.datasource.DataSource;
|
||||||
import fr.xephi.authme.initialization.DataFolder;
|
import fr.xephi.authme.initialization.DataFolder;
|
||||||
|
@ -4,6 +4,7 @@ import ch.jalu.configme.properties.Property;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import fr.xephi.authme.data.captcha.CaptchaCodeStorage;
|
||||||
import fr.xephi.authme.datasource.Columns;
|
import fr.xephi.authme.datasource.Columns;
|
||||||
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension;
|
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension;
|
||||||
import fr.xephi.authme.initialization.HasCleanup;
|
import fr.xephi.authme.initialization.HasCleanup;
|
||||||
@ -42,7 +43,7 @@ public class ClassesConsistencyTest {
|
|||||||
|
|
||||||
/** Expiring structure types. */
|
/** Expiring structure types. */
|
||||||
private static final Set<Class<?>> EXPIRING_STRUCTURES = ImmutableSet.of(
|
private static final Set<Class<?>> EXPIRING_STRUCTURES = ImmutableSet.of(
|
||||||
ExpiringSet.class, ExpiringMap.class, TimedCounter.class);
|
ExpiringSet.class, ExpiringMap.class, TimedCounter.class, CaptchaCodeStorage.class);
|
||||||
|
|
||||||
/** Immutable types, which are allowed to be used in non-private constants. */
|
/** Immutable types, which are allowed to be used in non-private constants. */
|
||||||
private static final Set<Class<?>> IMMUTABLE_TYPES = ImmutableSet.of(
|
private static final Set<Class<?>> IMMUTABLE_TYPES = ImmutableSet.of(
|
||||||
@ -157,10 +158,9 @@ public class ClassesConsistencyTest {
|
|||||||
public void shouldImplementHasCleanup() {
|
public void shouldImplementHasCleanup() {
|
||||||
// given / when / then
|
// given / when / then
|
||||||
for (Class<?> clazz : ALL_CLASSES) {
|
for (Class<?> clazz : ALL_CLASSES) {
|
||||||
if (hasExpiringCollectionAsField(clazz)) {
|
if (hasExpiringCollectionAsField(clazz) && !EXPIRING_STRUCTURES.contains(clazz)) {
|
||||||
assertThat("Class '" + clazz.getSimpleName() + "' has expiring collections, should implement HasCleanup",
|
assertThat("Class '" + clazz.getSimpleName() + "' has expiring collections, should implement HasCleanup",
|
||||||
HasCleanup.class.isAssignableFrom(clazz), equalTo(true));
|
HasCleanup.class.isAssignableFrom(clazz), equalTo(true));
|
||||||
// System.out.println("Successful check for " + clazz);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package fr.xephi.authme.command.executable.captcha;
|
package fr.xephi.authme.command.executable.captcha;
|
||||||
|
|
||||||
import fr.xephi.authme.data.LoginCaptchaManager;
|
|
||||||
import fr.xephi.authme.data.RegistrationCaptchaManager;
|
|
||||||
import fr.xephi.authme.data.auth.PlayerCache;
|
import fr.xephi.authme.data.auth.PlayerCache;
|
||||||
|
import fr.xephi.authme.data.captcha.LoginCaptchaManager;
|
||||||
|
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||||
import fr.xephi.authme.data.limbo.LimboService;
|
import fr.xephi.authme.data.limbo.LimboService;
|
||||||
import fr.xephi.authme.message.MessageKey;
|
import fr.xephi.authme.message.MessageKey;
|
||||||
import fr.xephi.authme.service.CommonService;
|
import fr.xephi.authme.service.CommonService;
|
||||||
@ -86,14 +86,14 @@ public class CaptchaCommandTest {
|
|||||||
given(playerCache.isAuthenticated(name)).willReturn(false);
|
given(playerCache.isAuthenticated(name)).willReturn(false);
|
||||||
given(loginCaptchaManager.isCaptchaRequired(name)).willReturn(true);
|
given(loginCaptchaManager.isCaptchaRequired(name)).willReturn(true);
|
||||||
String captchaCode = "3991";
|
String captchaCode = "3991";
|
||||||
given(loginCaptchaManager.checkCode(name, captchaCode)).willReturn(true);
|
given(loginCaptchaManager.checkCode(player, captchaCode)).willReturn(true);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
command.executeCommand(player, Collections.singletonList(captchaCode));
|
command.executeCommand(player, Collections.singletonList(captchaCode));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(loginCaptchaManager).isCaptchaRequired(name);
|
verify(loginCaptchaManager).isCaptchaRequired(name);
|
||||||
verify(loginCaptchaManager).checkCode(name, captchaCode);
|
verify(loginCaptchaManager).checkCode(player, captchaCode);
|
||||||
verifyNoMoreInteractions(loginCaptchaManager);
|
verifyNoMoreInteractions(loginCaptchaManager);
|
||||||
verify(commonService).send(player, MessageKey.CAPTCHA_SUCCESS);
|
verify(commonService).send(player, MessageKey.CAPTCHA_SUCCESS);
|
||||||
verify(commonService).send(player, MessageKey.LOGIN_MESSAGE);
|
verify(commonService).send(player, MessageKey.LOGIN_MESSAGE);
|
||||||
@ -109,7 +109,7 @@ public class CaptchaCommandTest {
|
|||||||
given(playerCache.isAuthenticated(name)).willReturn(false);
|
given(playerCache.isAuthenticated(name)).willReturn(false);
|
||||||
given(loginCaptchaManager.isCaptchaRequired(name)).willReturn(true);
|
given(loginCaptchaManager.isCaptchaRequired(name)).willReturn(true);
|
||||||
String captchaCode = "2468";
|
String captchaCode = "2468";
|
||||||
given(loginCaptchaManager.checkCode(name, captchaCode)).willReturn(false);
|
given(loginCaptchaManager.checkCode(player, captchaCode)).willReturn(false);
|
||||||
String newCode = "1337";
|
String newCode = "1337";
|
||||||
given(loginCaptchaManager.generateCode(name)).willReturn(newCode);
|
given(loginCaptchaManager.generateCode(name)).willReturn(newCode);
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ public class CaptchaCommandTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
verify(loginCaptchaManager).isCaptchaRequired(name);
|
verify(loginCaptchaManager).isCaptchaRequired(name);
|
||||||
verify(loginCaptchaManager).checkCode(name, captchaCode);
|
verify(loginCaptchaManager).checkCode(player, captchaCode);
|
||||||
verify(loginCaptchaManager).generateCode(name);
|
verify(loginCaptchaManager).generateCode(name);
|
||||||
verifyNoMoreInteractions(loginCaptchaManager);
|
verifyNoMoreInteractions(loginCaptchaManager);
|
||||||
verify(commonService).send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
|
verify(commonService).send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
|
||||||
@ -133,13 +133,13 @@ public class CaptchaCommandTest {
|
|||||||
given(loginCaptchaManager.isCaptchaRequired(name)).willReturn(false);
|
given(loginCaptchaManager.isCaptchaRequired(name)).willReturn(false);
|
||||||
given(registrationCaptchaManager.isCaptchaRequired(name)).willReturn(true);
|
given(registrationCaptchaManager.isCaptchaRequired(name)).willReturn(true);
|
||||||
String captchaCode = "A89Y3";
|
String captchaCode = "A89Y3";
|
||||||
given(registrationCaptchaManager.checkCode(name, captchaCode)).willReturn(true);
|
given(registrationCaptchaManager.checkCode(player, captchaCode)).willReturn(true);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
command.executeCommand(player, Collections.singletonList(captchaCode));
|
command.executeCommand(player, Collections.singletonList(captchaCode));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(registrationCaptchaManager).checkCode(name, captchaCode);
|
verify(registrationCaptchaManager).checkCode(player, captchaCode);
|
||||||
verify(loginCaptchaManager, only()).isCaptchaRequired(name);
|
verify(loginCaptchaManager, only()).isCaptchaRequired(name);
|
||||||
verify(commonService).send(player, MessageKey.CAPTCHA_SUCCESS);
|
verify(commonService).send(player, MessageKey.CAPTCHA_SUCCESS);
|
||||||
verify(commonService).send(player, MessageKey.REGISTER_MESSAGE);
|
verify(commonService).send(player, MessageKey.REGISTER_MESSAGE);
|
||||||
@ -152,14 +152,14 @@ public class CaptchaCommandTest {
|
|||||||
Player player = mockPlayerWithName(name);
|
Player player = mockPlayerWithName(name);
|
||||||
given(registrationCaptchaManager.isCaptchaRequired(name)).willReturn(true);
|
given(registrationCaptchaManager.isCaptchaRequired(name)).willReturn(true);
|
||||||
String captchaCode = "SFL3";
|
String captchaCode = "SFL3";
|
||||||
given(registrationCaptchaManager.checkCode(name, captchaCode)).willReturn(false);
|
given(registrationCaptchaManager.checkCode(player, captchaCode)).willReturn(false);
|
||||||
given(registrationCaptchaManager.generateCode(name)).willReturn("new code");
|
given(registrationCaptchaManager.generateCode(name)).willReturn("new code");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
command.executeCommand(player, Collections.singletonList(captchaCode));
|
command.executeCommand(player, Collections.singletonList(captchaCode));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(registrationCaptchaManager).checkCode(name, captchaCode);
|
verify(registrationCaptchaManager).checkCode(player, captchaCode);
|
||||||
verify(registrationCaptchaManager).generateCode(name);
|
verify(registrationCaptchaManager).generateCode(name);
|
||||||
verify(commonService).send(player, MessageKey.CAPTCHA_WRONG_ERROR, "new code");
|
verify(commonService).send(player, MessageKey.CAPTCHA_WRONG_ERROR, "new code");
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package fr.xephi.authme.command.executable.register;
|
package fr.xephi.authme.command.executable.register;
|
||||||
|
|
||||||
import fr.xephi.authme.TestHelper;
|
import fr.xephi.authme.TestHelper;
|
||||||
import fr.xephi.authme.data.RegistrationCaptchaManager;
|
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||||
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.process.Management;
|
import fr.xephi.authme.process.Management;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package fr.xephi.authme.data;
|
package fr.xephi.authme.data.captcha;
|
||||||
|
|
||||||
import fr.xephi.authme.ReflectionTestUtils;
|
import fr.xephi.authme.ReflectionTestUtils;
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||||
import fr.xephi.authme.util.expiring.TimedCounter;
|
import fr.xephi.authme.util.expiring.TimedCounter;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
@ -38,10 +39,12 @@ public class LoginCaptchaManagerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldCreateAndCheckCaptcha() {
|
public void shouldCreateAndCheckCaptcha() {
|
||||||
// given
|
// given
|
||||||
String player = "Miner";
|
String name = "Miner";
|
||||||
|
Player player = mock(Player.class);
|
||||||
|
given(player.getName()).willReturn(name);
|
||||||
Settings settings = mockSettings(1, 4);
|
Settings settings = mockSettings(1, 4);
|
||||||
LoginCaptchaManager manager = new LoginCaptchaManager(settings);
|
LoginCaptchaManager manager = new LoginCaptchaManager(settings);
|
||||||
String captchaCode = manager.getCaptchaCodeOrGenerateNew(player);
|
String captchaCode = manager.getCaptchaCodeOrGenerateNew(name);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
boolean badResult = manager.checkCode(player, "wrong_code");
|
boolean badResult = manager.checkCode(player, "wrong_code");
|
@ -1,9 +1,10 @@
|
|||||||
package fr.xephi.authme.data;
|
package fr.xephi.authme.data.captcha;
|
||||||
|
|
||||||
import fr.xephi.authme.ReflectionTestUtils;
|
import fr.xephi.authme.ReflectionTestUtils;
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||||
import fr.xephi.authme.util.expiring.ExpiringMap;
|
import fr.xephi.authme.util.expiring.ExpiringMap;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static fr.xephi.authme.AuthMeMatchers.stringWithLength;
|
import static fr.xephi.authme.AuthMeMatchers.stringWithLength;
|
||||||
@ -46,8 +47,11 @@ public class RegistrationCaptchaManagerTest {
|
|||||||
RegistrationCaptchaManager captchaManager = new RegistrationCaptchaManager(settings);
|
RegistrationCaptchaManager captchaManager = new RegistrationCaptchaManager(settings);
|
||||||
getCodeMap(captchaManager).put("test", captcha);
|
getCodeMap(captchaManager).put("test", captcha);
|
||||||
|
|
||||||
|
Player player = mock(Player.class);
|
||||||
|
given(player.getName()).willReturn("TeSt");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
boolean isSuccessful = captchaManager.checkCode("TeSt", captcha);
|
boolean isSuccessful = captchaManager.checkCode(player, captcha);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(isSuccessful, equalTo(true));
|
assertThat(isSuccessful, equalTo(true));
|
||||||
@ -72,11 +76,18 @@ public class RegistrationCaptchaManagerTest {
|
|||||||
assertThat(captcha1, equalTo(captcha2));
|
assertThat(captcha1, equalTo(captcha2));
|
||||||
assertThat(captcha1, stringWithLength(captchaLength));
|
assertThat(captcha1, stringWithLength(captchaLength));
|
||||||
|
|
||||||
|
// given (2)
|
||||||
|
Player player = mock(Player.class);
|
||||||
|
given(player.getName()).willReturn("toast");
|
||||||
|
|
||||||
// when (2) / then (2)
|
// when (2) / then (2)
|
||||||
assertThat(captchaManager.checkCode("toast", captcha1), equalTo(true));
|
assertThat(captchaManager.checkCode(player, captcha1), equalTo(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExpiringMap<String, String> getCodeMap(AbstractCaptchaManager captchaManager) {
|
@SuppressWarnings("unchecked")
|
||||||
return ReflectionTestUtils.getFieldValue(AbstractCaptchaManager.class, captchaManager, "captchaCodes");
|
private static ExpiringMap<String, String> getCodeMap(RegistrationCaptchaManager captchaManager) {
|
||||||
|
CaptchaCodeStorage captchaStorage = ReflectionTestUtils.getFieldValue(
|
||||||
|
RegistrationCaptchaManager.class, captchaManager, "captchaCodeStorage");
|
||||||
|
return ReflectionTestUtils.getFieldValue(CaptchaCodeStorage.class, captchaStorage, "captchaCodes");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package fr.xephi.authme.data.limbo;
|
package fr.xephi.authme.data.limbo;
|
||||||
|
|
||||||
import fr.xephi.authme.TestHelper;
|
import fr.xephi.authme.TestHelper;
|
||||||
import fr.xephi.authme.data.RegistrationCaptchaManager;
|
|
||||||
import fr.xephi.authme.data.auth.PlayerCache;
|
import fr.xephi.authme.data.auth.PlayerCache;
|
||||||
|
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||||
import fr.xephi.authme.message.MessageKey;
|
import fr.xephi.authme.message.MessageKey;
|
||||||
import fr.xephi.authme.message.Messages;
|
import fr.xephi.authme.message.Messages;
|
||||||
import fr.xephi.authme.service.BukkitService;
|
import fr.xephi.authme.service.BukkitService;
|
||||||
|
Loading…
Reference in New Issue
Block a user