Add svr2 credentials to RegistrationLockFailure responses

Add an svr2 credential to 423 responses for:
  - PUT v2/accounts/number
  - POST v1/registration

Also add some openapi annotations to those endpoints
This commit is contained in:
Ravi Khadiwala 2023-05-11 19:17:35 -05:00 committed by ravi-signal
parent 7395b5760a
commit 8c42199baf
10 changed files with 93 additions and 23 deletions

View File

@ -525,7 +525,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
config.getDynamoDbTables().getSubscriptions().getTableName(), dynamoDbAsyncClient); config.getDynamoDbTables().getSubscriptions().getTableName(), dynamoDbAsyncClient);
final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager( final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager(
accountsManager, clientPresenceManager, backupCredentialsGenerator, registrationRecoveryPasswordsManager, pushNotificationManager, rateLimiters); accountsManager, clientPresenceManager, backupCredentialsGenerator, svr2CredentialsGenerator, registrationRecoveryPasswordsManager, pushNotificationManager, rateLimiters);
final PhoneVerificationTokenManager phoneVerificationTokenManager = new PhoneVerificationTokenManager( final PhoneVerificationTokenManager phoneVerificationTokenManager = new PhoneVerificationTokenManager(
registrationServiceClient, registrationRecoveryPasswordsManager); registrationServiceClient, registrationRecoveryPasswordsManager);

View File

@ -8,13 +8,16 @@ package org.whispersystems.textsecuregcm.auth;
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name; import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import javax.annotation.Nullable;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Tags;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import javax.annotation.Nullable;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException; import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest; import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure; import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure;
@ -28,9 +31,6 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager; import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.textsecuregcm.util.Util;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
public class RegistrationLockVerificationManager { public class RegistrationLockVerificationManager {
public enum Flow { public enum Flow {
@ -54,20 +54,23 @@ public class RegistrationLockVerificationManager {
private final AccountsManager accounts; private final AccountsManager accounts;
private final ClientPresenceManager clientPresenceManager; private final ClientPresenceManager clientPresenceManager;
private final ExternalServiceCredentialsGenerator backupServiceCredentialGenerator; private final ExternalServiceCredentialsGenerator svr1CredentialGenerator;
private final ExternalServiceCredentialsGenerator svr2CredentialGenerator;
private final RateLimiters rateLimiters; private final RateLimiters rateLimiters;
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager; private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
private final PushNotificationManager pushNotificationManager; private final PushNotificationManager pushNotificationManager;
public RegistrationLockVerificationManager( public RegistrationLockVerificationManager(
final AccountsManager accounts, final ClientPresenceManager clientPresenceManager, final AccountsManager accounts, final ClientPresenceManager clientPresenceManager,
final ExternalServiceCredentialsGenerator backupServiceCredentialGenerator, final ExternalServiceCredentialsGenerator svr1CredentialGenerator,
final ExternalServiceCredentialsGenerator svr2CredentialGenerator,
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager, final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
final PushNotificationManager pushNotificationManager, final PushNotificationManager pushNotificationManager,
final RateLimiters rateLimiters) { final RateLimiters rateLimiters) {
this.accounts = accounts; this.accounts = accounts;
this.clientPresenceManager = clientPresenceManager; this.clientPresenceManager = clientPresenceManager;
this.backupServiceCredentialGenerator = backupServiceCredentialGenerator; this.svr1CredentialGenerator = svr1CredentialGenerator;
this.svr2CredentialGenerator = svr2CredentialGenerator;
this.registrationRecoveryPasswordsManager = registrationRecoveryPasswordsManager; this.registrationRecoveryPasswordsManager = registrationRecoveryPasswordsManager;
this.pushNotificationManager = pushNotificationManager; this.pushNotificationManager = pushNotificationManager;
this.rateLimiters = rateLimiters; this.rateLimiters = rateLimiters;
@ -138,8 +141,8 @@ public class RegistrationLockVerificationManager {
// Freezing the existing account credentials will definitively start the reglock timeout. // Freezing the existing account credentials will definitively start the reglock timeout.
// Until the timeout, the current reglock can still be supplied, // Until the timeout, the current reglock can still be supplied,
// along with phone number verification, to restore access. // along with phone number verification, to restore access.
final ExternalServiceCredentials existingBackupCredentials = final ExternalServiceCredentials existingSvr1Credentials = svr1CredentialGenerator.generateForUuid(account.getUuid());
backupServiceCredentialGenerator.generateForUuid(account.getUuid()); final ExternalServiceCredentials existingSvr2Credentials = svr2CredentialGenerator.generateForUuid(account.getUuid());
final Account updatedAccount; final Account updatedAccount;
if (!alreadyLocked) { if (!alreadyLocked) {
@ -170,7 +173,8 @@ public class RegistrationLockVerificationManager {
throw new WebApplicationException(Response.status(FAILURE_HTTP_STATUS) throw new WebApplicationException(Response.status(FAILURE_HTTP_STATUS)
.entity(new RegistrationLockFailure(existingRegistrationLock.getTimeRemaining().toMillis(), .entity(new RegistrationLockFailure(existingRegistrationLock.getTimeRemaining().toMillis(),
existingRegistrationLock.needsFailureCredentials() ? existingBackupCredentials : null)) existingRegistrationLock.needsFailureCredentials() ? existingSvr1Credentials : null,
existingRegistrationLock.needsFailureCredentials() ? existingSvr2Credentials : null))
.build()); .build());
} }

View File

@ -14,6 +14,9 @@ import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Tags;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.headers.Header;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.time.Instant; import java.time.Instant;
import java.util.Optional; import java.util.Optional;
@ -39,8 +42,9 @@ import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
import org.whispersystems.textsecuregcm.entities.ChangeNumberRequest; import org.whispersystems.textsecuregcm.entities.ChangeNumberRequest;
import org.whispersystems.textsecuregcm.entities.MismatchedDevices; import org.whispersystems.textsecuregcm.entities.MismatchedDevices;
import org.whispersystems.textsecuregcm.entities.PhoneNumberDiscoverabilityRequest; import org.whispersystems.textsecuregcm.entities.PhoneNumberDiscoverabilityRequest;
import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
import org.whispersystems.textsecuregcm.entities.PhoneNumberIdentityKeyDistributionRequest; import org.whispersystems.textsecuregcm.entities.PhoneNumberIdentityKeyDistributionRequest;
import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure;
import org.whispersystems.textsecuregcm.entities.StaleDevices; import org.whispersystems.textsecuregcm.entities.StaleDevices;
import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.limits.RateLimiters;
@ -77,6 +81,16 @@ public class AccountControllerV2 {
@Path("/number") @Path("/number")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Change number", description = "Changes a phone number for an existing account.")
@ApiResponse(responseCode = "200", description = "The phone number associated with the authenticated account was changed successfully", useReturnTypeSchema = true)
@ApiResponse(responseCode = "403", description = "Verification failed for the provided Registration Recovery Password")
@ApiResponse(responseCode = "409", description = "Mismatched number of devices or device ids in 'devices to notify' list", content = @Content(schema = @Schema(implementation = MismatchedDevices.class)))
@ApiResponse(responseCode = "410", description = "Mismatched registration ids in 'devices to notify' list", content = @Content(schema = @Schema(implementation = StaleDevices.class)))
@ApiResponse(responseCode = "422", description = "The request did not pass validation")
@ApiResponse(responseCode = "423", content = @Content(schema = @Schema(implementation = RegistrationLockFailure.class)))
@ApiResponse(responseCode = "429", description = "Too many attempts", headers = @Header(
name = "Retry-After",
description = "If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed"))
public AccountIdentityResponse changeNumber(@Auth final AuthenticatedAccount authenticatedAccount, public AccountIdentityResponse changeNumber(@Auth final AuthenticatedAccount authenticatedAccount,
@NotNull @Valid final ChangeNumberRequest request, @HeaderParam(HttpHeaders.USER_AGENT) final String userAgent) @NotNull @Valid final ChangeNumberRequest request, @HeaderParam(HttpHeaders.USER_AGENT) final String userAgent)
throws RateLimitExceededException, InterruptedException { throws RateLimitExceededException, InterruptedException {

View File

@ -13,6 +13,11 @@ import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Tags;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.headers.Header;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,6 +39,7 @@ import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager;
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager; import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse; import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest; import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure;
import org.whispersystems.textsecuregcm.entities.RegistrationRequest; import org.whispersystems.textsecuregcm.entities.RegistrationRequest;
import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.limits.RateLimiters;
@ -78,6 +84,22 @@ public class RegistrationController {
@POST @POST
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Registers an account",
description = """
Registers a new account or attempts to re-register an existing account. It is expected that a well-behaved client
could make up to three consecutive calls to this API:
1. gets 423 from existing registration lock \n
2. gets 409 from device available for transfer \n
3. success \n
""")
@ApiResponse(responseCode = "200", description = "The phone number associated with the authenticated account was changed successfully", useReturnTypeSchema = true)
@ApiResponse(responseCode = "403", description = "Verification failed for the provided Registration Recovery Password")
@ApiResponse(responseCode = "409", description = "The caller has not explicitly elected to skip transferring data from another device, but a device transfer is technically possible")
@ApiResponse(responseCode = "422", description = "The request did not pass validation")
@ApiResponse(responseCode = "423", content = @Content(schema = @Schema(implementation = RegistrationLockFailure.class)))
@ApiResponse(responseCode = "429", description = "Too many attempts", headers = @Header(
name = "Retry-After",
description = "If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed"))
public AccountIdentityResponse register( public AccountIdentityResponse register(
@HeaderParam(HttpHeaders.AUTHORIZATION) @NotNull final BasicAuthorizationHeader authorizationHeader, @HeaderParam(HttpHeaders.AUTHORIZATION) @NotNull final BasicAuthorizationHeader authorizationHeader,
@HeaderParam(HeaderUtils.X_SIGNAL_AGENT) final String signalAgent, @HeaderParam(HeaderUtils.X_SIGNAL_AGENT) final String signalAgent,

View File

@ -7,15 +7,18 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List; import java.util.List;
public class MismatchedDevices { public class MismatchedDevices {
@JsonProperty @JsonProperty
@Schema(description = "Devices present on the account but absent in the request")
public List<Long> missingDevices; public List<Long> missingDevices;
@JsonProperty @JsonProperty
@Schema(description = "Devices absent on the request but present in the account")
public List<Long> extraDevices; public List<Long> extraDevices;
@VisibleForTesting @VisibleForTesting

View File

@ -5,8 +5,16 @@
package org.whispersystems.textsecuregcm.entities; package org.whispersystems.textsecuregcm.entities;
import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
public record RegistrationLockFailure(long timeRemaining, ExternalServiceCredentials backupCredentials) { @Schema(description = "A token provided to the client via a push payload")
public record RegistrationLockFailure(
@Schema(description = "Time remaining in milliseconds before the existing registration lock expires")
long timeRemaining,
@Schema(description = "Credentials that can be used with SVR1")
ExternalServiceCredentials backupCredentials,
@Schema(description = "Credentials that can be used with SVR2")
ExternalServiceCredentials svr2Credentials) {
} }

View File

@ -6,12 +6,14 @@
package org.whispersystems.textsecuregcm.entities; package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List; import java.util.List;
public class StaleDevices { public class StaleDevices {
@JsonProperty @JsonProperty
@Schema(description = "Devices that are no longer active")
private List<Long> staleDevices; private List<Long> staleDevices;
public StaleDevices() {} public StaleDevices() {}

View File

@ -47,14 +47,16 @@ class RegistrationLockVerificationManagerTest {
private final AccountsManager accountsManager = mock(AccountsManager.class); private final AccountsManager accountsManager = mock(AccountsManager.class);
private final ClientPresenceManager clientPresenceManager = mock(ClientPresenceManager.class); private final ClientPresenceManager clientPresenceManager = mock(ClientPresenceManager.class);
private final ExternalServiceCredentialsGenerator backupServiceCredentialsGeneraor = mock( private final ExternalServiceCredentialsGenerator svr1CredentialsGenerator = mock(
ExternalServiceCredentialsGenerator.class);
private final ExternalServiceCredentialsGenerator svr2CredentialsGenerator = mock(
ExternalServiceCredentialsGenerator.class); ExternalServiceCredentialsGenerator.class);
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager = mock( private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager = mock(
RegistrationRecoveryPasswordsManager.class); RegistrationRecoveryPasswordsManager.class);
private static PushNotificationManager pushNotificationManager = mock(PushNotificationManager.class); private static PushNotificationManager pushNotificationManager = mock(PushNotificationManager.class);
private final RateLimiters rateLimiters = mock(RateLimiters.class); private final RateLimiters rateLimiters = mock(RateLimiters.class);
private final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager( private final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager(
accountsManager, clientPresenceManager, backupServiceCredentialsGeneraor, registrationRecoveryPasswordsManager, pushNotificationManager, rateLimiters); accountsManager, clientPresenceManager, svr1CredentialsGenerator, svr2CredentialsGenerator, registrationRecoveryPasswordsManager, pushNotificationManager, rateLimiters);
private final RateLimiter pinLimiter = mock(RateLimiter.class); private final RateLimiter pinLimiter = mock(RateLimiter.class);
@ -65,7 +67,9 @@ class RegistrationLockVerificationManagerTest {
void setUp() { void setUp() {
clearInvocations(pushNotificationManager); clearInvocations(pushNotificationManager);
when(rateLimiters.getPinLimiter()).thenReturn(pinLimiter); when(rateLimiters.getPinLimiter()).thenReturn(pinLimiter);
when(backupServiceCredentialsGeneraor.generateForUuid(any())) when(svr1CredentialsGenerator.generateForUuid(any()))
.thenReturn(mock(ExternalServiceCredentials.class));
when(svr2CredentialsGenerator.generateForUuid(any()))
.thenReturn(mock(ExternalServiceCredentials.class)); .thenReturn(mock(ExternalServiceCredentials.class));
final Device device = mock(Device.class); final Device device = mock(Device.class);

View File

@ -72,6 +72,7 @@ import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.signal.libsignal.usernames.BaseUsernameException; import org.signal.libsignal.usernames.BaseUsernameException;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager; import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash; import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
@ -83,6 +84,7 @@ import org.whispersystems.textsecuregcm.captcha.AssessmentResult;
import org.whispersystems.textsecuregcm.captcha.CaptchaChecker; import org.whispersystems.textsecuregcm.captcha.CaptchaChecker;
import org.whispersystems.textsecuregcm.captcha.RegistrationCaptchaManager; import org.whispersystems.textsecuregcm.captcha.RegistrationCaptchaManager;
import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration; import org.whispersystems.textsecuregcm.configuration.SecureBackupServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicCaptchaConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicCaptchaConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountAttributes;
@ -199,15 +201,25 @@ class AccountControllerTest {
private static DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class); private static DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
private byte[] registration_lock_key = new byte[32]; private byte[] registration_lock_key = new byte[32];
private static final SecureBackupServiceConfiguration BACKUP_CFG = MockUtils.buildMock( private static final SecureBackupServiceConfiguration SVR1_CFG = MockUtils.buildMock(
SecureBackupServiceConfiguration.class, SecureBackupServiceConfiguration.class,
cfg -> when(cfg.getUserAuthenticationTokenSharedSecret()).thenReturn(new byte[32])); cfg -> when(cfg.getUserAuthenticationTokenSharedSecret()).thenReturn(new byte[32]));
private static final ExternalServiceCredentialsGenerator backupCredentialsGenerator = SecureBackupController.credentialsGenerator( private static final SecureValueRecovery2Configuration SVR2_CFG = MockUtils.buildMock(
BACKUP_CFG); SecureValueRecovery2Configuration.class,
cfg -> {
when(cfg.userAuthenticationTokenSharedSecret()).thenReturn(new byte[32]);
when(cfg.userIdTokenSharedSecret()).thenReturn(new byte[32]);
});
private static final ExternalServiceCredentialsGenerator svr1CredentialsGenerator = SecureBackupController.credentialsGenerator(
SVR1_CFG);
private static final ExternalServiceCredentialsGenerator svr2CredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
SVR2_CFG);
private static final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager( private static final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager(
accountsManager, clientPresenceManager, backupCredentialsGenerator, registrationRecoveryPasswordsManager, accountsManager, clientPresenceManager, svr1CredentialsGenerator, svr2CredentialsGenerator, registrationRecoveryPasswordsManager,
pushNotificationManager, rateLimiters); pushNotificationManager, rateLimiters);
private static final RegistrationCaptchaManager registrationCaptchaManager = new RegistrationCaptchaManager( private static final RegistrationCaptchaManager registrationCaptchaManager = new RegistrationCaptchaManager(
captchaChecker, rateLimiters, Map.of(TEST_NUMBER, 123456), dynamicConfigurationManager); captchaChecker, rateLimiters, Map.of(TEST_NUMBER, 123456), dynamicConfigurationManager);
@ -1268,6 +1280,8 @@ class AccountControllerTest {
assertThat(failure.backupCredentials().username()).isEqualTo(SENDER_REG_LOCK_UUID.toString()); assertThat(failure.backupCredentials().username()).isEqualTo(SENDER_REG_LOCK_UUID.toString());
assertThat(failure.backupCredentials().password()).isNotEmpty(); assertThat(failure.backupCredentials().password()).isNotEmpty();
assertThat(failure.backupCredentials().password().startsWith(SENDER_REG_LOCK_UUID.toString())).isTrue(); assertThat(failure.backupCredentials().password().startsWith(SENDER_REG_LOCK_UUID.toString())).isTrue();
assertThat(failure.svr2Credentials()).isNotNull();
assertThat(failure.svr2Credentials()).isEqualTo(svr2CredentialsGenerator.generateFor(SENDER_REG_LOCK_UUID.toString()));
assertThat(failure.timeRemaining()).isGreaterThan(0); assertThat(failure.timeRemaining()).isGreaterThan(0);
// verify(senderRegLockAccount).lockAuthenticationCredentials(); // verify(senderRegLockAccount).lockAuthenticationCredentials();

View File

@ -71,7 +71,6 @@ import org.whispersystems.textsecuregcm.entities.AccountDataReportResponse;
import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse; import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
import org.whispersystems.textsecuregcm.entities.ChangeNumberRequest; import org.whispersystems.textsecuregcm.entities.ChangeNumberRequest;
import org.whispersystems.textsecuregcm.entities.PhoneNumberDiscoverabilityRequest; import org.whispersystems.textsecuregcm.entities.PhoneNumberDiscoverabilityRequest;
import org.whispersystems.textsecuregcm.entities.PhoneNumberIdentityKeyDistributionRequest;
import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession; import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
import org.whispersystems.textsecuregcm.entities.SignedPreKey; import org.whispersystems.textsecuregcm.entities.SignedPreKey;
import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiter;