[PM-39978] Fix compilation errors caused by Item type Send support in the SDK (#7224)

Co-authored-by: David Perez <david@livefront.com>
This commit is contained in:
Mike Amirault
2026-08-05 19:22:18 +00:00
committed by GitHub
co-authored by David Perez
parent 00a4942fc9
commit fc4a3b8faf
14 changed files with 282 additions and 20 deletions
@@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.auth.repository.util
import com.bitwarden.core.MasterPasswordUnlockData
import com.bitwarden.data.repository.util.toEnvironmentUrlsOrDefault
import com.bitwarden.network.model.KdfJson
import com.bitwarden.network.model.KdfTypeJson
import com.bitwarden.network.model.MasterPasswordUnlockDataJson
import com.bitwarden.network.model.OrganizationType
@@ -161,29 +162,43 @@ fun UserStateJson.updateMasterPasswordUnlock(
)
}
/**
* Updates the [UserStateJson] by setting the KDF values for the given [userId]. If the user is
* not present in the `UserStateJson`, nothing is updated.
*/
fun UserStateJson.updateKdf(
userId: String,
kdf: KdfJson?,
): UserStateJson {
val account = accounts[userId] ?: return this
val profile = account.profile
val updatedProfile = profile.copy(
kdfType = kdf?.kdfType,
kdfIterations = kdf?.iterations,
kdfMemory = kdf?.memory,
kdfParallelism = kdf?.parallelism,
)
val updatedAccount = account.copy(profile = updatedProfile)
return this.copy(
accounts = accounts
.toMutableMap()
.apply { replace(userId, updatedAccount) },
)
}
/**
* Updates the [UserStateJson] KDF settings to minimum requirements.
*/
fun UserStateJson.toUserStateJsonKdfUpdatedMinimums(): UserStateJson {
val account = this.activeAccount
val profile = account.profile
val updatedProfile = profile
.copy(
fun UserStateJson.toUserStateJsonKdfUpdatedMinimums(): UserStateJson =
this.updateKdf(
userId = this.activeUserId,
kdf = KdfJson(
kdfType = KdfTypeJson.PBKDF2_SHA256,
kdfIterations = DEFAULT_PBKDF2_ITERATIONS,
kdfMemory = null,
kdfParallelism = null,
)
val updatedAccount = account.copy(profile = updatedProfile)
return this
.copy(
accounts = accounts
.toMutableMap()
.apply {
replace(activeUserId, updatedAccount)
},
)
}
iterations = DEFAULT_PBKDF2_ITERATIONS,
memory = null,
parallelism = null,
),
)
/**
* Converts the given [UserStateJson] to a [UserState] using the given [vaultState].
@@ -5,9 +5,13 @@ import com.bitwarden.core.StateBridgeForeignImpl
import com.bitwarden.core.V2UpgradeToken
import com.bitwarden.core.WrappedAccountCryptographicState
import com.bitwarden.crypto.EncString
import com.bitwarden.crypto.Kdf
import com.bitwarden.crypto.PasswordProtectedKeyEnvelope
import com.bitwarden.crypto.SymmetricCryptoKey
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toKdfRequestModel
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
import com.x8bit.bitwarden.data.auth.repository.util.updateKdf
import com.x8bit.bitwarden.data.auth.repository.util.updateMasterPasswordUnlock
import com.x8bit.bitwarden.data.vault.repository.util.toSdkMasterPasswordUnlock
import com.x8bit.bitwarden.data.vault.repository.util.toV2UpgradeToken
@@ -134,4 +138,21 @@ internal class SdkStateBridge(
masterPasswordUnlock = null,
)
}
override suspend fun getKdfConfig(): Kdf? =
authDiskSource.userState?.accounts[userId]?.profile?.toSdkParams()
override suspend fun setKdfConfig(value: Kdf) {
authDiskSource.userState = authDiskSource.userState?.updateKdf(
userId = userId,
kdf = value.toKdfRequestModel(),
)
}
override suspend fun clearKdfConfig() {
authDiskSource.userState = authDiskSource.userState?.updateKdf(
userId = userId,
kdf = null,
)
}
}
@@ -77,6 +77,9 @@ class SendManagerImpl(
when (send.type) {
SendType.TEXT -> sendsService.createTextSend(send.toEncryptedNetworkSend())
SendType.FILE -> createFileSend(uri = fileUri, userId = userId, send = send)
SendType.ITEM -> {
NotImplementedError("[PM-41095] Support Item SendType").asFailure()
}
}
}
.map { createSendResponse ->
@@ -65,6 +65,7 @@ private fun SendType.toNetworkSendType(): SendTypeJson =
when (this) {
SendType.TEXT -> SendTypeJson.TEXT
SendType.FILE -> SendTypeJson.FILE
SendType.ITEM -> TODO("[PM-41095] Support Item SendType")
}
/**
@@ -403,6 +403,7 @@ private fun SendView.toDisplayItem(
iconRes = when (type) {
SendType.TEXT -> BitwardenDrawable.ic_file_text
SendType.FILE -> BitwardenDrawable.ic_file
SendType.ITEM -> TODO("[PM-41095] Support Item SendType")
},
),
extraIconList = toLabelIcons(clock = clock),
@@ -60,5 +60,7 @@ fun SendView.toViewState(
sizeBytes = null,
)
}
SendType.ITEM -> TODO("[PM-41095] Support Item SendType")
},
)
@@ -41,6 +41,7 @@ private fun List<SendView>.toSendContent(
type = when (sendView.type) {
SendType.TEXT -> SendState.ViewState.Content.SendItem.Type.TEXT
SendType.FILE -> SendState.ViewState.Content.SendItem.Type.FILE
SendType.ITEM -> TODO("[PM-41095] Support Item SendType")
},
iconList = sendView.toLabelIcons(),
shareUrl = sendView.toSendUrl(baseWebSendUrl),
@@ -10,4 +10,5 @@ fun SendType.toSendItemType(): SendItemType =
when (this) {
SendType.FILE -> SendItemType.FILE
SendType.TEXT -> SendItemType.TEXT
SendType.ITEM -> TODO("[PM-41095] Support Item SendType")
}
@@ -21,6 +21,7 @@ fun SendView.toViewSendViewStateContent(
sendType = when (this.type) {
SendType.FILE -> requireNotNull(this.file).toFileType()
SendType.TEXT -> requireNotNull(this.text).toTextType()
SendType.ITEM -> TODO("[PM-41095] Support Item SendType")
},
shareLink = this.toSendUrl(baseWebSendUrl = baseWebSendUrl),
sendName = this.name,
@@ -628,6 +628,7 @@ private fun SendView.toDisplayItem(
iconRes = when (type) {
SendType.TEXT -> BitwardenDrawable.ic_file_text
SendType.FILE -> BitwardenDrawable.ic_file
SendType.ITEM -> TODO("[PM-41095] Support Item SendType")
},
),
iconTestTag = null,
@@ -12,6 +12,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
import com.x8bit.bitwarden.data.auth.repository.model.createMockWrappedAccountCryptographicState
import com.x8bit.bitwarden.data.auth.repository.util.updateKdf
import com.x8bit.bitwarden.data.auth.repository.util.updateMasterPasswordUnlock
import com.x8bit.bitwarden.data.vault.repository.util.toSdkMasterPasswordUnlock
import io.mockk.mockk
@@ -306,6 +307,125 @@ class SdkStateBridgeTest {
assertNull(stateBridge.getMasterpasswordUnlockData())
}
@Test
fun `getKdfConfig should return null when there is no user state`() = runTest {
authDiskSource.userState = null
assertNull(stateBridge.getKdfConfig())
}
@Test
fun `getKdfConfig should return null when the user is not present in the user state`() =
runTest {
val otherUserId = "otherUserId"
authDiskSource.userState = UserStateJson(
activeUserId = otherUserId,
accounts = mapOf(otherUserId to ACCOUNT),
)
assertNull(stateBridge.getKdfConfig())
}
@Test
fun `getKdfConfig should return the stored PBKDF2 params as the sdk model`() = runTest {
authDiskSource.userState = USER_STATE
assertEquals(Kdf.Pbkdf2(iterations = 600_000u), stateBridge.getKdfConfig())
}
@Test
fun `getKdfConfig should return the stored ARGON2ID params as the sdk model`() = runTest {
authDiskSource.userState = USER_STATE.copy(
accounts = mapOf(
USER_ID to ACCOUNT.copy(
profile = ACCOUNT.profile.copy(
kdfType = KdfTypeJson.ARGON2_ID,
kdfIterations = 3,
kdfMemory = 64,
kdfParallelism = 4,
),
),
),
)
assertEquals(
Kdf.Argon2id(iterations = 3u, memory = 64u, parallelism = 4u),
stateBridge.getKdfConfig(),
)
}
@Test
fun `setKdfConfig should update the user state with the PBKDF2 params`() = runTest {
authDiskSource.userState = USER_STATE
stateBridge.setKdfConfig(value = Kdf.Pbkdf2(iterations = 700_000u))
assertEquals(
USER_STATE.updateKdf(
userId = USER_ID,
kdf = KdfJson(
kdfType = KdfTypeJson.PBKDF2_SHA256,
iterations = 700_000,
memory = null,
parallelism = null,
),
),
authDiskSource.userState,
)
}
@Test
fun `setKdfConfig should update the user state with the ARGON2ID params`() = runTest {
authDiskSource.userState = USER_STATE
stateBridge.setKdfConfig(
value = Kdf.Argon2id(iterations = 3u, memory = 64u, parallelism = 4u),
)
assertEquals(
USER_STATE.updateKdf(
userId = USER_ID,
kdf = KdfJson(
kdfType = KdfTypeJson.ARGON2_ID,
iterations = 3,
memory = 64,
parallelism = 4,
),
),
authDiskSource.userState,
)
}
@Test
fun `setKdfConfig should do nothing when the user state is null`() = runTest {
authDiskSource.userState = null
stateBridge.setKdfConfig(value = Kdf.Pbkdf2(iterations = 700_000u))
assertNull(authDiskSource.userState)
}
@Test
fun `clearKdfConfig should clear the kdf params from the user state`() = runTest {
authDiskSource.userState = USER_STATE
stateBridge.clearKdfConfig()
assertEquals(
USER_STATE.updateKdf(userId = USER_ID, kdf = null),
authDiskSource.userState,
)
}
@Test
fun `clearKdfConfig should do nothing when the user state is null`() = runTest {
authDiskSource.userState = null
stateBridge.clearKdfConfig()
assertNull(authDiskSource.userState)
}
}
private const val USER_ID: String = "userId"
@@ -452,4 +452,50 @@ fun createMockDisplayItemForSend(
itemType = SearchState.DisplayItem.ItemType.Sends(type = sendType),
)
}
SendType.ITEM -> {
SearchState.DisplayItem(
id = "mockId-$number",
title = "mockName-$number",
titleTestTag = "SendNameLabel",
subtitle = "Oct 27, 2023, 12:00 PM",
subtitleTestTag = "SendDateLabel",
iconData = IconData.Local(BitwardenDrawable.ic_file_text),
extraIconList = persistentListOf(
IconData.Local(
iconRes = BitwardenDrawable.ic_key,
contentDescription = BitwardenString.password.asText(),
testTag = "PasswordProtectedSendIcon",
),
IconData.Local(
iconRes = BitwardenDrawable.ic_send_max_access_count_reached,
contentDescription = BitwardenString.maximum_access_count_reached.asText(),
testTag = "MaxAccessSendIcon",
),
),
overflowOptions = persistentListOf(
ListingItemOverflowAction.SendAction.CopyUrlClick(
sendUrl = "https://vault.bitwarden.com/#/send/mockAccessId-$number/mockKey-$number",
),
ListingItemOverflowAction.SendAction.ShareUrlClick(
sendUrl = "https://vault.bitwarden.com/#/send/mockAccessId-$number/mockKey-$number",
),
ListingItemOverflowAction.SendAction.ViewClick(
sendId = "mockId-$number",
sendType = sendType,
),
ListingItemOverflowAction.SendAction.EditClick(
sendId = "mockId-$number",
sendType = sendType,
),
ListingItemOverflowAction.SendAction.RemovePasswordClick(sendId = "mockId-$number"),
ListingItemOverflowAction.SendAction.DeleteClick(sendId = "mockId-$number"),
),
overflowTestTag = "SendOptionsButton",
totpCode = null,
autofillSelectionOptions = persistentListOf(),
shouldDisplayMasterPasswordReprompt = false,
itemType = SearchState.DisplayItem.ItemType.Sends(type = sendType),
)
}
}
@@ -516,6 +516,55 @@ fun createMockDisplayItemForSend(
itemType = VaultItemListingState.DisplayItem.ItemType.Sends(type = sendType),
)
}
SendType.ITEM -> {
VaultItemListingState.DisplayItem(
id = "mockId-$number",
title = "mockName-$number".asText(),
titleTestTag = "SendNameLabel",
secondSubtitle = null,
secondSubtitleTestTag = null,
subtitle = "Oct 27, 2023, 12:00 PM",
subtitleTestTag = "SendDateLabel",
iconData = IconData.Local(BitwardenDrawable.ic_file_text),
extraIconList = persistentListOf(
IconData.Local(
iconRes = BitwardenDrawable.ic_key,
contentDescription = BitwardenString.password.asText(),
testTag = "PasswordProtectedSendIcon",
),
IconData.Local(
iconRes = BitwardenDrawable.ic_send_max_access_count_reached,
contentDescription = BitwardenString.maximum_access_count_reached.asText(),
testTag = "MaxAccessSendIcon",
),
),
overflowOptions = listOf(
ListingItemOverflowAction.SendAction.CopyUrlClick(
sendUrl = "https://send.bitwarden.com/#mockAccessId-$number/mockKey-$number",
),
ListingItemOverflowAction.SendAction.ShareUrlClick(
sendUrl = "https://send.bitwarden.com/#mockAccessId-$number/mockKey-$number",
),
ListingItemOverflowAction.SendAction.ViewClick(
sendId = "mockId-$number",
sendType = sendType,
),
ListingItemOverflowAction.SendAction.EditClick(
sendId = "mockId-$number",
sendType = sendType,
),
ListingItemOverflowAction.SendAction.RemovePasswordClick(sendId = "mockId-$number"),
ListingItemOverflowAction.SendAction.DeleteClick(sendId = "mockId-$number"),
),
optionsTestTag = "SendOptionsButton",
isAutofill = false,
isCredentialCreation = false,
shouldShowMasterPasswordReprompt = false,
iconTestTag = null,
itemType = VaultItemListingState.DisplayItem.ItemType.Sends(type = sendType),
)
}
}
/**
+1 -1
View File
@@ -29,7 +29,7 @@ androidxRoom = "2.8.4"
androidxSecurityCrypto = "1.1.0"
androidxSplash = "1.2.0"
androidxWork = "2.11.2"
bitwardenSdk = "3.0.0-8014-7eb08fbb"
bitwardenSdk = "3.0.0-8144-cf7ad654"
crashlytics = "3.0.7"
detekt = "1.23.8"
firebaseBom = "34.15.0"