mirror of
https://github.com/bitwarden/android.git
synced 2026-04-27 19:38:42 -05:00
[PM-13351] Prevent editing of TOTP key in 'can edit except passwords' collection (#4583)
This commit is contained in:
@@ -125,69 +125,19 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
coachMarkHighlightItem(
|
||||
key = AddEditItemCoachMark.TOTP,
|
||||
title = R.string.coachmark_2_of_3.asText(),
|
||||
description = R.string.you_ll_only_need_to_set_up_authenticator_key.asText(),
|
||||
onDismiss = onCoachMarkDismissed,
|
||||
leftAction = {
|
||||
CoachMarkActionText(
|
||||
actionLabel = stringResource(R.string.back),
|
||||
onActionClick = onPreviousCoachMark,
|
||||
)
|
||||
},
|
||||
rightAction = {
|
||||
CoachMarkActionText(
|
||||
actionLabel = stringResource(R.string.next),
|
||||
onActionClick = onNextCoachMark,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.standardHorizontalMargin(),
|
||||
) {
|
||||
if (loginState.totp != null) {
|
||||
BitwardenTextFieldWithActions(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
label = stringResource(id = R.string.totp),
|
||||
value = loginState.totp,
|
||||
trailingIconContent = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_clear,
|
||||
contentDescription = stringResource(id = R.string.delete),
|
||||
onClick = loginItemTypeHandlers.onClearTotpKeyClick,
|
||||
)
|
||||
},
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
actions = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_totp),
|
||||
onClick = {
|
||||
loginItemTypeHandlers.onCopyTotpKeyClick(loginState.totp)
|
||||
},
|
||||
)
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_camera,
|
||||
contentDescription = stringResource(id = R.string.camera),
|
||||
onClick = onTotpSetupClick,
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "LoginTotpEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenOutlinedButton(
|
||||
label = stringResource(id = R.string.setup_totp),
|
||||
icon = rememberVectorPainter(id = R.drawable.ic_light_bulb),
|
||||
onClick = onTotpSetupClick,
|
||||
modifier = Modifier
|
||||
.testTag("SetupTotpButton")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
item(key = AddEditItemCoachMark.TOTP) {
|
||||
TotpRow(
|
||||
totpKey = loginState.totp,
|
||||
canViewTotp = loginState.canViewPassword,
|
||||
loginItemTypeHandlers = loginItemTypeHandlers,
|
||||
onTotpSetupClick = onTotpSetupClick,
|
||||
onPreviousCoachMark = onPreviousCoachMark,
|
||||
onNextCoachMark = onNextCoachMark,
|
||||
onCoachMarkDismissed = onCoachMarkDismissed,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -585,6 +535,100 @@ private fun CoachMarkScope<AddEditItemCoachMark>.PasswordRow(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun CoachMarkScope<AddEditItemCoachMark>.TotpRow(
|
||||
totpKey: String?,
|
||||
canViewTotp: Boolean,
|
||||
loginItemTypeHandlers: VaultAddEditLoginTypeHandlers,
|
||||
onTotpSetupClick: () -> Unit,
|
||||
onPreviousCoachMark: () -> Unit,
|
||||
onNextCoachMark: () -> Unit,
|
||||
onCoachMarkDismissed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
CoachMarkHighlight(
|
||||
key = AddEditItemCoachMark.TOTP,
|
||||
title = stringResource(R.string.coachmark_2_of_3),
|
||||
description = stringResource(R.string.you_ll_only_need_to_set_up_authenticator_key),
|
||||
onDismiss = onCoachMarkDismissed,
|
||||
leftAction = {
|
||||
CoachMarkActionText(
|
||||
actionLabel = stringResource(R.string.back),
|
||||
onActionClick = onPreviousCoachMark,
|
||||
)
|
||||
},
|
||||
rightAction = {
|
||||
CoachMarkActionText(
|
||||
actionLabel = stringResource(R.string.next),
|
||||
onActionClick = onNextCoachMark,
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
) {
|
||||
if (totpKey != null) {
|
||||
if (canViewTotp) {
|
||||
BitwardenTextFieldWithActions(
|
||||
label = stringResource(id = R.string.totp),
|
||||
value = totpKey,
|
||||
trailingIconContent = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_clear,
|
||||
contentDescription = stringResource(id = R.string.delete),
|
||||
onClick = loginItemTypeHandlers.onClearTotpKeyClick,
|
||||
)
|
||||
},
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
actions = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_totp),
|
||||
onClick = {
|
||||
loginItemTypeHandlers.onCopyTotpKeyClick(totpKey)
|
||||
},
|
||||
)
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_camera,
|
||||
contentDescription = stringResource(id = R.string.camera),
|
||||
onClick = onTotpSetupClick,
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "LoginTotpEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
} else {
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.totp),
|
||||
value = totpKey,
|
||||
cardStyle = CardStyle.Full,
|
||||
textFieldTestTag = "LoginTotpEntry",
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
enabled = false,
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenOutlinedButton(
|
||||
label = stringResource(id = R.string.setup_totp),
|
||||
icon = rememberVectorPainter(id = R.drawable.ic_light_bulb),
|
||||
onClick = onTotpSetupClick,
|
||||
isEnabled = canViewTotp,
|
||||
modifier = Modifier
|
||||
.testTag("SetupTotpButton")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PasskeyField(
|
||||
creationDateTime: Text,
|
||||
|
||||
@@ -49,6 +49,7 @@ import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.FakePermissionManager
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
||||
import com.x8bit.bitwarden.ui.util.assertScrollableNodeDoesNotExist
|
||||
import com.x8bit.bitwarden.ui.util.isCoachMarkToolTip
|
||||
import com.x8bit.bitwarden.ui.util.isProgressBar
|
||||
import com.x8bit.bitwarden.ui.util.onAllNodesWithContentDescriptionAfterScroll
|
||||
@@ -1164,6 +1165,46 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in ItemType_Login state the SetupTOTP button should be visible but disabled based on state`() {
|
||||
mutableStateFlow.update { currentState ->
|
||||
updateLoginType(currentState) {
|
||||
copy(
|
||||
canViewPassword = false,
|
||||
totp = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTextAfterScroll(text = "Set up TOTP")
|
||||
.assertIsDisplayed()
|
||||
.assertIsNotEnabled()
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in ItemType_Login state the TOTP text field should be visible but disabled and the associated icon buttons should be invisible based on state`() {
|
||||
mutableStateFlow.update { currentState ->
|
||||
updateLoginType(currentState) {
|
||||
copy(
|
||||
canViewPassword = false,
|
||||
totp = "TestCode",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTextAfterScroll(text = "TOTP")
|
||||
.assertIsDisplayed()
|
||||
.assertIsNotEnabled()
|
||||
|
||||
composeTestRule.assertScrollableNodeDoesNotExist("Delete")
|
||||
composeTestRule.assertScrollableNodeDoesNotExist("Copy TOTP")
|
||||
composeTestRule.assertScrollableNodeDoesNotExist("Camera")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in ItemType_Login state changing URI text field should trigger UriValueChange`() {
|
||||
mutableStateFlow.update { currentState ->
|
||||
|
||||
Reference in New Issue
Block a user