mirror of
https://github.com/bitwarden/android.git
synced 2026-11-09 10:34:18 -06:00
[PM-44566] fix: Lock the Send password field when editing a Send with a password (#7559)
This commit is contained in:
+1
@@ -177,6 +177,7 @@ fun AddEditSendContent(
|
||||
onPasswordCopyClick = addSendHandlers.onPasswordCopyClick,
|
||||
password = state.common.passwordInput,
|
||||
isEnabled = !policyDisablesSend && enforcedWhoCanAccess == null,
|
||||
isExistingPasswordLocked = state.common.hasPassword,
|
||||
isSendsRestrictedByPolicy = policyDisablesSend,
|
||||
modifier = Modifier
|
||||
.testTag("SendAuthTypeChooser")
|
||||
|
||||
+53
-30
@@ -19,6 +19,7 @@ import com.bitwarden.ui.platform.base.util.cardStyle
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.bitwarden.ui.platform.components.field.BitwardenHiddenPasswordField
|
||||
import com.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
@@ -31,6 +32,13 @@ import com.x8bit.bitwarden.ui.tools.feature.send.addedit.model.SendAuth
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
/**
|
||||
* Stand-in value for a locked password field. The existing password is never readable, and
|
||||
* [BitwardenHiddenPasswordField] masks any non-empty value with a fixed number of dots, so this
|
||||
* only signals that a password is set.
|
||||
*/
|
||||
private const val LOCKED_PASSWORD_PLACEHOLDER: String = "locked"
|
||||
|
||||
/**
|
||||
* Displays UX for choosing authentication type for a send.
|
||||
*
|
||||
@@ -42,10 +50,12 @@ import kotlinx.collections.immutable.toPersistentList
|
||||
* (only relevant for [SendAuth.Email]).
|
||||
* @param onAddNewEmailClick Callback invoked when adding a new email.
|
||||
* @param onRemoveEmailClick Callback invoked when removing an email.
|
||||
* @param password The current password value (only relevant for [SendAuth.Password]).
|
||||
* @param onOpenPasswordGeneratorClick Callback invoked when the Generator button is clicked
|
||||
* @param onPasswordCopyClick Callback invoked when the Copy button is clicked
|
||||
* @param password The current password value (only relevant for [SendAuth.Password]).
|
||||
* @param isEnabled Whether the chooser is enabled.
|
||||
* @param isExistingPasswordLocked Whether the send already has a password that cannot be
|
||||
* overwritten and must be removed before a new one can be set.
|
||||
* @param isSendsRestrictedByPolicy if sends are restricted by a policy.
|
||||
* @param modifier Modifier for the composable.
|
||||
*/
|
||||
@@ -62,6 +72,7 @@ fun AddEditSendAuthTypeChooser(
|
||||
onPasswordCopyClick: (String) -> Unit,
|
||||
password: String,
|
||||
isEnabled: Boolean,
|
||||
isExistingPasswordLocked: Boolean,
|
||||
isSendsRestrictedByPolicy: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -113,36 +124,48 @@ fun AddEditSendAuthTypeChooser(
|
||||
}
|
||||
|
||||
is SendAuth.Password -> {
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = BitwardenString.password),
|
||||
value = password,
|
||||
onValueChange = onPasswordChange,
|
||||
cardStyle = CardStyle.Bottom,
|
||||
passwordFieldTestTag = "SendPasswordEntry",
|
||||
readOnly = isSendsRestrictedByPolicy,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = BitwardenDrawable.ic_generate,
|
||||
contentDescription = stringResource(id = BitwardenString.generate_password),
|
||||
onClick = {
|
||||
if (password.isEmpty()) {
|
||||
onOpenPasswordGeneratorClick()
|
||||
} else {
|
||||
shouldShowDialog = true
|
||||
}
|
||||
},
|
||||
modifier = Modifier.testTag(tag = "RegeneratePasswordButton"),
|
||||
)
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = BitwardenDrawable.ic_copy,
|
||||
contentDescription = stringResource(id = BitwardenString.copy_password),
|
||||
isEnabled = password.isNotEmpty(),
|
||||
onClick = {
|
||||
onPasswordCopyClick(password)
|
||||
},
|
||||
modifier = Modifier.testTag(tag = "CopyPasswordButton"),
|
||||
if (isExistingPasswordLocked) {
|
||||
BitwardenHiddenPasswordField(
|
||||
label = stringResource(id = BitwardenString.password),
|
||||
value = LOCKED_PASSWORD_PLACEHOLDER,
|
||||
cardStyle = CardStyle.Bottom,
|
||||
passwordFieldTestTag = "SendPasswordEntry",
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
} else {
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = BitwardenString.password),
|
||||
value = password,
|
||||
onValueChange = onPasswordChange,
|
||||
cardStyle = CardStyle.Bottom,
|
||||
passwordFieldTestTag = "SendPasswordEntry",
|
||||
readOnly = isSendsRestrictedByPolicy,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = BitwardenDrawable.ic_generate,
|
||||
contentDescription = stringResource(
|
||||
id = BitwardenString.generate_password,
|
||||
),
|
||||
onClick = {
|
||||
if (password.isEmpty()) {
|
||||
onOpenPasswordGeneratorClick()
|
||||
} else {
|
||||
shouldShowDialog = true
|
||||
}
|
||||
},
|
||||
modifier = Modifier.testTag(tag = "RegeneratePasswordButton"),
|
||||
)
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = BitwardenDrawable.ic_copy,
|
||||
contentDescription = stringResource(id = BitwardenString.copy_password),
|
||||
isEnabled = password.isNotEmpty(),
|
||||
onClick = {
|
||||
onPasswordCopyClick(password)
|
||||
},
|
||||
modifier = Modifier.testTag(tag = "CopyPasswordButton"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+103
@@ -213,6 +213,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
sendAuth = SendAuth.Password,
|
||||
passwordInput = "somePass",
|
||||
),
|
||||
@@ -237,6 +238,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
sendAuth = SendAuth.Password,
|
||||
passwordInput = "",
|
||||
),
|
||||
@@ -259,6 +261,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
),
|
||||
@@ -282,6 +285,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
sendAuth = SendAuth.Password,
|
||||
passwordInput = "somePass",
|
||||
),
|
||||
@@ -298,6 +302,97 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `password field should be disabled when the send already has a password`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = true,
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag(testTag = "SendPasswordEntry")
|
||||
.performScrollTo()
|
||||
.assertTextEquals("Password", "••••••••")
|
||||
.assertIsNotEnabled()
|
||||
.assert(!hasSetTextAction())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `password field should be editable when the send has no password`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag(testTag = "SendPasswordEntry")
|
||||
.performScrollTo()
|
||||
.assert(hasSetTextAction())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `password action buttons should be hidden when the send already has a password`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = true,
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag(testTag = "RegeneratePasswordButton")
|
||||
.assertDoesNotExist()
|
||||
composeTestRule
|
||||
.onNodeWithTag(testTag = "CopyPasswordButton")
|
||||
.assertDoesNotExist()
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Show")
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `password action buttons should be displayed when the send has no password`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag(testTag = "RegeneratePasswordButton")
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
composeTestRule
|
||||
.onNodeWithTag(testTag = "CopyPasswordButton")
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Show")
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on overflow button click should display overflow menu`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE.copy(
|
||||
@@ -803,6 +898,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `password input should change according to the state`() {
|
||||
val commonState = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
sendAuth = SendAuth.Password,
|
||||
passwordInput = "",
|
||||
)
|
||||
@@ -1281,6 +1377,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
passwordInput = "",
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
@@ -1550,6 +1647,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
passwordInput = "",
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
@@ -1574,6 +1672,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
passwordInput = "existing-password",
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
@@ -1605,6 +1704,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
passwordInput = "existing-password",
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
@@ -1636,6 +1736,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
passwordInput = "existing-password",
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
@@ -1667,6 +1768,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
passwordInput = "test-password",
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
@@ -1691,6 +1793,7 @@ class AddEditSendScreenTest : BitwardenComposeTest() {
|
||||
it.copy(
|
||||
viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
hasPassword = false,
|
||||
passwordInput = "",
|
||||
sendAuth = SendAuth.Password,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user