mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-22 03:58:03 +01:00
Add client challenges for prekey and message rate limiters
This commit is contained in:
@@ -17,43 +17,45 @@ import org.whispersystems.textsecuregcm.redis.AbstractRedisClusterTest;
|
||||
|
||||
public class CardinalityRateLimiterTest extends AbstractRedisClusterTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidate() {
|
||||
final int maxCardinality = 10;
|
||||
final CardinalityRateLimiter rateLimiter = new CardinalityRateLimiter(getRedisCluster(), "test", Duration.ofDays(1), Duration.ofDays(1), maxCardinality);
|
||||
@Test
|
||||
public void testValidate() {
|
||||
final int maxCardinality = 10;
|
||||
final CardinalityRateLimiter rateLimiter =
|
||||
new CardinalityRateLimiter(getRedisCluster(), "test", Duration.ofDays(1), maxCardinality);
|
||||
|
||||
final String source = "+18005551234";
|
||||
int validatedAttempts = 0;
|
||||
int blockedAttempts = 0;
|
||||
|
||||
for (int i = 0; i < maxCardinality * 2; i++) {
|
||||
try {
|
||||
rateLimiter.validate(source, String.valueOf(i));
|
||||
validatedAttempts++;
|
||||
} catch (final RateLimitExceededException e) {
|
||||
blockedAttempts++;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(validatedAttempts >= maxCardinality);
|
||||
assertTrue(blockedAttempts > 0);
|
||||
|
||||
final String secondSource = "+18005554321";
|
||||
final String source = "+18005551234";
|
||||
int validatedAttempts = 0;
|
||||
int blockedAttempts = 0;
|
||||
|
||||
for (int i = 0; i < maxCardinality * 2; i++) {
|
||||
try {
|
||||
rateLimiter.validate(secondSource, "test");
|
||||
rateLimiter.validate(source, String.valueOf(i), rateLimiter.getDefaultMaxCardinality());
|
||||
validatedAttempts++;
|
||||
} catch (final RateLimitExceededException e) {
|
||||
fail("New source should not trigger a rate limit exception on first attempted validation");
|
||||
blockedAttempts++;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(validatedAttempts >= maxCardinality);
|
||||
assertTrue(blockedAttempts > 0);
|
||||
|
||||
final String secondSource = "+18005554321";
|
||||
|
||||
try {
|
||||
rateLimiter.validate(secondSource, "test", rateLimiter.getDefaultMaxCardinality());
|
||||
} catch (final RateLimitExceededException e) {
|
||||
fail("New source should not trigger a rate limit exception on first attempted validation");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.whispersystems.textsecuregcm.limits;
|
||||
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitChallengeConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
|
||||
class PreKeyRateLimiterTest {
|
||||
|
||||
private Account account;
|
||||
|
||||
private PreKeyRateLimiter preKeyRateLimiter;
|
||||
|
||||
private DynamicRateLimitChallengeConfiguration rateLimitChallengeConfiguration;
|
||||
private RateLimiter dailyPreKeyLimiter;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
final RateLimiters rateLimiters = mock(RateLimiters.class);
|
||||
|
||||
dailyPreKeyLimiter = mock(RateLimiter.class);
|
||||
when(rateLimiters.getDailyPreKeysLimiter()).thenReturn(dailyPreKeyLimiter);
|
||||
|
||||
final DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||
rateLimitChallengeConfiguration = mock(DynamicRateLimitChallengeConfiguration.class);
|
||||
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
||||
|
||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
||||
when(dynamicConfiguration.getRateLimitChallengeConfiguration()).thenReturn(rateLimitChallengeConfiguration);
|
||||
|
||||
preKeyRateLimiter = new PreKeyRateLimiter(rateLimiters, dynamicConfigurationManager, mock(RateLimitResetMetricsManager.class));
|
||||
|
||||
account = mock(Account.class);
|
||||
when(account.getNumber()).thenReturn("+18005551111");
|
||||
when(account.getUuid()).thenReturn(UUID.randomUUID());
|
||||
}
|
||||
|
||||
@Test
|
||||
void enforcementConfiguration() throws RateLimitExceededException {
|
||||
|
||||
doThrow(RateLimitExceededException.class)
|
||||
.when(dailyPreKeyLimiter).validate(any());
|
||||
|
||||
when(rateLimitChallengeConfiguration.isPreKeyLimitEnforced()).thenReturn(false);
|
||||
|
||||
preKeyRateLimiter.validate(account);
|
||||
|
||||
when(rateLimitChallengeConfiguration.isPreKeyLimitEnforced()).thenReturn(true);
|
||||
|
||||
assertThrows(RateLimitExceededException.class, () -> preKeyRateLimiter.validate(account));
|
||||
|
||||
when(rateLimitChallengeConfiguration.isPreKeyLimitEnforced()).thenReturn(false);
|
||||
|
||||
preKeyRateLimiter.validate(account);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package org.whispersystems.textsecuregcm.limits;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.vdurmont.semver4j.Semver;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitChallengeConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
import org.whispersystems.textsecuregcm.util.ua.ClientPlatform;
|
||||
|
||||
class RateLimitChallengeManagerTest {
|
||||
|
||||
private PushChallengeManager pushChallengeManager;
|
||||
private RecaptchaClient recaptchaClient;
|
||||
private PreKeyRateLimiter preKeyRateLimiter;
|
||||
private UnsealedSenderRateLimiter unsealedSenderRateLimiter;
|
||||
private DynamicRateLimitChallengeConfiguration rateLimitChallengeConfiguration;
|
||||
private RateLimiters rateLimiters;
|
||||
|
||||
private RateLimitChallengeManager rateLimitChallengeManager;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
pushChallengeManager = mock(PushChallengeManager.class);
|
||||
recaptchaClient = mock(RecaptchaClient.class);
|
||||
preKeyRateLimiter = mock(PreKeyRateLimiter.class);
|
||||
unsealedSenderRateLimiter = mock(UnsealedSenderRateLimiter.class);
|
||||
rateLimiters = mock(RateLimiters.class);
|
||||
|
||||
final DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
||||
rateLimitChallengeConfiguration = mock(DynamicRateLimitChallengeConfiguration.class);
|
||||
|
||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
||||
when(dynamicConfiguration.getRateLimitChallengeConfiguration()).thenReturn(rateLimitChallengeConfiguration);
|
||||
|
||||
rateLimitChallengeManager = new RateLimitChallengeManager(
|
||||
pushChallengeManager,
|
||||
recaptchaClient,
|
||||
preKeyRateLimiter,
|
||||
unsealedSenderRateLimiter,
|
||||
rateLimiters,
|
||||
dynamicConfigurationManager);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void answerPushChallenge(final boolean successfulChallenge) throws RateLimitExceededException {
|
||||
final Account account = mock(Account.class);
|
||||
when(pushChallengeManager.answerChallenge(eq(account), any())).thenReturn(successfulChallenge);
|
||||
|
||||
when(rateLimiters.getPushChallengeAttemptLimiter()).thenReturn(mock(RateLimiter.class));
|
||||
when(rateLimiters.getPushChallengeSuccessLimiter()).thenReturn(mock(RateLimiter.class));
|
||||
when(rateLimiters.getRateLimitResetLimiter()).thenReturn(mock(RateLimiter.class));
|
||||
|
||||
rateLimitChallengeManager.answerPushChallenge(account, "challenge");
|
||||
|
||||
if (successfulChallenge) {
|
||||
verify(preKeyRateLimiter).handleRateLimitReset(account);
|
||||
verify(unsealedSenderRateLimiter).handleRateLimitReset(account);
|
||||
} else {
|
||||
verifyZeroInteractions(preKeyRateLimiter);
|
||||
verifyZeroInteractions(unsealedSenderRateLimiter);
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void answerRecaptchaChallenge(final boolean successfulChallenge) throws RateLimitExceededException {
|
||||
final Account account = mock(Account.class);
|
||||
when(recaptchaClient.verify(any(), any())).thenReturn(successfulChallenge);
|
||||
|
||||
when(rateLimiters.getRecaptchaChallengeAttemptLimiter()).thenReturn(mock(RateLimiter.class));
|
||||
when(rateLimiters.getRecaptchaChallengeSuccessLimiter()).thenReturn(mock(RateLimiter.class));
|
||||
when(rateLimiters.getRateLimitResetLimiter()).thenReturn(mock(RateLimiter.class));
|
||||
|
||||
rateLimitChallengeManager.answerRecaptchaChallenge(account, "captcha", "10.0.0.1");
|
||||
|
||||
if (successfulChallenge) {
|
||||
verify(preKeyRateLimiter).handleRateLimitReset(account);
|
||||
verify(unsealedSenderRateLimiter).handleRateLimitReset(account);
|
||||
} else {
|
||||
verifyZeroInteractions(preKeyRateLimiter);
|
||||
verifyZeroInteractions(unsealedSenderRateLimiter);
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void shouldIssueRateLimitChallenge(final String userAgent, final boolean expectIssueChallenge) {
|
||||
when(rateLimitChallengeConfiguration.getMinimumSupportedVersion(any())).thenReturn(Optional.empty());
|
||||
when(rateLimitChallengeConfiguration.getMinimumSupportedVersion(ClientPlatform.ANDROID))
|
||||
.thenReturn(Optional.of(new Semver("5.6.0")));
|
||||
when(rateLimitChallengeConfiguration.getMinimumSupportedVersion(ClientPlatform.DESKTOP))
|
||||
.thenReturn(Optional.of(new Semver("5.0.0-beta.2")));
|
||||
|
||||
assertEquals(expectIssueChallenge, rateLimitChallengeManager.shouldIssueRateLimitChallenge(userAgent));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> shouldIssueRateLimitChallenge() {
|
||||
return Stream.of(
|
||||
Arguments.of("Signal-Android/5.1.2 Android/30", false),
|
||||
Arguments.of("Signal-Android/5.6.0 Android/30", true),
|
||||
Arguments.of("Signal-Android/5.11.1 Android/30", true),
|
||||
Arguments.of("Signal-Desktop/5.0.0-beta.3 macOS/11", true),
|
||||
Arguments.of("Signal-Desktop/5.0.0-beta.1 Windows/3.1", false),
|
||||
Arguments.of("Signal-Desktop/5.2.0 Debian/11", true),
|
||||
Arguments.of("Signal-iOS/5.1.2 iOS/12.2", false),
|
||||
Arguments.of("anything-else", false)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void getChallengeOptions(final boolean captchaAttemptPermitted,
|
||||
final boolean captchaSuccessPermitted,
|
||||
final boolean pushAttemptPermitted,
|
||||
final boolean pushSuccessPermitted,
|
||||
final boolean expectCaptcha,
|
||||
final boolean expectPushChallenge) {
|
||||
|
||||
final RateLimiter recaptchaChallengeAttemptLimiter = mock(RateLimiter.class);
|
||||
final RateLimiter recaptchaChallengeSuccessLimiter = mock(RateLimiter.class);
|
||||
final RateLimiter pushChallengeAttemptLimiter = mock(RateLimiter.class);
|
||||
final RateLimiter pushChallengeSuccessLimiter = mock(RateLimiter.class);
|
||||
|
||||
when(rateLimiters.getRecaptchaChallengeAttemptLimiter()).thenReturn(recaptchaChallengeAttemptLimiter);
|
||||
when(rateLimiters.getRecaptchaChallengeSuccessLimiter()).thenReturn(recaptchaChallengeSuccessLimiter);
|
||||
when(rateLimiters.getPushChallengeAttemptLimiter()).thenReturn(pushChallengeAttemptLimiter);
|
||||
when(rateLimiters.getPushChallengeSuccessLimiter()).thenReturn(pushChallengeSuccessLimiter);
|
||||
|
||||
when(recaptchaChallengeAttemptLimiter.hasAvailablePermits(any(), anyInt())).thenReturn(captchaAttemptPermitted);
|
||||
when(recaptchaChallengeSuccessLimiter.hasAvailablePermits(any(), anyInt())).thenReturn(captchaSuccessPermitted);
|
||||
when(pushChallengeAttemptLimiter.hasAvailablePermits(any(), anyInt())).thenReturn(pushAttemptPermitted);
|
||||
when(pushChallengeSuccessLimiter.hasAvailablePermits(any(), anyInt())).thenReturn(pushSuccessPermitted);
|
||||
|
||||
final int expectedLength = (expectCaptcha ? 1 : 0) + (expectPushChallenge ? 1 : 0);
|
||||
|
||||
final List<String> options = rateLimitChallengeManager.getChallengeOptions(mock(Account.class));
|
||||
assertEquals(expectedLength, options.size());
|
||||
|
||||
if (expectCaptcha) {
|
||||
assertTrue(options.contains(RateLimitChallengeManager.OPTION_RECAPTCHA));
|
||||
}
|
||||
|
||||
if (expectPushChallenge) {
|
||||
assertTrue(options.contains(RateLimitChallengeManager.OPTION_PUSH_CHALLENGE));
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<Arguments> getChallengeOptions() {
|
||||
return Stream.of(
|
||||
Arguments.of(false, false, false, false, false, false),
|
||||
Arguments.of(false, false, false, true, false, false),
|
||||
Arguments.of(false, false, true, false, false, false),
|
||||
Arguments.of(false, false, true, true, false, true),
|
||||
Arguments.of(false, true, false, false, false, false),
|
||||
Arguments.of(false, true, false, true, false, false),
|
||||
Arguments.of(false, true, true, false, false, false),
|
||||
Arguments.of(false, true, true, true, false, true),
|
||||
Arguments.of(true, false, false, false, false, false),
|
||||
Arguments.of(true, false, false, true, false, false),
|
||||
Arguments.of(true, false, true, false, false, false),
|
||||
Arguments.of(true, false, true, true, false, true),
|
||||
Arguments.of(true, true, false, false, true, false),
|
||||
Arguments.of(true, true, false, true, true, false),
|
||||
Arguments.of(true, true, true, false, true, false),
|
||||
Arguments.of(true, true, true, true, true, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.whispersystems.textsecuregcm.limits;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import io.dropwizard.util.Duration;
|
||||
import io.micrometer.core.instrument.Counter;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import java.util.UUID;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.textsecuregcm.redis.AbstractRedisClusterTest;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
|
||||
public class RateLimitResetMetricsManagerTest extends AbstractRedisClusterTest {
|
||||
|
||||
private RateLimitResetMetricsManager metricsManager;
|
||||
private SimpleMeterRegistry meterRegistry;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
meterRegistry = new SimpleMeterRegistry();
|
||||
metricsManager = new RateLimitResetMetricsManager(getRedisCluster(), meterRegistry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecordMetrics() {
|
||||
|
||||
final Account firstAccount = mock(Account.class);
|
||||
when(firstAccount.getUuid()).thenReturn(UUID.randomUUID());
|
||||
final Account secondAccount = mock(Account.class);
|
||||
when(secondAccount.getUuid()).thenReturn(UUID.randomUUID());
|
||||
|
||||
metricsManager.recordMetrics(firstAccount, true, "counter", "enforced", "total", Duration.hours(1).toSeconds());
|
||||
metricsManager.recordMetrics(firstAccount, true, "counter", "enforced", "total", Duration.hours(1).toSeconds());
|
||||
metricsManager.recordMetrics(secondAccount, false, "counter", "unenforced", "total", Duration.hours(1).toSeconds());
|
||||
|
||||
final double counterTotal = meterRegistry.get("counter").counters().stream()
|
||||
.map(Counter::count)
|
||||
.reduce(Double::sum)
|
||||
.orElseThrow();
|
||||
assertEquals(3, counterTotal, 0.0);
|
||||
|
||||
final long enforcedCount = getRedisCluster().withCluster(conn -> conn.sync().pfcount("enforced"));
|
||||
assertEquals(1L, enforcedCount);
|
||||
|
||||
final long unenforcedCount = getRedisCluster().withCluster(conn -> conn.sync().pfcount("unenforced"));
|
||||
assertEquals(1L, unenforcedCount);
|
||||
|
||||
final long total = getRedisCluster().withCluster(conn -> conn.sync().pfcount("total"));
|
||||
assertEquals(2L, total);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.limits;
|
||||
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicMessageRateConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitChallengeConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitsConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||
import org.whispersystems.textsecuregcm.redis.AbstractRedisClusterTest;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
|
||||
public class UnsealedSenderRateLimiterTest extends AbstractRedisClusterTest {
|
||||
|
||||
private Account sender;
|
||||
private Account firstDestination;
|
||||
private Account secondDestination;
|
||||
|
||||
private UnsealedSenderRateLimiter unsealedSenderRateLimiter;
|
||||
|
||||
private DynamicRateLimitChallengeConfiguration rateLimitChallengeConfiguration;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
final RateLimiters rateLimiters = mock(RateLimiters.class);
|
||||
final CardinalityRateLimiter cardinalityRateLimiter =
|
||||
new CardinalityRateLimiter(getRedisCluster(), "test", Duration.ofDays(1), 1);
|
||||
|
||||
when(rateLimiters.getUnsealedSenderCardinalityLimiter()).thenReturn(cardinalityRateLimiter);
|
||||
when(rateLimiters.getRateLimitResetLimiter()).thenReturn(mock(RateLimiter.class));
|
||||
|
||||
final DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||
final DynamicRateLimitsConfiguration rateLimitsConfiguration = mock(DynamicRateLimitsConfiguration.class);
|
||||
rateLimitChallengeConfiguration = mock(DynamicRateLimitChallengeConfiguration.class);
|
||||
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
||||
|
||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
||||
when(dynamicConfiguration.getLimits()).thenReturn(rateLimitsConfiguration);
|
||||
when(rateLimitsConfiguration.getUnsealedSenderDefaultCardinalityLimit()).thenReturn(1);
|
||||
when(rateLimitsConfiguration.getUnsealedSenderPermitIncrement()).thenReturn(1);
|
||||
when(dynamicConfiguration.getRateLimitChallengeConfiguration()).thenReturn(rateLimitChallengeConfiguration);
|
||||
when(rateLimitChallengeConfiguration.isUnsealedSenderLimitEnforced()).thenReturn(true);
|
||||
|
||||
unsealedSenderRateLimiter = new UnsealedSenderRateLimiter(rateLimiters, getRedisCluster(), dynamicConfigurationManager,
|
||||
mock(RateLimitResetMetricsManager.class));
|
||||
|
||||
sender = mock(Account.class);
|
||||
when(sender.getNumber()).thenReturn("+18005551111");
|
||||
when(sender.getUuid()).thenReturn(UUID.randomUUID());
|
||||
|
||||
firstDestination = mock(Account.class);
|
||||
when(firstDestination.getNumber()).thenReturn("+18005552222");
|
||||
when(firstDestination.getUuid()).thenReturn(UUID.randomUUID());
|
||||
|
||||
secondDestination = mock(Account.class);
|
||||
when(secondDestination.getNumber()).thenReturn("+18005553333");
|
||||
when(secondDestination.getUuid()).thenReturn(UUID.randomUUID());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validate() throws RateLimitExceededException {
|
||||
unsealedSenderRateLimiter.validate(sender, firstDestination);
|
||||
|
||||
assertThrows(RateLimitExceededException.class, () -> unsealedSenderRateLimiter.validate(sender, secondDestination));
|
||||
|
||||
unsealedSenderRateLimiter.validate(sender, firstDestination);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleRateLimitReset() throws RateLimitExceededException {
|
||||
unsealedSenderRateLimiter.validate(sender, firstDestination);
|
||||
|
||||
assertThrows(RateLimitExceededException.class, () -> unsealedSenderRateLimiter.validate(sender, secondDestination));
|
||||
|
||||
unsealedSenderRateLimiter.handleRateLimitReset(sender);
|
||||
unsealedSenderRateLimiter.validate(sender, firstDestination);
|
||||
unsealedSenderRateLimiter.validate(sender, secondDestination);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enforcementConfiguration() throws RateLimitExceededException {
|
||||
|
||||
when(rateLimitChallengeConfiguration.isUnsealedSenderLimitEnforced()).thenReturn(false);
|
||||
|
||||
unsealedSenderRateLimiter.validate(sender, firstDestination);
|
||||
unsealedSenderRateLimiter.validate(sender, secondDestination);
|
||||
|
||||
when(rateLimitChallengeConfiguration.isUnsealedSenderLimitEnforced()).thenReturn(true);
|
||||
|
||||
final Account thirdDestination = mock(Account.class);
|
||||
when(thirdDestination.getNumber()).thenReturn("+18005554444");
|
||||
when(thirdDestination.getUuid()).thenReturn(UUID.randomUUID());
|
||||
|
||||
assertThrows(RateLimitExceededException.class, () -> unsealedSenderRateLimiter.validate(sender, thirdDestination));
|
||||
|
||||
when(rateLimitChallengeConfiguration.isUnsealedSenderLimitEnforced()).thenReturn(false);
|
||||
|
||||
final Account fourthDestination = mock(Account.class);
|
||||
when(fourthDestination.getNumber()).thenReturn("+18005555555");
|
||||
when(fourthDestination.getUuid()).thenReturn(UUID.randomUUID());
|
||||
unsealedSenderRateLimiter.validate(sender, fourthDestination);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user