diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt index bcbe03d837..a2add4b752 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt @@ -59,6 +59,7 @@ import com.bitwarden.ui.platform.manager.IntentManager import com.bitwarden.ui.platform.resource.BitwardenDrawable import com.bitwarden.ui.platform.resource.BitwardenPlurals import com.bitwarden.ui.platform.resource.BitwardenString +import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenMasterPasswordDialog import com.x8bit.bitwarden.ui.platform.composition.LocalAppReviewManager import com.x8bit.bitwarden.ui.platform.composition.LocalExitManager import com.x8bit.bitwarden.ui.platform.feature.search.model.SearchType @@ -404,6 +405,7 @@ private fun VaultScreenScaffold( } } +@Suppress("LongMethod") @Composable private fun VaultDialogs( dialogState: VaultState.DialogState?, @@ -462,6 +464,19 @@ private fun VaultDialogs( ) } + is VaultState.DialogState.VaultLoadKdfUpdateRequired -> { + BitwardenMasterPasswordDialog( + title = dialogState.title(), + message = dialogState.message(), + dismissButtonText = stringResource(BitwardenString.later), + onConfirmClick = { + vaultHandlers.onKdfUpdatePasswordRepromptSubmit(it) + }, + onDismissRequest = vaultHandlers.dialogDismiss, + filledButtonStyle = true, + ) + } + null -> Unit } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt index de03dbbe89..764f603d3a 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt @@ -1464,6 +1464,15 @@ data class VaultState( val cipherCount: Int, ) : DialogState() + /** + * Represents a dialog indicating that the user needs to update their kdf settings. + */ + @Parcelize + data class VaultLoadKdfUpdateRequired( + val title: Text, + val message: Text, + ) : DialogState() + /** * Represents an error dialog with the given [title] and [message]. */ diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/handlers/VaultHandlers.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/handlers/VaultHandlers.kt index d305b4191e..5b46a25c82 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/handlers/VaultHandlers.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/handlers/VaultHandlers.kt @@ -47,6 +47,7 @@ data class VaultHandlers( val dismissFlightRecorderSnackbar: () -> Unit, val onShareCipherDecryptionErrorClick: (selectedCipherId: String) -> Unit, val onShareAllCipherDecryptionErrorsClick: () -> Unit, + val onKdfUpdatePasswordRepromptSubmit: (password: String) -> Unit, ) { @Suppress("UndocumentedPublicClass") companion object { @@ -139,6 +140,12 @@ data class VaultHandlers( VaultAction.ShareAllCipherDecryptionErrorsClick, ) }, + onKdfUpdatePasswordRepromptSubmit = + { + viewModel.trySendAction( + VaultAction.KdfUpdatePasswordRepromptSubmit(it), + ) + }, ) } } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt index 4a31d2a7a7..b21348b5be 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt @@ -784,6 +784,90 @@ class VaultScreenTest : BitwardenComposeTest() { } } + @Test + fun `vault load KDF update required dialog should be shown or hidden according to the state`() { + val dialogTitle = "Master Password Update" + val dialogMessage = "Your master password does not meet the current security requirements." + composeTestRule.assertNoDialogExists() + composeTestRule + .onNodeWithText(dialogTitle) + .assertDoesNotExist() + composeTestRule + .onNodeWithText(dialogMessage) + .assertDoesNotExist() + + mutableStateFlow.update { + it.copy( + dialog = VaultState.DialogState.VaultLoadKdfUpdateRequired( + title = dialogTitle.asText(), + message = dialogMessage.asText(), + ), + ) + } + + composeTestRule + .onNodeWithText(dialogTitle) + .assertIsDisplayed() + composeTestRule + .onNodeWithText(dialogMessage) + .assertIsDisplayed() + } + + @Suppress("MaxLineLength") + @Test + fun `confirm button click on the VaultLoadKdfUpdateRequired dialog should send KdfUpdatePasswordRepromptSubmit`() { + val dialogTitle = "Master Password Update" + val dialogMessage = "Your master password does not meet the current security requirements." + val testPassword = "test_password" + mutableStateFlow.update { + it.copy( + dialog = VaultState.DialogState.VaultLoadKdfUpdateRequired( + title = dialogTitle.asText(), + message = dialogMessage.asText(), + ), + ) + } + + // Enter password in the input field + composeTestRule + .onNodeWithText("Master password") + .performTextInput(testPassword) + + // Click confirm button + composeTestRule + .onNodeWithText("Submit") + .performClick() + + verify { + viewModel.trySendAction( + VaultAction.KdfUpdatePasswordRepromptSubmit(testPassword), + ) + } + } + + @Test + fun `later button click on the VaultLoadKdfUpdateRequired dialog should send DialogDismiss`() { + val dialogTitle = "Master Password Update" + val dialogMessage = "Your master password does not meet the current security requirements." + val laterText = "Later" + mutableStateFlow.update { + it.copy( + dialog = VaultState.DialogState.VaultLoadKdfUpdateRequired( + title = dialogTitle.asText(), + message = dialogMessage.asText(), + ), + ) + } + + composeTestRule + .onNodeWithText(laterText) + .performClick() + + verify { + viewModel.trySendAction(VaultAction.DialogDismiss) + } + } + @Test fun `syncing dialog should be displayed according to state`() { composeTestRule.assertNoDialogExists() diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 2f5cdf5dc7..6f0d3375cf 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -1085,4 +1085,7 @@ Do you want to switch to this account? URI match detection controls how Bitwarden identifies autofill suggestions.\nWarning: “Starts with” is an advanced option with increased risk of exposing credentials. “Starts with” is an advanced option with increased risk of exposing credentials. “Regular expression” is an advanced option with increased risk of exposing credentials if used incorrectly. + Later + Update your encryption settings + The new recommended encryption settings will improve your account security. Enter your master password to update now.