mirror of
https://github.com/bitwarden/android.git
synced 2025-03-16 03:08:50 +03:00
[PM-8137] Populate add cipher form during passkey creation (#1431)
This commit is contained in:
parent
4128f16025
commit
d534acdf7e
13 changed files with 384 additions and 22 deletions
|
@ -1,6 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.autofill.fido2.manager
|
||||
|
||||
import com.bitwarden.vault.CipherView
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.datasource.network.model.PublicKeyCredentialCreationOptions
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CreateCredentialResult
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialRequest
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2ValidateOriginResult
|
||||
|
@ -17,6 +18,13 @@ interface Fido2CredentialManager {
|
|||
fido2CredentialRequest: Fido2CredentialRequest,
|
||||
): Fido2ValidateOriginResult
|
||||
|
||||
/**
|
||||
* Attempt to extract FIDO 2 passkey creation options from the system [requestJson], or null.
|
||||
*/
|
||||
fun getPasskeyCreateOptionsOrNull(
|
||||
requestJson: String,
|
||||
): PublicKeyCredentialCreationOptions?
|
||||
|
||||
/**
|
||||
* Attempt to create a FIDO2 credential from the given [credentialRequest] and associate it to
|
||||
* the given [cipherView].
|
||||
|
|
|
@ -40,6 +40,17 @@ class Fido2CredentialManagerImpl(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getPasskeyCreateOptionsOrNull(
|
||||
requestJson: String,
|
||||
): PublicKeyCredentialCreationOptions? =
|
||||
try {
|
||||
json.decodeFromString<PublicKeyCredentialCreationOptions>(requestJson)
|
||||
} catch (e: SerializationException) {
|
||||
null
|
||||
} catch (e: IllegalArgumentException) {
|
||||
null
|
||||
}
|
||||
|
||||
@Suppress("ReturnCount")
|
||||
private suspend fun validateCallingApplicationAssetLinks(
|
||||
fido2CredentialRequest: Fido2CredentialRequest,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager.util
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialRequest
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
|
@ -31,3 +32,12 @@ fun SpecialCircumstance.toAutofillSelectionDataOrNull(): AutofillSelectionData?
|
|||
SpecialCircumstance.VaultShortcut -> null
|
||||
is SpecialCircumstance.Fido2Save -> null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [Fido2CredentialRequest] when contained in the given [SpecialCircumstance].
|
||||
*/
|
||||
fun SpecialCircumstance.toFido2RequestOrNull(): Fido2CredentialRequest? =
|
||||
when (this) {
|
||||
is SpecialCircumstance.Fido2Save -> this.fido2CredentialRequest
|
||||
else -> null
|
||||
}
|
||||
|
|
|
@ -10,13 +10,12 @@ import java.security.MessageDigest
|
|||
* returned. If this [CallingAppInfo] is a native RP application the package name will be returned.
|
||||
* Otherwise, `null` is returned.
|
||||
*/
|
||||
fun CallingAppInfo.getFido2RpOrNull(): String? {
|
||||
return if (isOriginPopulated()) {
|
||||
fun CallingAppInfo.getFido2RpIdOrNull(): String? =
|
||||
if (isOriginPopulated()) {
|
||||
origin?.toHostOrPathOrNull()
|
||||
} else {
|
||||
packageName
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signing certificate hash formatted as a hex string.
|
||||
|
|
|
@ -8,11 +8,13 @@ import com.x8bit.bitwarden.R
|
|||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSaveItemOrNull
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSelectionDataOrNull
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toFido2RequestOrNull
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded
|
||||
|
@ -80,6 +82,7 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
private val clipboardManager: BitwardenClipboardManager,
|
||||
private val policyManager: PolicyManager,
|
||||
private val vaultRepository: VaultRepository,
|
||||
private val fido2CredentialManager: Fido2CredentialManager,
|
||||
generatorRepository: GeneratorRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val specialCircumstanceManager: SpecialCircumstanceManager,
|
||||
|
@ -102,6 +105,16 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
.specialCircumstance
|
||||
?.toAutofillSelectionDataOrNull()
|
||||
|
||||
val fido2CreationRequest = specialCircumstanceManager
|
||||
.specialCircumstance
|
||||
?.toFido2RequestOrNull()
|
||||
|
||||
val fido2CreationOptions = fido2CreationRequest
|
||||
?.let { request ->
|
||||
fido2CredentialManager
|
||||
.getPasskeyCreateOptionsOrNull(request.requestJson)
|
||||
}
|
||||
|
||||
val dialogState =
|
||||
if (!settingsRepository.initialAutofillDialogShown &&
|
||||
vaultAddEditType is VaultAddEditType.AddItem &&
|
||||
|
@ -120,6 +133,11 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
?.toDefaultAddTypeContent(isIndividualVaultDisabled)
|
||||
?: autofillSaveItem
|
||||
?.toDefaultAddTypeContent(isIndividualVaultDisabled)
|
||||
?: fido2CreationRequest
|
||||
?.toDefaultAddTypeContent(
|
||||
creationOptions = fido2CreationOptions,
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
)
|
||||
?: VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
|
@ -131,9 +149,9 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
is VaultAddEditType.CloneItem -> VaultAddEditState.ViewState.Loading
|
||||
},
|
||||
dialog = dialogState,
|
||||
// Set special conditions for autofill save
|
||||
shouldShowCloseButton = autofillSaveItem == null,
|
||||
shouldExitOnSave = autofillSaveItem != null,
|
||||
// Set special conditions for autofill and fido2 save
|
||||
shouldShowCloseButton = autofillSaveItem == null && fido2CreationOptions == null,
|
||||
shouldExitOnSave = autofillSaveItem != null || fido2CreationOptions != null,
|
||||
)
|
||||
},
|
||||
) {
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.addedit.util
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.datasource.network.model.PublicKeyCredentialCreationOptions
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialRequest
|
||||
import com.x8bit.bitwarden.data.platform.util.toUriOrNull
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toAndroidAppUriString
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Returns pre-filled content that may be used for an "add" type
|
||||
* [VaultAddEditState.ViewState.Content] during FIDO 2 credential creation.
|
||||
*/
|
||||
fun Fido2CredentialRequest.toDefaultAddTypeContent(
|
||||
creationOptions: PublicKeyCredentialCreationOptions?,
|
||||
isIndividualVaultDisabled: Boolean,
|
||||
): VaultAddEditState.ViewState.Content {
|
||||
|
||||
val rpUri = origin
|
||||
?.toUriOrNull()
|
||||
?.toString()
|
||||
?: packageName
|
||||
.toAndroidAppUriString()
|
||||
|
||||
val rpName = creationOptions
|
||||
?.relyingParty
|
||||
?.name
|
||||
.orEmpty()
|
||||
|
||||
val username = creationOptions
|
||||
?.user
|
||||
?.name
|
||||
.orEmpty()
|
||||
|
||||
return VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
name = rpName,
|
||||
),
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Login(
|
||||
username = username,
|
||||
uriList = listOf(
|
||||
UriItem(
|
||||
id = UUID.randomUUID().toString(),
|
||||
uri = rpUri,
|
||||
match = null,
|
||||
checksum = null,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
|
@ -24,7 +24,7 @@ import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
|||
import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.map
|
||||
import com.x8bit.bitwarden.data.platform.util.getFido2RpOrNull
|
||||
import com.x8bit.bitwarden.data.platform.util.getFido2RpIdOrNull
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
|
@ -920,7 +920,7 @@ data class VaultItemListingState(
|
|||
?.let { R.string.items_for_uri.asText(it) }
|
||||
?: fido2CredentialRequest
|
||||
?.callingAppInfo
|
||||
?.getFido2RpOrNull()
|
||||
?.getFido2RpIdOrNull()
|
||||
?.let { R.string.items_for_uri.asText(it) }
|
||||
?: itemListingType.titleText
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2ValidateOriginResult
|
|||
import com.x8bit.bitwarden.data.platform.manager.AssetManager
|
||||
import com.x8bit.bitwarden.data.platform.util.asFailure
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.createMockPublicKeyCredentialCreationOptions
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
|
@ -23,6 +24,7 @@ import kotlinx.serialization.SerializationException
|
|||
import kotlinx.serialization.json.Json
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
@ -40,18 +42,10 @@ class Fido2CredentialManagerTest {
|
|||
getDigitalAssetLinkForRp(relyingParty = any())
|
||||
} returns DEFAULT_STATEMENT_LIST.asSuccess()
|
||||
}
|
||||
private val mockCreateOptions = mockk<PublicKeyCredentialCreationOptions> {
|
||||
every {
|
||||
relyingParty
|
||||
} returns PublicKeyCredentialCreationOptions.PublicKeyCredentialRpEntity(
|
||||
name = "mockRpName",
|
||||
id = "www.bitwarden.com",
|
||||
)
|
||||
}
|
||||
private val json = mockk<Json> {
|
||||
every {
|
||||
decodeFromString<PublicKeyCredentialCreationOptions>(any())
|
||||
} returns mockCreateOptions
|
||||
} returns createMockPublicKeyCredentialCreationOptions(number = 1)
|
||||
}
|
||||
private val mockPrivilegedCallingAppInfo = mockk<CallingAppInfo> {
|
||||
every { packageName } returns "com.x8bit.bitwarden"
|
||||
|
@ -255,6 +249,44 @@ class Fido2CredentialManagerTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPasskeyCreateOptionsOrNull should return passkey options when deserialized`() =
|
||||
runTest {
|
||||
assertEquals(
|
||||
createMockPublicKeyCredentialCreationOptions(number = 1),
|
||||
fido2CredentialManager.getPasskeyCreateOptionsOrNull(
|
||||
requestJson = "",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPasskeyCreateOptionsOrNull should return null when deserialization fails`() =
|
||||
runTest {
|
||||
every {
|
||||
json.decodeFromString<PublicKeyCredentialCreationOptions>(any())
|
||||
} throws SerializationException()
|
||||
assertNull(
|
||||
fido2CredentialManager.getPasskeyCreateOptionsOrNull(
|
||||
requestJson = "",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getPasskeyCreateOptionsOrNull should return null when IllegalArgumentException is thrown`() =
|
||||
runTest {
|
||||
every {
|
||||
json.decodeFromString<PublicKeyCredentialCreationOptions>(any())
|
||||
} throws IllegalArgumentException()
|
||||
assertNull(
|
||||
fido2CredentialManager.getPasskeyCreateOptionsOrNull(
|
||||
requestJson = "",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createCredentialForCipher should return error while not implemented`() {
|
||||
val result = fido2CredentialManager.createCredentialForCipher(
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager.util
|
||||
|
||||
import android.content.pm.SigningInfo
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialRequest
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
|
@ -38,6 +40,9 @@ class SpecialCircumstanceExtensionsTest {
|
|||
passwordlessRequestData = mockk(),
|
||||
shouldFinishWhenComplete = true,
|
||||
),
|
||||
SpecialCircumstance.Fido2Save(
|
||||
fido2CredentialRequest = mockk(),
|
||||
),
|
||||
SpecialCircumstance.GeneratorShortcut,
|
||||
SpecialCircumstance.VaultShortcut,
|
||||
)
|
||||
|
@ -77,6 +82,9 @@ class SpecialCircumstanceExtensionsTest {
|
|||
passwordlessRequestData = mockk(),
|
||||
shouldFinishWhenComplete = true,
|
||||
),
|
||||
SpecialCircumstance.Fido2Save(
|
||||
fido2CredentialRequest = mockk(),
|
||||
),
|
||||
SpecialCircumstance.GeneratorShortcut,
|
||||
SpecialCircumstance.VaultShortcut,
|
||||
)
|
||||
|
@ -84,4 +92,49 @@ class SpecialCircumstanceExtensionsTest {
|
|||
assertNull(specialCircumstance.toAutofillSelectionDataOrNull())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toFido2RequestOrNull should return a null value for other types`() {
|
||||
listOf(
|
||||
SpecialCircumstance.AutofillSelection(
|
||||
autofillSelectionData = mockk(),
|
||||
shouldFinishWhenComplete = true,
|
||||
),
|
||||
SpecialCircumstance.AutofillSave(
|
||||
autofillSaveItem = mockk(),
|
||||
),
|
||||
SpecialCircumstance.ShareNewSend(
|
||||
data = mockk(),
|
||||
shouldFinishWhenComplete = true,
|
||||
),
|
||||
SpecialCircumstance.PasswordlessRequest(
|
||||
passwordlessRequestData = mockk(),
|
||||
shouldFinishWhenComplete = true,
|
||||
),
|
||||
SpecialCircumstance.GeneratorShortcut,
|
||||
SpecialCircumstance.VaultShortcut,
|
||||
)
|
||||
.forEach { specialCircumstance ->
|
||||
assertNull(specialCircumstance.toFido2RequestOrNull())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toFido2RequestOrNull should return a non-null value for Fido2Save`() {
|
||||
val fido2CredentialRequest = Fido2CredentialRequest(
|
||||
userId = "mockUserId",
|
||||
requestJson = "mockRequestJson",
|
||||
packageName = "mockPackageName",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = "mockOrigin",
|
||||
)
|
||||
assertEquals(
|
||||
fido2CredentialRequest,
|
||||
SpecialCircumstance
|
||||
.Fido2Save(
|
||||
fido2CredentialRequest = fido2CredentialRequest,
|
||||
)
|
||||
.toFido2RequestOrNull(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class CallingAppInfoExtensionsTest {
|
|||
every { origin } returns "invalidUri9685%^$^&(*"
|
||||
}
|
||||
|
||||
assertNull(mockCallingAppInfo.getFido2RpOrNull())
|
||||
assertNull(mockCallingAppInfo.getFido2RpIdOrNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -47,7 +47,7 @@ class CallingAppInfoExtensionsTest {
|
|||
every { origin } returns "mockUri"
|
||||
}
|
||||
|
||||
assertEquals("mockUri", mockCallingAppInfo.getFido2RpOrNull())
|
||||
assertEquals("mockUri", mockCallingAppInfo.getFido2RpIdOrNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -57,7 +57,7 @@ class CallingAppInfoExtensionsTest {
|
|||
every { origin } returns null
|
||||
}
|
||||
|
||||
assertNull(mockCallingAppInfo.getFido2RpOrNull())
|
||||
assertNull(mockCallingAppInfo.getFido2RpIdOrNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -67,7 +67,7 @@ class CallingAppInfoExtensionsTest {
|
|||
every { packageName } returns "mockPackageName"
|
||||
}
|
||||
|
||||
assertEquals("mockPackageName", mockCallingAppInfo.getFido2RpOrNull())
|
||||
assertEquals("mockPackageName", mockCallingAppInfo.getFido2RpIdOrNull())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.addedit
|
||||
|
||||
import android.content.pm.SigningInfo
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import app.cash.turbine.turbineScope
|
||||
|
@ -14,6 +15,8 @@ import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.Organization
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManager
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialRequest
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
|
@ -45,6 +48,7 @@ import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
|
|||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.createMockPublicKeyCredentialCreationOptions
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toDefaultAddTypeContent
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
||||
|
@ -113,6 +117,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
} returns emptyList()
|
||||
}
|
||||
private val fido2CredentialManager = mockk<Fido2CredentialManager>()
|
||||
private val vaultRepository: VaultRepository = mockk {
|
||||
every { vaultDataStateFlow } returns mutableVaultDataFlow
|
||||
every { totpCodeFlow } returns totpTestCodeFlow
|
||||
|
@ -293,6 +298,43 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial add state should be correct when fido2 save`() = runTest {
|
||||
val fido2CredentialRequest = Fido2CredentialRequest(
|
||||
userId = "mockUserId-1",
|
||||
requestJson = "mockRequestJson-1",
|
||||
packageName = "mockPackageName-1",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = null,
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save(
|
||||
fido2CredentialRequest = fido2CredentialRequest,
|
||||
)
|
||||
val fido2ContentState = fido2CredentialRequest.toDefaultAddTypeContent(
|
||||
creationOptions = createMockPublicKeyCredentialCreationOptions(number = 1),
|
||||
isIndividualVaultDisabled = false,
|
||||
)
|
||||
val vaultAddEditType = VaultAddEditType.AddItem(VaultItemCipherType.LOGIN)
|
||||
val initState = createVaultAddItemState(
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
commonContentViewState = fido2ContentState.common,
|
||||
typeContentViewState = fido2ContentState.type,
|
||||
)
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
savedStateHandle = createSavedStateHandleWithState(
|
||||
state = initState,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
initState,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
verify(exactly = 1) {
|
||||
vaultRepository.vaultDataStateFlow
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial edit state should be correct`() = runTest {
|
||||
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
|
||||
|
@ -1889,6 +1931,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
policyManager = policyManager,
|
||||
resourceManager = resourceManager,
|
||||
authRepository = authRepository,
|
||||
fido2CredentialManager = fido2CredentialManager,
|
||||
settingsRepository = settingsRepository,
|
||||
clock = fixedClock,
|
||||
)
|
||||
|
@ -2467,6 +2510,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
specialCircumstanceManager = specialCircumstanceManager,
|
||||
policyManager = policyManager,
|
||||
resourceManager = bitwardenResourceManager,
|
||||
fido2CredentialManager = fido2CredentialManager,
|
||||
authRepository = authRepository,
|
||||
settingsRepository = settingsRepository,
|
||||
clock = clock,
|
||||
|
@ -2702,7 +2746,6 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
viewModel.trySendAction(action)
|
||||
assertEquals(expectedState, viewModel.stateFlow.value.viewState)
|
||||
}
|
||||
|
||||
//endregion Helper functions
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.addedit.util
|
||||
|
||||
import android.content.pm.SigningInfo
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialRequest
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.util.UUID
|
||||
|
||||
class Fido2CredentialRequestExtensionsTest {
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockkStatic(UUID::class)
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
unmockkStatic(UUID::class)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `toDefaultAddTypeContent should return the correct content when calling app is not privileged`() {
|
||||
every { UUID.randomUUID().toString() } returns "uuid"
|
||||
assertEquals(
|
||||
VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
name = "mockPublicKeyCredentialRpEntityName-1",
|
||||
),
|
||||
isIndividualVaultDisabled = false,
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Login(
|
||||
username = "mockPublicKeyCredentialUserEntityName-1",
|
||||
uriList = listOf(
|
||||
UriItem(
|
||||
id = "uuid",
|
||||
uri = "androidapp://mockPackageName-1",
|
||||
match = null,
|
||||
checksum = null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Fido2CredentialRequest(
|
||||
userId = "mockUserId-1",
|
||||
requestJson = "mockRequestJson-1",
|
||||
packageName = "mockPackageName-1",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = null,
|
||||
)
|
||||
.toDefaultAddTypeContent(
|
||||
creationOptions = createMockPublicKeyCredentialCreationOptions(1),
|
||||
isIndividualVaultDisabled = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `toDefaultAddTypeContent should return the correct content when calling app is privileged`() {
|
||||
every { UUID.randomUUID().toString() } returns "uuid"
|
||||
assertEquals(
|
||||
VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
name = "mockPublicKeyCredentialRpEntityName-1",
|
||||
),
|
||||
isIndividualVaultDisabled = false,
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Login(
|
||||
username = "mockPublicKeyCredentialUserEntityName-1",
|
||||
uriList = listOf(
|
||||
UriItem(
|
||||
id = "uuid",
|
||||
uri = "www.test.com",
|
||||
match = null,
|
||||
checksum = null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Fido2CredentialRequest(
|
||||
userId = "mockUserId-1",
|
||||
requestJson = "mockRequestJson-1",
|
||||
packageName = "mockPackageName-1",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = "www.test.com",
|
||||
)
|
||||
.toDefaultAddTypeContent(
|
||||
creationOptions = createMockPublicKeyCredentialCreationOptions(number = 1),
|
||||
isIndividualVaultDisabled = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.addedit.util
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.datasource.network.model.PublicKeyCredentialCreationOptions
|
||||
|
||||
/**
|
||||
* Returns a mock FIDO 2 [PublicKeyCredentialCreationOptions] object to simulate a credential
|
||||
* creation request.
|
||||
*/
|
||||
fun createMockPublicKeyCredentialCreationOptions(number: Int) =
|
||||
PublicKeyCredentialCreationOptions(
|
||||
authenticatorSelection = PublicKeyCredentialCreationOptions
|
||||
.AuthenticatorSelectionCriteria(),
|
||||
challenge = "mockPublicKeyCredentialCreationOptionsChallenge-$number",
|
||||
excludeCredentials = listOf(
|
||||
PublicKeyCredentialCreationOptions.PublicKeyCredentialDescriptor(
|
||||
type = "mockPublicKeyCredentialDescriptorType-$number",
|
||||
id = "mockPublicKeyCredentialDescriptorId-$number",
|
||||
transports = listOf("mockPublicKeyCredentialDescriptorTransports-$number"),
|
||||
),
|
||||
),
|
||||
pubKeyCredParams = listOf(
|
||||
PublicKeyCredentialCreationOptions.PublicKeyCredentialParameters(
|
||||
type = "PublicKeyCredentialParametersType-$number",
|
||||
alg = number.toLong(),
|
||||
),
|
||||
),
|
||||
relyingParty = PublicKeyCredentialCreationOptions.PublicKeyCredentialRpEntity(
|
||||
name = "mockPublicKeyCredentialRpEntityName-$number",
|
||||
id = "mockPublicKeyCredentialRpEntity-$number",
|
||||
),
|
||||
user = PublicKeyCredentialCreationOptions.PublicKeyCredentialUserEntity(
|
||||
name = "mockPublicKeyCredentialUserEntityName-$number",
|
||||
id = "mockPublicKeyCredentialUserEntityId-$number",
|
||||
displayName = "mockPublicKeyCredentialUserEntityDisplayName-$number",
|
||||
),
|
||||
)
|
Loading…
Add table
Reference in a new issue