Add a prefix limiter
This commit is contained in:
parent
b3a1e6d0a5
commit
0b5053d49a
|
@ -32,6 +32,9 @@ public class RateLimitsConfiguration {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RateLimitConfiguration smsVoiceIp = new RateLimitConfiguration(1000, 1000);
|
private RateLimitConfiguration smsVoiceIp = new RateLimitConfiguration(1000, 1000);
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private RateLimitConfiguration smsVoicePrefix = new RateLimitConfiguration(1000, 1000);
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RateLimitConfiguration verifyNumber = new RateLimitConfiguration(2, 2);
|
private RateLimitConfiguration verifyNumber = new RateLimitConfiguration(2, 2);
|
||||||
|
|
||||||
|
@ -102,6 +105,10 @@ public class RateLimitsConfiguration {
|
||||||
return smsVoiceIp;
|
return smsVoiceIp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RateLimitConfiguration getSmsVoicePrefix() {
|
||||||
|
return smsVoicePrefix;
|
||||||
|
}
|
||||||
|
|
||||||
public RateLimitConfiguration getVerifyNumber() {
|
public RateLimitConfiguration getVerifyNumber() {
|
||||||
return verifyNumber;
|
return verifyNumber;
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,9 +85,12 @@ public class AccountController {
|
||||||
private final Meter newUserMeter = metricRegistry.meter(name(AccountController.class, "brand_new_user" ));
|
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 blockedHostMeter = metricRegistry.meter(name(AccountController.class, "blocked_host" ));
|
||||||
private final Meter filteredHostMeter = metricRegistry.meter(name(AccountController.class, "filtered_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 captchaSuccessMeter = metricRegistry.meter(name(AccountController.class, "captcha_success" ));
|
||||||
private final Meter captchaFailureMeter = metricRegistry.meter(name(AccountController.class, "captcha_failure" ));
|
private final Meter captchaFailureMeter = metricRegistry.meter(name(AccountController.class, "captcha_failure" ));
|
||||||
|
|
||||||
|
|
||||||
private final PendingAccountsManager pendingAccounts;
|
private final PendingAccountsManager pendingAccounts;
|
||||||
private final AccountsManager accounts;
|
private final AccountsManager accounts;
|
||||||
private final AbusiveHostRules abusiveHostRules;
|
private final AbusiveHostRules abusiveHostRules;
|
||||||
|
@ -420,10 +423,19 @@ public class AccountController {
|
||||||
rateLimiters.getSmsVoiceIpLimiter().validate(requester);
|
rateLimiters.getSmsVoiceIpLimiter().validate(requester);
|
||||||
} catch (RateLimitExceededException e) {
|
} catch (RateLimitExceededException e) {
|
||||||
logger.info("Rate limited exceeded: " + transport + ", " + number + ", " + requester + " (" + forwardedFor + ")");
|
logger.info("Rate limited exceeded: " + transport + ", " + number + ", " + requester + " (" + forwardedFor + ")");
|
||||||
|
rateLimitedPrefixMeter.mark();
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ public class RateLimiters {
|
||||||
private final RateLimiter voiceDestinationLimiter;
|
private final RateLimiter voiceDestinationLimiter;
|
||||||
private final RateLimiter voiceDestinationDailyLimiter;
|
private final RateLimiter voiceDestinationDailyLimiter;
|
||||||
private final RateLimiter smsVoiceIpLimiter;
|
private final RateLimiter smsVoiceIpLimiter;
|
||||||
|
private final RateLimiter smsVoicePrefixLimiter;
|
||||||
private final RateLimiter verifyLimiter;
|
private final RateLimiter verifyLimiter;
|
||||||
private final RateLimiter pinLimiter;
|
private final RateLimiter pinLimiter;
|
||||||
|
|
||||||
|
@ -58,6 +59,10 @@ public class RateLimiters {
|
||||||
config.getSmsVoiceIp().getBucketSize(),
|
config.getSmsVoiceIp().getBucketSize(),
|
||||||
config.getSmsVoiceIp().getLeakRatePerMinute());
|
config.getSmsVoiceIp().getLeakRatePerMinute());
|
||||||
|
|
||||||
|
this.smsVoicePrefixLimiter = new RateLimiter(cacheClient, "smsVoicePrefix",
|
||||||
|
config.getSmsVoicePrefix().getBucketSize(),
|
||||||
|
config.getSmsVoicePrefix().getLeakRatePerMinute());
|
||||||
|
|
||||||
this.verifyLimiter = new RateLimiter(cacheClient, "verify",
|
this.verifyLimiter = new RateLimiter(cacheClient, "verify",
|
||||||
config.getVerifyNumber().getBucketSize(),
|
config.getVerifyNumber().getBucketSize(),
|
||||||
config.getVerifyNumber().getLeakRatePerMinute());
|
config.getVerifyNumber().getLeakRatePerMinute());
|
||||||
|
@ -131,6 +136,10 @@ public class RateLimiters {
|
||||||
return smsVoiceIpLimiter;
|
return smsVoiceIpLimiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RateLimiter getSmsVoicePrefixLimiter() {
|
||||||
|
return smsVoicePrefixLimiter;
|
||||||
|
}
|
||||||
|
|
||||||
public RateLimiter getVoiceDestinationLimiter() {
|
public RateLimiter getVoiceDestinationLimiter() {
|
||||||
return voiceDestinationLimiter;
|
return voiceDestinationLimiter;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,14 @@ public class Util {
|
||||||
else return "0";
|
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) {
|
public static String encodeFormParams(Map<String, String> params) {
|
||||||
try {
|
try {
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
|
|
@ -63,6 +63,7 @@ public class AccountControllerTest {
|
||||||
private RateLimiter rateLimiter = mock(RateLimiter.class );
|
private RateLimiter rateLimiter = mock(RateLimiter.class );
|
||||||
private RateLimiter pinLimiter = mock(RateLimiter.class );
|
private RateLimiter pinLimiter = mock(RateLimiter.class );
|
||||||
private RateLimiter smsVoiceIpLimiter = mock(RateLimiter.class );
|
private RateLimiter smsVoiceIpLimiter = mock(RateLimiter.class );
|
||||||
|
private RateLimiter smsVoicePrefixLimiter = mock(RateLimiter.class);
|
||||||
private SmsSender smsSender = mock(SmsSender.class );
|
private SmsSender smsSender = mock(SmsSender.class );
|
||||||
private DirectoryQueue directoryQueue = mock(DirectoryQueue.class);
|
private DirectoryQueue directoryQueue = mock(DirectoryQueue.class);
|
||||||
private MessagesManager storedMessages = mock(MessagesManager.class );
|
private MessagesManager storedMessages = mock(MessagesManager.class );
|
||||||
|
@ -98,6 +99,7 @@ public class AccountControllerTest {
|
||||||
when(rateLimiters.getVerifyLimiter()).thenReturn(rateLimiter);
|
when(rateLimiters.getVerifyLimiter()).thenReturn(rateLimiter);
|
||||||
when(rateLimiters.getPinLimiter()).thenReturn(pinLimiter);
|
when(rateLimiters.getPinLimiter()).thenReturn(pinLimiter);
|
||||||
when(rateLimiters.getSmsVoiceIpLimiter()).thenReturn(smsVoiceIpLimiter);
|
when(rateLimiters.getSmsVoiceIpLimiter()).thenReturn(smsVoiceIpLimiter);
|
||||||
|
when(rateLimiters.getSmsVoicePrefixLimiter()).thenReturn(smsVoicePrefixLimiter);
|
||||||
|
|
||||||
when(timeProvider.getCurrentTimeMillis()).thenReturn(System.currentTimeMillis());
|
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