Adapt change number flow to use V2 API.

This commit is contained in:
Nicholas 2023-02-23 19:56:32 -05:00 committed by GitHub
parent a552a5a5bc
commit e4d4a5d9e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 66 additions and 28 deletions

View file

@ -45,7 +45,7 @@ class ChangeNumberConfirmFragment : LoggingFragment(R.layout.fragment_change_num
task.addOnSuccessListener {
Log.i(TAG, "Successfully registered SMS listener.")
navigateToVerify()
navigateToVerify(smsListenerEnabled = true)
}
task.addOnFailureListener { e ->
@ -57,8 +57,8 @@ class ChangeNumberConfirmFragment : LoggingFragment(R.layout.fragment_change_num
}
}
private fun navigateToVerify() {
findNavController().safeNavigate(R.id.action_changePhoneNumberConfirmFragment_to_changePhoneNumberVerifyFragment)
private fun navigateToVerify(smsListenerEnabled: Boolean = false) {
findNavController().safeNavigate(R.id.action_changePhoneNumberConfirmFragment_to_changePhoneNumberVerifyFragment, ChangeNumberVerifyFragmentArgs.Builder().setSmsListenerEnabled(smsListenerEnabled).build().toBundle())
}
companion object {

View file

@ -37,6 +37,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity
import org.whispersystems.signalservice.internal.ServiceResponse
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
import org.whispersystems.signalservice.internal.push.WhoAmIResponse
@ -94,7 +95,7 @@ class ChangeNumberRepository(
.timeout(15, TimeUnit.SECONDS)
}
fun changeNumber(code: String, newE164: String, pniUpdateMode: Boolean = false): Single<ServiceResponse<VerifyResponse>> {
fun changeNumber(sessionId: String, newE164: String, pniUpdateMode: Boolean = false): Single<ServiceResponse<VerifyResponse>> {
return Single.fromCallable {
var completed = false
var attempts = 0
@ -102,7 +103,7 @@ class ChangeNumberRepository(
while (!completed && attempts < 5) {
val (request: ChangePhoneNumberRequest, metadata: PendingChangeNumberMetadata) = createChangeNumberRequest(
code = code,
sessionId = sessionId,
newE164 = newE164,
registrationLock = null,
pniUpdateMode = pniUpdateMode
@ -127,7 +128,7 @@ class ChangeNumberRepository(
}
fun changeNumber(
code: String,
sessionId: String,
newE164: String,
pin: String,
tokenData: TokenData
@ -153,7 +154,7 @@ class ChangeNumberRepository(
while (!completed && attempts < 5) {
val (request: ChangePhoneNumberRequest, metadata: PendingChangeNumberMetadata) = createChangeNumberRequest(
code = code,
sessionId = sessionId,
newE164 = newE164,
registrationLock = registrationLock,
pniUpdateMode = false
@ -280,7 +281,7 @@ class ChangeNumberRepository(
@Suppress("UsePropertyAccessSyntax")
@WorkerThread
private fun createChangeNumberRequest(
code: String,
sessionId: String,
newE164: String,
registrationLock: String?,
pniUpdateMode: Boolean
@ -336,8 +337,9 @@ class ChangeNumberRepository(
}
val request = ChangePhoneNumberRequest(
sessionId,
null,
newE164,
code,
registrationLock,
pniIdentity.publicKey,
deviceMessages,
@ -355,5 +357,11 @@ class ChangeNumberRepository(
return ChangeNumberRequestData(request, metadata)
}
fun verifyAccount(sessionId: String, code: String): Single<ServiceResponse<RegistrationSessionMetadataResponse>> {
return Single.fromCallable {
accountManager.verifyAccount(code, sessionId)
}.subscribeOn(Schedulers.io())
}
data class ChangeNumberRequestData(val changeNumberRequest: ChangePhoneNumberRequest, val pendingChangeNumberMetadata: PendingChangeNumberMetadata)
}

View file

@ -49,11 +49,12 @@ class ChangeNumberVerifyFragment : LoggingFragment(R.layout.fragment_change_phon
}
private fun requestCode() {
val mode = if (ChangeNumberVerifyFragmentArgs.fromBundle(requireArguments()).smsListenerEnabled) VerifyAccountRepository.Mode.SMS_WITH_LISTENER else VerifyAccountRepository.Mode.SMS_WITHOUT_LISTENER
val mccMncProducer = MccMncProducer(requireContext())
lifecycleDisposable += viewModel
.ensureDecryptionsDrained()
.onErrorComplete()
.andThen(viewModel.requestVerificationCode(VerifyAccountRepository.Mode.SMS_WITHOUT_LISTENER, mccMncProducer.mcc, mccMncProducer.mnc))
.andThen(viewModel.requestVerificationCode(mode, mccMncProducer.mcc, mccMncProducer.mnc))
.observeOn(AndroidSchedulers.mainThread())
.subscribe { processor ->
if (processor.hasResult()) {

View file

@ -9,13 +9,16 @@ import androidx.lifecycle.ViewModel
import androidx.savedstate.SavedStateRegistryOwner
import com.google.i18n.phonenumbers.NumberParseException
import com.google.i18n.phonenumbers.PhoneNumberUtil
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.pin.KbsRepository
import org.thoughtcrime.securesms.pin.TokenData
import org.thoughtcrime.securesms.registration.RegistrationSessionProcessor
import org.thoughtcrime.securesms.registration.SmsRetrieverReceiver
import org.thoughtcrime.securesms.registration.VerifyAccountRepository
import org.thoughtcrime.securesms.registration.VerifyResponse
@ -26,6 +29,7 @@ import org.thoughtcrime.securesms.registration.viewmodel.BaseRegistrationViewMod
import org.thoughtcrime.securesms.registration.viewmodel.NumberViewState
import org.thoughtcrime.securesms.util.DefaultValueLiveData
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.exceptions.IncorrectCodeException
import org.whispersystems.signalservice.internal.ServiceResponse
import java.util.Objects
@ -152,11 +156,32 @@ class ChangeNumberViewModel(
}
override fun verifyAccountWithoutRegistrationLock(): Single<ServiceResponse<VerifyResponse>> {
return changeNumberRepository.changeNumber(textCodeEntered, number.e164Number)
val sessionId = sessionId ?: throw IllegalStateException("No valid registration session")
return changeNumberRepository.verifyAccount(sessionId, textCodeEntered)
.map { RegistrationSessionProcessor.RegistrationSessionProcessorForVerification(it) }
.observeOn(AndroidSchedulers.mainThread())
.doOnSuccess {
if (it.hasResult()) {
setCanSmsAtTime(it.getNextCodeViaSmsAttempt())
setCanCallAtTime(it.getNextCodeViaCallAttempt())
}
}
.observeOn(Schedulers.io())
.flatMap { processor ->
if (processor.isAlreadyVerified() || processor.hasResult() && processor.isVerified()) {
changeNumberRepository.changeNumber(sessionId, number.e164Number)
} else if (processor.error == null) {
Single.just<ServiceResponse<VerifyResponse>>(ServiceResponse.forApplicationError(IncorrectCodeException(), 403, null))
} else {
Single.just<ServiceResponse<VerifyResponse>>(ServiceResponse.coerceError(processor.response))
}
}
}
override fun verifyAccountWithRegistrationLock(pin: String, kbsTokenData: TokenData): Single<ServiceResponse<VerifyResponse>> {
return changeNumberRepository.changeNumber(textCodeEntered, number.e164Number, pin, kbsTokenData)
val sessionId = sessionId ?: throw IllegalStateException("No valid registration session")
return changeNumberRepository.changeNumber(sessionId, number.e164Number, pin, kbsTokenData)
}
@WorkerThread

View file

@ -24,7 +24,7 @@ class PnpInitializeDevicesJob private constructor(parameters: Parameters) : Base
companion object {
const val KEY = "PnpInitializeDevicesJob"
private val TAG = Log.tag(PnpInitializeDevicesJob::class.java)
private const val PLACEHOLDER_CODE = "123456"
private const val PLACEHOLDER_SESSION_ID = "123456789"
@JvmStatic
fun enqueueIfNecessary() {
@ -88,7 +88,7 @@ class PnpInitializeDevicesJob private constructor(parameters: Parameters) : Base
try {
Log.i(TAG, "Calling change number with our current number to distribute PNI messages")
changeNumberRepository
.changeNumber(code = PLACEHOLDER_CODE, newE164 = e164, pniUpdateMode = true)
.changeNumber(sessionId = PLACEHOLDER_SESSION_ID, newE164 = e164, pniUpdateMode = true)
.map(::VerifyResponseWithoutKbs)
.safeBlockingGet()
.resultOrThrow

View file

@ -73,6 +73,10 @@
android:name="org.thoughtcrime.securesms.components.settings.app.changenumber.ChangeNumberVerifyFragment"
tools:layout="@layout/fragment_change_phone_number_verify">
<argument android:name="sms_listener_enabled"
android:defaultValue="false"
app:argType="boolean" />
<action
android:id="@+id/action_changePhoneNumberVerifyFragment_to_captchaFragment"
app:destination="@id/captchaFragment"

View file

@ -20,7 +20,6 @@ import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.whispersystems.signalservice.api.account.AccountAttributes;
import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest;
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse;
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
import org.whispersystems.signalservice.api.crypto.ProfileCipherOutputStream;
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
@ -61,6 +60,7 @@ import org.whispersystems.signalservice.internal.push.BackupAuthCheckResponse;
import org.whispersystems.signalservice.internal.push.CdsiAuthResponse;
import org.whispersystems.signalservice.internal.push.ProfileAvatarData;
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse;
import org.whispersystems.signalservice.internal.push.RemoteConfigResponse;
import org.whispersystems.signalservice.internal.push.ReserveUsernameResponse;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
@ -315,9 +315,7 @@ public class SignalServiceAccountManager {
* @return The UUID of the user that was registered.
* @throws IOException for various HTTP and networking errors
*/
public ServiceResponse<RegistrationSessionMetadataResponse> verifyAccount(String verificationCode,
String sessionId)
{
public ServiceResponse<RegistrationSessionMetadataResponse> verifyAccount(@Nonnull String verificationCode, @Nonnull String sessionId) {
try {
RegistrationSessionMetadataResponse response = pushServiceSocket.submitVerificationCode(sessionId, verificationCode);
return ServiceResponse.forResult(response, 200, null);

View file

@ -14,10 +14,13 @@ import java.util.Map;
public final class ChangePhoneNumberRequest {
@JsonProperty
private String number;
private String sessionId;
@JsonProperty
private String code;
private String recoveryPassword;
@JsonProperty
private String number;
@JsonProperty("reglock")
private String registrationLock;
@ -36,18 +39,21 @@ public final class ChangePhoneNumberRequest {
@JsonProperty
private Map<String, Integer> pniRegistrationIds;
@SuppressWarnings("unused")
public ChangePhoneNumberRequest() {}
public ChangePhoneNumberRequest(String number,
String code,
public ChangePhoneNumberRequest(String sessionId,
String recoveryPassword,
String number,
String registrationLock,
IdentityKey pniIdentityKey,
List<OutgoingPushMessage> deviceMessages,
Map<String, SignedPreKeyEntity> devicePniSignedPrekeys,
Map<String, Integer> pniRegistrationIds)
{
this.sessionId = sessionId;
this.recoveryPassword = recoveryPassword;
this.number = number;
this.code = code;
this.registrationLock = registrationLock;
this.pniIdentityKey = pniIdentityKey;
this.deviceMessages = deviceMessages;
@ -59,10 +65,6 @@ public final class ChangePhoneNumberRequest {
return number;
}
public String getCode() {
return code;
}
public String getRegistrationLock() {
return registrationLock;
}

View file

@ -210,7 +210,7 @@ public class PushServiceSocket {
private static final String RESERVE_USERNAME_PATH = "/v1/accounts/username_hash/reserve";
private static final String CONFIRM_USERNAME_PATH = "/v1/accounts/username_hash/confirm";
private static final String DELETE_ACCOUNT_PATH = "/v1/accounts/me";
private static final String CHANGE_NUMBER_PATH = "/v1/accounts/number";
private static final String CHANGE_NUMBER_PATH = "/v2/accounts/number";
private static final String IDENTIFIER_REGISTERED_PATH = "/v1/accounts/account/%s";
private static final String PREKEY_METADATA_PATH = "/v2/keys?identity=%s";