#876 Keep track of wrong logins by (ip, username) and implement threshold
This commit is contained in:
+78
-16
@@ -1,5 +1,6 @@
|
||||
package fr.xephi.authme.cache;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.output.MessageKey;
|
||||
import fr.xephi.authme.output.Messages;
|
||||
@@ -11,17 +12,20 @@ import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Manager for handling tempbans
|
||||
* Manager for handling temporary bans.
|
||||
*/
|
||||
// TODO Gnat008 20160613: Figure out the best way to remove entries based on time
|
||||
// TODO #876: Implement HasCleanup interface
|
||||
public class TempbanManager implements SettingsDependent {
|
||||
|
||||
private static final long MINUTE_IN_MILLISECONDS = 60000;
|
||||
private static final long MINUTE_IN_MILLISECONDS = 60_000;
|
||||
// TODO #876: Make a setting out of this
|
||||
private static final long COUNTER_RETENTION_MILLIS = 6 * 60 * MINUTE_IN_MILLISECONDS;
|
||||
|
||||
private final ConcurrentHashMap<String, Integer> ipLoginFailureCounts;
|
||||
private final Map<String, Map<String, TimedCounter>> ipLoginFailureCounts;
|
||||
private final BukkitService bukkitService;
|
||||
private final Messages messages;
|
||||
|
||||
@@ -38,30 +42,40 @@ public class TempbanManager implements SettingsDependent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the failure count for the given IP address.
|
||||
* Increases the failure count for the given IP address/username combination.
|
||||
*
|
||||
* @param address The player's IP address
|
||||
* @param name The username
|
||||
*/
|
||||
public void increaseCount(String address) {
|
||||
public void increaseCount(String address, String name) {
|
||||
if (isEnabled) {
|
||||
Integer count = ipLoginFailureCounts.get(address);
|
||||
Map<String, TimedCounter> countsByName = ipLoginFailureCounts.get(address);
|
||||
if (countsByName == null) {
|
||||
countsByName = new ConcurrentHashMap<>();
|
||||
ipLoginFailureCounts.put(address, countsByName);
|
||||
}
|
||||
|
||||
if (count == null) {
|
||||
ipLoginFailureCounts.put(address, 1);
|
||||
TimedCounter counter = countsByName.get(name);
|
||||
if (counter == null) {
|
||||
countsByName.put(name, new TimedCounter(1));
|
||||
} else {
|
||||
ipLoginFailureCounts.put(address, count + 1);
|
||||
counter.increment(COUNTER_RETENTION_MILLIS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the failure count for a given IP address to 0.
|
||||
* Set the failure count for a given IP address / username combination to 0.
|
||||
*
|
||||
* @param address The IP address
|
||||
* @param name The username
|
||||
*/
|
||||
public void resetCount(String address) {
|
||||
public void resetCount(String address, String name) {
|
||||
if (isEnabled) {
|
||||
ipLoginFailureCounts.remove(address);
|
||||
Map<String, TimedCounter> map = ipLoginFailureCounts.get(address);
|
||||
if (map != null) {
|
||||
map.remove(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +87,14 @@ public class TempbanManager implements SettingsDependent {
|
||||
*/
|
||||
public boolean shouldTempban(String address) {
|
||||
if (isEnabled) {
|
||||
Integer count = ipLoginFailureCounts.get(address);
|
||||
return count != null && count >= threshold;
|
||||
Map<String, TimedCounter> countsByName = ipLoginFailureCounts.get(address);
|
||||
if (countsByName != null) {
|
||||
int total = 0;
|
||||
for (TimedCounter counter : countsByName.values()) {
|
||||
total += counter.getCount(COUNTER_RETENTION_MILLIS);
|
||||
}
|
||||
return total >= threshold;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -103,7 +123,7 @@ public class TempbanManager implements SettingsDependent {
|
||||
}
|
||||
});
|
||||
|
||||
resetCount(ip);
|
||||
ipLoginFailureCounts.remove(ip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,4 +133,46 @@ public class TempbanManager implements SettingsDependent {
|
||||
this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TEMPBAN);
|
||||
this.length = settings.getProperty(SecuritySettings.TEMPBAN_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counter with an associated timestamp, keeping track of when the last entry has been added.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final class TimedCounter {
|
||||
|
||||
private int counter;
|
||||
private long lastIncrementTimestamp = System.currentTimeMillis();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param start the initial value to set the counter to
|
||||
*/
|
||||
TimedCounter(int start) {
|
||||
this.counter = start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count, taking into account the last entry timestamp.
|
||||
*
|
||||
* @param threshold the threshold in milliseconds until when to consider a counter
|
||||
* @return the counter's value, or {@code 0} if it was last incremented longer ago than the threshold
|
||||
*/
|
||||
int getCount(long threshold) {
|
||||
if (System.currentTimeMillis() - lastIncrementTimestamp > threshold) {
|
||||
return 0;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the counter, taking into account the last entry timestamp.
|
||||
*
|
||||
* @param threshold in milliseconds, the time span until which to consider the existing number
|
||||
*/
|
||||
void increment(long threshold) {
|
||||
counter = getCount(threshold) + 1;
|
||||
lastIncrementTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
// Increase the counts here before knowing the result of the login.
|
||||
// If the login is successful, we clear the captcha count for the player.
|
||||
captchaManager.increaseCount(name);
|
||||
tempbanManager.increaseCount(ip);
|
||||
tempbanManager.increaseCount(ip, name);
|
||||
|
||||
String email = pAuth.getEmail();
|
||||
boolean passwordVerified = forceLogin || passwordSecurity.comparePassword(
|
||||
@@ -153,6 +153,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
database.updateSession(auth);
|
||||
|
||||
captchaManager.resetCounts(name);
|
||||
tempbanManager.resetCount(ip, name);
|
||||
player.setNoDamageTicks(0);
|
||||
|
||||
if (!forceLogin)
|
||||
|
||||
Reference in New Issue
Block a user