package fr.xephi.authme.data; import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.PlayerUtils; import fr.xephi.authme.util.expiring.TimedCounter; import org.bukkit.entity.Player; import javax.inject.Inject; import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import static fr.xephi.authme.util.Utils.MILLIS_PER_MINUTE; /** * Manager for handling temporary bans. */ public class TempbanManager implements SettingsDependent, HasCleanup { private final Map> ipLoginFailureCounts; private final BukkitService bukkitService; private final Messages messages; private boolean isEnabled; private int threshold; private int length; private long resetThreshold; private String customCommand; @Inject TempbanManager(BukkitService bukkitService, Messages messages, Settings settings) { this.ipLoginFailureCounts = new ConcurrentHashMap<>(); this.bukkitService = bukkitService; this.messages = messages; reload(settings); } /** * 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, String name) { if (isEnabled) { TimedCounter countsByName = ipLoginFailureCounts.computeIfAbsent( address, k -> new TimedCounter<>(resetThreshold, TimeUnit.MINUTES)); countsByName.increment(name); } } /** * 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, String name) { if (isEnabled) { TimedCounter counter = ipLoginFailureCounts.get(address); if (counter != null) { counter.remove(name); } } } /** * Return whether the IP address should be tempbanned. * * @param address The player's IP address * @return True if the IP should be tempbanned */ public boolean shouldTempban(String address) { if (isEnabled) { TimedCounter countsByName = ipLoginFailureCounts.get(address); if (countsByName != null) { return countsByName.total() >= threshold; } } return false; } /** * Tempban a player's IP address for failing to log in too many times. * This calculates the expire time based on the time the method was called. * * @param player The player to tempban */ public void tempbanPlayer(final Player player) { if (isEnabled) { final String name = player.getName(); final String ip = PlayerUtils.getPlayerIp(player); final String reason = messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS, player); final Date expires = new Date(); long newTime = expires.getTime() + (length * MILLIS_PER_MINUTE); expires.setTime(newTime); bukkitService.scheduleSyncDelayedTask(() -> { if(customCommand.isEmpty()) { bukkitService.banIp(ip, reason, expires, "AuthMe"); player.kickPlayer(reason); } else { String command = customCommand .replace("%player%", name) .replace("%ip%", ip); bukkitService.dispatchConsoleCommand(command); } }); ipLoginFailureCounts.remove(ip); } } @Override public void reload(Settings settings) { this.isEnabled = settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS); this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TEMPBAN); this.length = settings.getProperty(SecuritySettings.TEMPBAN_LENGTH); this.resetThreshold = settings.getProperty(SecuritySettings.TEMPBAN_MINUTES_BEFORE_RESET); this.customCommand = settings.getProperty(SecuritySettings.TEMPBAN_CUSTOM_COMMAND); } @Override public void performCleanup() { for (TimedCounter countsByIp : ipLoginFailureCounts.values()) { countsByIp.removeExpiredEntries(); } ipLoginFailureCounts.entrySet().removeIf(e -> e.getValue().isEmpty()); } }