From e8ee4b50ff8618889cf5b18c5472e9454b23bffc Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Fri, 28 Oct 2022 15:16:51 -0400 Subject: [PATCH] Retire the legacy "abusive hosts" system in favor of newer tools --- .../textsecuregcm/WhisperServerService.java | 4 +- .../controllers/AccountController.java | 41 +------ .../textsecuregcm/limits/RateLimiters.java | 9 -- .../storage/AbusiveHostRules.java | 52 --------- .../controllers/AccountControllerTest.java | 103 ++---------------- .../tests/storage/AbusiveHostRulesTest.java | 88 --------------- 6 files changed, 13 insertions(+), 284 deletions(-) delete mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/storage/AbusiveHostRules.java delete mode 100644 service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AbusiveHostRulesTest.java diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 2aabab06..27f5b8d1 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -163,7 +163,6 @@ import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator; import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; -import org.whispersystems.textsecuregcm.storage.AbusiveHostRules; import org.whispersystems.textsecuregcm.storage.AccountCleaner; import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawler; import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerCache; @@ -457,7 +456,6 @@ public class WhisperServerService extends Application dynamicConfigurationManager; @@ -171,7 +167,6 @@ public class AccountController { public AccountController( StoredVerificationCodeManager pendingAccounts, AccountsManager accounts, - AbusiveHostRules abusiveHostRules, RateLimiters rateLimiters, RegistrationServiceClient registrationServiceClient, DynamicConfigurationManager dynamicConfigurationManager, @@ -186,7 +181,6 @@ public class AccountController { ) { this.pendingAccounts = pendingAccounts; this.accounts = accounts; - this.abusiveHostRules = abusiveHostRules; this.rateLimiters = rateLimiters; this.registrationServiceClient = registrationServiceClient; this.dynamicConfigurationManager = dynamicConfigurationManager; @@ -204,7 +198,6 @@ public class AccountController { public AccountController( StoredVerificationCodeManager pendingAccounts, AccountsManager accounts, - AbusiveHostRules abusiveHostRules, RateLimiters rateLimiters, RegistrationServiceClient registrationServiceClient, DynamicConfigurationManager dynamicConfigurationManager, @@ -215,7 +208,7 @@ public class AccountController { ChangeNumberManager changeNumberManager, ExternalServiceCredentialGenerator backupServiceCredentialGenerator ) { - this(pendingAccounts, accounts, abusiveHostRules, rateLimiters, + this(pendingAccounts, accounts, rateLimiters, registrationServiceClient, dynamicConfigurationManager, turnTokenGenerator, testDevices, recaptchaClient, pushNotificationManager, changeNumberManager, backupServiceCredentialGenerator, null, Clock.systemUTC()); @@ -886,26 +879,12 @@ public class AccountController { boolean countryFiltered = captchaConfig.getSignupCountryCodes().contains(countryCode) || captchaConfig.getSignupRegions().contains(region); - if (abusiveHostRules.isBlocked(sourceHost)) { - blockedHostMeter.mark(); - logger.info("Blocked host: {}, {}, {} ({})", transport, number, sourceHost, forwardedFor); - if (countryFiltered) { - // this host was caught in the abusiveHostRules filter, but - // would be caught by country filter as well - countryFilterApplicable.mark(); - } - return true; - } - try { rateLimiters.getSmsVoiceIpLimiter().validate(sourceHost); } catch (RateLimitExceededException e) { logger.info("Rate limit exceeded: {}, {}, {} ({})", transport, number, sourceHost, forwardedFor); rateLimitedHostMeter.mark(); - if (shouldAutoBlock(sourceHost)) { - logger.info("Auto-block: {}", sourceHost); - abusiveHostRules.setBlockedHost(sourceHost); - } + return true; } @@ -914,10 +893,7 @@ public class AccountController { } catch (RateLimitExceededException e) { logger.info("Prefix rate limit exceeded: {}, {}, {} ({})", transport, number, sourceHost, forwardedFor); rateLimitedPrefixMeter.mark(); - if (shouldAutoBlock(sourceHost)) { - logger.info("Auto-block: {}", sourceHost); - abusiveHostRules.setBlockedHost(sourceHost); - } + return true; } @@ -925,6 +901,7 @@ public class AccountController { countryFilteredHostMeter.mark(); return true; } + return false; } @@ -944,16 +921,6 @@ public class AccountController { } } - private boolean shouldAutoBlock(String sourceHost) { - try { - rateLimiters.getAutoBlockLimiter().validate(sourceHost); - } catch (RateLimitExceededException e) { - return true; - } - - return false; - } - private String generatePushChallenge() { SecureRandom random = new SecureRandom(); byte[] challenge = new byte[16]; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java index adc625c7..4886241e 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java @@ -15,7 +15,6 @@ public class RateLimiters { private final RateLimiter voiceDestinationDailyLimiter; private final RateLimiter smsVoiceIpLimiter; private final RateLimiter smsVoicePrefixLimiter; - private final RateLimiter autoBlockLimiter; private final RateLimiter verifyLimiter; private final RateLimiter pinLimiter; @@ -60,10 +59,6 @@ public class RateLimiters { config.getSmsVoicePrefix().getBucketSize(), config.getSmsVoicePrefix().getLeakRatePerMinute()); - this.autoBlockLimiter = new RateLimiter(cacheCluster, "autoBlock", - config.getAutoBlock().getBucketSize(), - config.getAutoBlock().getLeakRatePerMinute()); - this.verifyLimiter = new LockingRateLimiter(cacheCluster, "verify", config.getVerifyNumber().getBucketSize(), config.getVerifyNumber().getLeakRatePerMinute()); @@ -158,10 +153,6 @@ public class RateLimiters { return smsVoicePrefixLimiter; } - public RateLimiter getAutoBlockLimiter() { - return autoBlockLimiter; - } - public RateLimiter getVoiceDestinationLimiter() { return voiceDestinationLimiter; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AbusiveHostRules.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AbusiveHostRules.java deleted file mode 100644 index 45b2c77e..00000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AbusiveHostRules.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.textsecuregcm.storage; - -import static com.codahale.metrics.MetricRegistry.name; - -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.SharedMetricRegistries; -import com.codahale.metrics.Timer; -import java.time.Duration; -import com.google.common.annotations.VisibleForTesting; -import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; -import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; -import org.whispersystems.textsecuregcm.util.Constants; - -public class AbusiveHostRules { - - private static final String KEY_PREFIX = "abusive_hosts::"; - private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME); - private final Timer getTimer = metricRegistry.timer(name(AbusiveHostRules.class, "get")); - private final Timer insertTimer = metricRegistry.timer(name(AbusiveHostRules.class, "setBlockedHost")); - - private final FaultTolerantRedisCluster redisCluster; - private final DynamicConfigurationManager configurationManager; - - public AbusiveHostRules(FaultTolerantRedisCluster redisCluster, final DynamicConfigurationManager configurationManager) { - this.redisCluster = redisCluster; - this.configurationManager = configurationManager; - } - - public boolean isBlocked(String host) { - try (Timer.Context timer = getTimer.time()) { - return this.redisCluster.withCluster(connection -> connection.sync().exists(prefix(host))) > 0; - } - } - - public void setBlockedHost(String host) { - Duration expireTime = configurationManager.getConfiguration().getAbusiveHostRules().getExpirationTime(); - try (Timer.Context timer = insertTimer.time()) { - this.redisCluster.useCluster(connection -> connection.sync().setex(prefix(host), expireTime.toSeconds(), "1")); - } - } - - @VisibleForTesting - public static String prefix(String keyName) { - return KEY_PREFIX + keyName; - } - -} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerTest.java index 803a12dc..93aeebd9 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerTest.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import com.fasterxml.jackson.core.JsonProcessingException; @@ -100,7 +99,6 @@ import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient; import org.whispersystems.textsecuregcm.registration.ClientType; import org.whispersystems.textsecuregcm.registration.MessageTransport; import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient; -import org.whispersystems.textsecuregcm.storage.AbusiveHostRules; import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.ChangeNumberManager; @@ -134,7 +132,6 @@ class AccountControllerTest { private static final UUID SENDER_TRANSFER_UUID = UUID.randomUUID(); private static final UUID RESERVATION_TOKEN = UUID.randomUUID(); - private static final String ABUSIVE_HOST = "192.168.1.1"; private static final String NICE_HOST = "127.0.0.1"; private static final String RATE_LIMITED_IP_HOST = "10.0.0.1"; private static final String RATE_LIMITED_PREFIX_HOST = "10.0.0.2"; @@ -147,7 +144,6 @@ class AccountControllerTest { private static StoredVerificationCodeManager pendingAccountsManager = mock(StoredVerificationCodeManager.class); private static AccountsManager accountsManager = mock(AccountsManager.class); - private static AbusiveHostRules abusiveHostRules = mock(AbusiveHostRules.class); private static RateLimiters rateLimiters = mock(RateLimiters.class); private static RateLimiter rateLimiter = mock(RateLimiter.class); private static RateLimiter pinLimiter = mock(RateLimiter.class); @@ -187,7 +183,6 @@ class AccountControllerTest { .setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .addResource(new AccountController(pendingAccountsManager, accountsManager, - abusiveHostRules, rateLimiters, registrationServiceClient, dynamicConfigurationManager, @@ -218,7 +213,6 @@ class AccountControllerTest { when(rateLimiters.getPinLimiter()).thenReturn(pinLimiter); when(rateLimiters.getSmsVoiceIpLimiter()).thenReturn(smsVoiceIpLimiter); when(rateLimiters.getSmsVoicePrefixLimiter()).thenReturn(smsVoicePrefixLimiter); - when(rateLimiters.getAutoBlockLimiter()).thenReturn(autoBlockLimiter); when(rateLimiters.getUsernameSetLimiter()).thenReturn(usernameSetLimiter); when(rateLimiters.getUsernameReserveLimiter()).thenReturn(usernameReserveLimiter); when(rateLimiters.getUsernameLookupLimiter()).thenReturn(usernameLookupLimiter); @@ -303,9 +297,6 @@ class AccountControllerTest { when(dynamicConfiguration.getCaptchaConfiguration()).thenReturn(signupCaptchaConfig); } - when(abusiveHostRules.isBlocked(eq(ABUSIVE_HOST))).thenReturn(true); - when(abusiveHostRules.isBlocked(eq(NICE_HOST))).thenReturn(false); - when(recaptchaClient.verify(eq(INVALID_CAPTCHA_TOKEN), anyString())) .thenReturn(RecaptchaClient.AssessmentResult.invalid()); when(recaptchaClient.verify(eq(VALID_CAPTCHA_TOKEN), anyString())) @@ -313,9 +304,6 @@ class AccountControllerTest { doThrow(new RateLimitExceededException(Duration.ZERO)).when(pinLimiter).validate(eq(SENDER_OVER_PIN)); - doThrow(new RateLimitExceededException(Duration.ZERO)).when(autoBlockLimiter).validate(eq(RATE_LIMITED_PREFIX_HOST)); - doThrow(new RateLimitExceededException(Duration.ZERO)).when(autoBlockLimiter).validate(eq(RATE_LIMITED_IP_HOST)); - doThrow(new RateLimitExceededException(Duration.ZERO)).when(smsVoicePrefixLimiter).validate(SENDER_OVER_PREFIX.substring(0, 4+2)); doThrow(new RateLimitExceededException(Duration.ZERO)).when(smsVoiceIpLimiter).validate(RATE_LIMITED_IP_HOST); doThrow(new RateLimitExceededException(Duration.ZERO)).when(smsVoiceIpLimiter).validate(RATE_LIMITED_HOST2); @@ -326,13 +314,11 @@ class AccountControllerTest { reset( pendingAccountsManager, accountsManager, - abusiveHostRules, rateLimiters, rateLimiter, pinLimiter, smsVoiceIpLimiter, smsVoicePrefixLimiter, - autoBlockLimiter, usernameSetLimiter, usernameReserveLimiter, usernameLookupLimiter, @@ -489,7 +475,6 @@ class AccountControllerTest { final Phonenumber.PhoneNumber expectedPhoneNumber = PhoneNumberUtil.getInstance().parse(SENDER, null); verify(registrationServiceClient).sendRegistrationCode(expectedPhoneNumber, MessageTransport.SMS, ClientType.UNKNOWN, null, AccountController.REGISTRATION_RPC_TIMEOUT); - verify(abusiveHostRules).isBlocked(eq(NICE_HOST)); verify(pendingAccountsManager).store(eq(SENDER), argThat( storedVerificationCode -> Arrays.equals(storedVerificationCode.sessionId(), sessionId) && "1234-push".equals(storedVerificationCode.pushCode()))); @@ -550,7 +535,6 @@ class AccountControllerTest { assertThat(response.getStatus()).isEqualTo(200); verify(registrationServiceClient).sendRegistrationCode(phoneNumber, MessageTransport.VOICE, ClientType.UNKNOWN, null, AccountController.REGISTRATION_RPC_TIMEOUT); - verify(abusiveHostRules).isBlocked(eq(NICE_HOST)); } @Test @@ -572,7 +556,6 @@ class AccountControllerTest { final Phonenumber.PhoneNumber phoneNumber = PhoneNumberUtil.getInstance().parse(SENDER_PREAUTH, null); verify(registrationServiceClient).sendRegistrationCode(phoneNumber, MessageTransport.SMS, ClientType.UNKNOWN, null, AccountController.REGISTRATION_RPC_TIMEOUT); - verify(abusiveHostRules).isBlocked(eq(NICE_HOST)); } @Test @@ -588,7 +571,6 @@ class AccountControllerTest { assertThat(response.getStatus()).isEqualTo(403); verifyNoInteractions(registrationServiceClient); - verifyNoInteractions(abusiveHostRules); } @Test @@ -649,24 +631,7 @@ class AccountControllerTest { } @Test - void testSendAbusiveHost() { - - Response response = - resources.getJerseyTest() - .target(String.format("/v1/accounts/sms/code/%s", SENDER)) - .queryParam("challenge", "1234-push") - .request() - .header(HttpHeaders.X_FORWARDED_FOR, ABUSIVE_HOST) - .get(); - - assertThat(response.getStatus()).isEqualTo(402); - - verify(abusiveHostRules).isBlocked(eq(ABUSIVE_HOST)); - verifyNoInteractions(registrationServiceClient); - } - - @Test - void testSendAbusiveHostWithValidCaptcha() throws NumberParseException { + void testSendWithValidCaptcha() throws NumberParseException { when(registrationServiceClient.sendRegistrationCode(any(), any(), any(), any(), any())) .thenReturn(CompletableFuture.completedFuture(new byte[16])); @@ -676,38 +641,36 @@ class AccountControllerTest { .target(String.format("/v1/accounts/sms/code/%s", SENDER)) .queryParam("captcha", VALID_CAPTCHA_TOKEN) .request() - .header(HttpHeaders.X_FORWARDED_FOR, ABUSIVE_HOST) + .header("X-Forwarded-For", NICE_HOST) .get(); assertThat(response.getStatus()).isEqualTo(200); final Phonenumber.PhoneNumber phoneNumber = PhoneNumberUtil.getInstance().parse(SENDER, null); - verifyNoInteractions(abusiveHostRules); - verify(recaptchaClient).verify(eq(VALID_CAPTCHA_TOKEN), eq(ABUSIVE_HOST)); + verify(recaptchaClient).verify(eq(VALID_CAPTCHA_TOKEN), eq(NICE_HOST)); verify(registrationServiceClient).sendRegistrationCode(phoneNumber, MessageTransport.SMS, ClientType.UNKNOWN, null, AccountController.REGISTRATION_RPC_TIMEOUT); } @Test - void testSendAbusiveHostWithInvalidCaptcha() { + void testSendWithInvalidCaptcha() { Response response = resources.getJerseyTest() .target(String.format("/v1/accounts/sms/code/%s", SENDER)) .queryParam("captcha", INVALID_CAPTCHA_TOKEN) .request() - .header(HttpHeaders.X_FORWARDED_FOR, ABUSIVE_HOST) + .header("X-Forwarded-For", NICE_HOST) .get(); assertThat(response.getStatus()).isEqualTo(402); - verifyNoInteractions(abusiveHostRules); - verify(recaptchaClient).verify(eq(INVALID_CAPTCHA_TOKEN), eq(ABUSIVE_HOST)); + verify(recaptchaClient).verify(eq(INVALID_CAPTCHA_TOKEN), eq(NICE_HOST)); verifyNoInteractions(registrationServiceClient); } @Test - void testSendRateLimitedHostAutoBlock() { + void testSendRateLimitedHost() { Response response = resources.getJerseyTest() .target(String.format("/v1/accounts/sms/code/%s", SENDER)) @@ -718,10 +681,6 @@ class AccountControllerTest { assertThat(response.getStatus()).isEqualTo(402); - verify(abusiveHostRules).isBlocked(eq(RATE_LIMITED_IP_HOST)); - verify(abusiveHostRules).setBlockedHost(eq(RATE_LIMITED_IP_HOST)); - verifyNoMoreInteractions(abusiveHostRules); - verifyNoInteractions(recaptchaClient); verifyNoInteractions(registrationServiceClient); } @@ -739,55 +698,10 @@ class AccountControllerTest { assertThat(response.getStatus()).isEqualTo(402); - verify(abusiveHostRules).isBlocked(eq(RATE_LIMITED_PREFIX_HOST)); - verify(abusiveHostRules).setBlockedHost(eq(RATE_LIMITED_PREFIX_HOST)); - verifyNoMoreInteractions(abusiveHostRules); - verifyNoInteractions(recaptchaClient); verifyNoInteractions(registrationServiceClient); } - @Test - void testSendRateLimitedHostNoAutoBlock() { - - Response response = - resources.getJerseyTest() - .target(String.format("/v1/accounts/sms/code/%s", SENDER)) - .queryParam("challenge", "1234-push") - .request() - .header(HttpHeaders.X_FORWARDED_FOR, RATE_LIMITED_HOST2) - .get(); - - assertThat(response.getStatus()).isEqualTo(402); - - verify(abusiveHostRules).isBlocked(eq(RATE_LIMITED_HOST2)); - verifyNoMoreInteractions(abusiveHostRules); - - verifyNoInteractions(recaptchaClient); - verifyNoInteractions(registrationServiceClient); - } - - - @Test - void testSendMultipleHost() { - - Response response = - resources.getJerseyTest() - .target(String.format("/v1/accounts/sms/code/%s", SENDER)) - .queryParam("challenge", "1234-push") - .request() - .header(HttpHeaders.X_FORWARDED_FOR, NICE_HOST + ", " + ABUSIVE_HOST) - .get(); - - assertThat(response.getStatus()).isEqualTo(402); - - verify(abusiveHostRules, times(1)).isBlocked(eq(ABUSIVE_HOST)); - - verifyNoMoreInteractions(abusiveHostRules); - verifyNoInteractions(registrationServiceClient); - } - - @Test void testSendRestrictedHostOut() { @@ -805,7 +719,6 @@ class AccountControllerTest { assertThat(response.getStatus()).isEqualTo(402); - verify(abusiveHostRules).isBlocked(eq(NICE_HOST)); verifyNoInteractions(registrationServiceClient); } @@ -884,7 +797,7 @@ class AccountControllerTest { resources.getJerseyTest() .target(String.format("/v1/accounts/sms/code/%s", TEST_NUMBER)) .request() - .header(HttpHeaders.X_FORWARDED_FOR, ABUSIVE_HOST) + .header("X-Forwarded-For", RATE_LIMITED_IP_HOST) .get(); final ArgumentCaptor captor = ArgumentCaptor.forClass(StoredVerificationCode.class); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AbusiveHostRulesTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AbusiveHostRulesTest.java deleted file mode 100644 index 0c4db676..00000000 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AbusiveHostRulesTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.textsecuregcm.tests.storage; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.fasterxml.jackson.core.JsonProcessingException; -import java.time.Duration; -import java.time.Instant; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; -import org.whispersystems.textsecuregcm.redis.RedisClusterExtension; -import org.whispersystems.textsecuregcm.storage.AbusiveHostRules; -import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; - -class AbusiveHostRulesTest { - - @RegisterExtension - private static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build(); - private AbusiveHostRules abusiveHostRules; - private DynamicConfigurationManager mockDynamicConfigManager; - - @BeforeEach - void setup() throws JsonProcessingException { - @SuppressWarnings("unchecked") - DynamicConfigurationManager m = mock(DynamicConfigurationManager.class); - this.mockDynamicConfigManager = m; - when(mockDynamicConfigManager.getConfiguration()).thenReturn(generateConfig(Duration.ofHours(1))); - this.abusiveHostRules = new AbusiveHostRules(REDIS_CLUSTER_EXTENSION.getRedisCluster(), mockDynamicConfigManager); - } - - DynamicConfiguration generateConfig(Duration expireDuration) throws JsonProcessingException { - final String configString = String.format(""" - captcha: - scoreFloor: 1.0 - abusiveHostRules: - expirationTime: %s - """, expireDuration); - return DynamicConfigurationManager - .parseConfiguration(configString, DynamicConfiguration.class) - .orElseThrow(); - } - - @Test - void testBlockedHost() { - REDIS_CLUSTER_EXTENSION.getRedisCluster().useCluster(connection -> - connection.sync().set(AbusiveHostRules.prefix("192.168.1.1"), "1")); - assertThat(abusiveHostRules.isBlocked("192.168.1.1")).isTrue(); - } - - @Test - void testUnblocked() { - REDIS_CLUSTER_EXTENSION.getRedisCluster().useCluster(connection -> - connection.sync().set(AbusiveHostRules.prefix("192.168.1.1"), "1")); - assertThat(abusiveHostRules.isBlocked("172.17.1.1")).isFalse(); - } - - @Test - void testInsertBlocked() { - abusiveHostRules.setBlockedHost("172.17.0.1"); - assertThat(abusiveHostRules.isBlocked("172.17.0.1")).isTrue(); - abusiveHostRules.setBlockedHost("172.17.0.1"); - assertThat(abusiveHostRules.isBlocked("172.17.0.1")).isTrue(); - } - - @Test - void testExpiration() throws Exception { - when(mockDynamicConfigManager.getConfiguration()).thenReturn(generateConfig(Duration.ofSeconds(1))); - abusiveHostRules.setBlockedHost("192.168.1.1"); - assertTimeoutPreemptively(Duration.ofSeconds(5), () -> { - while (true) { - if (!abusiveHostRules.isBlocked("192.168.1.1")) { - break; - } - } - }); - } - -}