Skip to content

Commit

Permalink
[PM-15863] Request master password before revealing private SSH key (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
SaintPatrck authored Dec 20, 2024
1 parent 35e8cec commit 5aa8369
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,16 @@ class VaultItemViewModel @Inject constructor(
action: VaultItemAction.ItemType.SshKey.PrivateKeyVisibilityClicked,
) {
onSshKeyContent { content, sshKey ->
if (content.common.requiresReprompt) {
updateDialogState(
VaultItemState.DialogState.MasterPasswordDialog(
action = PasswordRepromptAction.ViewPrivateKeyClicked(
isVisible = action.isVisible,
),
),
)
return@onSshKeyContent
}
mutableStateFlow.update { currentState ->
currentState.copy(
viewState = content.copy(
Expand Down Expand Up @@ -2231,4 +2241,18 @@ sealed class PasswordRepromptAction : Parcelable {
override val vaultItemAction: VaultItemAction
get() = VaultItemAction.Common.RestoreVaultItemClick
}

/**
* Indicates that we should launch the
* [VaultItemAction.ItemType.SshKey.PrivateKeyVisibilityClicked] upon password validation.
*/
@Parcelize
data class ViewPrivateKeyClicked(
val isVisible: Boolean,
) : PasswordRepromptAction() {
override val vaultItemAction: VaultItemAction
get() = VaultItemAction.ItemType.SshKey.PrivateKeyVisibilityClicked(
isVisible = isVisible,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2490,11 +2490,58 @@ class VaultItemViewModelTest : BaseViewModelTest() {

@Suppress("MaxLineLength")
@Test
fun `on PrivateKeyVisibilityClick should show password dialog when re-prompt is required`() =
fun `on PrivateKeyVisibilityClick should show private key when re-prompt is not required`() =
runTest {
val sshKeyViewState = createViewState(
common = DEFAULT_COMMON.copy(requiresReprompt = false),
type = DEFAULT_SSH_KEY_TYPE,
)
val sshKeyState = DEFAULT_STATE.copy(viewState = sshKeyViewState)
every {
mockCipherView.toViewState(
previousState = null,
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns sshKeyViewState
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
mutableAuthCodeItemFlow.value = DataState.Loaded(data = null)
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())

assertEquals(sshKeyState, viewModel.stateFlow.value)
viewModel.trySendAction(
VaultItemAction.ItemType.SshKey.PrivateKeyVisibilityClicked(
isVisible = true,
),
)
assertEquals(
sshKeyState.copy(
viewState = sshKeyViewState.copy(
common = DEFAULT_COMMON.copy(requiresReprompt = false),
type = DEFAULT_SSH_KEY_TYPE.copy(showPrivateKey = true),
),
),
viewModel.stateFlow.value,
)
verify(exactly = 1) {
mockCipherView.toViewState(
previousState = null,
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}

@Suppress("MaxLineLength")
@Test
fun `on PrivateKeyVisibilityClick should show password dialog when re-prompt is required`() =
runTest {
val sshKeyState = DEFAULT_STATE.copy(viewState = SSH_KEY_VIEW_STATE)
every {
mockCipherView.toViewState(
Expand All @@ -2518,15 +2565,22 @@ class VaultItemViewModelTest : BaseViewModelTest() {
)
assertEquals(
sshKeyState.copy(
viewState = sshKeyViewState.copy(
common = DEFAULT_COMMON,
type = DEFAULT_SSH_KEY_TYPE.copy(
showPrivateKey = true,
),
dialog = VaultItemState.DialogState.MasterPasswordDialog(
PasswordRepromptAction.ViewPrivateKeyClicked(isVisible = true),
),
),
viewModel.stateFlow.value,
)
verify(exactly = 1) {
mockCipherView.toViewState(
previousState = null,
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}

@Test
Expand Down

0 comments on commit 5aa8369

Please sign in to comment.