mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 21:15:48 +00:00
Additional error handling for registration v2.
This commit is contained in:
committed by
Cody Henthorne
parent
4f3ee9ca1d
commit
afe3cd1098
@@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.registration.PushChallengeRequest
|
||||
import org.thoughtcrime.securesms.registration.RegistrationData
|
||||
import org.thoughtcrime.securesms.registration.VerifyAccountRepository
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.BackupAuthCheckResult
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.RegisterAccountResult
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionCheckResult
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionCreationResult
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionResult
|
||||
@@ -337,10 +338,11 @@ object RegistrationRepository {
|
||||
/**
|
||||
* Submits the user-entered verification code to the service.
|
||||
*/
|
||||
suspend fun submitVerificationCode(context: Context, e164: String, password: String, sessionId: String, registrationData: RegistrationData): NetworkResult<RegistrationSessionMetadataResponse> =
|
||||
suspend fun submitVerificationCode(context: Context, e164: String, password: String, sessionId: String, registrationData: RegistrationData): VerificationCodeRequestResult =
|
||||
withContext(Dispatchers.IO) {
|
||||
val api: RegistrationApi = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password).registrationApi
|
||||
api.verifyAccount(sessionId = sessionId, verificationCode = registrationData.code)
|
||||
val result = api.verifyAccount(sessionId = sessionId, verificationCode = registrationData.code)
|
||||
return@withContext VerificationCodeRequestResult.from(result)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,7 +358,7 @@ object RegistrationRepository {
|
||||
/**
|
||||
* Submit the necessary assets as a verified account so that the user can actually use the service.
|
||||
*/
|
||||
suspend fun registerAccount(context: Context, sessionId: String?, registrationData: RegistrationData, pin: String? = null, masterKeyProducer: VerifyAccountRepository.MasterKeyProducer? = null): NetworkResult<AccountRegistrationResult> =
|
||||
suspend fun registerAccount(context: Context, sessionId: String?, registrationData: RegistrationData, pin: String? = null, masterKeyProducer: VerifyAccountRepository.MasterKeyProducer? = null): RegisterAccountResult =
|
||||
withContext(Dispatchers.IO) {
|
||||
val api: RegistrationApi = AccountManagerFactory.getInstance().createUnauthenticated(context, registrationData.e164, SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.password).registrationApi
|
||||
|
||||
@@ -389,7 +391,7 @@ object RegistrationRepository {
|
||||
val aciPreKeyCollection = org.thoughtcrime.securesms.registration.RegistrationRepository.generateSignedAndLastResortPreKeys(aciIdentity, SignalStore.account().aciPreKeys)
|
||||
val pniPreKeyCollection = org.thoughtcrime.securesms.registration.RegistrationRepository.generateSignedAndLastResortPreKeys(pniIdentity, SignalStore.account().pniPreKeys)
|
||||
|
||||
api.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, aciPreKeyCollection, pniPreKeyCollection, registrationData.fcmToken, true)
|
||||
val result: NetworkResult<AccountRegistrationResult> = api.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, aciPreKeyCollection, pniPreKeyCollection, registrationData.fcmToken, true)
|
||||
.map { accountRegistrationResponse ->
|
||||
AccountRegistrationResult(
|
||||
uuid = accountRegistrationResponse.uuid,
|
||||
@@ -402,6 +404,8 @@ object RegistrationRepository {
|
||||
pniPreKeyCollection = pniPreKeyCollection
|
||||
)
|
||||
}
|
||||
|
||||
return@withContext RegisterAccountResult.from(result)
|
||||
}
|
||||
|
||||
suspend fun createSessionAndBlockForPushChallenge(accountManager: RegistrationApi, fcmToken: String, mcc: String?, mnc: String?): NetworkResult<RegistrationSessionMetadataResponse> =
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.registration.v2.data.network
|
||||
|
||||
import org.thoughtcrime.securesms.registration.v2.data.RegistrationRepository
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.IncorrectRegistrationRecoveryPasswordException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.MalformedRequestException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException
|
||||
import org.whispersystems.signalservice.internal.push.AuthCredentials
|
||||
import org.whispersystems.signalservice.internal.push.LockedException
|
||||
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
|
||||
|
||||
/**
|
||||
* This is a processor to map a [VerifyAccountResponse] to all the known outcomes.
|
||||
*/
|
||||
sealed class RegisterAccountResult(cause: Throwable?) : RegistrationResult(cause) {
|
||||
companion object {
|
||||
fun from(networkResult: NetworkResult<RegistrationRepository.AccountRegistrationResult>): RegisterAccountResult {
|
||||
return when (networkResult) {
|
||||
is NetworkResult.Success -> Success(networkResult.result)
|
||||
is NetworkResult.ApplicationError -> UnknownError(networkResult.throwable)
|
||||
is NetworkResult.NetworkError -> UnknownError(networkResult.exception)
|
||||
is NetworkResult.StatusCodeError -> {
|
||||
when (val cause = networkResult.exception) {
|
||||
is IncorrectRegistrationRecoveryPasswordException -> IncorrectRecoveryPassword(cause)
|
||||
is AuthorizationFailedException -> AuthorizationFailed(cause)
|
||||
is MalformedRequestException -> MalformedRequest(cause)
|
||||
is RateLimitException -> createRateLimitProcessor(cause)
|
||||
is LockedException -> RegistrationLocked(cause = cause, timeRemaining = cause.timeRemaining, svr2Credentials = cause.svr2Credentials)
|
||||
else -> {
|
||||
if (networkResult.code == 422) {
|
||||
ValidationError(cause)
|
||||
} else {
|
||||
UnknownError(cause)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun createRateLimitProcessor(exception: RateLimitException): RegisterAccountResult {
|
||||
return if (exception.retryAfterMilliseconds.isPresent) {
|
||||
RateLimited(exception, exception.retryAfterMilliseconds.get())
|
||||
} else {
|
||||
AttemptsExhausted(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
class Success(val accountRegistrationResult: RegistrationRepository.AccountRegistrationResult) : RegisterAccountResult(null)
|
||||
class IncorrectRecoveryPassword(cause: Throwable) : RegisterAccountResult(cause)
|
||||
class AuthorizationFailed(cause: Throwable) : RegisterAccountResult(cause)
|
||||
class MalformedRequest(cause: Throwable) : RegisterAccountResult(cause)
|
||||
class ValidationError(cause: Throwable) : RegisterAccountResult(cause)
|
||||
class RateLimited(cause: Throwable, val timeRemaining: Long) : RegisterAccountResult(cause)
|
||||
class AttemptsExhausted(cause: Throwable) : RegisterAccountResult(cause)
|
||||
class RegistrationLocked(cause: Throwable, val timeRemaining: Long, val svr2Credentials: AuthCredentials?) : RegisterAccountResult(cause)
|
||||
class UnknownError(cause: Throwable) : RegisterAccountResult(cause)
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.registration.RegistrationData
|
||||
import org.thoughtcrime.securesms.registration.RegistrationUtil
|
||||
import org.thoughtcrime.securesms.registration.v2.data.RegistrationRepository
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.BackupAuthCheckResult
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.RegisterAccountResult
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionCheckResult
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionCreationResult
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionResult
|
||||
@@ -50,10 +51,8 @@ import org.thoughtcrime.securesms.registration.v2.data.network.VerificationCodeR
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.dualsim.MccMncProducer
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
import org.whispersystems.signalservice.api.SvrNoDataException
|
||||
import org.whispersystems.signalservice.api.kbs.MasterKey
|
||||
import org.whispersystems.signalservice.internal.push.LockedException
|
||||
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse
|
||||
import java.io.IOException
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
@@ -64,7 +63,7 @@ import kotlin.time.Duration.Companion.minutes
|
||||
class RegistrationV2ViewModel : ViewModel() {
|
||||
|
||||
private val store = MutableStateFlow(RegistrationV2State())
|
||||
private val password = Util.getSecret(18) // TODO [regv2]: persist this
|
||||
private val password = Util.getSecret(18)
|
||||
|
||||
private val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception ->
|
||||
Log.w(TAG, "CoroutineExceptionHandler invoked.", exception)
|
||||
@@ -168,7 +167,7 @@ class RegistrationV2ViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
val svrCredentialsResult = RegistrationRepository.hasValidSvrAuthCredentials(context, e164, password)
|
||||
val svrCredentialsResult: BackupAuthCheckResult = RegistrationRepository.hasValidSvrAuthCredentials(context, e164, password)
|
||||
|
||||
when (svrCredentialsResult) {
|
||||
is BackupAuthCheckResult.UnknownError -> {
|
||||
@@ -184,7 +183,9 @@ class RegistrationV2ViewModel : ViewModel() {
|
||||
return@launch
|
||||
}
|
||||
|
||||
is BackupAuthCheckResult.SuccessWithoutCredentials -> Log.d(TAG, "No local SVR auth credentials could be found and/or validated.")
|
||||
is BackupAuthCheckResult.SuccessWithoutCredentials -> {
|
||||
Log.d(TAG, "No local SVR auth credentials could be found and/or validated.")
|
||||
}
|
||||
}
|
||||
|
||||
val validSession = getOrCreateValidSession(context) ?: return@launch
|
||||
@@ -412,6 +413,34 @@ class RegistrationV2ViewModel : ViewModel() {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the request was successful and execution should continue
|
||||
*/
|
||||
private suspend fun handleRegistrationResult(context: Context, registrationData: RegistrationData, registrationResult: RegisterAccountResult, reglockEnabled: Boolean, errorHandler: (RegisterAccountResult) -> Unit): Boolean {
|
||||
when (registrationResult) {
|
||||
is RegisterAccountResult.Success -> {
|
||||
onSuccessfulRegistration(context, registrationData, registrationResult.accountRegistrationResult, reglockEnabled)
|
||||
return true
|
||||
}
|
||||
is RegisterAccountResult.IncorrectRecoveryPassword -> {
|
||||
Log.i(TAG, "Registration recovery password was incorrect, falling back to SMS verification.", registrationResult.getCause())
|
||||
setUserSkippedReRegisterFlow(true)
|
||||
}
|
||||
is RegisterAccountResult.RegistrationLocked -> {
|
||||
Log.i(TAG, "Account is registration locked!", registrationResult.getCause())
|
||||
}
|
||||
is RegisterAccountResult.AttemptsExhausted,
|
||||
is RegisterAccountResult.RateLimited,
|
||||
is RegisterAccountResult.AuthorizationFailed,
|
||||
is RegisterAccountResult.MalformedRequest,
|
||||
is RegisterAccountResult.ValidationError,
|
||||
is RegisterAccountResult.UnknownError -> Log.i(TAG, "Received error when trying to register!", registrationResult.getCause())
|
||||
}
|
||||
setInProgress(false)
|
||||
errorHandler(registrationResult)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun handleGenericError(cause: Throwable) {
|
||||
Log.w(TAG, "Encountered unknown error!", cause)
|
||||
store.update {
|
||||
@@ -437,7 +466,7 @@ class RegistrationV2ViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun verifyReRegisterWithPin(context: Context, pin: String, wrongPinHandler: () -> Unit) {
|
||||
fun verifyReRegisterWithPin(context: Context, pin: String, wrongPinHandler: () -> Unit, registrationErrorHandler: (RegisterAccountResult) -> Unit) {
|
||||
setInProgress(true)
|
||||
|
||||
// Local recovery password
|
||||
@@ -445,7 +474,7 @@ class RegistrationV2ViewModel : ViewModel() {
|
||||
if (RegistrationRepository.doesPinMatchLocalHash(pin)) {
|
||||
Log.d(TAG, "Found recovery password, attempting to re-register.")
|
||||
viewModelScope.launch(context = coroutineExceptionHandler) {
|
||||
verifyReRegisterInternal(context, pin, SignalStore.svr().getOrCreateMasterKey())
|
||||
verifyReRegisterInternal(context, pin, SignalStore.svr().getOrCreateMasterKey(), registrationErrorHandler)
|
||||
setInProgress(false)
|
||||
}
|
||||
} else {
|
||||
@@ -465,7 +494,7 @@ class RegistrationV2ViewModel : ViewModel() {
|
||||
val masterKey = RegistrationRepository.fetchMasterKeyFromSvrRemote(pin, authCredentials)
|
||||
setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword())
|
||||
updateSvrTriesRemaining(10)
|
||||
verifyReRegisterInternal(context, pin, masterKey)
|
||||
verifyReRegisterInternal(context, pin, masterKey, registrationErrorHandler)
|
||||
} catch (rejectedPin: SvrWrongPinException) {
|
||||
Log.w(TAG, "Submitted PIN was rejected by SVR.", rejectedPin)
|
||||
updateSvrTriesRemaining(rejectedPin.triesRemaining)
|
||||
@@ -486,7 +515,7 @@ class RegistrationV2ViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun verifyReRegisterInternal(context: Context, pin: String, masterKey: MasterKey) {
|
||||
private suspend fun verifyReRegisterInternal(context: Context, pin: String, masterKey: MasterKey, registrationErrorHandler: (RegisterAccountResult) -> Unit) {
|
||||
updateFcmToken(context)
|
||||
|
||||
val registrationData = getRegistrationData("")
|
||||
@@ -495,34 +524,31 @@ class RegistrationV2ViewModel : ViewModel() {
|
||||
val result = resultAndRegLockStatus.first
|
||||
val reglockEnabled = resultAndRegLockStatus.second
|
||||
|
||||
if (result !is NetworkResult.Success) {
|
||||
Log.w(TAG, "Error during registration!", result.getCause())
|
||||
return
|
||||
}
|
||||
|
||||
onSuccessfulRegistration(context, registrationData, result.result, reglockEnabled)
|
||||
handleRegistrationResult(context, registrationData, result, reglockEnabled, registrationErrorHandler)
|
||||
}
|
||||
|
||||
private suspend fun registerAccountInternal(context: Context, sessionId: String?, registrationData: RegistrationData, pin: String?, masterKey: MasterKey): Pair<NetworkResult<RegistrationRepository.AccountRegistrationResult>, Boolean> {
|
||||
val registrationResult = RegistrationRepository.registerAccount(context = context, sessionId = sessionId, registrationData = registrationData, pin = pin) { masterKey }
|
||||
/**
|
||||
* @return a [Pair] containing the server response and a boolean signifying whether the current account is registration locked.
|
||||
*/
|
||||
private suspend fun registerAccountInternal(context: Context, sessionId: String?, registrationData: RegistrationData, pin: String?, masterKey: MasterKey): Pair<RegisterAccountResult, Boolean> {
|
||||
val registrationResult: RegisterAccountResult = RegistrationRepository.registerAccount(context = context, sessionId = sessionId, registrationData = registrationData, pin = pin) { masterKey }
|
||||
|
||||
// TODO: check for wrong recovery password
|
||||
|
||||
// Check if reg lock is enabled
|
||||
if (registrationResult !is NetworkResult.StatusCodeError || registrationResult.exception !is LockedException) {
|
||||
if (registrationResult !is RegisterAccountResult.RegistrationLocked) {
|
||||
return Pair(registrationResult, false)
|
||||
}
|
||||
|
||||
Log.i(TAG, "Received a registration lock response when trying to register an account. Retrying with master key.")
|
||||
val lockedException = registrationResult.exception as LockedException
|
||||
store.update {
|
||||
it.copy(svrAuthCredentials = lockedException.svr2Credentials)
|
||||
it.copy(svrAuthCredentials = registrationResult.svr2Credentials)
|
||||
}
|
||||
|
||||
return Pair(RegistrationRepository.registerAccount(context = context, sessionId = sessionId, registrationData = registrationData, pin = pin) { masterKey }, true)
|
||||
}
|
||||
|
||||
fun verifyCodeWithoutRegistrationLock(context: Context, code: String) {
|
||||
fun verifyCodeWithoutRegistrationLock(context: Context, code: String, submissionErrorHandler: (VerificationCodeRequestResult, RegistrationRepository.Mode) -> Unit, registrationErrorHandler: (RegisterAccountResult) -> Unit) {
|
||||
store.update {
|
||||
it.copy(inProgress = true, registrationCheckpoint = RegistrationCheckpoint.VERIFICATION_CODE_ENTERED)
|
||||
}
|
||||
@@ -532,23 +558,23 @@ class RegistrationV2ViewModel : ViewModel() {
|
||||
Log.w(TAG, "Session ID was null. TODO: handle this better in the UI.")
|
||||
return
|
||||
}
|
||||
|
||||
val e164: String = getCurrentE164() ?: throw IllegalStateException()
|
||||
|
||||
viewModelScope.launch(context = coroutineExceptionHandler) {
|
||||
val registrationData = getRegistrationData(code)
|
||||
val verificationResponse = RegistrationRepository.submitVerificationCode(context, e164, password, sessionId, registrationData).successOrThrow()
|
||||
val verificationResponse = RegistrationRepository.submitVerificationCode(context, e164, password, sessionId, registrationData)
|
||||
|
||||
if (!verificationResponse.body.verified) {
|
||||
if (!verificationResponse.isSuccess()) {
|
||||
Log.w(TAG, "Could not verify code!")
|
||||
// TODO [regv2]: error handling
|
||||
handleSessionStateResult(context, verificationResponse, RegistrationRepository.Mode.NONE, submissionErrorHandler)
|
||||
return@launch
|
||||
}
|
||||
|
||||
setRegistrationCheckpoint(RegistrationCheckpoint.VERIFICATION_CODE_VALIDATED)
|
||||
|
||||
val registrationResponse = RegistrationRepository.registerAccount(context, sessionId, registrationData).successOrThrow()
|
||||
// TODO [regv2]: error handling
|
||||
onSuccessfulRegistration(context, registrationData, registrationResponse, false)
|
||||
val registrationResponse: RegisterAccountResult = RegistrationRepository.registerAccount(context, sessionId, registrationData)
|
||||
handleRegistrationResult(context, registrationData, registrationResponse, false, registrationErrorHandler)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.registration.fragments.ContactSupportBottomShe
|
||||
import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView
|
||||
import org.thoughtcrime.securesms.registration.fragments.SignalStrengthPhoneStateListener
|
||||
import org.thoughtcrime.securesms.registration.v2.data.RegistrationRepository
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.RegisterAccountResult
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.VerificationCodeRequestResult
|
||||
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint
|
||||
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel
|
||||
@@ -69,7 +70,7 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter
|
||||
}
|
||||
|
||||
binding.code.setOnCompleteListener {
|
||||
sharedViewModel.verifyCodeWithoutRegistrationLock(requireContext(), it)
|
||||
sharedViewModel.verifyCodeWithoutRegistrationLock(requireContext(), it, ::handleSessionErrorResponse, ::handleRegistrationErrorResponse)
|
||||
}
|
||||
|
||||
binding.havingTroubleButton.setOnClickListener {
|
||||
@@ -79,14 +80,14 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter
|
||||
binding.callMeCountDown.apply {
|
||||
setTextResources(R.string.RegistrationActivity_call, R.string.RegistrationActivity_call_me_instead_available_in)
|
||||
setOnClickListener {
|
||||
sharedViewModel.requestVerificationCall(requireContext(), ::handleErrorResponse)
|
||||
sharedViewModel.requestVerificationCall(requireContext(), ::handleSessionErrorResponse)
|
||||
}
|
||||
}
|
||||
|
||||
binding.resendSmsCountDown.apply {
|
||||
setTextResources(R.string.RegistrationActivity_resend_code, R.string.RegistrationActivity_resend_sms_available_in)
|
||||
setOnClickListener {
|
||||
sharedViewModel.requestSmsCode(requireContext(), ::handleErrorResponse)
|
||||
sharedViewModel.requestSmsCode(requireContext(), ::handleSessionErrorResponse)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleErrorResponse(requestResult: VerificationCodeRequestResult, mode: RegistrationRepository.Mode) {
|
||||
private fun handleSessionErrorResponse(requestResult: VerificationCodeRequestResult, mode: RegistrationRepository.Mode) {
|
||||
when (requestResult) {
|
||||
is VerificationCodeRequestResult.Success -> binding.keyboard.displaySuccess()
|
||||
is VerificationCodeRequestResult.RateLimited -> {
|
||||
@@ -149,6 +150,49 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRegistrationErrorResponse(result: RegisterAccountResult) {
|
||||
when (result) {
|
||||
is RegisterAccountResult.Success -> Log.d(TAG, "Register account was successful.")
|
||||
is RegisterAccountResult.AuthorizationFailed -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_error_connecting_to_service))
|
||||
is RegisterAccountResult.MalformedRequest -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_error_connecting_to_service))
|
||||
is RegisterAccountResult.RegistrationLocked -> {
|
||||
Log.w(TAG, "Account is registration locked, cannot register.")
|
||||
findNavController().safeNavigate(EnterCodeV2FragmentDirections.actionRequireKbsLockPin(result.timeRemaining))
|
||||
}
|
||||
is RegisterAccountResult.UnknownError -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_error_connecting_to_service))
|
||||
is RegisterAccountResult.ValidationError -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_error_connecting_to_service))
|
||||
is RegisterAccountResult.IncorrectRecoveryPassword -> {
|
||||
Log.w(TAG, "User somehow got recovery password error while entering code. This is very suspicious!")
|
||||
sharedViewModel.setUserSkippedReRegisterFlow(true)
|
||||
popBackStack()
|
||||
}
|
||||
|
||||
is RegisterAccountResult.AttemptsExhausted,
|
||||
is RegisterAccountResult.RateLimited -> presentRateLimitedDialog()
|
||||
}
|
||||
}
|
||||
|
||||
private fun presentRateLimitedDialog() {
|
||||
binding.keyboard.displayFailure().addListener(
|
||||
object : AssertedSuccessListener<Boolean?>() {
|
||||
override fun onSuccess(result: Boolean?) {
|
||||
MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
setTitle(R.string.RegistrationActivity_too_many_attempts)
|
||||
setMessage(R.string.RegistrationActivity_you_have_made_too_many_attempts_please_try_again_later)
|
||||
setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||
binding.callMeCountDown.visibility = View.VISIBLE
|
||||
binding.resendSmsCountDown.visibility = View.VISIBLE
|
||||
binding.wrongNumber.visibility = View.VISIBLE
|
||||
binding.code.clear()
|
||||
binding.keyboard.displayKeyboard()
|
||||
}
|
||||
show()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun presentRemoteErrorDialog(message: String, title: String? = null, positiveButtonListener: DialogInterface.OnClickListener? = null) {
|
||||
MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
title?.let {
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.lock.v2.PinKeyboardType
|
||||
import org.thoughtcrime.securesms.lock.v2.SvrConstants
|
||||
import org.thoughtcrime.securesms.registration.fragments.BaseRegistrationLockFragment
|
||||
import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate
|
||||
import org.thoughtcrime.securesms.registration.v2.data.network.RegisterAccountResult
|
||||
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint
|
||||
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2State
|
||||
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel
|
||||
@@ -125,9 +126,14 @@ class ReRegisterWithPinV2Fragment : LoggingFragment(R.layout.fragment_registrati
|
||||
|
||||
registrationViewModel.setRegistrationCheckpoint(RegistrationCheckpoint.PIN_CONFIRMED)
|
||||
|
||||
registrationViewModel.verifyReRegisterWithPin(requireContext(), pin) {
|
||||
reRegisterViewModel.markIncorrectGuess()
|
||||
}
|
||||
registrationViewModel.verifyReRegisterWithPin(
|
||||
context = requireContext(),
|
||||
pin = pin,
|
||||
wrongPinHandler = {
|
||||
reRegisterViewModel.markIncorrectGuess()
|
||||
},
|
||||
registrationErrorHandler = ::registrationErrorHandler
|
||||
)
|
||||
|
||||
// TODO [regv2]: check for registration lock + wrong pin and decrement SVR tries remaining
|
||||
}
|
||||
@@ -233,6 +239,15 @@ class ReRegisterWithPinV2Fragment : LoggingFragment(R.layout.fragment_registrati
|
||||
registrationViewModel.setUserSkippedReRegisterFlow(true)
|
||||
}
|
||||
|
||||
private fun presentRateLimitedDialog() {
|
||||
MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
setTitle(R.string.RegistrationActivity_too_many_attempts)
|
||||
setMessage(R.string.RegistrationActivity_you_have_made_too_many_attempts_please_try_again_later)
|
||||
setPositiveButton(android.R.string.ok, null)
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun genericErrorDialog() {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setMessage(R.string.RegistrationActivity_error_connecting_to_service)
|
||||
@@ -240,4 +255,26 @@ class ReRegisterWithPinV2Fragment : LoggingFragment(R.layout.fragment_registrati
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun registrationErrorHandler(result: RegisterAccountResult) {
|
||||
when (result) {
|
||||
is RegisterAccountResult.Success -> Log.d(TAG, "Register account was successful.")
|
||||
is RegisterAccountResult.AuthorizationFailed,
|
||||
is RegisterAccountResult.MalformedRequest,
|
||||
is RegisterAccountResult.UnknownError,
|
||||
is RegisterAccountResult.ValidationError,
|
||||
is RegisterAccountResult.RegistrationLocked -> {
|
||||
Log.i(TAG, "Registration failed.", result.getCause())
|
||||
genericErrorDialog()
|
||||
}
|
||||
|
||||
is RegisterAccountResult.IncorrectRecoveryPassword -> {
|
||||
registrationViewModel.setUserSkippedReRegisterFlow(true)
|
||||
findNavController().safeNavigate(ReRegisterWithPinV2FragmentDirections.actionReRegisterWithPinFragmentToEnterPhoneNumberV2Fragment())
|
||||
}
|
||||
|
||||
is RegisterAccountResult.AttemptsExhausted,
|
||||
is RegisterAccountResult.RateLimited -> presentRateLimitedDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,10 +317,6 @@ public class SignalServiceAccountManager {
|
||||
}
|
||||
}
|
||||
|
||||
public @Nonnull VerifyAccountResponse registerAccountV2(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, PreKeyCollection aciPreKeys, PreKeyCollection pniPreKeys, String fcmToken, boolean skipDeviceTransfer) throws IOException {
|
||||
return pushServiceSocket.submitRegistrationRequest(sessionId, recoveryPassword, attributes, aciPreKeys, pniPreKeys, fcmToken, skipDeviceTransfer);
|
||||
}
|
||||
|
||||
public @Nonnull ServiceResponse<VerifyAccountResponse> changeNumber(@Nonnull ChangePhoneNumberRequest changePhoneNumberRequest) {
|
||||
try {
|
||||
VerifyAccountResponse response = this.pushServiceSocket.changeNumber(changePhoneNumberRequest);
|
||||
|
||||
Reference in New Issue
Block a user