Parse locale strings when sending voice verification codes.

This commit is contained in:
Jon Chambers 2021-03-10 17:52:40 -05:00 committed by Jon Chambers
parent 3ea535a412
commit ca2f7d2eed
5 changed files with 79 additions and 11 deletions

View File

@ -16,6 +16,7 @@ import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@ -184,7 +185,7 @@ public class AccountController {
@PathParam("number") String number,
@HeaderParam("X-Forwarded-For") String forwardedFor,
@HeaderParam("User-Agent") List<String> userAgent,
@HeaderParam("Accept-Language") Optional<String> locale,
@HeaderParam("Accept-Language") Optional<String> acceptLanguage,
@QueryParam("client") Optional<String> client,
@QueryParam("captcha") Optional<String> captcha,
@QueryParam("challenge") Optional<String> pushChallenge)
@ -247,7 +248,11 @@ public class AccountController {
} else if (transport.equals("sms")) {
smsSender.deliverSmsVerification(number, client, verificationCode.getVerificationCodeDisplay());
} else if (transport.equals("voice")) {
smsSender.deliverVoxVerification(number, verificationCode.getVerificationCode(), locale);
final Optional<Locale> maybeLocale = acceptLanguage.map(Locale.LanguageRange::parse)
.flatMap(ranges -> ranges.stream().findFirst())
.map(range -> Locale.forLanguageTag(range.getRange()));
smsSender.deliverVoxVerification(number, verificationCode.getVerificationCode(), maybeLocale);
}
metricRegistry.meter(name(AccountController.class, "create", Util.getCountryCode(number))).mark();

View File

@ -5,6 +5,7 @@
package org.whispersystems.textsecuregcm.sms;
import java.util.Locale;
import java.util.Optional;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@ -24,7 +25,7 @@ public class SmsSender {
twilioSender.deliverSmsVerification(destination, clientType, verificationCode);
}
public void deliverVoxVerification(String destination, String verificationCode, Optional<String> locale) {
twilioSender.deliverVoxVerification(destination, verificationCode, locale);
public void deliverVoxVerification(String destination, String verificationCode, Optional<Locale> maybeLocale) {
twilioSender.deliverVoxVerification(destination, verificationCode, maybeLocale);
}
}

View File

@ -30,6 +30,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.configuration.TwilioConfiguration;
@ -143,11 +144,19 @@ public class TwilioSmsSender {
}
}
public CompletableFuture<Boolean> deliverVoxVerification(String destination, String verificationCode, Optional<String> locale) {
public CompletableFuture<Boolean> deliverVoxVerification(String destination, String verificationCode, Optional<Locale> maybeLocale) {
String url = "https://" + localDomain + "/v1/voice/description/" + verificationCode;
if (locale.isPresent()) {
url += "?l=" + locale.get();
if (maybeLocale.isPresent()) {
final String localeString = maybeLocale.map(locale -> {
if (StringUtils.isNotBlank(locale.getCountry())) {
return locale.getLanguage().toLowerCase() + "-" + locale.getCountry().toUpperCase();
} else {
return locale.getLanguage().toLowerCase();
}
}).get();
url += "?l=" + localeString;
}
Map<String, String> requestParameters = new HashMap<>();

View File

@ -28,6 +28,7 @@ import java.security.SecureRandom;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@ -163,6 +164,7 @@ public class AccountControllerTest {
when(rateLimiters.getSmsDestinationLimiter()).thenReturn(rateLimiter);
when(rateLimiters.getVoiceDestinationLimiter()).thenReturn(rateLimiter);
when(rateLimiters.getVoiceDestinationDailyLimiter()).thenReturn(rateLimiter);
when(rateLimiters.getVerifyLimiter()).thenReturn(rateLimiter);
when(rateLimiters.getPinLimiter()).thenReturn(pinLimiter);
when(rateLimiters.getSmsVoiceIpLimiter()).thenReturn(smsVoiceIpLimiter);
@ -294,6 +296,56 @@ public class AccountControllerTest {
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
}
@Test
public void testSendCodeVoiceNoLocale() throws Exception {
Response response =
resources.getJerseyTest()
.target(String.format("/v1/accounts/voice/code/%s", SENDER))
.queryParam("challenge", "1234-push")
.request()
.header("X-Forwarded-For", NICE_HOST)
.get();
assertThat(response.getStatus()).isEqualTo(200);
verify(smsSender).deliverVoxVerification(eq(SENDER), anyString(), eq(Optional.empty()));
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
}
@Test
public void testSendCodeVoiceSingleLocale() throws Exception {
Response response =
resources.getJerseyTest()
.target(String.format("/v1/accounts/voice/code/%s", SENDER))
.queryParam("challenge", "1234-push")
.request()
.header("Accept-Language", "pt-BR")
.header("X-Forwarded-For", NICE_HOST)
.get();
assertThat(response.getStatus()).isEqualTo(200);
verify(smsSender).deliverVoxVerification(eq(SENDER), anyString(), eq(Optional.of(Locale.forLanguageTag("pt-BR"))));
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
}
@Test
public void testSendCodeVoiceMultipleLocales() throws Exception {
Response response =
resources.getJerseyTest()
.target(String.format("/v1/accounts/voice/code/%s", SENDER))
.queryParam("challenge", "1234-push")
.request()
.header("Accept-Language", "en-US;q=1, ar-US;q=0.9, fa-US;q=0.8, zh-Hans-US;q=0.7, ru-RU;q=0.6, zh-Hant-US;q=0.5")
.header("X-Forwarded-For", NICE_HOST)
.get();
assertThat(response.getStatus()).isEqualTo(200);
verify(smsSender).deliverVoxVerification(eq(SENDER), anyString(), eq(Optional.of(Locale.forLanguageTag("en-US"))));
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
}
@Test
public void testSendCodeWithValidPreauth() throws Exception {
Response response =

View File

@ -18,6 +18,7 @@ import static org.mockito.Mockito.*;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import javax.annotation.Nonnull;
import org.junit.Before;
@ -135,13 +136,13 @@ public class TwilioSmsSenderTest {
TwilioConfiguration configuration = createTwilioConfiguration();
TwilioSmsSender sender = new TwilioSmsSender("http://localhost:" + wireMockRule.port(), configuration, dynamicConfigurationManager);
boolean success = sender.deliverVoxVerification("+14153333333", "123-456", Optional.of("en_US")).join();
boolean success = sender.deliverVoxVerification("+14153333333", "123-456", Optional.of(Locale.US)).join();
assertThat(success).isTrue();
verify(1, postRequestedFor(urlEqualTo("/2010-04-01/Accounts/" + ACCOUNT_ID + "/Calls.json"))
.withHeader("Content-Type", equalTo("application/x-www-form-urlencoded"))
.withRequestBody(matching("To=%2B14153333333&From=%2B1415(1111111|2222222)&Url=https%3A%2F%2Ftest.com%2Fv1%2Fvoice%2Fdescription%2F123-456%3Fl%3Den_US")));
.withRequestBody(matching("To=%2B14153333333&From=%2B1415(1111111|2222222)&Url=https%3A%2F%2Ftest.com%2Fv1%2Fvoice%2Fdescription%2F123-456%3Fl%3Den-US")));
}
@Test
@ -178,13 +179,13 @@ public class TwilioSmsSenderTest {
TwilioConfiguration configuration = createTwilioConfiguration();
TwilioSmsSender sender = new TwilioSmsSender("http://localhost:" + wireMockRule.port(), configuration, dynamicConfigurationManager);
boolean success = sender.deliverVoxVerification("+14153333333", "123-456", Optional.of("en_US")).join();
boolean success = sender.deliverVoxVerification("+14153333333", "123-456", Optional.of(Locale.US)).join();
assertThat(success).isFalse();
verify(3, postRequestedFor(urlEqualTo("/2010-04-01/Accounts/" + ACCOUNT_ID + "/Calls.json"))
.withHeader("Content-Type", equalTo("application/x-www-form-urlencoded"))
.withRequestBody(matching("To=%2B14153333333&From=%2B1415(1111111|2222222)&Url=https%3A%2F%2Ftest.com%2Fv1%2Fvoice%2Fdescription%2F123-456%3Fl%3Den_US")));
.withRequestBody(matching("To=%2B14153333333&From=%2B1415(1111111|2222222)&Url=https%3A%2F%2Ftest.com%2Fv1%2Fvoice%2Fdescription%2F123-456%3Fl%3Den-US")));
}