mirror of
https://github.com/bitwarden/android.git
synced 2026-05-06 07:48:22 -05:00
[PM-21203] Old user migration login error. (#5136)
This commit is contained in:
@@ -1613,7 +1613,7 @@ class AuthRepositoryImpl(
|
||||
* A helper function to extract the common logic of logging in through
|
||||
* any of the available methods.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Suppress("LongMethod", "MaxLineLength")
|
||||
private suspend fun loginCommon(
|
||||
email: String,
|
||||
password: String? = null,
|
||||
@@ -1677,6 +1677,10 @@ class AuthRepositoryImpl(
|
||||
error = loginResponse.errorMessage,
|
||||
)
|
||||
|
||||
is GetTokenResponseJson.Invalid.InvalidType.EncryptionKeyMigrationRequired -> {
|
||||
LoginResult.EncryptionKeyMigrationRequired
|
||||
}
|
||||
|
||||
is GetTokenResponseJson.Invalid.InvalidType.GenericInvalid -> {
|
||||
LoginResult.Error(
|
||||
errorMessage = loginResponse.errorMessage,
|
||||
|
||||
@@ -14,6 +14,11 @@ sealed class LoginResult {
|
||||
*/
|
||||
data class CaptchaRequired(val captchaId: String) : LoginResult()
|
||||
|
||||
/**
|
||||
* Encryption key migration is required.
|
||||
*/
|
||||
data object EncryptionKeyMigrationRequired : LoginResult()
|
||||
|
||||
/**
|
||||
* Two-factor verification is required.
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.os.Parcelable
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.data.repository.util.baseIdentityUrl
|
||||
import com.bitwarden.data.repository.util.baseWebVaultUrlOrDefault
|
||||
import com.bitwarden.ui.util.Text
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.R
|
||||
@@ -22,6 +23,7 @@ import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.util.toUriOrNull
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.utils.generateRandomString
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
@@ -154,6 +156,7 @@ class EnterpriseSignOnViewModel @Inject constructor(
|
||||
prevalidateSso()
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun handleOnLoginResult(action: EnterpriseSignOnAction.Internal.OnLoginResult) {
|
||||
when (val loginResult = action.loginResult) {
|
||||
is LoginResult.CaptchaRequired -> {
|
||||
@@ -197,6 +200,20 @@ class EnterpriseSignOnViewModel @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
is LoginResult.EncryptionKeyMigrationRequired -> {
|
||||
val vaultUrl =
|
||||
environmentRepository
|
||||
.environment
|
||||
.environmentUrlData
|
||||
.baseWebVaultUrlOrDefault
|
||||
|
||||
showError(
|
||||
message = R.string
|
||||
.this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden
|
||||
.asText(vaultUrl.toUriOrNull()?.host ?: vaultUrl),
|
||||
)
|
||||
}
|
||||
|
||||
LoginResult.CertificateError -> {
|
||||
showError(message = R.string.we_couldnt_verify_the_servers_certificate.asText())
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.data.repository.util.baseWebVaultUrlOrDefault
|
||||
import com.bitwarden.ui.util.Text
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.R
|
||||
@@ -16,6 +17,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.LogoutReason
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.util.toUriOrNull
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
||||
@@ -37,7 +39,7 @@ private const val KEY_STATE = "state"
|
||||
@HiltViewModel
|
||||
class LoginViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
environmentRepository: EnvironmentRepository,
|
||||
private val environmentRepository: EnvironmentRepository,
|
||||
private val authRepository: AuthRepository,
|
||||
private val vaultRepository: VaultRepository,
|
||||
) : BaseViewModel<LoginState, LoginEvent, LoginAction>(
|
||||
@@ -166,6 +168,25 @@ class LoginViewModel @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
is LoginResult.EncryptionKeyMigrationRequired -> {
|
||||
val vaultUrl =
|
||||
environmentRepository
|
||||
.environment
|
||||
.environmentUrlData
|
||||
.baseWebVaultUrlOrDefault
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = LoginState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string
|
||||
.this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden
|
||||
.asText(vaultUrl.toUriOrNull()?.host ?: vaultUrl),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is LoginResult.TwoFactorRequired -> {
|
||||
mutableStateFlow.update { it.copy(dialogState = null) }
|
||||
sendEvent(
|
||||
|
||||
@@ -286,6 +286,7 @@ class LoginWithDeviceViewModel @Inject constructor(
|
||||
|
||||
// NO-OP: This result should not be possible here
|
||||
is LoginResult.ConfirmKeyConnectorDomain -> Unit
|
||||
LoginResult.EncryptionKeyMigrationRequired -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -282,7 +282,7 @@ class TwoFactorLoginViewModel @Inject constructor(
|
||||
/**
|
||||
* Handle the login result.
|
||||
*/
|
||||
@Suppress("MaxLineLength")
|
||||
@Suppress("MaxLineLength", "LongMethod")
|
||||
private fun handleReceiveLoginResult(action: TwoFactorLoginAction.Internal.ReceiveLoginResult) {
|
||||
// Dismiss the loading overlay.
|
||||
mutableStateFlow.update { it.copy(dialogState = null) }
|
||||
@@ -353,6 +353,7 @@ class TwoFactorLoginViewModel @Inject constructor(
|
||||
|
||||
// NO-OP: This result should not be possible here
|
||||
is LoginResult.ConfirmKeyConnectorDomain -> Unit
|
||||
LoginResult.EncryptionKeyMigrationRequired -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1265,4 +1265,5 @@ Do you want to switch to this account?</string>
|
||||
<string name="unable_to_read_certificate">Unable to read certificate.</string>
|
||||
<string name="cannot_delete_your_account">Cannot delete your account</string>
|
||||
<string name="cannot_delete_your_account_explanation">This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details.</string>
|
||||
<string name="this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden">This account will soon be deleted. Log in at %1$s to continue using Bitwarden.</string>
|
||||
</resources>
|
||||
|
||||
@@ -2392,6 +2392,47 @@ class AuthRepositoryTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `login get token returns invalid request should return EncryptionKeyMigrationRequired`() =
|
||||
runTest {
|
||||
coEvery { identityService.preLogin(EMAIL) } returns PRE_LOGIN_SUCCESS.asSuccess()
|
||||
coEvery {
|
||||
identityService.getToken(
|
||||
email = EMAIL,
|
||||
authModel = IdentityTokenAuthModel.MasterPassword(
|
||||
username = EMAIL,
|
||||
password = PASSWORD_HASH,
|
||||
),
|
||||
captchaToken = null,
|
||||
uniqueAppId = UNIQUE_APP_ID,
|
||||
)
|
||||
} returns GetTokenResponseJson
|
||||
.Invalid(
|
||||
errorModel = GetTokenResponseJson.Invalid.ErrorModel(
|
||||
errorMessage =
|
||||
"Encryption key migration is required. Please log in to the web vault at",
|
||||
),
|
||||
)
|
||||
.asSuccess()
|
||||
|
||||
val result = repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
||||
assertEquals(LoginResult.EncryptionKeyMigrationRequired, result)
|
||||
assertEquals(AuthState.Unauthenticated, repository.authStateFlow.value)
|
||||
coVerify {
|
||||
identityService.preLogin(email = EMAIL)
|
||||
identityService.getToken(
|
||||
email = EMAIL,
|
||||
authModel = IdentityTokenAuthModel.MasterPassword(
|
||||
username = EMAIL,
|
||||
password = PASSWORD_HASH,
|
||||
),
|
||||
captchaToken = null,
|
||||
uniqueAppId = UNIQUE_APP_ID,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login with device get token fails should return Error with no message`() = runTest {
|
||||
val error = Throwable("Fail!")
|
||||
|
||||
@@ -4,6 +4,8 @@ import android.net.Uri
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.data.datasource.disk.model.EnvironmentUrlDataJson
|
||||
import com.bitwarden.data.repository.model.Environment
|
||||
import com.bitwarden.network.model.VerifiedOrganizationDomainSsoDetailsResponse
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.R
|
||||
@@ -461,6 +463,231 @@ class EnterpriseSignOnViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `ssoCallbackResultFlow Success with same state with login EncryptionKeyMigrationRequired should update dialogState with web vault url`() =
|
||||
runTest {
|
||||
val orgIdentifier = "Bitwarden"
|
||||
coEvery {
|
||||
authRepository.login(any(), any(), any(), any(), any(), any())
|
||||
} returns LoginResult.EncryptionKeyMigrationRequired
|
||||
|
||||
environmentRepository.environment = Environment.SelfHosted(
|
||||
environmentUrlData = EnvironmentUrlDataJson(
|
||||
base = "",
|
||||
webVault = "vault.bitwarden.com",
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel(
|
||||
ssoData = DEFAULT_SSO_DATA,
|
||||
)
|
||||
val ssoCallbackResult = SsoCallbackResult.Success(state = "abc", code = "lmn")
|
||||
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(
|
||||
DEFAULT_STATE,
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
EnterpriseSignOnAction.OrgIdentifierInputChange(orgIdentifier),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
orgIdentifierInput = orgIdentifier,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
mutableSsoCallbackResultFlow.tryEmit(ssoCallbackResult)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = EnterpriseSignOnState.DialogState.Loading(
|
||||
R.string.logging_in.asText(),
|
||||
),
|
||||
orgIdentifierInput = orgIdentifier,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = EnterpriseSignOnState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden
|
||||
.asText("vault.bitwarden.com"),
|
||||
),
|
||||
orgIdentifierInput = orgIdentifier,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
authRepository.login(
|
||||
email = "test@gmail.com",
|
||||
ssoCode = "lmn",
|
||||
ssoCodeVerifier = "def",
|
||||
ssoRedirectUri = "bitwarden://sso-callback",
|
||||
captchaToken = null,
|
||||
organizationIdentifier = orgIdentifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `ssoCallbackResultFlow Success with same state with login EncryptionKeyMigrationRequired should update dialogState with base url`() =
|
||||
runTest {
|
||||
val orgIdentifier = "Bitwarden"
|
||||
coEvery {
|
||||
authRepository.login(any(), any(), any(), any(), any(), any())
|
||||
} returns LoginResult.EncryptionKeyMigrationRequired
|
||||
|
||||
environmentRepository.environment = Environment.SelfHosted(
|
||||
environmentUrlData = EnvironmentUrlDataJson(
|
||||
base = "base.bitwarden.com",
|
||||
webVault = "",
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel(
|
||||
ssoData = DEFAULT_SSO_DATA,
|
||||
)
|
||||
val ssoCallbackResult = SsoCallbackResult.Success(state = "abc", code = "lmn")
|
||||
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(
|
||||
DEFAULT_STATE,
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
EnterpriseSignOnAction.OrgIdentifierInputChange(orgIdentifier),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
orgIdentifierInput = orgIdentifier,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
mutableSsoCallbackResultFlow.tryEmit(ssoCallbackResult)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = EnterpriseSignOnState.DialogState.Loading(
|
||||
R.string.logging_in.asText(),
|
||||
),
|
||||
orgIdentifierInput = orgIdentifier,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = EnterpriseSignOnState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden
|
||||
.asText("base.bitwarden.com"),
|
||||
),
|
||||
orgIdentifierInput = orgIdentifier,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
authRepository.login(
|
||||
email = "test@gmail.com",
|
||||
ssoCode = "lmn",
|
||||
ssoCodeVerifier = "def",
|
||||
ssoRedirectUri = "bitwarden://sso-callback",
|
||||
captchaToken = null,
|
||||
organizationIdentifier = orgIdentifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `ssoCallbackResultFlow Success with same state with login EncryptionKeyMigrationRequired should update dialogState with default url`() =
|
||||
runTest {
|
||||
val orgIdentifier = "Bitwarden"
|
||||
coEvery {
|
||||
authRepository.login(any(), any(), any(), any(), any(), any())
|
||||
} returns LoginResult.EncryptionKeyMigrationRequired
|
||||
|
||||
environmentRepository.environment = Environment.SelfHosted(
|
||||
environmentUrlData = EnvironmentUrlDataJson(
|
||||
base = "",
|
||||
webVault = "",
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel(
|
||||
ssoData = DEFAULT_SSO_DATA,
|
||||
)
|
||||
val ssoCallbackResult = SsoCallbackResult.Success(state = "abc", code = "lmn")
|
||||
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(
|
||||
DEFAULT_STATE,
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
viewModel.trySendAction(
|
||||
EnterpriseSignOnAction.OrgIdentifierInputChange(orgIdentifier),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
orgIdentifierInput = orgIdentifier,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
mutableSsoCallbackResultFlow.tryEmit(ssoCallbackResult)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = EnterpriseSignOnState.DialogState.Loading(
|
||||
R.string.logging_in.asText(),
|
||||
),
|
||||
orgIdentifierInput = orgIdentifier,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = EnterpriseSignOnState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden
|
||||
.asText("vault.bitwarden.com"),
|
||||
),
|
||||
orgIdentifierInput = orgIdentifier,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
authRepository.login(
|
||||
email = "test@gmail.com",
|
||||
ssoCode = "lmn",
|
||||
ssoCodeVerifier = "def",
|
||||
ssoRedirectUri = "bitwarden://sso-callback",
|
||||
captchaToken = null,
|
||||
organizationIdentifier = orgIdentifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ssoCallbackResultFlow Success with same state with login CertificateError should show loading dialog then show certificate error dialog`() =
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@Suppress("LargeClass")
|
||||
class LoginViewModelTest : BaseViewModelTest() {
|
||||
|
||||
private val mutableCaptchaTokenResultFlow =
|
||||
@@ -333,6 +334,146 @@ class LoginViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `LoginButtonClick login returns EncryptionKeyMigrationRequired should update errorDialogState with webVault url`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
authRepository.login(
|
||||
email = EMAIL,
|
||||
password = "",
|
||||
captchaToken = null,
|
||||
)
|
||||
} returns LoginResult.EncryptionKeyMigrationRequired
|
||||
fakeEnvironmentRepository.environment = Environment.SelfHosted(
|
||||
environmentUrlData = EnvironmentUrlDataJson(
|
||||
base = "",
|
||||
webVault = "vault.bitwarden.com",
|
||||
),
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
val defaultSelfHostedState = DEFAULT_STATE.copy(environmentLabel = "")
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(defaultSelfHostedState, awaitItem())
|
||||
viewModel.trySendAction(LoginAction.LoginButtonClick)
|
||||
assertEquals(
|
||||
defaultSelfHostedState.copy(
|
||||
environmentLabel = "",
|
||||
dialogState = LoginState.DialogState.Loading(
|
||||
message = R.string.logging_in.asText(),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
defaultSelfHostedState.copy(
|
||||
environmentLabel = "",
|
||||
dialogState = LoginState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden
|
||||
.asText("vault.bitwarden.com"),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
coVerify {
|
||||
authRepository.login(email = EMAIL, password = "", captchaToken = null)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `LoginButtonClick login returns EncryptionKeyMigrationRequired should update errorDialogState with base url`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
authRepository.login(
|
||||
email = EMAIL,
|
||||
password = "",
|
||||
captchaToken = null,
|
||||
)
|
||||
} returns LoginResult.EncryptionKeyMigrationRequired
|
||||
fakeEnvironmentRepository.environment = Environment.SelfHosted(
|
||||
environmentUrlData = EnvironmentUrlDataJson(
|
||||
base = "base.bitwarden.com",
|
||||
webVault = "",
|
||||
),
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
val defaultSelfHostedState = DEFAULT_STATE.copy(environmentLabel = "")
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(defaultSelfHostedState, awaitItem())
|
||||
viewModel.trySendAction(LoginAction.LoginButtonClick)
|
||||
assertEquals(
|
||||
defaultSelfHostedState.copy(
|
||||
dialogState = LoginState.DialogState.Loading(
|
||||
message = R.string.logging_in.asText(),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
defaultSelfHostedState.copy(
|
||||
dialogState = LoginState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden
|
||||
.asText("base.bitwarden.com"),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
coVerify {
|
||||
authRepository.login(email = EMAIL, password = "", captchaToken = null)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `LoginButtonClick login returns EncryptionKeyMigrationRequired should update errorDialogState with default url`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
authRepository.login(
|
||||
email = EMAIL,
|
||||
password = "",
|
||||
captchaToken = null,
|
||||
)
|
||||
} returns LoginResult.EncryptionKeyMigrationRequired
|
||||
fakeEnvironmentRepository.environment = Environment.SelfHosted(
|
||||
environmentUrlData = EnvironmentUrlDataJson(
|
||||
base = "",
|
||||
webVault = "",
|
||||
),
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
val defaultSelfHostedState = DEFAULT_STATE.copy(environmentLabel = "")
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(defaultSelfHostedState, awaitItem())
|
||||
viewModel.trySendAction(LoginAction.LoginButtonClick)
|
||||
assertEquals(
|
||||
defaultSelfHostedState.copy(
|
||||
dialogState = LoginState.DialogState.Loading(
|
||||
message = R.string.logging_in.asText(),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
defaultSelfHostedState.copy(
|
||||
dialogState = LoginState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.this_account_will_soon_be_deleted_log_in_at_x_to_continue_using_bitwarden
|
||||
.asText("vault.bitwarden.com"),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
coVerify {
|
||||
authRepository.login(email = EMAIL, password = "", captchaToken = null)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `LoginButtonClick login returns CertificateError should update errorDialogState`() =
|
||||
|
||||
@@ -119,6 +119,11 @@ sealed class GetTokenResponseJson {
|
||||
*/
|
||||
data object NewDeviceVerification : InvalidType()
|
||||
|
||||
/**
|
||||
* Represents an invalid response indicating that a new device verification is required.
|
||||
*/
|
||||
data object EncryptionKeyMigrationRequired : InvalidType()
|
||||
|
||||
/**
|
||||
* Represents generic invalid response
|
||||
*/
|
||||
@@ -128,6 +133,12 @@ sealed class GetTokenResponseJson {
|
||||
val invalidType: InvalidType
|
||||
get() = if (errorMessage?.lowercase() == "new device verification required") {
|
||||
InvalidType.NewDeviceVerification
|
||||
} else if (errorMessage
|
||||
?.lowercase()
|
||||
?.contains(
|
||||
"encryption key migration is required. please log in to the web vault at",
|
||||
) == true) {
|
||||
InvalidType.EncryptionKeyMigrationRequired
|
||||
} else {
|
||||
InvalidType.GenericInvalid
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user