Add Twilio Verify experiment to AccountController
This commit is contained in:
parent
4c019aef15
commit
13053da97f
|
@ -138,6 +138,7 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
||||||
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
||||||
import org.whispersystems.textsecuregcm.sms.TwilioSmsSender;
|
import org.whispersystems.textsecuregcm.sms.TwilioSmsSender;
|
||||||
|
import org.whispersystems.textsecuregcm.sms.TwilioVerifyExperimentEnrollmentManager;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
||||||
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
|
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
|
@ -382,6 +383,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
ExperimentEnrollmentManager experimentEnrollmentManager = new ExperimentEnrollmentManager(dynamicConfigurationManager);
|
ExperimentEnrollmentManager experimentEnrollmentManager = new ExperimentEnrollmentManager(dynamicConfigurationManager);
|
||||||
|
|
||||||
|
TwilioVerifyExperimentEnrollmentManager verifyExperimentEnrollmentManager = new TwilioVerifyExperimentEnrollmentManager(
|
||||||
|
config.getVoiceVerificationConfiguration(), experimentEnrollmentManager);
|
||||||
|
|
||||||
ExternalServiceCredentialGenerator storageCredentialsGenerator = new ExternalServiceCredentialGenerator(config.getSecureStorageServiceConfiguration().getUserAuthenticationTokenSharedSecret(), new byte[0], false);
|
ExternalServiceCredentialGenerator storageCredentialsGenerator = new ExternalServiceCredentialGenerator(config.getSecureStorageServiceConfiguration().getUserAuthenticationTokenSharedSecret(), new byte[0], false);
|
||||||
ExternalServiceCredentialGenerator backupCredentialsGenerator = new ExternalServiceCredentialGenerator(config.getSecureBackupServiceConfiguration().getUserAuthenticationTokenSharedSecret(), new byte[0], false);
|
ExternalServiceCredentialGenerator backupCredentialsGenerator = new ExternalServiceCredentialGenerator(config.getSecureBackupServiceConfiguration().getUserAuthenticationTokenSharedSecret(), new byte[0], false);
|
||||||
ExternalServiceCredentialGenerator paymentsCredentialsGenerator = new ExternalServiceCredentialGenerator(config.getPaymentsServiceConfiguration().getUserAuthenticationTokenSharedSecret(), new byte[0], false);
|
ExternalServiceCredentialGenerator paymentsCredentialsGenerator = new ExternalServiceCredentialGenerator(config.getPaymentsServiceConfiguration().getUserAuthenticationTokenSharedSecret(), new byte[0], false);
|
||||||
|
@ -485,7 +489,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
environment.jersey().register(new TimestampResponseFilter());
|
environment.jersey().register(new TimestampResponseFilter());
|
||||||
|
|
||||||
environment.jersey().register(new AccountController(pendingAccountsManager, accountsManager, usernamesManager, abusiveHostRules, rateLimiters, smsSender, directoryQueue, messagesManager, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(), recaptchaClient, gcmSender, apnSender, backupCredentialsGenerator));
|
environment.jersey().register(new AccountController(pendingAccountsManager, accountsManager, usernamesManager, abusiveHostRules, rateLimiters, smsSender, directoryQueue, messagesManager, dynamicConfigurationManager, turnTokenGenerator, config.getTestDevices(), recaptchaClient, gcmSender, apnSender, backupCredentialsGenerator, verifyExperimentEnrollmentManager));
|
||||||
environment.jersey().register(new DeviceController(pendingDevicesManager, accountsManager, messagesManager, directoryQueue, rateLimiters, config.getMaxDevices()));
|
environment.jersey().register(new DeviceController(pendingDevicesManager, accountsManager, messagesManager, directoryQueue, rateLimiters, config.getMaxDevices()));
|
||||||
environment.jersey().register(new DirectoryController(directoryCredentialsGenerator));
|
environment.jersey().register(new DirectoryController(directoryCredentialsGenerator));
|
||||||
environment.jersey().register(new ProvisioningController(rateLimiters, provisioningManager));
|
environment.jersey().register(new ProvisioningController(rateLimiters, provisioningManager));
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class StoredVerificationCode {
|
public class StoredVerificationCode {
|
||||||
|
@ -23,12 +24,21 @@ public class StoredVerificationCode {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String pushCode;
|
private String pushCode;
|
||||||
|
|
||||||
public StoredVerificationCode() {}
|
@JsonProperty
|
||||||
|
private String twilioVerificationSid;
|
||||||
|
|
||||||
|
public StoredVerificationCode() {
|
||||||
|
}
|
||||||
|
|
||||||
public StoredVerificationCode(String code, long timestamp, String pushCode) {
|
public StoredVerificationCode(String code, long timestamp, String pushCode) {
|
||||||
|
this(code, timestamp, pushCode, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredVerificationCode(String code, long timestamp, String pushCode, String twilioVerificationSid) {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.pushCode = pushCode;
|
this.pushCode = pushCode;
|
||||||
|
this.twilioVerificationSid = twilioVerificationSid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCode() {
|
public String getCode() {
|
||||||
|
@ -43,6 +53,10 @@ public class StoredVerificationCode {
|
||||||
return pushCode;
|
return pushCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<String> getTwilioVerificationSid() {
|
||||||
|
return Optional.ofNullable(twilioVerificationSid);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isValid(String theirCodeString) {
|
public boolean isValid(String theirCodeString) {
|
||||||
if (timestamp + TimeUnit.MINUTES.toMillis(10) < System.currentTimeMillis()) {
|
if (timestamp + TimeUnit.MINUTES.toMillis(10) < System.currentTimeMillis()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -57,5 +71,4 @@ public class StoredVerificationCode {
|
||||||
|
|
||||||
return MessageDigest.isEqual(ourCode, theirCode);
|
return MessageDigest.isEqual(ourCode, theirCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
|
@ -64,6 +65,7 @@ import org.whispersystems.textsecuregcm.push.GCMSender;
|
||||||
import org.whispersystems.textsecuregcm.push.GcmMessage;
|
import org.whispersystems.textsecuregcm.push.GcmMessage;
|
||||||
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
|
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
|
||||||
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
||||||
|
import org.whispersystems.textsecuregcm.sms.TwilioVerifyExperimentEnrollmentManager;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
||||||
import org.whispersystems.textsecuregcm.storage.AbusiveHostRule;
|
import org.whispersystems.textsecuregcm.storage.AbusiveHostRule;
|
||||||
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
|
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
|
||||||
|
@ -99,11 +101,15 @@ public class AccountController {
|
||||||
private static final String ACCOUNT_CREATE_COUNTER_NAME = name(AccountController.class, "create");
|
private static final String ACCOUNT_CREATE_COUNTER_NAME = name(AccountController.class, "create");
|
||||||
private static final String ACCOUNT_VERIFY_COUNTER_NAME = name(AccountController.class, "verify");
|
private static final String ACCOUNT_VERIFY_COUNTER_NAME = name(AccountController.class, "verify");
|
||||||
|
|
||||||
|
private static final String TWILIO_VERIFY_ERROR_COUNTER_NAME = name(AccountController.class, "twilioVerifyError");
|
||||||
|
|
||||||
private static final String CHALLENGE_PRESENT_TAG_NAME = "present";
|
private static final String CHALLENGE_PRESENT_TAG_NAME = "present";
|
||||||
private static final String CHALLENGE_MATCH_TAG_NAME = "matches";
|
private static final String CHALLENGE_MATCH_TAG_NAME = "matches";
|
||||||
private static final String COUNTRY_CODE_TAG_NAME = "countryCode";
|
private static final String COUNTRY_CODE_TAG_NAME = "countryCode";
|
||||||
private static final String VERFICATION_TRANSPORT_TAG_NAME = "transport";
|
private static final String VERFICATION_TRANSPORT_TAG_NAME = "transport";
|
||||||
|
|
||||||
|
private static final String VERIFY_EXPERIMENT_TAG_NAME = "twilioVerify";
|
||||||
|
|
||||||
private final PendingAccountsManager pendingAccounts;
|
private final PendingAccountsManager pendingAccounts;
|
||||||
private final AccountsManager accounts;
|
private final AccountsManager accounts;
|
||||||
private final UsernamesManager usernames;
|
private final UsernamesManager usernames;
|
||||||
|
@ -120,6 +126,8 @@ public class AccountController {
|
||||||
private final APNSender apnSender;
|
private final APNSender apnSender;
|
||||||
private final ExternalServiceCredentialGenerator backupServiceCredentialGenerator;
|
private final ExternalServiceCredentialGenerator backupServiceCredentialGenerator;
|
||||||
|
|
||||||
|
private final TwilioVerifyExperimentEnrollmentManager verifyExperimentEnrollmentManager;
|
||||||
|
|
||||||
public AccountController(PendingAccountsManager pendingAccounts,
|
public AccountController(PendingAccountsManager pendingAccounts,
|
||||||
AccountsManager accounts,
|
AccountsManager accounts,
|
||||||
UsernamesManager usernames,
|
UsernamesManager usernames,
|
||||||
|
@ -134,7 +142,8 @@ public class AccountController {
|
||||||
RecaptchaClient recaptchaClient,
|
RecaptchaClient recaptchaClient,
|
||||||
GCMSender gcmSender,
|
GCMSender gcmSender,
|
||||||
APNSender apnSender,
|
APNSender apnSender,
|
||||||
ExternalServiceCredentialGenerator backupServiceCredentialGenerator)
|
ExternalServiceCredentialGenerator backupServiceCredentialGenerator,
|
||||||
|
TwilioVerifyExperimentEnrollmentManager verifyExperimentEnrollmentManager)
|
||||||
{
|
{
|
||||||
this.pendingAccounts = pendingAccounts;
|
this.pendingAccounts = pendingAccounts;
|
||||||
this.accounts = accounts;
|
this.accounts = accounts;
|
||||||
|
@ -151,6 +160,7 @@ public class AccountController {
|
||||||
this.gcmSender = gcmSender;
|
this.gcmSender = gcmSender;
|
||||||
this.apnSender = apnSender;
|
this.apnSender = apnSender;
|
||||||
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator;
|
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator;
|
||||||
|
this.verifyExperimentEnrollmentManager = verifyExperimentEnrollmentManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
|
@ -247,15 +257,11 @@ public class AccountController {
|
||||||
VerificationCode verificationCode = generateVerificationCode(number);
|
VerificationCode verificationCode = generateVerificationCode(number);
|
||||||
StoredVerificationCode storedVerificationCode = new StoredVerificationCode(verificationCode.getVerificationCode(),
|
StoredVerificationCode storedVerificationCode = new StoredVerificationCode(verificationCode.getVerificationCode(),
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
storedChallenge.map(StoredVerificationCode::getPushCode).orElse(null));
|
storedChallenge.map(StoredVerificationCode::getPushCode).orElse(null),
|
||||||
|
storedChallenge.flatMap(StoredVerificationCode::getTwilioVerificationSid).orElse(null));
|
||||||
|
|
||||||
pendingAccounts.store(number, storedVerificationCode);
|
pendingAccounts.store(number, storedVerificationCode);
|
||||||
|
|
||||||
if (testDevices.containsKey(number)) {
|
|
||||||
// noop
|
|
||||||
} else if (transport.equals("sms")) {
|
|
||||||
smsSender.deliverSmsVerification(number, client, verificationCode.getVerificationCodeDisplay());
|
|
||||||
} else if (transport.equals("voice")) {
|
|
||||||
final List<Locale.LanguageRange> languageRanges;
|
final List<Locale.LanguageRange> languageRanges;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -264,8 +270,49 @@ public class AccountController {
|
||||||
return Response.status(400).build();
|
return Response.status(400).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
smsSender.deliverVoxVerification(number, verificationCode.getVerificationCode(), languageRanges);
|
final boolean enrolledInVerifyExperiment = verifyExperimentEnrollmentManager.isEnrolled(client, number, languageRanges, transport);
|
||||||
|
final CompletableFuture<Optional<String>> sendVerificationWithTwilioVerifyFuture;
|
||||||
|
|
||||||
|
if (testDevices.containsKey(number)) {
|
||||||
|
// noop
|
||||||
|
sendVerificationWithTwilioVerifyFuture = CompletableFuture.completedFuture(Optional.empty());
|
||||||
|
} else if (transport.equals("sms")) {
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
sendVerificationWithTwilioVerifyFuture = smsSender.deliverSmsVerificationWithTwilioVerify(number, client, verificationCode.getVerificationCode(), languageRanges);
|
||||||
|
} else {
|
||||||
|
smsSender.deliverSmsVerification(number, client, verificationCode.getVerificationCodeDisplay());
|
||||||
|
sendVerificationWithTwilioVerifyFuture = CompletableFuture.completedFuture(Optional.empty());
|
||||||
}
|
}
|
||||||
|
} else if (transport.equals("voice")) {
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
sendVerificationWithTwilioVerifyFuture = smsSender.deliverVoxVerificationWithTwilioVerify(number, verificationCode.getVerificationCode(), languageRanges);
|
||||||
|
} else {
|
||||||
|
smsSender.deliverVoxVerification(number, verificationCode.getVerificationCode(), languageRanges);
|
||||||
|
sendVerificationWithTwilioVerifyFuture = CompletableFuture.completedFuture(Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
sendVerificationWithTwilioVerifyFuture = CompletableFuture.completedFuture(Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
sendVerificationWithTwilioVerifyFuture.whenComplete((maybeVerificationSid, throwable) -> {
|
||||||
|
if (throwable != null) {
|
||||||
|
Metrics.counter(TWILIO_VERIFY_ERROR_COUNTER_NAME).increment();
|
||||||
|
|
||||||
|
logger.warn("Error with Twilio Verify", throwable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
maybeVerificationSid.ifPresent(twilioVerificationSid -> {
|
||||||
|
StoredVerificationCode storedVerificationCodeWithVerificationSid = new StoredVerificationCode(
|
||||||
|
storedVerificationCode.getCode(),
|
||||||
|
storedVerificationCode.getTimestamp(),
|
||||||
|
storedVerificationCode.getPushCode(),
|
||||||
|
twilioVerificationSid);
|
||||||
|
pendingAccounts.store(number, storedVerificationCodeWithVerificationSid);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
metricRegistry.meter(name(AccountController.class, "create", Util.getCountryCode(number))).mark();
|
metricRegistry.meter(name(AccountController.class, "create", Util.getCountryCode(number))).mark();
|
||||||
|
|
||||||
|
@ -274,6 +321,7 @@ public class AccountController {
|
||||||
tags.add(Tag.of(COUNTRY_CODE_TAG_NAME, Util.getCountryCode(number)));
|
tags.add(Tag.of(COUNTRY_CODE_TAG_NAME, Util.getCountryCode(number)));
|
||||||
tags.add(Tag.of(VERFICATION_TRANSPORT_TAG_NAME, transport));
|
tags.add(Tag.of(VERFICATION_TRANSPORT_TAG_NAME, transport));
|
||||||
tags.add(UserAgentTagUtil.getPlatformTag(userAgent));
|
tags.add(UserAgentTagUtil.getPlatformTag(userAgent));
|
||||||
|
tags.add(Tag.of(VERIFY_EXPERIMENT_TAG_NAME, String.valueOf(enrolledInVerifyExperiment)));
|
||||||
|
|
||||||
Metrics.counter(ACCOUNT_CREATE_COUNTER_NAME, tags).increment();
|
Metrics.counter(ACCOUNT_CREATE_COUNTER_NAME, tags).increment();
|
||||||
}
|
}
|
||||||
|
@ -311,6 +359,9 @@ public class AccountController {
|
||||||
throw new WebApplicationException(Response.status(403).build());
|
throw new WebApplicationException(Response.status(403).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storedVerificationCode.flatMap(StoredVerificationCode::getTwilioVerificationSid)
|
||||||
|
.ifPresent(smsSender::reportVerificationSucceeded);
|
||||||
|
|
||||||
Optional<Account> existingAccount = accounts.get(number);
|
Optional<Account> existingAccount = accounts.get(number);
|
||||||
Optional<StoredRegistrationLock> existingRegistrationLock = existingAccount.map(Account::getRegistrationLock);
|
Optional<StoredRegistrationLock> existingRegistrationLock = existingAccount.map(Account::getRegistrationLock);
|
||||||
Optional<ExternalServiceCredentials> existingBackupCredentials = existingAccount.map(Account::getUuid)
|
Optional<ExternalServiceCredentials> existingBackupCredentials = existingAccount.map(Account::getUuid)
|
||||||
|
@ -345,6 +396,7 @@ public class AccountController {
|
||||||
final List<Tag> tags = new ArrayList<>();
|
final List<Tag> tags = new ArrayList<>();
|
||||||
tags.add(Tag.of(COUNTRY_CODE_TAG_NAME, Util.getCountryCode(number)));
|
tags.add(Tag.of(COUNTRY_CODE_TAG_NAME, Util.getCountryCode(number)));
|
||||||
tags.add(UserAgentTagUtil.getPlatformTag(userAgent));
|
tags.add(UserAgentTagUtil.getPlatformTag(userAgent));
|
||||||
|
tags.add(Tag.of(VERIFY_EXPERIMENT_TAG_NAME, String.valueOf(storedVerificationCode.get().getTwilioVerificationSid().isPresent())));
|
||||||
|
|
||||||
Metrics.counter(ACCOUNT_VERIFY_COUNTER_NAME, tags).increment();
|
Metrics.counter(ACCOUNT_VERIFY_COUNTER_NAME, tags).increment();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,17 @@
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.storage;
|
package org.whispersystems.textsecuregcm.storage;
|
||||||
|
|
||||||
|
import static com.codahale.metrics.MetricRegistry.name;
|
||||||
|
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.codahale.metrics.SharedMetricRegistries;
|
import com.codahale.metrics.SharedMetricRegistries;
|
||||||
import com.codahale.metrics.Timer;
|
import com.codahale.metrics.Timer;
|
||||||
|
import java.util.Optional;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||||
import org.whispersystems.textsecuregcm.storage.mappers.StoredVerificationCodeRowMapper;
|
import org.whispersystems.textsecuregcm.storage.mappers.StoredVerificationCodeRowMapper;
|
||||||
import org.whispersystems.textsecuregcm.util.Constants;
|
import org.whispersystems.textsecuregcm.util.Constants;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static com.codahale.metrics.MetricRegistry.name;
|
|
||||||
|
|
||||||
public class PendingAccounts {
|
public class PendingAccounts {
|
||||||
|
|
||||||
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
||||||
|
@ -30,17 +30,23 @@ public class PendingAccounts {
|
||||||
this.database.getDatabase().registerRowMapper(new StoredVerificationCodeRowMapper());
|
this.database.getDatabase().registerRowMapper(new StoredVerificationCodeRowMapper());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
public void insert (String number, String verificationCode, long timestamp, String pushCode) {
|
public void insert (String number, String verificationCode, long timestamp, String pushCode) {
|
||||||
|
insert(number, verificationCode, timestamp, pushCode, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert(String number, String verificationCode, long timestamp, String pushCode, String twilioVerificationSid) {
|
||||||
database.use(jdbi -> jdbi.useHandle(handle -> {
|
database.use(jdbi -> jdbi.useHandle(handle -> {
|
||||||
try (Timer.Context ignored = insertTimer.time()) {
|
try (Timer.Context ignored = insertTimer.time()) {
|
||||||
handle.createUpdate("INSERT INTO pending_accounts (number, verification_code, timestamp, push_code) " +
|
handle.createUpdate("INSERT INTO pending_accounts (number, verification_code, timestamp, push_code, twilio_verification_sid) " +
|
||||||
"VALUES (:number, :verification_code, :timestamp, :push_code) " +
|
"VALUES (:number, :verification_code, :timestamp, :push_code, :twilio_verification_sid) " +
|
||||||
"ON CONFLICT(number) DO UPDATE " +
|
"ON CONFLICT(number) DO UPDATE " +
|
||||||
"SET verification_code = EXCLUDED.verification_code, timestamp = EXCLUDED.timestamp, push_code = EXCLUDED.push_code")
|
"SET verification_code = EXCLUDED.verification_code, timestamp = EXCLUDED.timestamp, push_code = EXCLUDED.push_code, twilio_verification_sid = EXCLUDED.twilio_verification_sid")
|
||||||
.bind("verification_code", verificationCode)
|
.bind("verification_code", verificationCode)
|
||||||
.bind("timestamp", timestamp)
|
.bind("timestamp", timestamp)
|
||||||
.bind("number", number)
|
.bind("number", number)
|
||||||
.bind("push_code", pushCode)
|
.bind("push_code", pushCode)
|
||||||
|
.bind("twilio_verification_sid", twilioVerificationSid)
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -49,7 +55,7 @@ public class PendingAccounts {
|
||||||
public Optional<StoredVerificationCode> getCodeForNumber(String number) {
|
public Optional<StoredVerificationCode> getCodeForNumber(String number) {
|
||||||
return database.with(jdbi ->jdbi.withHandle(handle -> {
|
return database.with(jdbi ->jdbi.withHandle(handle -> {
|
||||||
try (Timer.Context ignored = getCodeForNumberTimer.time()) {
|
try (Timer.Context ignored = getCodeForNumberTimer.time()) {
|
||||||
return handle.createQuery("SELECT verification_code, timestamp, push_code FROM pending_accounts WHERE number = :number")
|
return handle.createQuery("SELECT verification_code, timestamp, push_code, twilio_verification_sid FROM pending_accounts WHERE number = :number")
|
||||||
.bind("number", number)
|
.bind("number", number)
|
||||||
.mapTo(StoredVerificationCode.class)
|
.mapTo(StoredVerificationCode.class)
|
||||||
.findFirst();
|
.findFirst();
|
||||||
|
|
|
@ -34,7 +34,8 @@ public class PendingAccountsManager {
|
||||||
|
|
||||||
public void store(String number, StoredVerificationCode code) {
|
public void store(String number, StoredVerificationCode code) {
|
||||||
memcacheSet(number, code);
|
memcacheSet(number, code);
|
||||||
pendingAccounts.insert(number, code.getCode(), code.getTimestamp(), code.getPushCode());
|
pendingAccounts.insert(number, code.getCode(), code.getTimestamp(), code.getPushCode(),
|
||||||
|
code.getTwilioVerificationSid().orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(String number) {
|
public void remove(String number) {
|
||||||
|
|
|
@ -4,17 +4,16 @@
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.storage;
|
package org.whispersystems.textsecuregcm.storage;
|
||||||
|
|
||||||
|
import static com.codahale.metrics.MetricRegistry.name;
|
||||||
|
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.codahale.metrics.SharedMetricRegistries;
|
import com.codahale.metrics.SharedMetricRegistries;
|
||||||
import com.codahale.metrics.Timer;
|
import com.codahale.metrics.Timer;
|
||||||
|
import java.util.Optional;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||||
import org.whispersystems.textsecuregcm.storage.mappers.StoredVerificationCodeRowMapper;
|
import org.whispersystems.textsecuregcm.storage.mappers.StoredVerificationCodeRowMapper;
|
||||||
import org.whispersystems.textsecuregcm.util.Constants;
|
import org.whispersystems.textsecuregcm.util.Constants;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static com.codahale.metrics.MetricRegistry.name;
|
|
||||||
|
|
||||||
public class PendingDevices {
|
public class PendingDevices {
|
||||||
|
|
||||||
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
||||||
|
@ -45,7 +44,7 @@ public class PendingDevices {
|
||||||
public Optional<StoredVerificationCode> getCodeForNumber(String number) {
|
public Optional<StoredVerificationCode> getCodeForNumber(String number) {
|
||||||
return database.with(jdbi -> jdbi.withHandle(handle -> {
|
return database.with(jdbi -> jdbi.withHandle(handle -> {
|
||||||
try (Timer.Context timer = getCodeForNumberTimer.time()) {
|
try (Timer.Context timer = getCodeForNumberTimer.time()) {
|
||||||
return handle.createQuery("SELECT verification_code, timestamp, NULL as push_code FROM pending_devices WHERE number = :number")
|
return handle.createQuery("SELECT verification_code, timestamp, NULL as push_code, NULL as twilio_verification_sid FROM pending_devices WHERE number = :number")
|
||||||
.bind("number", number)
|
.bind("number", number)
|
||||||
.mapTo(StoredVerificationCode.class)
|
.mapTo(StoredVerificationCode.class)
|
||||||
.findFirst();
|
.findFirst();
|
||||||
|
|
|
@ -18,6 +18,7 @@ public class StoredVerificationCodeRowMapper implements RowMapper<StoredVerifica
|
||||||
public StoredVerificationCode map(ResultSet resultSet, StatementContext ctx) throws SQLException {
|
public StoredVerificationCode map(ResultSet resultSet, StatementContext ctx) throws SQLException {
|
||||||
return new StoredVerificationCode(resultSet.getString("verification_code"),
|
return new StoredVerificationCode(resultSet.getString("verification_code"),
|
||||||
resultSet.getLong("timestamp"),
|
resultSet.getLong("timestamp"),
|
||||||
resultSet.getString("push_code"));
|
resultSet.getString("push_code"),
|
||||||
|
resultSet.getString("twilio_verification_sid"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,4 +367,12 @@
|
||||||
</addColumn>
|
</addColumn>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet id="24" author="chris">
|
||||||
|
<addColumn tableName="pending_accounts">
|
||||||
|
<column name="twilio_verification_sid" type="text">
|
||||||
|
<constraints nullable="true" />
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
@ -7,19 +7,10 @@ package org.whispersystems.textsecuregcm.tests.controllers;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyList;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.mockito.Mockito.doThrow;
|
|
||||||
import static org.mockito.Mockito.eq;
|
|
||||||
import static org.mockito.Mockito.isA;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.reset;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||||
|
@ -34,6 +25,7 @@ import java.util.Locale;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
|
@ -48,6 +40,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.CsvSource;
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.ArgumentMatcher;
|
import org.mockito.ArgumentMatcher;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
||||||
|
@ -76,6 +69,7 @@ import org.whispersystems.textsecuregcm.push.GCMSender;
|
||||||
import org.whispersystems.textsecuregcm.push.GcmMessage;
|
import org.whispersystems.textsecuregcm.push.GcmMessage;
|
||||||
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
|
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
|
||||||
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
||||||
|
import org.whispersystems.textsecuregcm.sms.TwilioVerifyExperimentEnrollmentManager;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
||||||
import org.whispersystems.textsecuregcm.storage.AbusiveHostRule;
|
import org.whispersystems.textsecuregcm.storage.AbusiveHostRule;
|
||||||
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
|
import org.whispersystems.textsecuregcm.storage.AbusiveHostRules;
|
||||||
|
@ -139,6 +133,9 @@ class AccountControllerTest {
|
||||||
|
|
||||||
private static DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
private static DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||||
|
|
||||||
|
private static TwilioVerifyExperimentEnrollmentManager verifyExperimentEnrollmentManager = mock(
|
||||||
|
TwilioVerifyExperimentEnrollmentManager.class);
|
||||||
|
|
||||||
private byte[] registration_lock_key = new byte[32];
|
private byte[] registration_lock_key = new byte[32];
|
||||||
private static ExternalServiceCredentialGenerator storageCredentialGenerator = new ExternalServiceCredentialGenerator(new byte[32], new byte[32], false);
|
private static ExternalServiceCredentialGenerator storageCredentialGenerator = new ExternalServiceCredentialGenerator(new byte[32], new byte[32], false);
|
||||||
|
|
||||||
|
@ -163,7 +160,8 @@ class AccountControllerTest {
|
||||||
recaptchaClient,
|
recaptchaClient,
|
||||||
gcmSender,
|
gcmSender,
|
||||||
apnSender,
|
apnSender,
|
||||||
storageCredentialGenerator))
|
storageCredentialGenerator,
|
||||||
|
verifyExperimentEnrollmentManager))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
@ -267,7 +265,8 @@ class AccountControllerTest {
|
||||||
recaptchaClient,
|
recaptchaClient,
|
||||||
gcmSender,
|
gcmSender,
|
||||||
apnSender,
|
apnSender,
|
||||||
usernamesManager);
|
usernamesManager,
|
||||||
|
verifyExperimentEnrollmentManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -328,8 +327,18 @@ class AccountControllerTest {
|
||||||
verifyNoMoreInteractions(gcmSender);
|
verifyNoMoreInteractions(gcmSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendCode() throws Exception {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendCode(final boolean enrolledInVerifyExperiment) throws Exception {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
||||||
|
@ -340,12 +349,35 @@ class AccountControllerTest {
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
ArgumentCaptor<StoredVerificationCode> storedVerificationCodeArgumentCaptor = ArgumentCaptor
|
||||||
|
.forClass(StoredVerificationCode.class);
|
||||||
|
|
||||||
|
verify(smsSender).deliverSmsVerificationWithTwilioVerify(eq(SENDER), eq(Optional.empty()), anyString(), eq(Collections.emptyList()));
|
||||||
|
verify(pendingAccountsManager, times(2)).store(eq(SENDER), storedVerificationCodeArgumentCaptor.capture());
|
||||||
|
|
||||||
|
assertThat(storedVerificationCodeArgumentCaptor.getValue().getTwilioVerificationSid())
|
||||||
|
.isEqualTo(Optional.of("VerificationSid"));
|
||||||
|
|
||||||
|
} else {
|
||||||
verify(smsSender).deliverSmsVerification(eq(SENDER), eq(Optional.empty()), anyString());
|
verify(smsSender).deliverSmsVerification(eq(SENDER), eq(Optional.empty()), anyString());
|
||||||
|
}
|
||||||
|
verifyNoMoreInteractions(smsSender);
|
||||||
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void testSendCodeVoiceNoLocale() throws Exception {
|
@ValueSource(booleans = {false, true})
|
||||||
|
public void testSendCodeVoiceNoLocale(final boolean enrolledInVerifyExperiment) throws Exception {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverVoxVerificationWithTwilioVerify(anyString(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/voice/code/%s", SENDER))
|
.target(String.format("/v1/accounts/voice/code/%s", SENDER))
|
||||||
|
@ -356,12 +388,26 @@ class AccountControllerTest {
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
verify(smsSender).deliverVoxVerificationWithTwilioVerify(eq(SENDER), anyString(), eq(Collections.emptyList()));
|
||||||
|
} else {
|
||||||
verify(smsSender).deliverVoxVerification(eq(SENDER), anyString(), eq(Collections.emptyList()));
|
verify(smsSender).deliverVoxVerification(eq(SENDER), anyString(), eq(Collections.emptyList()));
|
||||||
|
}
|
||||||
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void testSendCodeVoiceSingleLocale() throws Exception {
|
@ValueSource(booleans = {false, true})
|
||||||
|
public void testSendCodeVoiceSingleLocale(final boolean enrolledInVerifyExperiment) throws Exception {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverVoxVerificationWithTwilioVerify(anyString(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/voice/code/%s", SENDER))
|
.target(String.format("/v1/accounts/voice/code/%s", SENDER))
|
||||||
|
@ -373,12 +419,27 @@ class AccountControllerTest {
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
verify(smsSender)
|
||||||
|
.deliverVoxVerificationWithTwilioVerify(eq(SENDER), anyString(), eq(Locale.LanguageRange.parse("pt-BR")));
|
||||||
|
} else {
|
||||||
verify(smsSender).deliverVoxVerification(eq(SENDER), anyString(), eq(Locale.LanguageRange.parse("pt-BR")));
|
verify(smsSender).deliverVoxVerification(eq(SENDER), anyString(), eq(Locale.LanguageRange.parse("pt-BR")));
|
||||||
|
}
|
||||||
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void testSendCodeVoiceMultipleLocales() throws Exception {
|
@ValueSource(booleans = {false, true})
|
||||||
|
public void testSendCodeVoiceMultipleLocales(final boolean enrolledInVerifyExperiment) throws Exception {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverVoxVerificationWithTwilioVerify(anyString(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/voice/code/%s", SENDER))
|
.target(String.format("/v1/accounts/voice/code/%s", SENDER))
|
||||||
|
@ -390,7 +451,13 @@ class AccountControllerTest {
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
|
|
||||||
verify(smsSender).deliverVoxVerification(eq(SENDER), anyString(), eq(Locale.LanguageRange.parse("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")));
|
if (enrolledInVerifyExperiment) {
|
||||||
|
verify(smsSender).deliverVoxVerificationWithTwilioVerify(eq(SENDER), anyString(), eq(Locale.LanguageRange
|
||||||
|
.parse("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")));
|
||||||
|
} else {
|
||||||
|
verify(smsSender).deliverVoxVerification(eq(SENDER), anyString(), eq(Locale.LanguageRange
|
||||||
|
.parse("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")));
|
||||||
|
}
|
||||||
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,11 +475,22 @@ class AccountControllerTest {
|
||||||
assertThat(response.getStatus()).isEqualTo(400);
|
assertThat(response.getStatus()).isEqualTo(400);
|
||||||
|
|
||||||
verify(smsSender, never()).deliverVoxVerification(eq(SENDER), anyString(), any());
|
verify(smsSender, never()).deliverVoxVerification(eq(SENDER), anyString(), any());
|
||||||
|
verify(smsSender, never()).deliverVoxVerificationWithTwilioVerify(eq(SENDER), anyString(), any());
|
||||||
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendCodeWithValidPreauth() throws Exception {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendCodeWithValidPreauth(final boolean enrolledInVerifyExperiment) throws Exception {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER_PREAUTH))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER_PREAUTH))
|
||||||
|
@ -423,7 +501,12 @@ class AccountControllerTest {
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
verify(smsSender).deliverSmsVerificationWithTwilioVerify(eq(SENDER_PREAUTH), eq(Optional.empty()), anyString(),
|
||||||
|
eq(Collections.emptyList()));
|
||||||
|
} else {
|
||||||
verify(smsSender).deliverSmsVerification(eq(SENDER_PREAUTH), eq(Optional.empty()), anyString());
|
verify(smsSender).deliverSmsVerification(eq(SENDER_PREAUTH), eq(Optional.empty()), anyString());
|
||||||
|
}
|
||||||
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
verify(abusiveHostRules).getAbusiveHostRulesFor(eq(NICE_HOST));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,11 +538,22 @@ class AccountControllerTest {
|
||||||
assertThat(response.getStatus()).isEqualTo(402);
|
assertThat(response.getStatus()).isEqualTo(402);
|
||||||
|
|
||||||
verify(smsSender, never()).deliverSmsVerification(eq(SENDER_PREAUTH), eq(Optional.empty()), anyString());
|
verify(smsSender, never()).deliverSmsVerification(eq(SENDER_PREAUTH), eq(Optional.empty()), anyString());
|
||||||
|
verify(smsSender, never()).deliverSmsVerificationWithTwilioVerify(eq(SENDER_PREAUTH), eq(Optional.empty()), anyString(), anyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendiOSCode() throws Exception {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendiOSCode(final boolean enrolledInVerifyExperiment) throws Exception {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
||||||
|
@ -471,11 +565,26 @@ class AccountControllerTest {
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
verify(smsSender).deliverSmsVerificationWithTwilioVerify(eq(SENDER), eq(Optional.of("ios")), anyString(),
|
||||||
|
eq(Collections.emptyList()));
|
||||||
|
} else {
|
||||||
verify(smsSender).deliverSmsVerification(eq(SENDER), eq(Optional.of("ios")), anyString());
|
verify(smsSender).deliverSmsVerification(eq(SENDER), eq(Optional.of("ios")), anyString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendAndroidNgCode(final boolean enrolledInVerifyExperiment) throws Exception {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSendAndroidNgCode() throws Exception {
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
||||||
|
@ -487,11 +596,26 @@ class AccountControllerTest {
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
verify(smsSender).deliverSmsVerificationWithTwilioVerify(eq(SENDER), eq(Optional.of("android-ng")), anyString(),
|
||||||
|
eq(Collections.emptyList()));
|
||||||
|
} else {
|
||||||
verify(smsSender).deliverSmsVerification(eq(SENDER), eq(Optional.of("android-ng")), anyString());
|
verify(smsSender).deliverSmsVerification(eq(SENDER), eq(Optional.of("android-ng")), anyString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendAbusiveHost(final boolean enrolledInVerifyExperiment) {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSendAbusiveHost() {
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
||||||
|
@ -506,8 +630,18 @@ class AccountControllerTest {
|
||||||
verifyNoMoreInteractions(smsSender);
|
verifyNoMoreInteractions(smsSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendAbusiveHostWithValidCaptcha() throws IOException {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendAbusiveHostWithValidCaptcha(final boolean enrolledInVerifyExperiment) throws IOException {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
||||||
|
@ -520,11 +654,25 @@ class AccountControllerTest {
|
||||||
|
|
||||||
verifyNoMoreInteractions(abusiveHostRules);
|
verifyNoMoreInteractions(abusiveHostRules);
|
||||||
verify(recaptchaClient).verify(eq(VALID_CAPTCHA_TOKEN), eq(ABUSIVE_HOST));
|
verify(recaptchaClient).verify(eq(VALID_CAPTCHA_TOKEN), eq(ABUSIVE_HOST));
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
verify(smsSender).deliverSmsVerificationWithTwilioVerify(eq(SENDER), eq(Optional.empty()), anyString(),
|
||||||
|
eq(Collections.emptyList()));
|
||||||
|
} else {
|
||||||
verify(smsSender).deliverSmsVerification(eq(SENDER), eq(Optional.empty()), anyString());
|
verify(smsSender).deliverSmsVerification(eq(SENDER), eq(Optional.empty()), anyString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendAbusiveHostWithInvalidCaptcha() {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendAbusiveHostWithInvalidCaptcha(final boolean enrolledInVerifyExperiment) {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
||||||
|
@ -540,8 +688,18 @@ class AccountControllerTest {
|
||||||
verifyNoMoreInteractions(smsSender);
|
verifyNoMoreInteractions(smsSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendRateLimitedHostAutoBlock() {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendRateLimitedHostAutoBlock(final boolean enrolledInVerifyExperiment) {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
||||||
|
@ -560,8 +718,18 @@ class AccountControllerTest {
|
||||||
verifyNoMoreInteractions(smsSender);
|
verifyNoMoreInteractions(smsSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendRateLimitedPrefixAutoBlock() {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendRateLimitedPrefixAutoBlock(final boolean enrolledInVerifyExperiment) {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER_OVER_PREFIX))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER_OVER_PREFIX))
|
||||||
|
@ -580,8 +748,18 @@ class AccountControllerTest {
|
||||||
verifyNoMoreInteractions(smsSender);
|
verifyNoMoreInteractions(smsSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendRateLimitedHostNoAutoBlock() {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendRateLimitedHostNoAutoBlock(final boolean enrolledInVerifyExperiment) {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
||||||
|
@ -600,8 +778,13 @@ class AccountControllerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendMultipleHost() {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendMultipleHost(final boolean enrolledInVerifyExperiment) {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
||||||
|
@ -619,8 +802,13 @@ class AccountControllerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendRestrictedHostOut() {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendRestrictedHostOut(final boolean enrolledInVerifyExperiment) {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
.target(String.format("/v1/accounts/sms/code/%s", SENDER))
|
||||||
|
@ -635,8 +823,18 @@ class AccountControllerTest {
|
||||||
verifyNoMoreInteractions(smsSender);
|
verifyNoMoreInteractions(smsSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testSendRestrictedIn() throws Exception {
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testSendRestrictedIn(final boolean enrolledInVerifyExperiment) throws Exception {
|
||||||
|
|
||||||
|
when(verifyExperimentEnrollmentManager.isEnrolled(any(), anyString(), anyList(), anyString()))
|
||||||
|
.thenReturn(enrolledInVerifyExperiment);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(smsSender.deliverSmsVerificationWithTwilioVerify(anyString(), any(), anyString(), anyList()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(Optional.of("VerificationSid")));
|
||||||
|
}
|
||||||
|
|
||||||
final String number = "+12345678901";
|
final String number = "+12345678901";
|
||||||
final String challenge = "challenge";
|
final String challenge = "challenge";
|
||||||
|
|
||||||
|
@ -652,11 +850,24 @@ class AccountControllerTest {
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
verify(smsSender).deliverSmsVerificationWithTwilioVerify(eq(number), eq(Optional.empty()), anyString(),
|
||||||
|
eq(Collections.emptyList()));
|
||||||
|
} else {
|
||||||
verify(smsSender).deliverSmsVerification(eq(number), eq(Optional.empty()), anyString());
|
verify(smsSender).deliverSmsVerification(eq(number), eq(Optional.empty()), anyString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
verifyNoMoreInteractions(smsSender);
|
||||||
void testVerifyCode() throws Exception {
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(booleans = {false, true})
|
||||||
|
void testVerifyCode(final boolean enrolledInVerifyExperiment) throws Exception {
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
when(pendingAccountsManager.getCodeForNumber(SENDER)).thenReturn(
|
||||||
|
Optional.of(new StoredVerificationCode("1234", System.currentTimeMillis(), "1234-push", "VerificationSid")));;
|
||||||
|
}
|
||||||
|
|
||||||
AccountCreationResult result =
|
AccountCreationResult result =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
.target(String.format("/v1/accounts/code/%s", "1234"))
|
.target(String.format("/v1/accounts/code/%s", "1234"))
|
||||||
|
@ -674,6 +885,10 @@ class AccountControllerTest {
|
||||||
verify(directoryQueue, times(1)).refreshRegisteredUser(argThat(account -> SENDER.equals(account.getNumber())));
|
verify(directoryQueue, times(1)).refreshRegisteredUser(argThat(account -> SENDER.equals(account.getNumber())));
|
||||||
|
|
||||||
assertThat(accountArgumentCaptor.getValue().isDiscoverableByPhoneNumber()).isTrue();
|
assertThat(accountArgumentCaptor.getValue().isDiscoverableByPhoneNumber()).isTrue();
|
||||||
|
|
||||||
|
if (enrolledInVerifyExperiment) {
|
||||||
|
verify(smsSender).reportVerificationSucceeded("VerificationSid");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -75,6 +75,28 @@ class PendingAccountsTest {
|
||||||
assertThat(resultSet.next()).isFalse();
|
assertThat(resultSet.next()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testStoreWithTwilioVerificationSid() throws SQLException {
|
||||||
|
pendingAccounts.insert("+14151112222", null, 1111, null, "id1");
|
||||||
|
|
||||||
|
PreparedStatement statement = db.getTestDatabase().getConnection()
|
||||||
|
.prepareStatement("SELECT * FROM pending_accounts WHERE number = ?");
|
||||||
|
statement.setString(1, "+14151112222");
|
||||||
|
|
||||||
|
ResultSet resultSet = statement.executeQuery();
|
||||||
|
|
||||||
|
if (resultSet.next()) {
|
||||||
|
assertThat(resultSet.getString("verification_code")).isNull();
|
||||||
|
assertThat(resultSet.getLong("timestamp")).isEqualTo(1111);
|
||||||
|
assertThat(resultSet.getString("push_code")).isNull();
|
||||||
|
assertThat(resultSet.getString("twilio_verification_sid")).isEqualTo("id1");
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("no results");
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(resultSet.next()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testRetrieve() throws Exception {
|
void testRetrieve() throws Exception {
|
||||||
pendingAccounts.insert("+14151112222", "4321", 2222, null);
|
pendingAccounts.insert("+14151112222", "4321", 2222, null);
|
||||||
|
@ -106,6 +128,23 @@ class PendingAccountsTest {
|
||||||
assertThat(missingCode.isPresent()).isFalse();
|
assertThat(missingCode.isPresent()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRetrieveWithTwilioVerificationSid() throws Exception {
|
||||||
|
pendingAccounts.insert("+14151112222", "4321", 2222, "bar", "id1");
|
||||||
|
pendingAccounts.insert("+14151113333", "1212", 5555, "bang", "id2");
|
||||||
|
|
||||||
|
Optional<StoredVerificationCode> verificationCode = pendingAccounts.getCodeForNumber("+14151112222");
|
||||||
|
|
||||||
|
assertThat(verificationCode).isPresent();
|
||||||
|
assertThat(verificationCode.get().getCode()).isEqualTo("4321");
|
||||||
|
assertThat(verificationCode.get().getTimestamp()).isEqualTo(2222);
|
||||||
|
assertThat(verificationCode.get().getPushCode()).isEqualTo("bar");
|
||||||
|
assertThat(verificationCode.get().getTwilioVerificationSid()).contains("id1");
|
||||||
|
|
||||||
|
Optional<StoredVerificationCode> missingCode = pendingAccounts.getCodeForNumber("+11111111111");
|
||||||
|
assertThat(missingCode).isNotPresent();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testOverwrite() throws Exception {
|
void testOverwrite() throws Exception {
|
||||||
pendingAccounts.insert("+14151112222", "4321", 2222, null);
|
pendingAccounts.insert("+14151112222", "4321", 2222, null);
|
||||||
|
@ -131,6 +170,19 @@ class PendingAccountsTest {
|
||||||
assertThat(verificationCode.get().getPushCode()).isEqualTo("bang");
|
assertThat(verificationCode.get().getPushCode()).isEqualTo("bang");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testOverwriteWithTwilioVerificationSid() throws Exception {
|
||||||
|
pendingAccounts.insert("+14151112222", "4321", 2222, "bar", "id1");
|
||||||
|
pendingAccounts.insert("+14151112222", "4444", 3333, "bang", "id2");
|
||||||
|
|
||||||
|
Optional<StoredVerificationCode> verificationCode = pendingAccounts.getCodeForNumber("+14151112222");
|
||||||
|
|
||||||
|
assertThat(verificationCode.isPresent()).isTrue();
|
||||||
|
assertThat(verificationCode.get().getCode()).isEqualTo("4444");
|
||||||
|
assertThat(verificationCode.get().getTimestamp()).isEqualTo(3333);
|
||||||
|
assertThat(verificationCode.get().getPushCode()).isEqualTo("bang");
|
||||||
|
assertThat(verificationCode.get().getTwilioVerificationSid()).contains("id2");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testVacuum() {
|
void testVacuum() {
|
||||||
|
@ -167,6 +219,4 @@ class PendingAccountsTest {
|
||||||
assertThat(verificationCode.get().getTimestamp()).isEqualTo(5555);
|
assertThat(verificationCode.get().getTimestamp()).isEqualTo(5555);
|
||||||
assertThat(verificationCode.get().getPushCode()).isNull();
|
assertThat(verificationCode.get().getPushCode()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue