mirror of
https://github.com/bitwarden/android.git
synced 2025-03-16 03:08:50 +03:00
BIT-2232: Don't require password for PIN setup for users w/o password (#1252)
This commit is contained in:
parent
bc350773be
commit
f2301e15b9
5 changed files with 90 additions and 7 deletions
|
@ -42,7 +42,9 @@ data class UserState(
|
|||
* @property isVaultUnlocked Whether or not the user's vault is currently unlocked.
|
||||
* @property needsPasswordReset If the user needs to reset their password.
|
||||
* @property needsMasterPassword Indicates whether the user needs to create a password (e.g.
|
||||
* they logged in using SSO and don't yet have one).
|
||||
* they logged in using SSO and don't yet have one). NOTE: This should **not** be used to
|
||||
* determine whether a user has a master password. There are cases in which a user can both
|
||||
* not have a password but still not need one, such as TDE.
|
||||
* @property organizations List of [Organization]s the user is associated with, if any.
|
||||
* @property isBiometricsEnabled Indicates that the biometrics mechanism for unlocking the
|
||||
* user's vault is enabled.
|
||||
|
|
|
@ -206,6 +206,7 @@ fun AccountSecurityScreen(
|
|||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
UnlockWithPinRow(
|
||||
isUnlockWithPasswordEnabled = state.isUnlockWithPasswordEnabled,
|
||||
isUnlockWithPinEnabled = state.isUnlockWithPinEnabled,
|
||||
onUnlockWithPinToggleAction = remember(viewModel) {
|
||||
{ viewModel.trySendAction(it) }
|
||||
|
@ -400,6 +401,7 @@ private fun UnlockWithBiometricsRow(
|
|||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun UnlockWithPinRow(
|
||||
isUnlockWithPasswordEnabled: Boolean,
|
||||
isUnlockWithPinEnabled: Boolean,
|
||||
onUnlockWithPinToggleAction: (AccountSecurityAction.UnlockWithPinToggle) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
|
@ -440,10 +442,19 @@ private fun UnlockWithPinRow(
|
|||
onSubmitClick = {
|
||||
if (pin.isNotEmpty()) {
|
||||
shouldShowPinInputDialog = false
|
||||
shouldShowPinConfirmationDialog = true
|
||||
onUnlockWithPinToggleAction(
|
||||
AccountSecurityAction.UnlockWithPinToggle.PendingEnabled,
|
||||
)
|
||||
if (isUnlockWithPasswordEnabled) {
|
||||
shouldShowPinConfirmationDialog = true
|
||||
onUnlockWithPinToggleAction(
|
||||
AccountSecurityAction.UnlockWithPinToggle.PendingEnabled,
|
||||
)
|
||||
} else {
|
||||
onUnlockWithPinToggleAction(
|
||||
AccountSecurityAction.UnlockWithPinToggle.Enabled(
|
||||
pin = pin,
|
||||
shouldRequireMasterPasswordOnRestart = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
shouldShowPinInputDialog = false
|
||||
onUnlockWithPinToggleAction(
|
||||
|
|
|
@ -50,6 +50,12 @@ class AccountSecurityViewModel @Inject constructor(
|
|||
fingerprintPhrase = "".asText(), // This will be filled in dynamically
|
||||
isApproveLoginRequestsEnabled = settingsRepository.isApprovePasswordlessLoginsEnabled,
|
||||
isUnlockWithBiometricsEnabled = settingsRepository.isUnlockWithBiometricsEnabled,
|
||||
isUnlockWithPasswordEnabled = authRepository
|
||||
.userStateFlow
|
||||
.value
|
||||
?.activeAccount
|
||||
?.trustedDevice
|
||||
?.hasMasterPassword != false,
|
||||
isUnlockWithPinEnabled = settingsRepository.isUnlockWithPinEnabled,
|
||||
vaultTimeout = settingsRepository.vaultTimeout,
|
||||
vaultTimeoutAction = settingsRepository.vaultTimeoutAction,
|
||||
|
@ -357,6 +363,7 @@ data class AccountSecurityState(
|
|||
val fingerprintPhrase: Text,
|
||||
val isApproveLoginRequestsEnabled: Boolean,
|
||||
val isUnlockWithBiometricsEnabled: Boolean,
|
||||
val isUnlockWithPasswordEnabled: Boolean,
|
||||
val isUnlockWithPinEnabled: Boolean,
|
||||
val vaultTimeout: VaultTimeout,
|
||||
val vaultTimeoutAction: VaultTimeoutAction,
|
||||
|
|
|
@ -517,7 +517,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `PIN input dialog Submit click with non-empty pin should show a confirmation dialog and send UnlockWithPinToggle PendingEnabled`() {
|
||||
fun `PIN input dialog Submit click with non-empty pin and isUnlockWithPasswordEnabled true should show a confirmation dialog and send UnlockWithPinToggle PendingEnabled`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(isUnlockWithPinEnabled = false)
|
||||
}
|
||||
|
@ -558,6 +558,41 @@ class AccountSecurityScreenTest : BaseComposeTest() {
|
|||
verify { viewModel.trySendAction(AccountSecurityAction.UnlockWithPinToggle.PendingEnabled) }
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `PIN input dialog Submit click with non-empty pin and isUnlockWithPasswordEnabled false should show a confirmation dialog and send UnlockWithPinToggle Enabled`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
isUnlockWithPinEnabled = false,
|
||||
isUnlockWithPasswordEnabled = false,
|
||||
)
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText("Unlock with PIN code")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithText("PIN")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performTextInput("1234")
|
||||
composeTestRule
|
||||
.onAllNodesWithText("Submit")
|
||||
.filterToOne(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
composeTestRule.assertNoDialogExists()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
AccountSecurityAction.UnlockWithPinToggle.Enabled(
|
||||
pin = "1234",
|
||||
shouldRequireMasterPasswordOnRestart = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `PIN confirmation dialog No click should send UnlockWithPinToggle Enabled and close the dialog`() {
|
||||
|
@ -1482,6 +1517,7 @@ class AccountSecurityScreenTest : BaseComposeTest() {
|
|||
fingerprintPhrase = "fingerprint-placeholder".asText(),
|
||||
isApproveLoginRequestsEnabled = false,
|
||||
isUnlockWithBiometricsEnabled = false,
|
||||
isUnlockWithPasswordEnabled = true,
|
||||
isUnlockWithPinEnabled = false,
|
||||
vaultTimeout = VaultTimeout.ThirtyMinutes,
|
||||
vaultTimeoutAction = VaultTimeoutAction.LOCK,
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.x8bit.bitwarden.R
|
|||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
|
@ -28,6 +29,7 @@ import io.mockk.just
|
|||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
|
@ -40,7 +42,10 @@ import org.junit.jupiter.api.Test
|
|||
class AccountSecurityViewModelTest : BaseViewModelTest() {
|
||||
|
||||
private val fakeEnvironmentRepository = FakeEnvironmentRepository()
|
||||
private val authRepository: AuthRepository = mockk(relaxed = true)
|
||||
private val mutableUserStateFlow = MutableStateFlow<UserState?>(DEFAULT_USER_STATE)
|
||||
private val authRepository: AuthRepository = mockk(relaxed = true) {
|
||||
every { userStateFlow } returns mutableUserStateFlow
|
||||
}
|
||||
private val vaultRepository: VaultRepository = mockk(relaxed = true)
|
||||
private val settingsRepository: SettingsRepository = mockk {
|
||||
every { isUnlockWithBiometricsEnabled } returns false
|
||||
|
@ -559,9 +564,31 @@ private val DEFAULT_STATE: AccountSecurityState = AccountSecurityState(
|
|||
fingerprintPhrase = FINGERPRINT.asText(),
|
||||
isApproveLoginRequestsEnabled = false,
|
||||
isUnlockWithBiometricsEnabled = false,
|
||||
isUnlockWithPasswordEnabled = true,
|
||||
isUnlockWithPinEnabled = false,
|
||||
vaultTimeout = VaultTimeout.ThirtyMinutes,
|
||||
vaultTimeoutAction = VaultTimeoutAction.LOCK,
|
||||
vaultTimeoutPolicyMinutes = null,
|
||||
vaultTimeoutPolicyAction = null,
|
||||
)
|
||||
|
||||
private val DEFAULT_USER_STATE = UserState(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = listOf(
|
||||
UserState.Account(
|
||||
userId = "activeUserId",
|
||||
name = "Active User",
|
||||
email = "active@bitwarden.com",
|
||||
avatarColorHex = "#aa00aa",
|
||||
environment = Environment.Us,
|
||||
isPremium = true,
|
||||
isLoggedIn = true,
|
||||
isVaultUnlocked = true,
|
||||
needsPasswordReset = false,
|
||||
isBiometricsEnabled = false,
|
||||
organizations = emptyList(),
|
||||
needsMasterPassword = false,
|
||||
trustedDevice = null,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue