diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/credentials/manager/CredentialProviderCompletionManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/credentials/manager/CredentialProviderCompletionManagerImpl.kt index c40a99ef41..46e99645f9 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/credentials/manager/CredentialProviderCompletionManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/credentials/manager/CredentialProviderCompletionManagerImpl.kt @@ -131,8 +131,8 @@ class CredentialProviderCompletionManagerImpl( intent = intent, response = GetCredentialResponse( credential = PasswordCredential( - id = result.credential.username ?: "", - password = result.credential.password ?: "", + id = result.username.orEmpty(), + password = result.password, ), ), ) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/credentials/manager/model/GetPasswordCredentialResult.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/credentials/manager/model/GetPasswordCredentialResult.kt index fdd1eaa110..be4f357410 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/credentials/manager/model/GetPasswordCredentialResult.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/credentials/manager/model/GetPasswordCredentialResult.kt @@ -1,7 +1,6 @@ package com.x8bit.bitwarden.ui.credentials.manager.model import com.bitwarden.ui.util.Text -import com.bitwarden.vault.LoginView /** * Represents the result of Password authentication. @@ -10,10 +9,13 @@ sealed class GetPasswordCredentialResult { /** * Represents a successful authentication of Password credentials. */ - data class Success(val credential: LoginView) : GetPasswordCredentialResult() + data class Success( + val username: String?, + val password: String, + ) : GetPasswordCredentialResult() /** - * Indicates the user cancelled authentication. + * Indicates the user canceled authentication. */ data object Cancelled : GetPasswordCredentialResult() diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt index 6cecd2ed98..df13936cc7 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt @@ -2549,11 +2549,28 @@ class VaultItemListingViewModel @Inject constructor( bitwardenCredentialManager.isUserVerified = false clearDialogState() - val event = selectedCipher.login + val event = selectedCipher + .login ?.let { credential -> - VaultItemListingEvent.CompleteProviderGetPasswordCredentialRequest( - GetPasswordCredentialResult.Success(credential = credential), - ) + credential + .password + ?.takeIf { it.isNotEmpty() } + ?.let { password -> + VaultItemListingEvent.CompleteProviderGetPasswordCredentialRequest( + GetPasswordCredentialResult.Success( + username = credential.username, + password = password, + ), + ) + } + ?: VaultItemListingEvent.CompleteProviderGetPasswordCredentialRequest( + @Suppress("MaxLineLength") + GetPasswordCredentialResult.Error( + message = BitwardenString + .password_operation_failed_because_the_selected_item_does_not_have_a_valid_password + .asText(), + ), + ) } ?: VaultItemListingEvent.CompleteProviderGetPasswordCredentialRequest( GetPasswordCredentialResult.Error( diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/credentials/manager/CredentialProviderCompletionManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/credentials/manager/CredentialProviderCompletionManagerTest.kt index e5f9e2b3e4..3e4a650b10 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/credentials/manager/CredentialProviderCompletionManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/credentials/manager/CredentialProviderCompletionManagerTest.kt @@ -14,7 +14,6 @@ import androidx.credentials.provider.PublicKeyCredentialEntry import com.bitwarden.ui.util.asText import com.x8bit.bitwarden.data.credentials.manager.CredentialManagerPendingIntentManager import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFido2CredentialAutofillView -import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockLoginView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockPasswordCredentialAutofillCipherLogin import com.x8bit.bitwarden.ui.credentials.manager.model.AssertFido2CredentialResult import com.x8bit.bitwarden.ui.credentials.manager.model.CreateCredentialResult @@ -228,8 +227,12 @@ class CredentialProviderCompletionManagerTest { @Suppress("MaxLineLength") @Test fun `completePasswordGet should set GetCredentialResponse, set activity result, then finish activity when result is Success`() { - credentialProviderCompletionManager - .completePasswordGet(GetPasswordCredentialResult.Success(createMockLoginView(1))) + credentialProviderCompletionManager.completePasswordGet( + GetPasswordCredentialResult.Success( + username = "mockUsername-1", + password = "mockPassword-1", + ), + ) verifyActivityResultIsSetAndFinishedAfter { PendingIntentHandler.setGetCredentialResponse(any(), any()) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt index 6a85d64373..79fa2cc695 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt @@ -50,7 +50,6 @@ import com.bitwarden.ui.util.performYesDialogButtonClick import com.bitwarden.vault.CipherType import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView -import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockLoginView import com.x8bit.bitwarden.ui.credentials.manager.CredentialProviderCompletionManager import com.x8bit.bitwarden.ui.credentials.manager.model.AssertFido2CredentialResult import com.x8bit.bitwarden.ui.credentials.manager.model.CreateCredentialResult @@ -2056,7 +2055,10 @@ class VaultItemListingScreenTest : BitwardenComposeTest() { @Suppress("MaxLineLength") @Test fun `GetPasswordCredentialResult event should call CredentialProviderCompletionManager with result`() { - val result = GetPasswordCredentialResult.Success(createMockLoginView(1)) + val result = GetPasswordCredentialResult.Success( + username = "mockUsername-1", + password = "mockPassword-1", + ) mutableEventFlow.tryEmit( VaultItemListingEvent.CompleteProviderGetPasswordCredentialRequest(result), ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt index 4b77344595..336aa95555 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt @@ -92,7 +92,6 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionV import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockDecryptCipherListResult import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockLoginListView -import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockLoginView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFido2CredentialList import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView import com.x8bit.bitwarden.data.vault.manager.model.GetCipherResult @@ -5531,15 +5530,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { number = 1, totp = "mockTotp-1", ) - val mockLoginView = createMockLoginView( - number = 1, - totp = "mockTotp-1", - clock = clock, - password = "mockPassword-1", - fido2Credentials = null, - ) - val mockGetPasswordRequest = createMockProviderGetPasswordCredentialRequest(1) - .copy(cipherId = "mockId-1") + val mockGetPasswordRequest = createMockProviderGetPasswordCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.ProviderGetPasswordRequest( passwordGetRequest = mockGetPasswordRequest, @@ -5578,7 +5569,69 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { viewModel.eventFlow.test { assertEquals( VaultItemListingEvent.CompleteProviderGetPasswordCredentialRequest( - result = GetPasswordCredentialResult.Success(mockLoginView), + result = GetPasswordCredentialResult.Success( + username = "mockUsername-1", + password = "mockPassword-1", + ), + ), + awaitItem(), + ) + assertNull(viewModel.stateFlow.value.dialogState) + } + } + + @Suppress("MaxLineLength") + @Test + fun `UserVerificationSuccess should set isUserVerified to true, and emit CompleteGetPassword with error result`() = + runTest { + val mockLoginListView = createMockLoginListView( + number = 1, + totp = "mockTotp-1", + ) + val mockGetPasswordRequest = createMockProviderGetPasswordCredentialRequest(number = 1) + specialCircumstanceManager.specialCircumstance = + SpecialCircumstance.ProviderGetPasswordRequest( + passwordGetRequest = mockGetPasswordRequest, + ) + every { + ProviderGetCredentialRequest.fromBundle(any()) + } returns mockk(relaxed = true) { + every { + credentialOptions + } returns listOf( + mockk(relaxed = true), + ) + } + every { + vaultRepository + .decryptCipherListResultStateFlow + .value + .data + } returns createMockDecryptCipherListResult( + number = 1, + successes = listOf( + createMockCipherListView( + number = 1, + type = CipherListViewType.Login(mockLoginListView), + ), + ), + ) + + val viewModel = createVaultItemListingViewModel() + viewModel.trySendAction( + VaultItemListingsAction.UserVerificationSuccess( + selectedCipherView = createMockCipherView(number = 1, password = ""), + ), + ) + + viewModel.eventFlow.test { + assertEquals( + VaultItemListingEvent.CompleteProviderGetPasswordCredentialRequest( + result = GetPasswordCredentialResult.Error( + message = BitwardenString + .password_operation_failed_because_the_selected_item_does_not_have_a_valid_password + .asText(), + ), ), awaitItem(), ) diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index f97fbd10f8..8aab92b532 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -941,6 +941,7 @@ Do you want to switch to this account? Passkey operation failed because no item was selected. Passkey operation failed because the request is unsupported. Password operation failed because the selected item does not exist. + Password operation failed because the selected item does not have a valid password. Password operation failed because no item was selected. Credential operation failed because the request is invalid. Credential operation failed because the request is unsupported.