package fr.xephi.authme.data; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; 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.expiring.TimedCounter; import org.bukkit.entity.Player; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToScheduleSyncDelayedTask; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.lessThan; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; /** * Test for {@link TempbanManager}. */ @RunWith(MockitoJUnitRunner.class) public class TempbanManagerTest { private static final long DATE_TOLERANCE_MILLISECONDS = 200L; private static final long TEST_EXPIRATION_THRESHOLD = 120_000L; @Mock private BukkitService bukkitService; @Mock private Messages messages; @Test public void shouldAddCounts() { // given Settings settings = mockSettings(3, 60, ""); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); String address = "192.168.1.1"; // when manager.increaseCount(address, "Bob"); manager.increaseCount(address, "Todd"); // then assertThat(manager.shouldTempban(address), equalTo(false)); assertHasCount(manager, address, "Bob", 1); assertHasCount(manager, address, "Todd", 1); manager.increaseCount(address, "Bob"); assertThat(manager.shouldTempban(address), equalTo(true)); assertThat(manager.shouldTempban("10.0.0.1"), equalTo(false)); } @Test public void shouldIncreaseAndResetCount() { // given String address = "192.168.1.2"; Settings settings = mockSettings(3, 60, ""); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); // when manager.increaseCount(address, "test"); manager.increaseCount(address, "test"); manager.increaseCount(address, "test"); // then assertThat(manager.shouldTempban(address), equalTo(true)); assertHasCount(manager, address, "test", 3); // when 2 manager.resetCount(address, "test"); // then 2 assertThat(manager.shouldTempban(address), equalTo(false)); assertHasNoEntries(manager, address); } @Test public void shouldNotIncreaseCountForDisabledTempban() { // given String address = "192.168.1.3"; Settings settings = mockSettings(1, 5, ""); given(settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS)).willReturn(false); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); // when manager.increaseCount(address, "username"); // then assertThat(manager.shouldTempban(address), equalTo(false)); assertHasNoEntries(manager, address); } @Test public void shouldNotCheckCountIfTempbanIsDisabled() { // given String address = "192.168.1.4"; Settings settings = mockSettings(1, 5, ""); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); given(settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS)).willReturn(false); // when manager.increaseCount(address, "username"); // assumptions assertThat(manager.shouldTempban(address), equalTo(true)); assertHasCount(manager, address, "username", 1); // end assumptions manager.reload(settings); boolean result = manager.shouldTempban(address); // then assertThat(result, equalTo(false)); } @Test public void shouldNotIssueBanIfDisabled() { // given Settings settings = mockSettings(0, 0, ""); given(settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS)).willReturn(false); Player player = mock(Player.class); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); // when manager.tempbanPlayer(player); // then verifyZeroInteractions(player, bukkitService); } @Test public void shouldBanPlayerIp() { // given Player player = mock(Player.class); String ip = "123.45.67.89"; TestHelper.mockPlayerIp(player, ip); String banReason = "IP ban too many logins"; given(messages.retrieveSingle(player, MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason); Settings settings = mockSettings(2, 100, ""); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); setBukkitServiceToScheduleSyncDelayedTask(bukkitService); // when manager.tempbanPlayer(player); // then verify(player).kickPlayer(banReason); ArgumentCaptor captor = ArgumentCaptor.forClass(Date.class); verify(bukkitService).banIp(eq(ip), eq(banReason), captor.capture(), eq("AuthMe")); // Compute the expected expiration date and check that the actual date is within the difference tolerance Calendar cal = Calendar.getInstance(); cal.add(Calendar.MINUTE, 100); long expectedExpiration = cal.getTime().getTime(); assertThat(Math.abs(captor.getValue().getTime() - expectedExpiration), lessThan(DATE_TOLERANCE_MILLISECONDS)); } @Test public void shouldBanPlayerIpCustom() { // given Player player = mock(Player.class); given(player.getName()).willReturn("Bob"); String ip = "143.45.77.89"; TestHelper.mockPlayerIp(player, ip); String banCommand = "banip %ip% 15d IP ban too many logins"; Settings settings = mockSettings(2, 100, banCommand); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); setBukkitServiceToScheduleSyncDelayedTask(bukkitService); // when manager.tempbanPlayer(player); // then verify(bukkitService).dispatchConsoleCommand(banCommand.replace("%ip%", ip)); } @Test public void shouldResetCountAfterBan() { // given Player player = mock(Player.class); String ip = "22.44.66.88"; TestHelper.mockPlayerIp(player, ip); String banReason = "kick msg"; given(messages.retrieveSingle(player, MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason); Settings settings = mockSettings(10, 60, ""); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); manager.increaseCount(ip, "user"); manager.increaseCount(ip, "name2"); manager.increaseCount(ip, "user"); setBukkitServiceToScheduleSyncDelayedTask(bukkitService); // when manager.tempbanPlayer(player); // then verify(player).kickPlayer(banReason); assertHasNoEntries(manager, ip); } @Test public void shouldPerformCleanup() { // given Map> counts = new HashMap<>(); TimedCounter counter1 = mockCounter(); given(counter1.isEmpty()).willReturn(true); counts.put("11.11.11.11", counter1); TimedCounter counter2 = mockCounter(); given(counter2.isEmpty()).willReturn(false); counts.put("33.33.33.33", counter2); TempbanManager manager = new TempbanManager(bukkitService, messages, mockSettings(3, 10, "")); ReflectionTestUtils.setField(TempbanManager.class, manager, "ipLoginFailureCounts", counts); // when manager.performCleanup(); // then verify(counter1).removeExpiredEntries(); verify(counter2).removeExpiredEntries(); assertThat(counts.keySet(), contains("33.33.33.33")); } private static Settings mockSettings(int maxTries, int tempbanLength, String customCommand) { Settings settings = mock(Settings.class); given(settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS)).willReturn(true); given(settings.getProperty(SecuritySettings.MAX_LOGIN_TEMPBAN)).willReturn(maxTries); given(settings.getProperty(SecuritySettings.TEMPBAN_LENGTH)).willReturn(tempbanLength); given(settings.getProperty(SecuritySettings.TEMPBAN_MINUTES_BEFORE_RESET)) .willReturn((int) TEST_EXPIRATION_THRESHOLD / 60_000); given(settings.getProperty(SecuritySettings.TEMPBAN_CUSTOM_COMMAND)).willReturn(customCommand); return settings; } private static void assertHasNoEntries(TempbanManager manager, String address) { Map> playerCounts = ReflectionTestUtils .getFieldValue(TempbanManager.class, manager, "ipLoginFailureCounts"); TimedCounter counter = playerCounts.get(address); assertThat(counter == null || counter.isEmpty(), equalTo(true)); } private static void assertHasCount(TempbanManager manager, String address, String name, int count) { Map> playerCounts = ReflectionTestUtils .getFieldValue(TempbanManager.class, manager, "ipLoginFailureCounts"); assertThat(playerCounts.get(address).get(name), equalTo(count)); } @SuppressWarnings("unchecked") private static TimedCounter mockCounter() { return mock(TimedCounter.class); } }