mirror of
https://github.com/bitwarden/android.git
synced 2026-07-28 06:45:48 -05:00
[PM-20127] Only prompt passkey user verification once (#5026)
This commit is contained in:
@@ -691,6 +691,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||
val viewModel = createViewModel()
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = DEFAULT_USER_STATE.activeUserId,
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
val fido2Intent = createMockIntent(
|
||||
@@ -715,7 +716,10 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||
@Test
|
||||
fun `on ReceiveFirstIntent with fido2 create request data should set the user verification based on request`() {
|
||||
val viewModel = createViewModel()
|
||||
val createCredentialRequest = createMockFido2CreateCredentialRequest(number = 1)
|
||||
val createCredentialRequest = createMockFido2CreateCredentialRequest(
|
||||
number = 1,
|
||||
isUserPreVerified = true,
|
||||
)
|
||||
val fido2Intent = createMockIntent(
|
||||
mockFido2CreateCredentialRequest = createCredentialRequest,
|
||||
)
|
||||
@@ -739,6 +743,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||
val viewModel = createViewModel()
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "selectedUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
val mockIntent = createMockIntent(
|
||||
@@ -769,6 +774,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||
val viewModel = createViewModel()
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = DEFAULT_USER_STATE.activeUserId,
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
val mockIntent = createMockIntent(
|
||||
|
||||
+1
@@ -10,5 +10,6 @@ fun createMockFido2CredentialAssertionRequest(
|
||||
userId = userId,
|
||||
cipherId = "mockCipherId-$number",
|
||||
credentialId = "mockCredentialId-$number",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
|
||||
+2
@@ -8,8 +8,10 @@ import androidx.core.os.bundleOf
|
||||
*/
|
||||
fun createMockFido2CreateCredentialRequest(
|
||||
number: Int,
|
||||
isUserPreVerified: Boolean = false,
|
||||
requestData: Bundle = bundleOf(),
|
||||
): Fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId-$number",
|
||||
isUserPreVerified = isUserPreVerified,
|
||||
requestData = requestData,
|
||||
)
|
||||
|
||||
+81
-2
@@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.autofill.fido2.util
|
||||
import android.content.Intent
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.credentials.provider.BeginGetCredentialRequest
|
||||
import androidx.credentials.provider.BiometricPromptResult
|
||||
import androidx.credentials.provider.PendingIntentHandler
|
||||
import androidx.credentials.provider.ProviderCreateCredentialRequest
|
||||
import androidx.credentials.provider.ProviderGetCredentialRequest
|
||||
@@ -18,6 +19,7 @@ import io.mockk.unmockkObject
|
||||
import io.mockk.unmockkStatic
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
@@ -58,7 +60,11 @@ class Fido2IntentUtilsTest {
|
||||
every { ProviderCreateCredentialRequest.asBundle(any()) } returns bundleOf()
|
||||
every {
|
||||
PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
|
||||
} returns mockk()
|
||||
} returns mockk(relaxed = true) {
|
||||
every { biometricPromptResult } returns mockk(relaxed = true) {
|
||||
every { isSuccessful } returns false
|
||||
}
|
||||
}
|
||||
|
||||
val createRequest = intent.getFido2CreateCredentialRequestOrNull()
|
||||
assertEquals(
|
||||
@@ -67,6 +73,39 @@ class Fido2IntentUtilsTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getFido2CreateCredentialRequestOrNull should set user verification based on biometric prompt result`() {
|
||||
val intent = mockk<Intent> {
|
||||
every { getStringExtra(EXTRA_KEY_USER_ID) } returns "mockUserId"
|
||||
}
|
||||
val mockBiometricPromptResult = mockk<BiometricPromptResult>(relaxed = true) {
|
||||
every { isSuccessful } returns false
|
||||
}
|
||||
val mockProviderCreateCredentialRequest =
|
||||
mockk<ProviderCreateCredentialRequest>(relaxed = true) {
|
||||
every { biometricPromptResult } returns mockBiometricPromptResult
|
||||
}
|
||||
every { ProviderCreateCredentialRequest.asBundle(any()) } returns bundleOf()
|
||||
every {
|
||||
PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
|
||||
} returns mockProviderCreateCredentialRequest
|
||||
|
||||
// Verify false is returned when biometric prompt is unsuccessful
|
||||
var createRequest = intent.getFido2CreateCredentialRequestOrNull()
|
||||
assertFalse(createRequest!!.isUserPreVerified)
|
||||
|
||||
// Verify true is returned when biometric prompt is successful
|
||||
every { mockBiometricPromptResult.isSuccessful } returns true
|
||||
createRequest = intent.getFido2CreateCredentialRequestOrNull()
|
||||
assert(createRequest!!.isUserPreVerified)
|
||||
|
||||
// Verify false is returned when biometric prompt result is null
|
||||
every { mockProviderCreateCredentialRequest.biometricPromptResult } returns null
|
||||
createRequest = intent.getFido2CreateCredentialRequestOrNull()
|
||||
assertFalse(createRequest!!.isUserPreVerified)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getFido2CreateCredentialRequestOrNull should return null when build version is below 34`() {
|
||||
@@ -108,7 +147,11 @@ class Fido2IntentUtilsTest {
|
||||
every { ProviderGetCredentialRequest.asBundle(any()) } returns bundleOf()
|
||||
every {
|
||||
PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
|
||||
} returns mockk()
|
||||
} returns mockk(relaxed = true) {
|
||||
every { biometricPromptResult } returns mockk(relaxed = true) {
|
||||
every { isSuccessful } returns false
|
||||
}
|
||||
}
|
||||
|
||||
val assertionRequest = intent.getFido2AssertionRequestOrNull()
|
||||
|
||||
@@ -116,6 +159,42 @@ class Fido2IntentUtilsTest {
|
||||
assertEquals("mockUserId", assertionRequest?.userId)
|
||||
assertEquals("mockCipherId", assertionRequest?.cipherId)
|
||||
assertEquals("mockCredentialId", assertionRequest?.credentialId)
|
||||
assertEquals(false, assertionRequest?.isUserPreVerified)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getFido2AssertionRequestOrNull should set user verification based on biometric prompt result`() {
|
||||
val intent = mockk<Intent> {
|
||||
every { getStringExtra(EXTRA_KEY_USER_ID) } returns "mockUserId"
|
||||
every { getStringExtra(EXTRA_KEY_CIPHER_ID) } returns "mockCipherId"
|
||||
every { getStringExtra(EXTRA_KEY_CREDENTIAL_ID) } returns "mockCredentialId"
|
||||
}
|
||||
val mockBiometricPromptResult = mockk<BiometricPromptResult>(relaxed = true) {
|
||||
every { isSuccessful } returns false
|
||||
}
|
||||
val mockGetCredentialRequest =
|
||||
mockk<ProviderGetCredentialRequest>(relaxed = true) {
|
||||
every { biometricPromptResult } returns mockBiometricPromptResult
|
||||
}
|
||||
every { ProviderCreateCredentialRequest.asBundle(any()) } returns bundleOf()
|
||||
every {
|
||||
PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
|
||||
} returns mockGetCredentialRequest
|
||||
|
||||
// Verify false is returned when biometric prompt is unsuccessful
|
||||
var assertionRequest = intent.getFido2AssertionRequestOrNull()
|
||||
assertFalse(assertionRequest!!.isUserPreVerified)
|
||||
|
||||
// Verify true is returned when biometric prompt is successful
|
||||
every { mockBiometricPromptResult.isSuccessful } returns true
|
||||
assertionRequest = intent.getFido2AssertionRequestOrNull()
|
||||
assert(assertionRequest!!.isUserPreVerified)
|
||||
|
||||
// Verify false is returned when biometric prompt result is null
|
||||
every { mockGetCredentialRequest.biometricPromptResult } returns null
|
||||
assertionRequest = intent.getFido2AssertionRequestOrNull()
|
||||
assertFalse(assertionRequest!!.isUserPreVerified)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
@@ -148,6 +148,7 @@ class SpecialCircumstanceExtensionsTest {
|
||||
fun `toFido2RequestOrNull should return a non-null value for Fido2Save`() {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
assertEquals(
|
||||
|
||||
+1
@@ -664,6 +664,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
|
||||
fun `when the active user has an unlocked vault but there is a Fido2Save special circumstance the nav state should be VaultUnlockedForFido2Save`() {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "activeUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
|
||||
+3
@@ -396,6 +396,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
} returns mockk(relaxed = true)
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId-1",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save(
|
||||
@@ -880,6 +881,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
runTest {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
@@ -959,6 +961,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
val mockUserId = "mockUserId"
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = mockUserId,
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
|
||||
+2
@@ -59,6 +59,7 @@ class Fido2CredentialRequestExtensionsTest {
|
||||
),
|
||||
Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId-1",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
.toDefaultAddTypeContent(
|
||||
@@ -96,6 +97,7 @@ class Fido2CredentialRequestExtensionsTest {
|
||||
),
|
||||
Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId-1",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
.toDefaultAddTypeContent(
|
||||
|
||||
+309
-1
@@ -4,6 +4,8 @@ import android.net.Uri
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.credentials.CreatePublicKeyCredentialRequest
|
||||
import androidx.credentials.GetPublicKeyCredentialOption
|
||||
import androidx.credentials.provider.BeginGetCredentialRequest
|
||||
import androidx.credentials.provider.BeginGetPublicKeyCredentialOption
|
||||
import androidx.credentials.provider.ProviderCreateCredentialRequest
|
||||
import androidx.credentials.provider.ProviderGetCredentialRequest
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
@@ -37,6 +39,7 @@ import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2ValidateOriginResult
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.UserVerificationRequirement
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CreateCredentialRequest
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CredentialAssertionRequest
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2GetCredentialsRequest
|
||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
|
||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManagerImpl
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||
@@ -68,6 +71,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.autofill.fido2.manager.model.AssertFido2CredentialResult
|
||||
import com.x8bit.bitwarden.ui.autofill.fido2.manager.model.GetFido2CredentialsResult
|
||||
import com.x8bit.bitwarden.ui.autofill.fido2.manager.model.RegisterFido2CredentialResult
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
||||
@@ -217,17 +221,30 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
mockk<GetPublicKeyCredentialOption>(relaxed = true),
|
||||
)
|
||||
}
|
||||
private val mockBeginGetPublicKeyCredentialOption =
|
||||
mockk<BeginGetPublicKeyCredentialOption>(relaxed = true)
|
||||
private val mockBeginGetCredentialRequest = mockk<BeginGetCredentialRequest>(relaxed = true) {
|
||||
every {
|
||||
beginGetCredentialOptions
|
||||
} returns listOf(
|
||||
mockBeginGetPublicKeyCredentialOption,
|
||||
)
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockkObject(
|
||||
ProviderCreateCredentialRequest.Companion,
|
||||
ProviderGetCredentialRequest.Companion,
|
||||
BeginGetCredentialRequest.Companion,
|
||||
)
|
||||
every { ProviderCreateCredentialRequest.fromBundle(any()) } returns mockk(relaxed = true)
|
||||
every {
|
||||
ProviderGetCredentialRequest.fromBundle(any())
|
||||
} returns mockProviderGetCredentialRequest
|
||||
every {
|
||||
BeginGetCredentialRequest.fromBundle(any())
|
||||
} returns mockBeginGetCredentialRequest
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -235,6 +252,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
unmockkObject(
|
||||
ProviderCreateCredentialRequest.Companion,
|
||||
ProviderGetCredentialRequest.Companion,
|
||||
BeginGetCredentialRequest.Companion,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -252,7 +270,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
fun `initial dialog state should be correct when Fido2CreateCredentialRequest is present`() =
|
||||
runTest {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
"mockUserId",
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save(
|
||||
@@ -1859,6 +1878,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "activeUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
|
||||
@@ -2553,10 +2573,12 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
//region FIDO2 process handling
|
||||
@Test
|
||||
fun `Fido2CreateCredentialRequest should be evaluated before observing vault data`() {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save(
|
||||
@@ -2590,6 +2612,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
fun `Fido2ValidateOriginResult should update dialog state on Unknown error`() = runTest {
|
||||
val mockFido2CreateRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
|
||||
@@ -2628,6 +2651,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
runTest {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
|
||||
@@ -2666,6 +2690,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
runTest {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
|
||||
@@ -2704,6 +2729,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
runTest {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
|
||||
@@ -2742,6 +2768,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
runTest {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
|
||||
@@ -2780,6 +2807,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
runTest {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
|
||||
@@ -2818,6 +2846,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
runTest {
|
||||
val fido2CreateCredentialRequest = Fido2CreateCredentialRequest(
|
||||
userId = "mockUserId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
)
|
||||
|
||||
@@ -2990,6 +3019,261 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `Fido2GetCredentialsRequest should validate request and emit CompleteFido2GetCredentialsRequest event`() =
|
||||
runTest {
|
||||
setupMockUri()
|
||||
val mockGetCredentialsRequest = createMockFido2GetCredentialsRequest(number = 1)
|
||||
val mockFido2CredentialsList = createMockSdkFido2CredentialList(number = 1)
|
||||
val mockCipherView = createMockCipherView(
|
||||
number = 1,
|
||||
fido2Credentials = mockFido2CredentialsList,
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.Fido2GetCredentials(
|
||||
mockGetCredentialsRequest,
|
||||
)
|
||||
every {
|
||||
fido2CredentialManager.getPasskeyAssertionOptionsOrNull(any())
|
||||
} returns mockk(relaxed = true)
|
||||
coEvery {
|
||||
fido2OriginManager.validateOrigin(
|
||||
callingAppInfo = any(),
|
||||
relyingPartyId = any(),
|
||||
)
|
||||
} returns Fido2ValidateOriginResult.Success("mockOrigin")
|
||||
every {
|
||||
vaultRepository
|
||||
.ciphersStateFlow
|
||||
.value
|
||||
.data
|
||||
} returns listOf(mockCipherView)
|
||||
|
||||
val dataState = DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(mockCipherView),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
mutableVaultDataStateFlow.value = dataState
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
assertEquals(
|
||||
VaultItemListingEvent.CompleteFido2GetCredentialsRequest(
|
||||
result = GetFido2CredentialsResult.Success(
|
||||
userId = "mockUserId-1",
|
||||
option = mockBeginGetPublicKeyCredentialOption,
|
||||
credentials = emptyList(),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Fido2GetCredentialsRequest should display error dialog when request is invalid`() =
|
||||
runTest {
|
||||
setupMockUri()
|
||||
val mockGetCredentialsRequest = createMockFido2GetCredentialsRequest(number = 1)
|
||||
val mockFido2CredentialsList = createMockSdkFido2CredentialList(number = 1)
|
||||
val mockCipherView = createMockCipherView(
|
||||
number = 1,
|
||||
fido2Credentials = mockFido2CredentialsList,
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.Fido2GetCredentials(
|
||||
mockGetCredentialsRequest,
|
||||
)
|
||||
|
||||
every {
|
||||
vaultRepository
|
||||
.ciphersStateFlow
|
||||
.value
|
||||
.data
|
||||
} returns listOf(mockCipherView)
|
||||
every {
|
||||
fido2CredentialManager.getPasskeyAssertionOptionsOrNull(any())
|
||||
} returns mockk(relaxed = true)
|
||||
every {
|
||||
mockBeginGetCredentialRequest.beginGetCredentialOptions
|
||||
} returns emptyList()
|
||||
|
||||
val dataState = DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(mockCipherView),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
mutableVaultDataStateFlow.value = dataState
|
||||
|
||||
assertEquals(
|
||||
VaultItemListingState.DialogState.Fido2OperationFail(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message =
|
||||
R.string.passkey_operation_failed_because_the_request_is_invalid.asText(),
|
||||
),
|
||||
viewModel.stateFlow.value.dialogState,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `Fido2GetCredentialsRequest should display error dialog when relyingParty cannot be identified`() =
|
||||
runTest {
|
||||
setupMockUri()
|
||||
val mockGetCredentialsRequest = createMockFido2GetCredentialsRequest(number = 1)
|
||||
val mockFido2CredentialsList = createMockSdkFido2CredentialList(number = 1)
|
||||
val mockCipherView = createMockCipherView(
|
||||
number = 1,
|
||||
fido2Credentials = mockFido2CredentialsList,
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.Fido2GetCredentials(
|
||||
mockGetCredentialsRequest,
|
||||
)
|
||||
|
||||
every {
|
||||
vaultRepository
|
||||
.ciphersStateFlow
|
||||
.value
|
||||
.data
|
||||
} returns listOf(mockCipherView)
|
||||
every {
|
||||
fido2CredentialManager.getPasskeyAssertionOptionsOrNull(any())
|
||||
} returns mockk(relaxed = true) {
|
||||
every { relyingPartyId } returns null
|
||||
}
|
||||
|
||||
val dataState = DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(mockCipherView),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
mutableVaultDataStateFlow.value = dataState
|
||||
|
||||
assertEquals(
|
||||
VaultItemListingState.DialogState.Fido2OperationFail(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message =
|
||||
R.string.passkey_operation_failed_because_relying_party_cannot_be_identified
|
||||
.asText(),
|
||||
),
|
||||
viewModel.stateFlow.value.dialogState,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `Fido2GetCredentialsRequest should display error dialog when callingApp cannot be verified`() =
|
||||
runTest {
|
||||
setupMockUri()
|
||||
val mockGetCredentialsRequest = createMockFido2GetCredentialsRequest(number = 1)
|
||||
val mockFido2CredentialsList = createMockSdkFido2CredentialList(number = 1)
|
||||
val mockCipherView = createMockCipherView(
|
||||
number = 1,
|
||||
fido2Credentials = mockFido2CredentialsList,
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.Fido2GetCredentials(
|
||||
mockGetCredentialsRequest,
|
||||
)
|
||||
|
||||
every {
|
||||
vaultRepository
|
||||
.ciphersStateFlow
|
||||
.value
|
||||
.data
|
||||
} returns listOf(mockCipherView)
|
||||
every {
|
||||
fido2CredentialManager.getPasskeyAssertionOptionsOrNull(any())
|
||||
} returns mockk(relaxed = true)
|
||||
every {
|
||||
mockBeginGetCredentialRequest.callingAppInfo
|
||||
} returns null
|
||||
|
||||
val dataState = DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(mockCipherView),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
mutableVaultDataStateFlow.value = dataState
|
||||
|
||||
assertEquals(
|
||||
VaultItemListingState.DialogState.Fido2OperationFail(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message =
|
||||
R.string.passkey_operation_failed_because_app_could_not_be_verified
|
||||
.asText(),
|
||||
),
|
||||
viewModel.stateFlow.value.dialogState,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Fido2GetCredentialsRequest should display error dialog when origin validation fails`() =
|
||||
runTest {
|
||||
setupMockUri()
|
||||
val mockGetCredentialsRequest = createMockFido2GetCredentialsRequest(number = 1)
|
||||
val mockFido2CredentialsList = createMockSdkFido2CredentialList(number = 1)
|
||||
val mockCipherView = createMockCipherView(
|
||||
number = 1,
|
||||
fido2Credentials = mockFido2CredentialsList,
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.Fido2GetCredentials(
|
||||
mockGetCredentialsRequest,
|
||||
)
|
||||
every {
|
||||
fido2CredentialManager.getPasskeyAssertionOptionsOrNull(any())
|
||||
} returns mockk(relaxed = true)
|
||||
coEvery {
|
||||
fido2OriginManager.validateOrigin(
|
||||
callingAppInfo = any(),
|
||||
relyingPartyId = any(),
|
||||
)
|
||||
} returns Fido2ValidateOriginResult.Error.Unknown
|
||||
|
||||
val dataState = DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(mockCipherView),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
collectionViewList = listOf(createMockCollectionView(number = 1)),
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
mutableVaultDataStateFlow.value = dataState
|
||||
|
||||
assertEquals(
|
||||
VaultItemListingState.DialogState.Fido2OperationFail(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
viewModel.stateFlow.value.dialogState,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `Fido2AssertionRequest should display loading dialog then request user verification when user is not verified and verification is REQUIRED`() =
|
||||
@@ -3651,6 +3935,28 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `UserVerificationCancelled should clear dialog state, set isUserVerified to false, and emit CompleteFido2Assertion with cancelled result`() =
|
||||
runTest {
|
||||
specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Assertion(
|
||||
createMockFido2CredentialAssertionRequest(number = 1),
|
||||
)
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
viewModel.trySendAction(VaultItemListingsAction.UserVerificationCancelled)
|
||||
|
||||
verify { fido2CredentialManager.isUserVerified = false }
|
||||
assertNull(viewModel.stateFlow.value.dialogState)
|
||||
viewModel.eventFlow.test {
|
||||
assertEquals(
|
||||
VaultItemListingEvent.CompleteFido2Assertion(
|
||||
result = AssertFido2CredentialResult.Cancelled,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UserVerificationFail should display Fido2ErrorDialog and set isUserVerified to false`() {
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
@@ -4511,6 +4817,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
//endregion FIDO2 process handling
|
||||
|
||||
@Test
|
||||
fun `InternetConnectionErrorReceived should show network error if no internet connection`() =
|
||||
runTest {
|
||||
|
||||
+1
@@ -887,6 +887,7 @@ class VaultItemListingDataExtensionsTest {
|
||||
autofillSelectionData = null,
|
||||
fido2CreationData = Fido2CreateCredentialRequest(
|
||||
userId = "userId",
|
||||
isUserPreVerified = false,
|
||||
requestData = bundleOf(),
|
||||
),
|
||||
fido2CredentialAutofillViews = null,
|
||||
|
||||
Reference in New Issue
Block a user