From a80f903df0320b3dd3ba1ce714aba5446dc0d3a5 Mon Sep 17 00:00:00 2001 From: Ramsey Smith <142836716+ramsey-livefront@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:37:53 -0600 Subject: [PATCH] BIT-2246: Update link for recovery code process (#1303) --- .../twofactorlogin/TwoFactorLoginScreen.kt | 5 ++--- .../twofactorlogin/TwoFactorLoginViewModel.kt | 20 +++++++++++++++++-- .../TwoFactorLoginScreenTest.kt | 5 +++-- .../TwoFactorLoginViewModelTest.kt | 18 ++++++++++++++++- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreen.kt index b60b82a17..efa21ddfe 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreen.kt @@ -32,7 +32,6 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.core.net.toUri import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -97,8 +96,8 @@ fun TwoFactorLoginScreen( when (event) { TwoFactorLoginEvent.NavigateBack -> onNavigateBack() - TwoFactorLoginEvent.NavigateToRecoveryCode -> { - intentManager.launchUri("https://bitwarden.com/help/lost-two-step-device".toUri()) + is TwoFactorLoginEvent.NavigateToRecoveryCode -> { + intentManager.launchUri(uri = event.uri) } is TwoFactorLoginEvent.NavigateToCaptcha -> { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginViewModel.kt index 3b23f96d3..1781584cb 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginViewModel.kt @@ -3,6 +3,7 @@ package com.x8bit.bitwarden.ui.auth.feature.twofactorlogin import android.net.Uri import android.os.Parcelable import androidx.annotation.DrawableRes +import androidx.core.net.toUri import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.x8bit.bitwarden.R @@ -19,6 +20,8 @@ import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult import com.x8bit.bitwarden.data.auth.repository.util.DuoCallbackTokenResult import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha import com.x8bit.bitwarden.data.auth.util.YubiKeyResult +import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository +import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.button import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.imageRes import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.isDuo @@ -44,6 +47,7 @@ private const val KEY_STATE = "state" @Suppress("TooManyFunctions") class TwoFactorLoginViewModel @Inject constructor( private val authRepository: AuthRepository, + private val environmentRepository: EnvironmentRepository, savedStateHandle: SavedStateHandle, ) : BaseViewModel( initialState = savedStateHandle[KEY_STATE] @@ -60,6 +64,16 @@ class TwoFactorLoginViewModel @Inject constructor( password = TwoFactorLoginArgs(savedStateHandle).password, ), ) { + + private val recover2faUri: Uri + get() { + val baseUrl = environmentRepository + .environment + .environmentUrlData + .baseWebVaultUrlOrDefault + return "$baseUrl/#/recover-2fa".toUri() + } + init { // As state updates, write to saved state handle. stateFlow @@ -357,7 +371,7 @@ class TwoFactorLoginViewModel @Inject constructor( private fun handleSelectAuthMethod(action: TwoFactorLoginAction.SelectAuthMethod) { when (action.authMethod) { TwoFactorAuthMethod.RECOVERY_CODE -> { - sendEvent(TwoFactorLoginEvent.NavigateToRecoveryCode) + sendEvent(TwoFactorLoginEvent.NavigateToRecoveryCode(recover2faUri)) } TwoFactorAuthMethod.EMAIL -> { @@ -520,8 +534,10 @@ sealed class TwoFactorLoginEvent { /** * Navigates to the recovery code help page. + * + * @param uri The recovery uri. */ - data object NavigateToRecoveryCode : TwoFactorLoginEvent() + data class NavigateToRecoveryCode(val uri: Uri) : TwoFactorLoginEvent() /** * Shows a toast with the given [message]. diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreenTest.kt index 4e8950280..aff2547fd 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginScreenTest.kt @@ -255,9 +255,10 @@ class TwoFactorLoginScreenTest : BaseComposeTest() { @Test fun `NavigateToRecoveryCode should launch the recovery code uri`() { - mutableEventFlow.tryEmit(TwoFactorLoginEvent.NavigateToRecoveryCode) + val mockUri = mockk() + mutableEventFlow.tryEmit(TwoFactorLoginEvent.NavigateToRecoveryCode(mockUri)) verify { - intentManager.launchUri(any()) + intentManager.launchUri(mockUri) } } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginViewModelTest.kt index bfe311d67..2beffe885 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/twofactorlogin/TwoFactorLoginViewModelTest.kt @@ -1,6 +1,7 @@ package com.x8bit.bitwarden.ui.auth.feature.twofactorlogin import android.net.Uri +import androidx.core.net.toUri import androidx.lifecycle.SavedStateHandle import app.cash.turbine.test import com.x8bit.bitwarden.R @@ -14,6 +15,8 @@ import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult import com.x8bit.bitwarden.data.auth.repository.util.DuoCallbackTokenResult import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha import com.x8bit.bitwarden.data.auth.util.YubiKeyResult +import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository +import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest import com.x8bit.bitwarden.ui.platform.base.util.asText @@ -45,6 +48,11 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() { every { duoTokenResultFlow } returns mutableDuoTokenResultFlow every { yubiKeyResultFlow } returns mutableYubiKeyResultFlow } + private val environmentRepository: EnvironmentRepository = mockk(relaxed = true) { + every { + environment.environmentUrlData.baseWebVaultUrlOrDefault + } returns "https://vault.bitwarden.com" + } @BeforeEach fun setUp() { @@ -515,6 +523,11 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() { @Test fun `SelectAuthMethod with RECOVERY_CODE should launch the NavigateToRecoveryCode event`() = runTest { + val mockkUri = mockk() + every { + Uri.parse("https://vault.bitwarden.com/#/recover-2fa") + } returns mockkUri + val viewModel = createViewModel() viewModel.eventFlow.test { viewModel.trySendAction( @@ -523,7 +536,9 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() { ), ) assertEquals( - TwoFactorLoginEvent.NavigateToRecoveryCode, + TwoFactorLoginEvent.NavigateToRecoveryCode( + uri = "https://vault.bitwarden.com/#/recover-2fa".toUri(), + ), awaitItem(), ) } @@ -611,6 +626,7 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() { ): TwoFactorLoginViewModel = TwoFactorLoginViewModel( authRepository = authRepository, + environmentRepository = environmentRepository, savedStateHandle = SavedStateHandle().also { it["state"] = state it["email_address"] = "example@email.com"