mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 23:28:04 +01:00
Add /v1/verification
This commit is contained in:
@@ -22,8 +22,7 @@ class StoredVerificationCodeTest {
|
||||
|
||||
private static Stream<Arguments> isValid() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
new StoredVerificationCode("code", System.currentTimeMillis(), null, null), "code", true),
|
||||
Arguments.of(new StoredVerificationCode("code", System.currentTimeMillis(), null, null), "code", true),
|
||||
Arguments.of(new StoredVerificationCode("code", System.currentTimeMillis(), null, null), "incorrect", false),
|
||||
Arguments.of(new StoredVerificationCode("", System.currentTimeMillis(), null, null), "", false)
|
||||
);
|
||||
|
||||
@@ -75,6 +75,7 @@ import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||
import org.whispersystems.textsecuregcm.captcha.AssessmentResult;
|
||||
import org.whispersystems.textsecuregcm.captcha.CaptchaChecker;
|
||||
import org.whispersystems.textsecuregcm.captcha.RegistrationCaptchaManager;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicCaptchaConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
@@ -197,6 +198,8 @@ class AccountControllerTest {
|
||||
|
||||
private static final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager(
|
||||
accountsManager, clientPresenceManager, backupCredentialsGenerator, rateLimiters);
|
||||
private static final RegistrationCaptchaManager registrationCaptchaManager = new RegistrationCaptchaManager(
|
||||
captchaChecker, rateLimiters, Map.of(TEST_NUMBER, 123456), dynamicConfigurationManager);
|
||||
|
||||
private static final ResourceExtension resources = ResourceExtension.builder()
|
||||
.addProvider(AuthHelper.getAuthFilter())
|
||||
@@ -217,8 +220,7 @@ class AccountControllerTest {
|
||||
registrationServiceClient,
|
||||
dynamicConfigurationManager,
|
||||
turnTokenGenerator,
|
||||
Map.of(TEST_NUMBER, 123456),
|
||||
captchaChecker,
|
||||
registrationCaptchaManager,
|
||||
pushNotificationManager,
|
||||
changeNumberManager,
|
||||
registrationLockVerificationManager,
|
||||
@@ -250,30 +252,43 @@ class AccountControllerTest {
|
||||
when(rateLimiters.getUsernameLookupLimiter()).thenReturn(usernameLookupLimiter);
|
||||
|
||||
when(senderPinAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
|
||||
when(senderPinAccount.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.empty(), Optional.empty(), System.currentTimeMillis()));
|
||||
when(senderPinAccount.getRegistrationLock()).thenReturn(
|
||||
new StoredRegistrationLock(Optional.empty(), Optional.empty(), System.currentTimeMillis()));
|
||||
|
||||
when(senderHasStorage.getUuid()).thenReturn(UUID.randomUUID());
|
||||
when(senderHasStorage.isStorageSupported()).thenReturn(true);
|
||||
when(senderHasStorage.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.empty(), Optional.empty(), System.currentTimeMillis()));
|
||||
when(senderHasStorage.getRegistrationLock()).thenReturn(
|
||||
new StoredRegistrationLock(Optional.empty(), Optional.empty(), System.currentTimeMillis()));
|
||||
|
||||
when(senderRegLockAccount.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.of(registrationLockCredentials.hash()), Optional.of(registrationLockCredentials.salt()), System.currentTimeMillis()));
|
||||
when(senderRegLockAccount.getRegistrationLock()).thenReturn(
|
||||
new StoredRegistrationLock(Optional.of(registrationLockCredentials.hash()),
|
||||
Optional.of(registrationLockCredentials.salt()), System.currentTimeMillis()));
|
||||
when(senderRegLockAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
|
||||
when(senderRegLockAccount.getUuid()).thenReturn(SENDER_REG_LOCK_UUID);
|
||||
when(senderRegLockAccount.getNumber()).thenReturn(SENDER_REG_LOCK);
|
||||
|
||||
when(senderTransfer.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.empty(), Optional.empty(), System.currentTimeMillis()));
|
||||
when(senderTransfer.getRegistrationLock()).thenReturn(
|
||||
new StoredRegistrationLock(Optional.empty(), Optional.empty(), System.currentTimeMillis()));
|
||||
when(senderTransfer.getUuid()).thenReturn(SENDER_TRANSFER_UUID);
|
||||
when(senderTransfer.getNumber()).thenReturn(SENDER_TRANSFER);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER)).thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "1234-push", null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "1234-push", null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_OLD)).thenReturn(Optional.empty());
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_PIN)).thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), null, null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_REG_LOCK)).thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), null, null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_OVER_PIN)).thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), null, null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_OVER_PREFIX)).thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "1234-push", null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_PREAUTH)).thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "validchallenge", null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_HAS_STORAGE)).thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), null, null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_TRANSFER)).thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), null, null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_PIN)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), null, null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_REG_LOCK)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), null, null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_OVER_PIN)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), null, null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_OVER_PREFIX)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "1234-push", null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_PREAUTH)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "validchallenge", null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_HAS_STORAGE)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), null, null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_TRANSFER)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), null, null)));
|
||||
|
||||
when(accountsManager.getByE164(eq(SENDER_PIN))).thenReturn(Optional.of(senderPinAccount));
|
||||
when(accountsManager.getByE164(eq(SENDER_REG_LOCK))).thenReturn(Optional.of(senderRegLockAccount));
|
||||
@@ -953,7 +968,8 @@ class AccountControllerTest {
|
||||
final String challenge = "challenge";
|
||||
final byte[] sessionId = "session".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER)).thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), challenge, null)));
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER)).thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), challenge, null)));
|
||||
when(registrationServiceClient.sendRegistrationCode(any(), any(), any(), any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(sessionId));
|
||||
|
||||
@@ -1103,8 +1119,8 @@ class AccountControllerTest {
|
||||
final byte[] sessionId = "session".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_REG_LOCK))
|
||||
.thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), "666666-push", sessionId)));
|
||||
.thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "666666-push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(sessionId, "666666", AccountController.REGISTRATION_RPC_TIMEOUT))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1137,8 +1153,8 @@ class AccountControllerTest {
|
||||
final byte[] sessionId = "session".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_REG_LOCK))
|
||||
.thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), "666666-push", sessionId)));
|
||||
.thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "666666-push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(sessionId, "666666", AccountController.REGISTRATION_RPC_TIMEOUT))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1164,8 +1180,8 @@ class AccountControllerTest {
|
||||
final byte[] sessionId = "session".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_REG_LOCK))
|
||||
.thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), "666666-push", sessionId)));
|
||||
.thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "666666-push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(sessionId, "666666", AccountController.REGISTRATION_RPC_TIMEOUT))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1191,8 +1207,8 @@ class AccountControllerTest {
|
||||
final byte[] sessionId = "session".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_REG_LOCK))
|
||||
.thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), "666666-push", sessionId)));
|
||||
.thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "666666-push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(sessionId, "666666", AccountController.REGISTRATION_RPC_TIMEOUT))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1224,8 +1240,7 @@ class AccountControllerTest {
|
||||
final byte[] sessionId = "session".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_TRANSFER))
|
||||
.thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), "1234-push", sessionId)));
|
||||
.thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "1234-push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(sessionId, "1234", AccountController.REGISTRATION_RPC_TIMEOUT))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1249,8 +1264,7 @@ class AccountControllerTest {
|
||||
final byte[] sessionId = "session".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_TRANSFER))
|
||||
.thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), "1234-push", sessionId)));
|
||||
.thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "1234-push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(sessionId, "1234", AccountController.REGISTRATION_RPC_TIMEOUT))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1274,8 +1288,7 @@ class AccountControllerTest {
|
||||
final byte[] sessionId = "session".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(SENDER_TRANSFER))
|
||||
.thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), "1234-push", sessionId)));
|
||||
.thenReturn(Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "1234-push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(sessionId, "1234", AccountController.REGISTRATION_RPC_TIMEOUT))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1299,8 +1312,8 @@ class AccountControllerTest {
|
||||
final String code = "987654";
|
||||
final byte[] sessionId = "session".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), "push", sessionId)));
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(any(), any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1400,8 +1413,8 @@ class AccountControllerTest {
|
||||
final String code = "987654";
|
||||
final byte[] sessionId = "session-id".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of(
|
||||
new StoredVerificationCode(code, System.currentTimeMillis(), "push", sessionId)));
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(code, System.currentTimeMillis(), "push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(any(), any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(false));
|
||||
@@ -1426,8 +1439,8 @@ class AccountControllerTest {
|
||||
final String code = "987654";
|
||||
final byte[] sessionId = "session-id".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of(
|
||||
new StoredVerificationCode(code, System.currentTimeMillis(), "push", sessionId)));
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(code, System.currentTimeMillis(), "push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(any(), any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1460,8 +1473,8 @@ class AccountControllerTest {
|
||||
final String code = "987654";
|
||||
final byte[] sessionId = "session-id".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of(
|
||||
new StoredVerificationCode(code, System.currentTimeMillis(), "push", sessionId)));
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(code, System.currentTimeMillis(), "push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(any(), any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1539,8 +1552,8 @@ class AccountControllerTest {
|
||||
final String reglock = "setec-astronomy";
|
||||
final byte[] sessionId = "session-id".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), "push", sessionId)));
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(any(), any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
@@ -1591,15 +1604,15 @@ class AccountControllerTest {
|
||||
when(AuthHelper.VALID_ACCOUNT.getDevice(2L)).thenReturn(Optional.of(device2));
|
||||
when(AuthHelper.VALID_ACCOUNT.getDevice(3L)).thenReturn(Optional.of(device3));
|
||||
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of(
|
||||
new StoredVerificationCode(null, System.currentTimeMillis(), "push", sessionId)));
|
||||
when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(
|
||||
Optional.of(new StoredVerificationCode(null, System.currentTimeMillis(), "push", sessionId)));
|
||||
|
||||
when(registrationServiceClient.checkVerificationCode(any(), any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
|
||||
var deviceMessages = List.of(
|
||||
new IncomingMessage(1, 2, 2, "content2"),
|
||||
new IncomingMessage(1, 3, 3, "content3"));
|
||||
new IncomingMessage(1, 2, 2, "content2"),
|
||||
new IncomingMessage(1, 3, 3, "content3"));
|
||||
var deviceKeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey(), 3L, new SignedPreKey());
|
||||
|
||||
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 47, 3L, 89);
|
||||
@@ -2231,8 +2244,10 @@ class AccountControllerTest {
|
||||
Arguments.of("123456", null, false),
|
||||
Arguments.of(null, new StoredVerificationCode(null, 0, null, null), false),
|
||||
Arguments.of(null, new StoredVerificationCode(null, 0, "123456", null), false),
|
||||
Arguments.of("654321", new StoredVerificationCode(null, 0, "123456", null), false),
|
||||
Arguments.of("123456", new StoredVerificationCode(null, 0, "123456", null), true)
|
||||
Arguments.of("654321", new StoredVerificationCode(null, 0, "123456", null),
|
||||
false),
|
||||
Arguments.of("123456", new StoredVerificationCode(null, 0, "123456", null),
|
||||
true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -59,7 +60,7 @@ import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager
|
||||
import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
|
||||
import org.whispersystems.textsecuregcm.entities.ChangeNumberRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.PhoneNumberDiscoverabilityRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.RegistrationSession;
|
||||
import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.mappers.ImpossiblePhoneNumberExceptionMapper;
|
||||
@@ -78,7 +79,9 @@ import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||
class AccountControllerV2Test {
|
||||
|
||||
public static final String NEW_NUMBER = PhoneNumberUtil.getInstance().format(
|
||||
private static final long SESSION_EXPIRATION_SECONDS = Duration.ofMinutes(10).toSeconds();
|
||||
|
||||
private static final String NEW_NUMBER = PhoneNumberUtil.getInstance().format(
|
||||
PhoneNumberUtil.getInstance().getExampleNumber("US"),
|
||||
PhoneNumberUtil.PhoneNumberFormat.E164);
|
||||
|
||||
@@ -146,7 +149,9 @@ class AccountControllerV2Test {
|
||||
void changeNumberSuccess() throws Exception {
|
||||
|
||||
when(registrationServiceClient.getSession(any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(new RegistrationSession(NEW_NUMBER, true))));
|
||||
.thenReturn(CompletableFuture.completedFuture(
|
||||
Optional.of(new RegistrationServiceSession(new byte[16], NEW_NUMBER, true, null, null, null,
|
||||
SESSION_EXPIRATION_SECONDS))));
|
||||
|
||||
final AccountIdentityResponse accountIdentityResponse =
|
||||
resources.getJerseyTest()
|
||||
@@ -245,7 +250,7 @@ class AccountControllerV2Test {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void registrationServiceSessionCheck(@Nullable final RegistrationSession session, final int expectedStatus,
|
||||
void registrationServiceSessionCheck(@Nullable final RegistrationServiceSession session, final int expectedStatus,
|
||||
final String message) {
|
||||
when(registrationServiceClient.getSession(any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.ofNullable(session)));
|
||||
@@ -263,8 +268,14 @@ class AccountControllerV2Test {
|
||||
static Stream<Arguments> registrationServiceSessionCheck() {
|
||||
return Stream.of(
|
||||
Arguments.of(null, 401, "session not found"),
|
||||
Arguments.of(new RegistrationSession("+18005551234", false), 400, "session number mismatch"),
|
||||
Arguments.of(new RegistrationSession(NEW_NUMBER, false), 401, "session not verified")
|
||||
Arguments.of(new RegistrationServiceSession(new byte[16], "+18005551234", false, null, null, null,
|
||||
SESSION_EXPIRATION_SECONDS), 400,
|
||||
"session number mismatch"),
|
||||
Arguments.of(
|
||||
new RegistrationServiceSession(new byte[16], NEW_NUMBER, false, null, null, null,
|
||||
SESSION_EXPIRATION_SECONDS),
|
||||
401,
|
||||
"session not verified")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -273,7 +284,9 @@ class AccountControllerV2Test {
|
||||
void registrationLock(final RegistrationLockError error) throws Exception {
|
||||
when(registrationServiceClient.getSession(any(), any()))
|
||||
.thenReturn(
|
||||
CompletableFuture.completedFuture(Optional.of(new RegistrationSession(NEW_NUMBER, true))));
|
||||
CompletableFuture.completedFuture(
|
||||
Optional.of(new RegistrationServiceSession(new byte[16], NEW_NUMBER, true, null, null, null,
|
||||
SESSION_EXPIRATION_SECONDS))));
|
||||
|
||||
when(accountsManager.getByE164(any())).thenReturn(Optional.of(mock(Account.class)));
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -43,7 +44,7 @@ import org.whispersystems.textsecuregcm.auth.RegistrationLockError;
|
||||
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
|
||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||
import org.whispersystems.textsecuregcm.entities.RegistrationRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.RegistrationSession;
|
||||
import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.mappers.ImpossiblePhoneNumberExceptionMapper;
|
||||
@@ -59,11 +60,12 @@ import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
@ExtendWith(DropwizardExtensionsSupport.class)
|
||||
class RegistrationControllerTest {
|
||||
|
||||
private static final long SESSION_EXPIRATION_SECONDS = Duration.ofMinutes(10).toSeconds();
|
||||
|
||||
private static final String NUMBER = PhoneNumberUtil.getInstance().format(
|
||||
PhoneNumberUtil.getInstance().getExampleNumber("US"),
|
||||
PhoneNumberUtil.PhoneNumberFormat.E164);
|
||||
|
||||
public static final String PASSWORD = "password";
|
||||
private static final String PASSWORD = "password";
|
||||
|
||||
private final AccountsManager accountsManager = mock(AccountsManager.class);
|
||||
private final RegistrationServiceClient registrationServiceClient = mock(RegistrationServiceClient.class);
|
||||
@@ -187,7 +189,7 @@ class RegistrationControllerTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void registrationServiceSessionCheck(@Nullable final RegistrationSession session, final int expectedStatus,
|
||||
void registrationServiceSessionCheck(@Nullable final RegistrationServiceSession session, final int expectedStatus,
|
||||
final String message) {
|
||||
when(registrationServiceClient.getSession(any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.ofNullable(session)));
|
||||
@@ -204,8 +206,15 @@ class RegistrationControllerTest {
|
||||
static Stream<Arguments> registrationServiceSessionCheck() {
|
||||
return Stream.of(
|
||||
Arguments.of(null, 401, "session not found"),
|
||||
Arguments.of(new RegistrationSession("+18005551234", false), 400, "session number mismatch"),
|
||||
Arguments.of(new RegistrationSession(NUMBER, false), 401, "session not verified")
|
||||
Arguments.of(
|
||||
new RegistrationServiceSession(new byte[16], "+18005551234", false, null, null, null,
|
||||
SESSION_EXPIRATION_SECONDS),
|
||||
400,
|
||||
"session number mismatch"),
|
||||
Arguments.of(
|
||||
new RegistrationServiceSession(new byte[16], NUMBER, false, null, null, null, SESSION_EXPIRATION_SECONDS),
|
||||
401,
|
||||
"session not verified")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -244,7 +253,10 @@ class RegistrationControllerTest {
|
||||
@EnumSource(RegistrationLockError.class)
|
||||
void registrationLock(final RegistrationLockError error) throws Exception {
|
||||
when(registrationServiceClient.getSession(any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(new RegistrationSession(NUMBER, true))));
|
||||
.thenReturn(
|
||||
CompletableFuture.completedFuture(
|
||||
Optional.of(new RegistrationServiceSession(new byte[16], NUMBER, true, null, null, null,
|
||||
SESSION_EXPIRATION_SECONDS))));
|
||||
|
||||
when(accountsManager.getByE164(any())).thenReturn(Optional.of(mock(Account.class)));
|
||||
|
||||
@@ -275,7 +287,10 @@ class RegistrationControllerTest {
|
||||
void deviceTransferAvailable(final boolean existingAccount, final boolean transferSupported,
|
||||
final boolean skipDeviceTransfer, final int expectedStatus) throws Exception {
|
||||
when(registrationServiceClient.getSession(any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(new RegistrationSession(NUMBER, true))));
|
||||
.thenReturn(
|
||||
CompletableFuture.completedFuture(
|
||||
Optional.of(new RegistrationServiceSession(new byte[16], NUMBER, true, null, null, null,
|
||||
SESSION_EXPIRATION_SECONDS))));
|
||||
|
||||
final Optional<Account> maybeAccount;
|
||||
if (existingAccount) {
|
||||
@@ -301,7 +316,10 @@ class RegistrationControllerTest {
|
||||
@Test
|
||||
void registrationSuccess() throws Exception {
|
||||
when(registrationServiceClient.getSession(any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(new RegistrationSession(NUMBER, true))));
|
||||
.thenReturn(
|
||||
CompletableFuture.completedFuture(
|
||||
Optional.of(new RegistrationServiceSession(new byte[16], NUMBER, true, null, null, null,
|
||||
SESSION_EXPIRATION_SECONDS))));
|
||||
when(accountsManager.create(any(), any(), any(), any(), any()))
|
||||
.thenReturn(mock(Account.class));
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.storage;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
||||
|
||||
class SerializedExpireableJsonDynamoStoreTest {
|
||||
|
||||
static abstract class Tests<T> {
|
||||
|
||||
private static final String TABLE_NAME = "test";
|
||||
private static final String KEY = "foo";
|
||||
|
||||
static final Clock clock = Clock.systemUTC();
|
||||
|
||||
interface Value {
|
||||
|
||||
String v();
|
||||
}
|
||||
|
||||
@RegisterExtension
|
||||
static final DynamoDbExtension DYNAMO_DB_EXTENSION = DynamoDbExtension.builder()
|
||||
.tableName(TABLE_NAME)
|
||||
.hashKey(SerializedExpireableJsonDynamoStore.KEY_KEY)
|
||||
.attributeDefinition(AttributeDefinition.builder()
|
||||
.attributeName(SerializedExpireableJsonDynamoStore.KEY_KEY)
|
||||
.attributeType(ScalarAttributeType.S)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
private SerializedExpireableJsonDynamoStore<T> store;
|
||||
|
||||
abstract SerializedExpireableJsonDynamoStore<T> getStore(final DynamoDbAsyncClient dynamoDbClient,
|
||||
final String tableName);
|
||||
|
||||
abstract T testValue(final String v);
|
||||
|
||||
abstract T maybeExpiredTestValue(final String v);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
store = getStore(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), TABLE_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStoreAndFind() throws Exception {
|
||||
assertEquals(Optional.empty(), store.findForKey(KEY).get(1, TimeUnit.SECONDS));
|
||||
|
||||
final T original = testValue("1234");
|
||||
final T second = testValue("5678");
|
||||
|
||||
store.insert(KEY, original).get(1, TimeUnit.SECONDS);
|
||||
{
|
||||
final Optional<T> maybeValue = store.findForKey(KEY).get(1, TimeUnit.SECONDS);
|
||||
|
||||
assertTrue(maybeValue.isPresent());
|
||||
assertEquals(original, maybeValue.get());
|
||||
}
|
||||
|
||||
assertThrows(Exception.class, () -> store.insert(KEY, second).get(1, TimeUnit.SECONDS));
|
||||
|
||||
assertDoesNotThrow(() -> store.update(KEY, second).get(1, TimeUnit.SECONDS));
|
||||
{
|
||||
final Optional<T> maybeValue = store.findForKey(KEY).get(1, TimeUnit.SECONDS);
|
||||
|
||||
assertTrue(maybeValue.isPresent());
|
||||
assertEquals(second, maybeValue.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemove() throws Exception {
|
||||
assertEquals(Optional.empty(), store.findForKey(KEY).get(1, TimeUnit.SECONDS));
|
||||
|
||||
store.insert(KEY, testValue("1234")).get(1, TimeUnit.SECONDS);
|
||||
assertTrue(store.findForKey(KEY).get(1, TimeUnit.SECONDS).isPresent());
|
||||
|
||||
store.remove(KEY).get(1, TimeUnit.SECONDS);
|
||||
assertFalse(store.findForKey(KEY).get(1, TimeUnit.SECONDS).isPresent());
|
||||
|
||||
final T v = maybeExpiredTestValue("1234");
|
||||
store.insert(KEY, v).get(1, TimeUnit.SECONDS);
|
||||
|
||||
assertEquals(v instanceof SerializedExpireableJsonDynamoStore.Expireable,
|
||||
store.findForKey(KEY).get(1, TimeUnit.SECONDS).isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
record Expires(String v, long timestamp) implements SerializedExpireableJsonDynamoStore.Expireable, Tests.Value {
|
||||
|
||||
static final Duration EXPIRATION = Duration.ofSeconds(30);
|
||||
|
||||
@Override
|
||||
public long getExpirationEpochSeconds() {
|
||||
return Instant.ofEpochMilli(timestamp()).plus(EXPIRATION).getEpochSecond();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Expireable extends Tests<Expires> {
|
||||
|
||||
class ExpiresStore extends SerializedExpireableJsonDynamoStore<Expires> {
|
||||
|
||||
public ExpiresStore(final DynamoDbAsyncClient dynamoDbClient, final String tableName) {
|
||||
super(dynamoDbClient, tableName, clock);
|
||||
}
|
||||
}
|
||||
|
||||
private static final long VALID_TIMESTAMP = Instant.now().toEpochMilli();
|
||||
private static final long EXPIRED_TIMESTAMP = Instant.now().minus(Expires.EXPIRATION).minus(
|
||||
Duration.ofHours(1)).toEpochMilli();
|
||||
|
||||
@Override
|
||||
SerializedExpireableJsonDynamoStore<Expires> getStore(final DynamoDbAsyncClient dynamoDbClient,
|
||||
final String tableName) {
|
||||
return new ExpiresStore(dynamoDbClient, tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
Expires testValue(final String v) {
|
||||
return new Expires(v, VALID_TIMESTAMP);
|
||||
}
|
||||
|
||||
@Override
|
||||
Expires maybeExpiredTestValue(final String v) {
|
||||
return new Expires(v, EXPIRED_TIMESTAMP);
|
||||
}
|
||||
}
|
||||
|
||||
record DoesNotExpire(String v) implements Tests.Value {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class NotExpireable extends Tests<DoesNotExpire> {
|
||||
|
||||
class DoesNotExpireStore extends SerializedExpireableJsonDynamoStore<DoesNotExpire> {
|
||||
|
||||
public DoesNotExpireStore(final DynamoDbAsyncClient dynamoDbClient, final String tableName) {
|
||||
super(dynamoDbClient, tableName, clock);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
SerializedExpireableJsonDynamoStore<DoesNotExpire> getStore(final DynamoDbAsyncClient dynamoDbClient,
|
||||
final String tableName) {
|
||||
return new DoesNotExpireStore(dynamoDbClient, tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
DoesNotExpire testValue(final String v) {
|
||||
return new DoesNotExpire(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
DoesNotExpire maybeExpiredTestValue(final String v) {
|
||||
return new DoesNotExpire(v);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,8 +53,10 @@ class VerificationCodeStoreTest {
|
||||
void testStoreAndFind() {
|
||||
assertEquals(Optional.empty(), verificationCodeStore.findForNumber(PHONE_NUMBER));
|
||||
|
||||
final StoredVerificationCode originalCode = new StoredVerificationCode("1234", VALID_TIMESTAMP, "abcd", "session".getBytes(StandardCharsets.UTF_8));
|
||||
final StoredVerificationCode secondCode = new StoredVerificationCode("5678", VALID_TIMESTAMP, "efgh", "changed-session".getBytes(StandardCharsets.UTF_8));
|
||||
final StoredVerificationCode originalCode = new StoredVerificationCode("1234", VALID_TIMESTAMP, "abcd",
|
||||
"session".getBytes(StandardCharsets.UTF_8));
|
||||
final StoredVerificationCode secondCode = new StoredVerificationCode("5678", VALID_TIMESTAMP, "efgh",
|
||||
"changed-session".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
verificationCodeStore.insert(PHONE_NUMBER, originalCode);
|
||||
{
|
||||
@@ -77,13 +79,15 @@ class VerificationCodeStoreTest {
|
||||
void testRemove() {
|
||||
assertEquals(Optional.empty(), verificationCodeStore.findForNumber(PHONE_NUMBER));
|
||||
|
||||
verificationCodeStore.insert(PHONE_NUMBER, new StoredVerificationCode("1234", VALID_TIMESTAMP, "abcd", "session".getBytes(StandardCharsets.UTF_8)));
|
||||
verificationCodeStore.insert(PHONE_NUMBER,
|
||||
new StoredVerificationCode("1234", VALID_TIMESTAMP, "abcd", "session".getBytes(StandardCharsets.UTF_8)));
|
||||
assertTrue(verificationCodeStore.findForNumber(PHONE_NUMBER).isPresent());
|
||||
|
||||
verificationCodeStore.remove(PHONE_NUMBER);
|
||||
assertFalse(verificationCodeStore.findForNumber(PHONE_NUMBER).isPresent());
|
||||
|
||||
verificationCodeStore.insert(PHONE_NUMBER, new StoredVerificationCode("1234", EXPIRED_TIMESTAMP, "abcd", "session".getBytes(StandardCharsets.UTF_8)));
|
||||
verificationCodeStore.insert(PHONE_NUMBER,
|
||||
new StoredVerificationCode("1234", EXPIRED_TIMESTAMP, "abcd", "session".getBytes(StandardCharsets.UTF_8)));
|
||||
assertFalse(verificationCodeStore.findForNumber(PHONE_NUMBER).isPresent());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.storage;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.whispersystems.textsecuregcm.registration.VerificationSession;
|
||||
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
|
||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
||||
import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
|
||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
||||
|
||||
class VerificationSessionsTest {
|
||||
|
||||
private static final String TABLE_NAME = "verification_sessions_test";
|
||||
|
||||
private static final Clock clock = Clock.systemUTC();
|
||||
|
||||
@RegisterExtension
|
||||
static final DynamoDbExtension DYNAMO_DB_EXTENSION = DynamoDbExtension.builder()
|
||||
.tableName(TABLE_NAME)
|
||||
.hashKey(VerificationSessions.KEY_KEY)
|
||||
.attributeDefinition(AttributeDefinition.builder()
|
||||
.attributeName(VerificationSessions.KEY_KEY)
|
||||
.attributeType(ScalarAttributeType.S)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
private VerificationSessions verificationSessions;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
verificationSessions = new VerificationSessions(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), TABLE_NAME, clock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExpiration() {
|
||||
final Instant created = Instant.now().minusSeconds(60);
|
||||
final Instant updates = Instant.now();
|
||||
final Duration remoteExpiration = Duration.ofMinutes(2);
|
||||
|
||||
final VerificationSession verificationSession = new VerificationSession(null,
|
||||
List.of(VerificationSession.Information.PUSH_CHALLENGE), Collections.emptyList(), true,
|
||||
created.toEpochMilli(), updates.toEpochMilli(), remoteExpiration.toSeconds());
|
||||
|
||||
assertEquals(updates.plus(remoteExpiration).getEpochSecond(), verificationSession.getExpirationEpochSeconds());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStore() {
|
||||
|
||||
assertTimeoutPreemptively(Duration.ofSeconds(5), () -> {
|
||||
|
||||
final String sessionId = "sessionId";
|
||||
|
||||
final Optional<VerificationSession> absentSession = verificationSessions.findForKey(sessionId).join();
|
||||
assertTrue(absentSession.isEmpty());
|
||||
|
||||
final VerificationSession session = new VerificationSession(null,
|
||||
List.of(VerificationSession.Information.PUSH_CHALLENGE), Collections.emptyList(), true,
|
||||
clock.millis(), clock.millis(), Duration.ofMinutes(1).toSeconds());
|
||||
|
||||
verificationSessions.insert(sessionId, session).join();
|
||||
|
||||
assertEquals(session, verificationSessions.findForKey(sessionId).join().orElseThrow());
|
||||
|
||||
final CompletionException ce = assertThrows(CompletionException.class,
|
||||
() -> verificationSessions.insert(sessionId, session).join());
|
||||
|
||||
final Throwable t = ExceptionUtils.unwrap(ce);
|
||||
assertTrue(t instanceof ConditionalCheckFailedException,
|
||||
"inserting with the same key should fail conditional checks");
|
||||
|
||||
final VerificationSession updatedSession = new VerificationSession(null, Collections.emptyList(),
|
||||
List.of(VerificationSession.Information.PUSH_CHALLENGE), true, clock.millis(), clock.millis(),
|
||||
Duration.ofMinutes(2).toSeconds());
|
||||
verificationSessions.update(sessionId, updatedSession).join();
|
||||
|
||||
assertEquals(updatedSession, verificationSessions.findForKey(sessionId).join().orElseThrow());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
@@ -36,8 +35,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
|
||||
Reference in New Issue
Block a user