Fix storageCapable bug in regV5.
This commit is contained in:
parent
c55f281213
commit
f375750cf8
@ -24,7 +24,8 @@ data class PersistedFlowState(
|
||||
val doNotAttemptRecoveryPassword: Boolean,
|
||||
val pendingRestoreOption: PendingRestoreOption? = null,
|
||||
val restoredAepValue: String? = null,
|
||||
val restoreMethodToken: String? = null
|
||||
val restoreMethodToken: String? = null,
|
||||
val storageCapable: Boolean = false
|
||||
)
|
||||
|
||||
/**
|
||||
@ -38,7 +39,8 @@ fun RegistrationFlowState.toPersistedFlowState(): PersistedFlowState {
|
||||
doNotAttemptRecoveryPassword = doNotAttemptRecoveryPassword,
|
||||
pendingRestoreOption = pendingRestoreOption,
|
||||
restoredAepValue = unverifiedRestoredAep?.value,
|
||||
restoreMethodToken = restoreMethodToken
|
||||
restoreMethodToken = restoreMethodToken,
|
||||
storageCapable = storageCapable
|
||||
)
|
||||
}
|
||||
|
||||
@ -64,6 +66,7 @@ fun PersistedFlowState.toRegistrationFlowState(
|
||||
doNotAttemptRecoveryPassword = doNotAttemptRecoveryPassword,
|
||||
pendingRestoreOption = pendingRestoreOption,
|
||||
unverifiedRestoredAep = restoredAepValue?.let { AccountEntropyPool(it) },
|
||||
restoreMethodToken = restoreMethodToken
|
||||
restoreMethodToken = restoreMethodToken,
|
||||
storageCapable = storageCapable
|
||||
)
|
||||
}
|
||||
|
||||
@ -25,9 +25,14 @@ sealed interface RegistrationFlowEvent {
|
||||
/** The e164 associated with this registration attempt has been updated. */
|
||||
data class E164Chosen(val e164: String) : RegistrationFlowEvent
|
||||
|
||||
/** The user has successfully registered. */
|
||||
data class Registered(val accountEntropyPool: AccountEntropyPool) : RegistrationFlowEvent {
|
||||
override fun toString(): String = "Registered(accountEntropyPool=${accountEntropyPool.displayValue.censor()})"
|
||||
/**
|
||||
* The user has successfully registered.
|
||||
*
|
||||
* @param storageCapable Whether the server reports that this account already has SVR/PIN data, as returned in the
|
||||
* registration response. Used later (e.g. when skipping a restore) to decide between PIN entry and PIN creation.
|
||||
*/
|
||||
data class Registered(val accountEntropyPool: AccountEntropyPool, val storageCapable: Boolean) : RegistrationFlowEvent {
|
||||
override fun toString(): String = "Registered(accountEntropyPool=${accountEntropyPool.displayValue.censor()}, storageCapable=$storageCapable)"
|
||||
}
|
||||
|
||||
/** The master key has been restored from SVR. */
|
||||
|
||||
@ -30,6 +30,9 @@ data class RegistrationFlowState(
|
||||
/** The AEP we generated as part of this registration. */
|
||||
val accountEntropyPool: AccountEntropyPool? = null,
|
||||
|
||||
/** Whether the server reported that this account already has SVR/PIN data, captured from the registration response. */
|
||||
val storageCapable: Boolean = false,
|
||||
|
||||
/** The master key we restored from SVR. Needed for initial storage service restore, but afterwards we'll generate a new one. */
|
||||
val temporaryMasterKey: MasterKey? = null,
|
||||
|
||||
@ -55,6 +58,6 @@ data class RegistrationFlowState(
|
||||
val isRestoringNavigationState: Boolean = true
|
||||
) : Parcelable {
|
||||
override fun toString(): String {
|
||||
return "RegistrationFlowState(backStack=${backStack.joinToString()}, sessionMetadata=${sessionMetadata.let { "present" }}, sessionE164=$sessionE164, accountEntropyPool=${accountEntropyPool?.displayValue?.censor()}, temporaryMasterKey=${temporaryMasterKey?.toString()?.censor()}, preExistingRegistrationData=${preExistingRegistrationData?.let { "present" }}, doNotAttemptRecoveryPassword=$doNotAttemptRecoveryPassword, pendingRestoreOption=$pendingRestoreOption, unverifiedRestoredAep=${unverifiedRestoredAep?.displayValue?.censor()}, restoreMethodToken=${restoreMethodToken?.censor()}, isRestoringNavigation=$isRestoringNavigationState)"
|
||||
return "RegistrationFlowState(backStack=${backStack.joinToString()}, sessionMetadata=${sessionMetadata.let { "present" }}, sessionE164=$sessionE164, accountEntropyPool=${accountEntropyPool?.displayValue?.censor()}, storageCapable=$storageCapable, temporaryMasterKey=${temporaryMasterKey?.toString()?.censor()}, preExistingRegistrationData=${preExistingRegistrationData?.let { "present" }}, doNotAttemptRecoveryPassword=$doNotAttemptRecoveryPassword, pendingRestoreOption=$pendingRestoreOption, unverifiedRestoredAep=${unverifiedRestoredAep?.displayValue?.censor()}, restoreMethodToken=${restoreMethodToken?.censor()}, isRestoringNavigation=$isRestoringNavigationState)"
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ class RegistrationViewModel(private val repository: RegistrationRepository, save
|
||||
is RegistrationFlowEvent.ResetState -> RegistrationFlowState(isRestoringNavigationState = false)
|
||||
is RegistrationFlowEvent.SessionUpdated -> state.copy(sessionMetadata = event.session)
|
||||
is RegistrationFlowEvent.E164Chosen -> state.copy(sessionE164 = event.e164)
|
||||
is RegistrationFlowEvent.Registered -> state.copy(accountEntropyPool = event.accountEntropyPool)
|
||||
is RegistrationFlowEvent.Registered -> state.copy(accountEntropyPool = event.accountEntropyPool, storageCapable = event.storageCapable)
|
||||
is RegistrationFlowEvent.MasterKeyRestoredFromSvr -> state.copy(temporaryMasterKey = event.masterKey)
|
||||
is RegistrationFlowEvent.NavigateToScreen -> applyNavigationToScreenEvent(state, event)
|
||||
is RegistrationFlowEvent.NavigateBack -> {
|
||||
|
||||
@ -71,10 +71,10 @@ class EnterAepForRemoteBackupPreRegistrationViewModel(
|
||||
when (val result = repository.registerAccountWithRecoveryPassword(e164, recoveryPassword, existingAccountEntropyPool = aep)) {
|
||||
is RequestResult.Success -> {
|
||||
Log.i(TAG, "[Submit] Successfully registered using RRP from user-supplied AEP.")
|
||||
val (_, keyMaterial) = result.result
|
||||
val (response, keyMaterial) = result.result
|
||||
|
||||
stateEmitter(inputState.copy(isRegistering = false))
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool))
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool, response.storageCapable))
|
||||
parentEventEmitter.navigateTo(RegistrationRoute.RemoteRestore(aep))
|
||||
}
|
||||
is RequestResult.NonSuccess -> {
|
||||
|
||||
@ -234,7 +234,7 @@ class PhoneNumberEntryViewModel(
|
||||
Log.i(TAG, "[Register] Successfully re-registered using RRP from pre-existing data.")
|
||||
val (response, keyMaterial) = registerResult.result
|
||||
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool))
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool, response.storageCapable))
|
||||
|
||||
if (response.storageCapable) {
|
||||
parentEventEmitter.navigateTo(RegistrationRoute.PinEntryForSvrRestore)
|
||||
@ -320,7 +320,7 @@ class PhoneNumberEntryViewModel(
|
||||
Log.i(TAG, "[LocalRestore] Successfully registered using RRP from restored AEP.")
|
||||
val (response, keyMaterial) = result.result
|
||||
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool))
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool, response.storageCapable))
|
||||
|
||||
if (response.storageCapable) {
|
||||
parentEventEmitter.navigateTo(RegistrationRoute.PinEntryForSvrRestore)
|
||||
|
||||
@ -134,7 +134,7 @@ class PinEntryForRegistrationLockViewModel(
|
||||
is RequestResult.Success -> {
|
||||
Log.i(TAG, "[PinEntered] Successfully registered!")
|
||||
val (response, keyMaterial) = registerResult.result
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool))
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool, response.storageCapable))
|
||||
when {
|
||||
response.reregistration -> parentEventEmitter.navigateTo(RegistrationRoute.ArchiveRestoreSelection.forPostRegister())
|
||||
else -> repository.finishRegistrationOrCreateProfile(parentEventEmitter)
|
||||
|
||||
@ -112,7 +112,7 @@ class QuickRestoreQrViewModel(
|
||||
is RequestResult.Success -> {
|
||||
val (response, keyMaterial) = registerResult.result
|
||||
Log.i(TAG, "[Register] Success! reregistration: ${response.reregistration}")
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool))
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool, response.storageCapable))
|
||||
parentEventEmitter.navigateTo(RegistrationRoute.ArchiveRestoreSelection.forQuickRestore(hasRemoteBackup = message.tier != null))
|
||||
}
|
||||
is RequestResult.NonSuccess -> {
|
||||
|
||||
@ -11,9 +11,11 @@ data class ArchiveRestoreSelectionState(
|
||||
val restoreOptions: List<ArchiveRestoreOption> = emptyList(),
|
||||
val showSkipWarningDialog: Boolean = false,
|
||||
/** Token that, if present, indicates that the user did a quick restore, and we should hit a network endpoint to indicate our restore selection. */
|
||||
val restoreMethodToken: String? = null
|
||||
val restoreMethodToken: String? = null,
|
||||
/** Whether the account already has SVR/PIN data on the server. Determines whether skipping restore leads to PIN entry or PIN creation. */
|
||||
val storageCapable: Boolean = false
|
||||
) {
|
||||
override fun toString(): String = "ArchiveRestoreSelectionState(restoreOptions=$restoreOptions, showSkipWarningDialog=$showSkipWarningDialog, restoreMethodToken=${restoreMethodToken?.censor()})"
|
||||
override fun toString(): String = "ArchiveRestoreSelectionState(restoreOptions=$restoreOptions, showSkipWarningDialog=$showSkipWarningDialog, restoreMethodToken=${restoreMethodToken?.censor()}, storageCapable=$storageCapable)"
|
||||
|
||||
val showSkipButton: Boolean get() = ArchiveRestoreOption.None !in restoreOptions
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ class ArchiveRestoreSelectionViewModel(
|
||||
|
||||
@VisibleForTesting
|
||||
fun applyParentState(state: ArchiveRestoreSelectionState, parentState: RegistrationFlowState): ArchiveRestoreSelectionState {
|
||||
return state.copy(restoreMethodToken = parentState.restoreMethodToken)
|
||||
return state.copy(restoreMethodToken = parentState.restoreMethodToken, storageCapable = parentState.storageCapable)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@ -108,7 +108,13 @@ class ArchiveRestoreSelectionViewModel(
|
||||
is ArchiveRestoreSelectionScreenEvents.ConfirmSkip -> {
|
||||
notifyOldDevice(state.restoreMethodToken, NetworkController.RestoreMethod.DECLINE)
|
||||
repository.setRestoreDecision(RestoreDecision.SKIPPED)
|
||||
parentEventEmitter.navigateTo(RegistrationRoute.PinCreate)
|
||||
if (state.storageCapable) {
|
||||
Log.i(TAG, "[ConfirmSkip] Account is storage capable. Navigating to PIN entry to restore the existing PIN.")
|
||||
parentEventEmitter.navigateTo(RegistrationRoute.PinEntryForSvrRestore)
|
||||
} else {
|
||||
Log.i(TAG, "[ConfirmSkip] Account is not storage capable. Navigating to PIN creation.")
|
||||
parentEventEmitter.navigateTo(RegistrationRoute.PinCreate)
|
||||
}
|
||||
state.copy(showSkipWarningDialog = false)
|
||||
}
|
||||
is ArchiveRestoreSelectionScreenEvents.DismissSkipWarning -> {
|
||||
|
||||
@ -173,7 +173,7 @@ class VerificationCodeViewModel(
|
||||
is RequestResult.Success -> {
|
||||
val (response, keyMaterial) = registerResult.result
|
||||
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool))
|
||||
parentEventEmitter(RegistrationFlowEvent.Registered(keyMaterial.accountEntropyPool, response.storageCapable))
|
||||
|
||||
when {
|
||||
response.reregistration -> parentEventEmitter.navigateTo(RegistrationRoute.ArchiveRestoreSelection.forPostRegister())
|
||||
|
||||
@ -174,6 +174,7 @@ class PersistedFlowStateTest {
|
||||
sessionMetadata = session,
|
||||
sessionE164 = "+15551234567",
|
||||
accountEntropyPool = AccountEntropyPool.generate(),
|
||||
storageCapable = true,
|
||||
temporaryMasterKey = MasterKey(ByteArray(32)),
|
||||
doNotAttemptRecoveryPassword = true
|
||||
)
|
||||
@ -184,6 +185,7 @@ class PersistedFlowStateTest {
|
||||
assertThat(persisted.sessionMetadata).isEqualTo(session)
|
||||
assertThat(persisted.sessionE164).isEqualTo("+15551234567")
|
||||
assertThat(persisted.doNotAttemptRecoveryPassword).isEqualTo(true)
|
||||
assertThat(persisted.storageCapable).isEqualTo(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -202,7 +204,8 @@ class PersistedFlowStateTest {
|
||||
backStack = listOf(RegistrationRoute.Welcome, RegistrationRoute.PinCreate),
|
||||
sessionMetadata = session,
|
||||
sessionE164 = "+15551234567",
|
||||
doNotAttemptRecoveryPassword = true
|
||||
doNotAttemptRecoveryPassword = true,
|
||||
storageCapable = true
|
||||
)
|
||||
|
||||
val aep = AccountEntropyPool.generate()
|
||||
@ -221,5 +224,6 @@ class PersistedFlowStateTest {
|
||||
assertThat(flowState.temporaryMasterKey).isEqualTo(masterKey)
|
||||
assertThat(flowState.preExistingRegistrationData).isNull()
|
||||
assertThat(flowState.doNotAttemptRecoveryPassword).isEqualTo(true)
|
||||
assertThat(flowState.storageCapable).isEqualTo(true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,7 +236,7 @@ class RegistrationViewModelTest {
|
||||
val viewModel = RegistrationViewModel(mockRepository, SavedStateHandle())
|
||||
advanceUntilIdle()
|
||||
|
||||
viewModel.onEvent(RegistrationFlowEvent.Registered(AccountEntropyPool.generate()))
|
||||
viewModel.onEvent(RegistrationFlowEvent.Registered(AccountEntropyPool.generate(), storageCapable = false))
|
||||
advanceUntilIdle()
|
||||
|
||||
coVerify(exactly = 0) { mockRepository.saveFlowState(any()) }
|
||||
@ -380,10 +380,11 @@ class RegistrationViewModelTest {
|
||||
|
||||
val result = viewModel.applyEvent(
|
||||
RegistrationFlowState(),
|
||||
RegistrationFlowEvent.Registered(aep)
|
||||
RegistrationFlowEvent.Registered(aep, storageCapable = true)
|
||||
)
|
||||
|
||||
assertThat(result.accountEntropyPool).isEqualTo(aep)
|
||||
assertThat(result.storageCapable).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -190,9 +190,9 @@ class ArchiveRestoreSelectionViewModelTest {
|
||||
// ==================== ConfirmSkip Tests ====================
|
||||
|
||||
@Test
|
||||
fun `ConfirmSkip navigates to PinCreate and clears dialog`() = runTest {
|
||||
fun `ConfirmSkip when not storage capable navigates to PinCreate and clears dialog`() = runTest {
|
||||
val viewModel = createViewModel(isPreRegistration = false)
|
||||
val initialState = ArchiveRestoreSelectionState(showSkipWarningDialog = true)
|
||||
val initialState = ArchiveRestoreSelectionState(showSkipWarningDialog = true, storageCapable = false)
|
||||
|
||||
viewModel.applyEvent(initialState, ArchiveRestoreSelectionScreenEvents.ConfirmSkip, stateEmitter)
|
||||
|
||||
@ -205,6 +205,22 @@ class ArchiveRestoreSelectionViewModelTest {
|
||||
assertThat(emittedStates.last().showSkipWarningDialog).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ConfirmSkip when storage capable navigates to PinEntryForSvrRestore and clears dialog`() = runTest {
|
||||
val viewModel = createViewModel(isPreRegistration = false)
|
||||
val initialState = ArchiveRestoreSelectionState(showSkipWarningDialog = true, storageCapable = true)
|
||||
|
||||
viewModel.applyEvent(initialState, ArchiveRestoreSelectionScreenEvents.ConfirmSkip, stateEmitter)
|
||||
|
||||
coVerify { mockRepository.setRestoreDecision(RestoreDecision.SKIPPED) }
|
||||
assertThat(emittedParentEvents).hasSize(1)
|
||||
assertThat(emittedParentEvents.first())
|
||||
.isInstanceOf<RegistrationFlowEvent.NavigateToScreen>()
|
||||
.prop(RegistrationFlowEvent.NavigateToScreen::route)
|
||||
.isEqualTo(RegistrationRoute.PinEntryForSvrRestore)
|
||||
assertThat(emittedStates.last().showSkipWarningDialog).isFalse()
|
||||
}
|
||||
|
||||
// ==================== DismissSkipWarning Tests ====================
|
||||
|
||||
@Test
|
||||
@ -249,4 +265,13 @@ class ArchiveRestoreSelectionViewModelTest {
|
||||
|
||||
assertThat(viewModel.state.value.showSkipButton).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applyParentState copies storageCapable from parent`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
|
||||
val result = viewModel.applyParentState(ArchiveRestoreSelectionState(), RegistrationFlowState(storageCapable = true))
|
||||
|
||||
assertThat(result.storageCapable).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user