mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 17:08:27 +01:00
Configure fail-open policy on individual rate limiters
This commit is contained in:
committed by
Jon Chambers
parent
e9bd5da2c3
commit
771a700acd
@@ -8,7 +8,6 @@ package org.whispersystems.textsecuregcm.limits;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@@ -16,15 +15,15 @@ class RateLimiterConfigTest {
|
||||
|
||||
@Test
|
||||
void leakRatePerMillis() {
|
||||
assertEquals(0.001, new RateLimiterConfig(1, Duration.ofSeconds(1)).leakRatePerMillis());
|
||||
assertEquals(1e6, new RateLimiterConfig(1, Duration.ofNanos(1)).leakRatePerMillis());
|
||||
assertEquals(0.001, new RateLimiterConfig(1, Duration.ofSeconds(1), false).leakRatePerMillis());
|
||||
assertEquals(1e6, new RateLimiterConfig(1, Duration.ofNanos(1), false).leakRatePerMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
void isRegenerationRatePositive() {
|
||||
assertTrue(new RateLimiterConfig(1, Duration.ofSeconds(1)).hasPositiveRegenerationRate());
|
||||
assertTrue(new RateLimiterConfig(1, Duration.ofNanos(1)).hasPositiveRegenerationRate());
|
||||
assertFalse(new RateLimiterConfig(1, Duration.ZERO).hasPositiveRegenerationRate());
|
||||
assertFalse(new RateLimiterConfig(1, Duration.ofSeconds(-1)).hasPositiveRegenerationRate());
|
||||
assertTrue(new RateLimiterConfig(1, Duration.ofSeconds(1), false).hasPositiveRegenerationRate());
|
||||
assertTrue(new RateLimiterConfig(1, Duration.ofNanos(1), false).hasPositiveRegenerationRate());
|
||||
assertFalse(new RateLimiterConfig(1, Duration.ZERO, false).hasPositiveRegenerationRate());
|
||||
assertFalse(new RateLimiterConfig(1, Duration.ofSeconds(-1), false).hasPositiveRegenerationRate());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.limits;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -22,8 +23,9 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitPolicy;
|
||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient;
|
||||
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
|
||||
@@ -57,7 +59,7 @@ public class RateLimitersLuaScriptTest {
|
||||
final RateLimiters.For descriptor = RateLimiters.For.REGISTRATION;
|
||||
final FaultTolerantRedisClusterClient redisCluster = REDIS_CLUSTER_EXTENSION.getRedisCluster();
|
||||
final RateLimiters limiters = new RateLimiters(
|
||||
Map.of(descriptor.id(), new RateLimiterConfig(60, Duration.ofSeconds(1))),
|
||||
Map.of(descriptor.id(), new RateLimiterConfig(60, Duration.ofSeconds(1), false)),
|
||||
dynamicConfig,
|
||||
RateLimiters.defaultScript(redisCluster),
|
||||
redisCluster,
|
||||
@@ -74,7 +76,7 @@ public class RateLimitersLuaScriptTest {
|
||||
final RateLimiters.For descriptor = RateLimiters.For.REGISTRATION;
|
||||
final FaultTolerantRedisClusterClient redisCluster = REDIS_CLUSTER_EXTENSION.getRedisCluster();
|
||||
final RateLimiters limiters = new RateLimiters(
|
||||
Map.of(descriptor.id(), new RateLimiterConfig(1000, Duration.ofSeconds(1))),
|
||||
Map.of(descriptor.id(), new RateLimiterConfig(1000, Duration.ofSeconds(1), false)),
|
||||
dynamicConfig,
|
||||
RateLimiters.defaultScript(redisCluster),
|
||||
redisCluster,
|
||||
@@ -119,20 +121,25 @@ public class RateLimitersLuaScriptTest {
|
||||
assertEquals(750L, decodeBucket(key).orElseThrow().tokensRemaining);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailOpen() throws Exception {
|
||||
when(configuration.getRateLimitPolicy()).thenReturn(new DynamicRateLimitPolicy(true));
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
public void testFailOpen(final boolean failOpen) {
|
||||
final RateLimiters.For descriptor = RateLimiters.For.REGISTRATION;
|
||||
final FaultTolerantRedisClusterClient redisCluster = mock(FaultTolerantRedisClusterClient.class);
|
||||
final RateLimiters limiters = new RateLimiters(
|
||||
Map.of(descriptor.id(), new RateLimiterConfig(1000, Duration.ofSeconds(1))),
|
||||
Map.of(descriptor.id(), new RateLimiterConfig(1000, Duration.ofSeconds(1), failOpen)),
|
||||
dynamicConfig,
|
||||
RateLimiters.defaultScript(redisCluster),
|
||||
redisCluster,
|
||||
Clock.systemUTC());
|
||||
when(redisCluster.withCluster(any())).thenThrow(new RedisException("fail"));
|
||||
final RateLimiter rateLimiter = limiters.forDescriptor(descriptor);
|
||||
rateLimiter.validate("test", 200);
|
||||
|
||||
if (failOpen) {
|
||||
assertDoesNotThrow(() -> rateLimiter.validate("test", 200));
|
||||
} else {
|
||||
assertThrows(RedisException.class, () -> rateLimiter.validate("test", 200));
|
||||
}
|
||||
}
|
||||
|
||||
private String serializeToOldBucketValueFormat(
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.limits;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitPolicy;
|
||||
import org.whispersystems.textsecuregcm.redis.ClusterLuaScript;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisClusterClient;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
@@ -56,30 +55,31 @@ public class RateLimitersTest {
|
||||
prekeys:
|
||||
bucketSize: 150
|
||||
permitRegenerationDuration: PT6S
|
||||
failOpen: true
|
||||
attachmentCreate:
|
||||
bucketSize: 4
|
||||
permitRegenerationDuration: PT30S
|
||||
rateLimitPolicy:
|
||||
failOpen: true
|
||||
failOpen: true
|
||||
""";
|
||||
|
||||
public record GenericHolder(
|
||||
@Valid @NotNull @JsonProperty Map<String, RateLimiterConfig> limits,
|
||||
@Valid @JsonProperty DynamicRateLimitPolicy rateLimitPolicy) {
|
||||
public record SimpleDynamicConfiguration(@Valid @NotNull @JsonProperty Map<String, RateLimiterConfig> limits) {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateConfigs() throws Exception {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
final GenericHolder cfg = DynamicConfigurationManager.parseConfiguration(BAD_YAML, GenericHolder.class).orElseThrow();
|
||||
final RateLimiters rateLimiters = new RateLimiters(cfg.limits(), dynamicConfig, validateScript, redisCluster, clock);
|
||||
final SimpleDynamicConfiguration dynamicConfiguration =
|
||||
DynamicConfigurationManager.parseConfiguration(BAD_YAML, SimpleDynamicConfiguration.class).orElseThrow();
|
||||
|
||||
final RateLimiters rateLimiters = new RateLimiters(dynamicConfiguration.limits(), dynamicConfig, validateScript, redisCluster, clock);
|
||||
rateLimiters.validateValuesAndConfigs();
|
||||
});
|
||||
|
||||
final GenericHolder cfg = DynamicConfigurationManager.parseConfiguration(GOOD_YAML, GenericHolder.class).orElseThrow();
|
||||
assertTrue(cfg.rateLimitPolicy.failOpen());
|
||||
final RateLimiters rateLimiters = new RateLimiters(cfg.limits(), dynamicConfig, validateScript, redisCluster, clock);
|
||||
rateLimiters.validateValuesAndConfigs();
|
||||
final SimpleDynamicConfiguration dynamicConfiguration =
|
||||
DynamicConfigurationManager.parseConfiguration(GOOD_YAML, SimpleDynamicConfiguration.class).orElseThrow();
|
||||
|
||||
final RateLimiters rateLimiters = new RateLimiters(dynamicConfiguration.limits(), dynamicConfig, validateScript, redisCluster, clock);
|
||||
assertDoesNotThrow(rateLimiters::validateValuesAndConfigs);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,9 +116,9 @@ public class RateLimitersTest {
|
||||
|
||||
@Test
|
||||
void testChangingConfiguration() {
|
||||
final RateLimiterConfig initialRateLimiterConfig = new RateLimiterConfig(4, Duration.ofMinutes(1));
|
||||
final RateLimiterConfig updatedRateLimiterCongig = new RateLimiterConfig(17, Duration.ofSeconds(3));
|
||||
final RateLimiterConfig baseConfig = new RateLimiterConfig(1, Duration.ofMinutes(1));
|
||||
final RateLimiterConfig initialRateLimiterConfig = new RateLimiterConfig(4, Duration.ofMinutes(1), false);
|
||||
final RateLimiterConfig updatedRateLimiterCongig = new RateLimiterConfig(17, Duration.ofSeconds(3), false);
|
||||
final RateLimiterConfig baseConfig = new RateLimiterConfig(1, Duration.ofMinutes(1), false);
|
||||
|
||||
final Map<String, RateLimiterConfig> limitsConfigMap = new HashMap<>();
|
||||
|
||||
@@ -146,8 +146,8 @@ public class RateLimitersTest {
|
||||
@Test
|
||||
public void testRateLimiterHasItsPrioritiesStraight() throws Exception {
|
||||
final RateLimiters.For descriptor = RateLimiters.For.CAPTCHA_CHALLENGE_ATTEMPT;
|
||||
final RateLimiterConfig configForDynamic = new RateLimiterConfig(1, Duration.ofMinutes(1));
|
||||
final RateLimiterConfig configForStatic = new RateLimiterConfig(2, Duration.ofSeconds(30));
|
||||
final RateLimiterConfig configForDynamic = new RateLimiterConfig(1, Duration.ofMinutes(1), false);
|
||||
final RateLimiterConfig configForStatic = new RateLimiterConfig(2, Duration.ofSeconds(30), false);
|
||||
final RateLimiterConfig defaultConfig = descriptor.defaultConfig();
|
||||
|
||||
final Map<String, RateLimiterConfig> mapForDynamic = new HashMap<>();
|
||||
@@ -188,7 +188,7 @@ public class RateLimitersTest {
|
||||
|
||||
@Override
|
||||
public RateLimiterConfig defaultConfig() {
|
||||
return new RateLimiterConfig(1, Duration.ofMinutes(1));
|
||||
return new RateLimiterConfig(1, Duration.ofMinutes(1), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user