Add a prefix limiter
This commit is contained in:
parent
b3a1e6d0a5
commit
0b5053d49a
|
@ -32,6 +32,9 @@ public class RateLimitsConfiguration {
|
|||
@JsonProperty
|
||||
private RateLimitConfiguration smsVoiceIp = new RateLimitConfiguration(1000, 1000);
|
||||
|
||||
@JsonProperty
|
||||
private RateLimitConfiguration smsVoicePrefix = new RateLimitConfiguration(1000, 1000);
|
||||
|
||||
@JsonProperty
|
||||
private RateLimitConfiguration verifyNumber = new RateLimitConfiguration(2, 2);
|
||||
|
||||
|
@ -102,6 +105,10 @@ public class RateLimitsConfiguration {
|
|||
return smsVoiceIp;
|
||||
}
|
||||
|
||||
public RateLimitConfiguration getSmsVoicePrefix() {
|
||||
return smsVoicePrefix;
|
||||
}
|
||||
|
||||
public RateLimitConfiguration getVerifyNumber() {
|
||||
return verifyNumber;
|
||||
}
|
||||
|
|
|
@ -80,13 +80,16 @@ import io.dropwizard.auth.Auth;
|
|||
@Path("/v1/accounts")
|
||||
public class AccountController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AccountController.class);
|
||||
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
||||
private final Meter newUserMeter = metricRegistry.meter(name(AccountController.class, "brand_new_user" ));
|
||||
private final Meter blockedHostMeter = metricRegistry.meter(name(AccountController.class, "blocked_host" ));
|
||||
private final Meter filteredHostMeter = metricRegistry.meter(name(AccountController.class, "filtered_host" ));
|
||||
private final Meter captchaSuccessMeter = metricRegistry.meter(name(AccountController.class, "captcha_success"));
|
||||
private final Meter captchaFailureMeter = metricRegistry.meter(name(AccountController.class, "captcha_failure"));
|
||||
private final Logger logger = LoggerFactory.getLogger(AccountController.class);
|
||||
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
||||
private final Meter newUserMeter = metricRegistry.meter(name(AccountController.class, "brand_new_user" ));
|
||||
private final Meter blockedHostMeter = metricRegistry.meter(name(AccountController.class, "blocked_host" ));
|
||||
private final Meter filteredHostMeter = metricRegistry.meter(name(AccountController.class, "filtered_host" ));
|
||||
private final Meter rateLimitedHostMeter = metricRegistry.meter(name(AccountController.class, "rate_limited_host" ));
|
||||
private final Meter rateLimitedPrefixMeter = metricRegistry.meter(name(AccountController.class, "rate_limited_prefix"));
|
||||
private final Meter captchaSuccessMeter = metricRegistry.meter(name(AccountController.class, "captcha_success" ));
|
||||
private final Meter captchaFailureMeter = metricRegistry.meter(name(AccountController.class, "captcha_failure" ));
|
||||
|
||||
|
||||
private final PendingAccountsManager pendingAccounts;
|
||||
private final AccountsManager accounts;
|
||||
|
@ -420,10 +423,19 @@ public class AccountController {
|
|||
rateLimiters.getSmsVoiceIpLimiter().validate(requester);
|
||||
} catch (RateLimitExceededException e) {
|
||||
logger.info("Rate limited exceeded: " + transport + ", " + number + ", " + requester + " (" + forwardedFor + ")");
|
||||
rateLimitedPrefixMeter.mark();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
rateLimiters.getSmsVoicePrefixLimiter().validate(Util.getNumberPrefix(number));
|
||||
} catch (RateLimitExceededException e) {
|
||||
logger.info("Prefix rate limit exceeded: " + transport + ", " + number + ", (" + String.join(", ", requesters) + ")");
|
||||
rateLimitedPrefixMeter.mark();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ public class RateLimiters {
|
|||
private final RateLimiter voiceDestinationLimiter;
|
||||
private final RateLimiter voiceDestinationDailyLimiter;
|
||||
private final RateLimiter smsVoiceIpLimiter;
|
||||
private final RateLimiter smsVoicePrefixLimiter;
|
||||
private final RateLimiter verifyLimiter;
|
||||
private final RateLimiter pinLimiter;
|
||||
|
||||
|
@ -58,6 +59,10 @@ public class RateLimiters {
|
|||
config.getSmsVoiceIp().getBucketSize(),
|
||||
config.getSmsVoiceIp().getLeakRatePerMinute());
|
||||
|
||||
this.smsVoicePrefixLimiter = new RateLimiter(cacheClient, "smsVoicePrefix",
|
||||
config.getSmsVoicePrefix().getBucketSize(),
|
||||
config.getSmsVoicePrefix().getLeakRatePerMinute());
|
||||
|
||||
this.verifyLimiter = new RateLimiter(cacheClient, "verify",
|
||||
config.getVerifyNumber().getBucketSize(),
|
||||
config.getVerifyNumber().getLeakRatePerMinute());
|
||||
|
@ -131,6 +136,10 @@ public class RateLimiters {
|
|||
return smsVoiceIpLimiter;
|
||||
}
|
||||
|
||||
public RateLimiter getSmsVoicePrefixLimiter() {
|
||||
return smsVoicePrefixLimiter;
|
||||
}
|
||||
|
||||
public RateLimiter getVoiceDestinationLimiter() {
|
||||
return voiceDestinationLimiter;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,14 @@ public class Util {
|
|||
else return "0";
|
||||
}
|
||||
|
||||
public static String getNumberPrefix(String number) {
|
||||
String countryCode = getCountryCode(number);
|
||||
int remaining = number.length() - (1 + countryCode.length());
|
||||
int prefixLength = Math.min(4, remaining);
|
||||
|
||||
return number.substring(0, 1 + countryCode.length() + prefixLength);
|
||||
}
|
||||
|
||||
public static String encodeFormParams(Map<String, String> params) {
|
||||
try {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
|
|
@ -63,6 +63,7 @@ public class AccountControllerTest {
|
|||
private RateLimiter rateLimiter = mock(RateLimiter.class );
|
||||
private RateLimiter pinLimiter = mock(RateLimiter.class );
|
||||
private RateLimiter smsVoiceIpLimiter = mock(RateLimiter.class );
|
||||
private RateLimiter smsVoicePrefixLimiter = mock(RateLimiter.class);
|
||||
private SmsSender smsSender = mock(SmsSender.class );
|
||||
private DirectoryQueue directoryQueue = mock(DirectoryQueue.class);
|
||||
private MessagesManager storedMessages = mock(MessagesManager.class );
|
||||
|
@ -98,6 +99,7 @@ public class AccountControllerTest {
|
|||
when(rateLimiters.getVerifyLimiter()).thenReturn(rateLimiter);
|
||||
when(rateLimiters.getPinLimiter()).thenReturn(pinLimiter);
|
||||
when(rateLimiters.getSmsVoiceIpLimiter()).thenReturn(smsVoiceIpLimiter);
|
||||
when(rateLimiters.getSmsVoicePrefixLimiter()).thenReturn(smsVoicePrefixLimiter);
|
||||
|
||||
when(timeProvider.getCurrentTimeMillis()).thenReturn(System.currentTimeMillis());
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package org.whispersystems.textsecuregcm.tests.util;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
public class NumberPrefixTest {
|
||||
|
||||
@Test
|
||||
public void testPrefixes() {
|
||||
assertThat(Util.getNumberPrefix("+14151234567")).isEqualTo("+14151");
|
||||
assertThat(Util.getNumberPrefix("+22587654321")).isEqualTo("+2258765");
|
||||
assertThat(Util.getNumberPrefix("+298654321")).isEqualTo("+2986543");
|
||||
assertThat(Util.getNumberPrefix("+12")).isEqualTo("+12");
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue