mirror of
https://github.com/bitwarden/android.git
synced 2026-08-26 22:04:07 -05:00
[PM-32009] feat: Add infrastructure for new vault item types (#6828)
This commit is contained in:
@@ -53,9 +53,8 @@ val CipherView.subtitle: String?
|
||||
|
||||
CipherType.SECURE_NOTE,
|
||||
CipherType.SSH_KEY,
|
||||
CipherType.BANK_ACCOUNT,
|
||||
-> null
|
||||
|
||||
CipherType.BANK_ACCOUNT -> TODO("PM-32810: Add Bank Account Type")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+49
-3
@@ -14,6 +14,7 @@ import com.bitwarden.network.model.SecureNoteTypeJson
|
||||
import com.bitwarden.network.model.SyncResponseJson
|
||||
import com.bitwarden.network.model.UriMatchTypeJson
|
||||
import com.bitwarden.vault.Attachment
|
||||
import com.bitwarden.vault.BankAccount
|
||||
import com.bitwarden.vault.Card
|
||||
import com.bitwarden.vault.CardListView
|
||||
import com.bitwarden.vault.Cipher
|
||||
@@ -66,6 +67,9 @@ fun Cipher.toEncryptedNetworkCipher(
|
||||
card = card?.toEncryptedNetworkCard(),
|
||||
key = key,
|
||||
sshKey = sshKey?.toEncryptedNetworkSshKey(),
|
||||
bankAccount = bankAccount?.toEncryptedNetworkBankAccount(),
|
||||
driversLicense = null,
|
||||
passport = null,
|
||||
archivedDate = archivedDate,
|
||||
encryptedFor = encryptedFor,
|
||||
)
|
||||
@@ -96,6 +100,9 @@ fun Cipher.toEncryptedNetworkCipherResponse(
|
||||
card = card?.toEncryptedNetworkCard(),
|
||||
attachments = attachments?.toNetworkAttachmentList(),
|
||||
sshKey = sshKey?.toEncryptedNetworkSshKey(),
|
||||
bankAccount = bankAccount?.toEncryptedNetworkBankAccount(),
|
||||
driversLicense = null,
|
||||
passport = null,
|
||||
shouldOrganizationUseTotp = organizationUseTotp,
|
||||
shouldEdit = edit,
|
||||
revisionDate = revisionDate,
|
||||
@@ -149,6 +156,24 @@ private fun Card.toEncryptedNetworkCard(): SyncResponseJson.Cipher.Card =
|
||||
brand = brand,
|
||||
)
|
||||
|
||||
/**
|
||||
* Converts a Bitwarden SDK [BankAccount] object to a corresponding
|
||||
* [SyncResponseJson.Cipher.BankAccount] object.
|
||||
*/
|
||||
private fun BankAccount.toEncryptedNetworkBankAccount(): SyncResponseJson.Cipher.BankAccount =
|
||||
SyncResponseJson.Cipher.BankAccount(
|
||||
bankName = bankName,
|
||||
nameOnAccount = nameOnAccount,
|
||||
accountType = accountType,
|
||||
accountNumber = accountNumber,
|
||||
routingNumber = routingNumber,
|
||||
branchNumber = branchNumber,
|
||||
pin = pin,
|
||||
swiftCode = swiftCode,
|
||||
iban = iban,
|
||||
bankContactPhone = bankContactPhone,
|
||||
)
|
||||
|
||||
private fun SshKey.toEncryptedNetworkSshKey(): SyncResponseJson.Cipher.SshKey =
|
||||
SyncResponseJson.Cipher.SshKey(
|
||||
publicKey = publicKey,
|
||||
@@ -373,7 +398,7 @@ private fun CipherType.toNetworkCipherType(): CipherTypeJson =
|
||||
CipherType.CARD -> CipherTypeJson.CARD
|
||||
CipherType.IDENTITY -> CipherTypeJson.IDENTITY
|
||||
CipherType.SSH_KEY -> CipherTypeJson.SSH_KEY
|
||||
CipherType.BANK_ACCOUNT -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherType.BANK_ACCOUNT -> CipherTypeJson.BANK_ACCOUNT
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,9 +426,8 @@ fun SyncResponseJson.Cipher.toEncryptedSdkCipher(): Cipher =
|
||||
identity = identity?.toSdkIdentity(),
|
||||
sshKey = sshKey?.toSdkSshKey(),
|
||||
card = card?.toSdkCard(),
|
||||
bankAccount = bankAccount?.toSdkBankAccount(),
|
||||
secureNote = secureNote?.toSdkSecureNote(),
|
||||
// TODO: PM-32810: Add Bank Account Type
|
||||
bankAccount = null,
|
||||
favorite = isFavorite,
|
||||
reprompt = reprompt.toSdkRepromptType(),
|
||||
organizationUseTotp = shouldOrganizationUseTotp,
|
||||
@@ -492,6 +516,24 @@ fun SyncResponseJson.Cipher.Card.toSdkCard(): Card =
|
||||
number = number,
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.BankAccount] into the corresponding Bitwarden SDK
|
||||
* [BankAccount].
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.BankAccount.toSdkBankAccount(): BankAccount =
|
||||
BankAccount(
|
||||
bankName = bankName,
|
||||
nameOnAccount = nameOnAccount,
|
||||
accountType = accountType,
|
||||
accountNumber = accountNumber,
|
||||
routingNumber = routingNumber,
|
||||
branchNumber = branchNumber,
|
||||
pin = pin,
|
||||
swiftCode = swiftCode,
|
||||
iban = iban,
|
||||
bankContactPhone = bankContactPhone,
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.SecureNote] into
|
||||
* the corresponding Bitwarden SDK [SecureNote].
|
||||
@@ -610,6 +652,10 @@ fun CipherTypeJson.toSdkCipherType(): CipherType =
|
||||
CipherTypeJson.CARD -> CipherType.CARD
|
||||
CipherTypeJson.IDENTITY -> CipherType.IDENTITY
|
||||
CipherTypeJson.SSH_KEY -> CipherType.SSH_KEY
|
||||
CipherTypeJson.BANK_ACCOUNT -> CipherType.BANK_ACCOUNT
|
||||
CipherTypeJson.DRIVERS_LICENSE,
|
||||
CipherTypeJson.PASSPORT,
|
||||
-> throw IllegalArgumentException("SDK mapping not yet available for $this")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -24,12 +24,12 @@ import com.bitwarden.vault.CipherType
|
||||
import com.bitwarden.vault.CipherView
|
||||
import com.bitwarden.vault.LoginUriView
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
|
||||
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilitySelectionManager
|
||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||
import com.x8bit.bitwarden.data.autofill.util.login
|
||||
import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
|
||||
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
|
||||
|
||||
+1
-1
@@ -266,7 +266,7 @@ private val CipherListViewType.iconRes: Int
|
||||
is CipherListViewType.Card -> BitwardenDrawable.ic_payment_card
|
||||
CipherListViewType.Identity -> BitwardenDrawable.ic_id_card
|
||||
CipherListViewType.SshKey -> BitwardenDrawable.ic_ssh_key
|
||||
CipherListViewType.BankAccount -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherListViewType.BankAccount -> BitwardenDrawable.ic_note
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+15
@@ -34,6 +34,21 @@ enum class CreateVaultItemType(
|
||||
*/
|
||||
SSH_KEY(BitwardenString.type_ssh_key),
|
||||
|
||||
/**
|
||||
* A bank account cipher.
|
||||
*/
|
||||
BANK_ACCOUNT(BitwardenString.type_bank_account),
|
||||
|
||||
/**
|
||||
* A driver's license cipher.
|
||||
*/
|
||||
DRIVERS_LICENSE(BitwardenString.type_drivers_license),
|
||||
|
||||
/**
|
||||
* A passport cipher.
|
||||
*/
|
||||
PASSPORT(BitwardenString.type_passport),
|
||||
|
||||
/**
|
||||
* A cipher item folder
|
||||
*/
|
||||
|
||||
+3
@@ -13,5 +13,8 @@ fun CreateVaultItemType.toVaultItemCipherTypeOrNull(): VaultItemCipherType? = wh
|
||||
CreateVaultItemType.IDENTITY -> VaultItemCipherType.IDENTITY
|
||||
CreateVaultItemType.SECURE_NOTE -> VaultItemCipherType.SECURE_NOTE
|
||||
CreateVaultItemType.SSH_KEY -> VaultItemCipherType.SSH_KEY
|
||||
CreateVaultItemType.BANK_ACCOUNT -> VaultItemCipherType.BANK_ACCOUNT
|
||||
CreateVaultItemType.DRIVERS_LICENSE -> VaultItemCipherType.DRIVERS_LICENSE
|
||||
CreateVaultItemType.PASSPORT -> VaultItemCipherType.PASSPORT
|
||||
CreateVaultItemType.FOLDER -> null
|
||||
}
|
||||
|
||||
+7
@@ -73,6 +73,9 @@ fun CoachMarkScope<AddEditItemCoachMark>.VaultAddEditContent(
|
||||
|
||||
is VaultAddEditState.ViewState.Content.ItemType.Identity -> Unit
|
||||
is VaultAddEditState.ViewState.Content.ItemType.SshKey -> Unit
|
||||
is VaultAddEditState.ViewState.Content.ItemType.BankAccount -> Unit
|
||||
is VaultAddEditState.ViewState.Content.ItemType.DriversLicense -> Unit
|
||||
is VaultAddEditState.ViewState.Content.ItemType.Passport -> Unit
|
||||
is VaultAddEditState.ViewState.Content.ItemType.Login -> {
|
||||
loginItemTypeHandlers.onSetupTotpClick(isGranted)
|
||||
}
|
||||
@@ -275,6 +278,10 @@ fun CoachMarkScope<AddEditItemCoachMark>.VaultAddEditContent(
|
||||
sshKeyTypeHandlers = sshKeyItemTypeHandlers,
|
||||
)
|
||||
}
|
||||
|
||||
is VaultAddEditState.ViewState.Content.ItemType.BankAccount -> Unit
|
||||
is VaultAddEditState.ViewState.Content.ItemType.DriversLicense -> Unit
|
||||
is VaultAddEditState.ViewState.Content.ItemType.Passport -> Unit
|
||||
}
|
||||
|
||||
vaultAddEditAdditionalOptions(
|
||||
|
||||
+102
@@ -85,6 +85,7 @@ import com.x8bit.bitwarden.ui.vault.feature.util.canAssignToCollections
|
||||
import com.x8bit.bitwarden.ui.vault.feature.util.hasDeletePermissionInAtLeastOneCollection
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.util.toCipherView
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultBankAccountType
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCollection
|
||||
@@ -434,6 +435,14 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun handleSaveClick() = onContent { content ->
|
||||
if (!content.type.isSdkSupported) {
|
||||
sendEvent(
|
||||
VaultAddEditEvent.ShowSnackbar(
|
||||
message = BitwardenString.an_error_has_occurred.asText(),
|
||||
),
|
||||
)
|
||||
return@onContent
|
||||
}
|
||||
if (hasValidationErrors(content)) return@onContent
|
||||
|
||||
mutableStateFlow.update {
|
||||
@@ -2543,6 +2552,9 @@ data class VaultAddEditState(
|
||||
VaultItemCipherType.IDENTITY -> BitwardenString.new_identity.asText()
|
||||
VaultItemCipherType.SECURE_NOTE -> BitwardenString.new_note.asText()
|
||||
VaultItemCipherType.SSH_KEY -> BitwardenString.new_ssh_key.asText()
|
||||
VaultItemCipherType.BANK_ACCOUNT -> BitwardenString.new_bank_account.asText()
|
||||
VaultItemCipherType.DRIVERS_LICENSE -> BitwardenString.new_drivers_license.asText()
|
||||
VaultItemCipherType.PASSPORT -> BitwardenString.new_passport.asText()
|
||||
}
|
||||
|
||||
is VaultAddEditType.EditItem -> when (cipherType) {
|
||||
@@ -2551,6 +2563,9 @@ data class VaultAddEditState(
|
||||
VaultItemCipherType.IDENTITY -> BitwardenString.edit_identity.asText()
|
||||
VaultItemCipherType.SECURE_NOTE -> BitwardenString.edit_note.asText()
|
||||
VaultItemCipherType.SSH_KEY -> BitwardenString.edit_ssh_key.asText()
|
||||
VaultItemCipherType.BANK_ACCOUNT -> BitwardenString.edit_bank_account.asText()
|
||||
VaultItemCipherType.DRIVERS_LICENSE -> BitwardenString.edit_drivers_license.asText()
|
||||
VaultItemCipherType.PASSPORT -> BitwardenString.edit_passport.asText()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2642,6 +2657,9 @@ data class VaultAddEditState(
|
||||
IDENTITY(BitwardenString.type_identity),
|
||||
SECURE_NOTES(BitwardenString.type_secure_note),
|
||||
SSH_KEYS(BitwardenString.type_ssh_key),
|
||||
BANK_ACCOUNT(BitwardenString.type_bank_account),
|
||||
DRIVERS_LICENSE(BitwardenString.type_drivers_license),
|
||||
PASSPORT(BitwardenString.type_passport),
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2750,6 +2768,11 @@ data class VaultAddEditState(
|
||||
*/
|
||||
abstract val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
|
||||
|
||||
/**
|
||||
* Whether this item type has SDK support for save operations.
|
||||
*/
|
||||
open val isSdkSupported: Boolean get() = true
|
||||
|
||||
/**
|
||||
* Represents the login item information.
|
||||
*
|
||||
@@ -2925,6 +2948,85 @@ data class VaultAddEditState(
|
||||
override val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
|
||||
get() = persistentListOf()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the bank account item information.
|
||||
*/
|
||||
@Parcelize
|
||||
data class BankAccount(
|
||||
val bankName: String = "",
|
||||
val nameOnAccount: String = "",
|
||||
val accountType: VaultBankAccountType = VaultBankAccountType.SELECT,
|
||||
val accountNumber: String = "",
|
||||
val routingNumber: String = "",
|
||||
val branchNumber: String = "",
|
||||
val pin: String = "",
|
||||
val swiftCode: String = "",
|
||||
val iban: String = "",
|
||||
val bankContactPhone: String = "",
|
||||
) : ItemType() {
|
||||
override val itemTypeOption: ItemTypeOption
|
||||
get() = ItemTypeOption.BANK_ACCOUNT
|
||||
|
||||
override val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
|
||||
get() = persistentListOf()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the driver's license item information.
|
||||
*/
|
||||
@Parcelize
|
||||
data class DriversLicense(
|
||||
val firstName: String = "",
|
||||
val middleName: String = "",
|
||||
val lastName: String = "",
|
||||
val licenseNumber: String = "",
|
||||
val issuingCountry: String = "",
|
||||
val issuingState: String = "",
|
||||
val expirationMonth: String = "",
|
||||
val expirationDay: String = "",
|
||||
val expirationYear: String = "",
|
||||
val licenseClass: String = "",
|
||||
) : ItemType() {
|
||||
override val itemTypeOption: ItemTypeOption
|
||||
get() = ItemTypeOption.DRIVERS_LICENSE
|
||||
|
||||
override val isSdkSupported: Boolean get() = false
|
||||
|
||||
override val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
|
||||
get() = persistentListOf()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the passport item information.
|
||||
*/
|
||||
@Parcelize
|
||||
data class Passport(
|
||||
val surname: String = "",
|
||||
val givenName: String = "",
|
||||
val dobMonth: String = "",
|
||||
val dobDay: String = "",
|
||||
val dobYear: String = "",
|
||||
val nationality: String = "",
|
||||
val passportNumber: String = "",
|
||||
val passportType: String = "",
|
||||
val issuingCountry: String = "",
|
||||
val issuingAuthority: String = "",
|
||||
val issueMonth: String = "",
|
||||
val issueDay: String = "",
|
||||
val issueYear: String = "",
|
||||
val expirationMonth: String = "",
|
||||
val expirationDay: String = "",
|
||||
val expirationYear: String = "",
|
||||
) : ItemType() {
|
||||
override val itemTypeOption: ItemTypeOption
|
||||
get() = ItemTypeOption.PASSPORT
|
||||
|
||||
override val isSdkSupported: Boolean get() = false
|
||||
|
||||
override val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
|
||||
get() = persistentListOf()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
@@ -67,4 +67,7 @@ private val VaultAddEditState.ViewState.Content.ItemType.defaultLinkedFieldTypeO
|
||||
is VaultAddEditState.ViewState.Content.ItemType.Login -> VaultLinkedFieldType.USERNAME
|
||||
is VaultAddEditState.ViewState.Content.ItemType.SecureNotes -> null
|
||||
is VaultAddEditState.ViewState.Content.ItemType.SshKey -> null
|
||||
is VaultAddEditState.ViewState.Content.ItemType.BankAccount -> null
|
||||
is VaultAddEditState.ViewState.Content.ItemType.DriversLicense -> null
|
||||
is VaultAddEditState.ViewState.Content.ItemType.Passport -> null
|
||||
}
|
||||
|
||||
+16
-1
@@ -21,6 +21,7 @@ import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultBankAccountType
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCollection
|
||||
@@ -99,7 +100,18 @@ fun CipherView.toViewState(
|
||||
fingerprint = sshKey?.fingerprint.orEmpty(),
|
||||
)
|
||||
|
||||
CipherType.BANK_ACCOUNT -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherType.BANK_ACCOUNT -> VaultAddEditState.ViewState.Content.ItemType.BankAccount(
|
||||
bankName = bankAccount?.bankName.orEmpty(),
|
||||
nameOnAccount = bankAccount?.nameOnAccount.orEmpty(),
|
||||
accountType = bankAccount?.accountType.toBankAccountTypeOrDefault(),
|
||||
accountNumber = bankAccount?.accountNumber.orEmpty(),
|
||||
routingNumber = bankAccount?.routingNumber.orEmpty(),
|
||||
branchNumber = bankAccount?.branchNumber.orEmpty(),
|
||||
pin = bankAccount?.pin.orEmpty(),
|
||||
swiftCode = bankAccount?.swiftCode.orEmpty(),
|
||||
iban = bankAccount?.iban.orEmpty(),
|
||||
bankContactPhone = bankAccount?.bankContactPhone.orEmpty(),
|
||||
)
|
||||
},
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
originalCipher = this,
|
||||
@@ -331,6 +343,9 @@ private fun String?.toExpirationMonthOrDefault(): VaultCardExpirationMonth =
|
||||
.find { it.number == this }
|
||||
?: VaultCardExpirationMonth.SELECT
|
||||
|
||||
private fun String?.toBankAccountTypeOrDefault(): VaultBankAccountType =
|
||||
this?.let { VaultBankAccountType.parse(it) } ?: VaultBankAccountType.SELECT
|
||||
|
||||
private fun String.appendCloneTextIfRequired(
|
||||
isClone: Boolean,
|
||||
resourceManager: ResourceManager,
|
||||
|
||||
+9
@@ -26,4 +26,13 @@ fun VaultItemCipherType.toItemType(): VaultAddEditState.ViewState.Content.ItemTy
|
||||
VaultItemCipherType.IDENTITY -> VaultAddEditState.ViewState.Content.ItemType.Identity()
|
||||
VaultItemCipherType.SECURE_NOTE -> VaultAddEditState.ViewState.Content.ItemType.SecureNotes
|
||||
VaultItemCipherType.SSH_KEY -> VaultAddEditState.ViewState.Content.ItemType.SshKey()
|
||||
VaultItemCipherType.BANK_ACCOUNT -> {
|
||||
VaultAddEditState.ViewState.Content.ItemType.BankAccount()
|
||||
}
|
||||
VaultItemCipherType.DRIVERS_LICENSE -> {
|
||||
VaultAddEditState.ViewState.Content.ItemType.DriversLicense()
|
||||
}
|
||||
VaultItemCipherType.PASSPORT -> {
|
||||
VaultAddEditState.ViewState.Content.ItemType.Passport()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,6 +429,20 @@ private fun VaultItemContent(
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
is VaultItemState.ViewState.Content.ItemType.BankAccount,
|
||||
is VaultItemState.ViewState.Content.ItemType.DriversLicense,
|
||||
is VaultItemState.ViewState.Content.ItemType.Passport,
|
||||
-> {
|
||||
// TODO(PM-32810): Render dedicated content for new item types once the UI
|
||||
// ships in the phase-05-07 PR. Until then these are gated behind the
|
||||
// pm-32009-new-item-types feature flag and cannot be received.
|
||||
VaultItemSecureNoteContent(
|
||||
commonState = viewState.common,
|
||||
vaultCommonItemTypeHandlers = vaultCommonItemTypeHandlers,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1449,6 +1449,9 @@ data class VaultItemState(
|
||||
VaultItemCipherType.IDENTITY -> BitwardenString.view_identity.asText()
|
||||
VaultItemCipherType.SECURE_NOTE -> BitwardenString.view_note.asText()
|
||||
VaultItemCipherType.SSH_KEY -> BitwardenString.view_ssh_key.asText()
|
||||
VaultItemCipherType.BANK_ACCOUNT -> BitwardenString.view_bank_account.asText()
|
||||
VaultItemCipherType.DRIVERS_LICENSE -> BitwardenString.view_drivers_license.asText()
|
||||
VaultItemCipherType.PASSPORT -> BitwardenString.view_passport.asText()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1869,6 +1872,60 @@ data class VaultItemState(
|
||||
val fingerprint: String,
|
||||
val showPrivateKey: Boolean,
|
||||
) : ItemType()
|
||||
|
||||
/**
|
||||
* Represents the `BankAccount` item type.
|
||||
*/
|
||||
data class BankAccount(
|
||||
val bankName: String?,
|
||||
val nameOnAccount: String?,
|
||||
val accountType: String?,
|
||||
val accountNumber: String?,
|
||||
val routingNumber: String?,
|
||||
val branchNumber: String?,
|
||||
val pin: String?,
|
||||
val swiftCode: String?,
|
||||
val iban: String?,
|
||||
val bankContactPhone: String?,
|
||||
) : ItemType()
|
||||
|
||||
/**
|
||||
* Represents the `DriversLicense` item type.
|
||||
*/
|
||||
data class DriversLicense(
|
||||
val firstName: String?,
|
||||
val middleName: String?,
|
||||
val lastName: String?,
|
||||
val licenseNumber: String?,
|
||||
val issuingCountry: String?,
|
||||
val issuingState: String?,
|
||||
val expirationMonth: String?,
|
||||
val expirationDay: String?,
|
||||
val expirationYear: String?,
|
||||
val licenseClass: String?,
|
||||
) : ItemType()
|
||||
|
||||
/**
|
||||
* Represents the `Passport` item type.
|
||||
*/
|
||||
data class Passport(
|
||||
val surname: String?,
|
||||
val givenName: String?,
|
||||
val dobMonth: String?,
|
||||
val dobDay: String?,
|
||||
val dobYear: String?,
|
||||
val nationality: String?,
|
||||
val passportNumber: String?,
|
||||
val passportType: String?,
|
||||
val issuingCountry: String?,
|
||||
val issuingAuthority: String?,
|
||||
val issueMonth: String?,
|
||||
val issueDay: String?,
|
||||
val issueYear: String?,
|
||||
val expirationMonth: String?,
|
||||
val expirationDay: String?,
|
||||
val expirationYear: String?,
|
||||
) : ItemType()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-2
@@ -214,7 +214,20 @@ fun CipherView.toViewState(
|
||||
)
|
||||
}
|
||||
|
||||
CipherType.BANK_ACCOUNT -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherType.BANK_ACCOUNT -> {
|
||||
VaultItemState.ViewState.Content.ItemType.BankAccount(
|
||||
bankName = bankAccount?.bankName,
|
||||
nameOnAccount = bankAccount?.nameOnAccount,
|
||||
accountType = bankAccount?.accountType,
|
||||
accountNumber = bankAccount?.accountNumber,
|
||||
routingNumber = bankAccount?.routingNumber,
|
||||
branchNumber = bankAccount?.branchNumber,
|
||||
pin = bankAccount?.pin,
|
||||
swiftCode = bankAccount?.swiftCode,
|
||||
iban = bankAccount?.iban,
|
||||
bankContactPhone = bankAccount?.bankContactPhone,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -305,7 +318,7 @@ private val CipherType.iconRes: Int
|
||||
CipherType.IDENTITY -> BitwardenDrawable.ic_id_card
|
||||
CipherType.SSH_KEY -> BitwardenDrawable.ic_ssh_key
|
||||
CipherType.LOGIN -> BitwardenDrawable.ic_globe
|
||||
CipherType.BANK_ACCOUNT -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherType.BANK_ACCOUNT -> BitwardenDrawable.ic_note
|
||||
}
|
||||
|
||||
@get:DrawableRes
|
||||
|
||||
+19
-15
@@ -11,9 +11,11 @@ import androidx.credentials.provider.ProviderCreateCredentialRequest
|
||||
import androidx.credentials.provider.ProviderGetCredentialRequest
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.manager.toast.ToastManager
|
||||
import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.core.data.repository.util.map
|
||||
import com.bitwarden.core.util.persistentListOfNotNull
|
||||
import com.bitwarden.data.repository.util.baseIconUrl
|
||||
import com.bitwarden.data.repository.util.baseWebSendUrl
|
||||
import com.bitwarden.data.repository.util.baseWebVaultUrlOrDefault
|
||||
@@ -58,6 +60,7 @@ import com.x8bit.bitwarden.data.credentials.model.ValidateOriginResult
|
||||
import com.x8bit.bitwarden.data.credentials.parser.RelyingPartyParser
|
||||
import com.x8bit.bitwarden.data.credentials.repository.PrivilegedAppRepository
|
||||
import com.x8bit.bitwarden.data.credentials.util.getCreatePasskeyCredentialRequestOrNull
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager
|
||||
@@ -152,6 +155,7 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
private val toastManager: ToastManager,
|
||||
private val premiumStateManager: PremiumStateManager,
|
||||
snackbarRelayManager: SnackbarRelayManager<SnackbarRelay>,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
) : BaseViewModel<VaultItemListingState, VaultItemListingEvent, VaultItemListingsAction>(
|
||||
initialState = run {
|
||||
val userState = requireNotNull(authRepository.userStateFlow.value)
|
||||
@@ -705,6 +709,9 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
CreateVaultItemType.IDENTITY,
|
||||
CreateVaultItemType.SECURE_NOTE,
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.BANK_ACCOUNT,
|
||||
CreateVaultItemType.DRIVERS_LICENSE,
|
||||
CreateVaultItemType.PASSPORT,
|
||||
-> {
|
||||
vaultItemType
|
||||
.toVaultItemCipherTypeOrNull()
|
||||
@@ -841,19 +848,16 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun createVaultItemTypeSelectionExcludedOptions(): ImmutableList<CreateVaultItemType> {
|
||||
// If policy is enable for any organization, exclude the card option
|
||||
return if (state.restrictItemTypesPolicyOrgIds.isNotEmpty()) {
|
||||
persistentListOf(
|
||||
CreateVaultItemType.CARD,
|
||||
CreateVaultItemType.FOLDER,
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
)
|
||||
} else {
|
||||
persistentListOf(
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.FOLDER,
|
||||
)
|
||||
}
|
||||
val isNewItemTypesEnabled = featureFlagManager
|
||||
.getFeatureFlag(FlagKey.NewItemTypes)
|
||||
return persistentListOfNotNull(
|
||||
CreateVaultItemType.CARD.takeIf { state.restrictItemTypesPolicyOrgIds.isNotEmpty() },
|
||||
CreateVaultItemType.FOLDER,
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.BANK_ACCOUNT.takeUnless { isNewItemTypesEnabled },
|
||||
CreateVaultItemType.DRIVERS_LICENSE.takeUnless { isNewItemTypesEnabled },
|
||||
CreateVaultItemType.PASSPORT.takeUnless { isNewItemTypesEnabled },
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleAddVaultItemClick() {
|
||||
@@ -1339,7 +1343,7 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
CipherType.CARD -> VaultItemCipherType.CARD
|
||||
CipherType.IDENTITY -> VaultItemCipherType.IDENTITY
|
||||
CipherType.SSH_KEY -> VaultItemCipherType.SSH_KEY
|
||||
CipherType.BANK_ACCOUNT -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherType.BANK_ACCOUNT -> VaultItemCipherType.BANK_ACCOUNT
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -1361,7 +1365,7 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
CipherType.CARD -> VaultItemCipherType.CARD
|
||||
CipherType.IDENTITY -> VaultItemCipherType.IDENTITY
|
||||
CipherType.SSH_KEY -> VaultItemCipherType.SSH_KEY
|
||||
CipherType.BANK_ACCOUNT -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherType.BANK_ACCOUNT -> VaultItemCipherType.BANK_ACCOUNT
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
+1
-1
@@ -588,7 +588,7 @@ private val CipherListViewType.iconRes: Int
|
||||
is CipherListViewType.Card -> BitwardenDrawable.ic_payment_card
|
||||
CipherListViewType.Identity -> BitwardenDrawable.ic_id_card
|
||||
CipherListViewType.SshKey -> BitwardenDrawable.ic_ssh_key
|
||||
CipherListViewType.BankAccount -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherListViewType.BankAccount -> BitwardenDrawable.ic_note
|
||||
}
|
||||
|
||||
private fun List<CipherListView>.applyFilters(
|
||||
|
||||
@@ -117,7 +117,7 @@ class VaultViewModel @Inject constructor(
|
||||
private val browserAutofillDialogManager: BrowserAutofillDialogManager,
|
||||
private val credentialExchangeRegistryManager: CredentialExchangeRegistryManager,
|
||||
private val buildInfoManager: BuildInfoManager,
|
||||
featureFlagManager: FeatureFlagManager,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
snackbarRelayManager: SnackbarRelayManager<SnackbarRelay>,
|
||||
) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
|
||||
initialState = run {
|
||||
@@ -434,12 +434,17 @@ class VaultViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleSelectAddItemType() {
|
||||
val isNewItemTypesEnabled = featureFlagManager
|
||||
.getFeatureFlag(FlagKey.NewItemTypes)
|
||||
// If policy is enable for any organization, exclude the card option
|
||||
val excludedOptions = persistentListOfNotNull(
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.CARD.takeUnless {
|
||||
state.restrictItemTypesPolicyOrgIds.isEmpty()
|
||||
},
|
||||
CreateVaultItemType.BANK_ACCOUNT.takeUnless { isNewItemTypesEnabled },
|
||||
CreateVaultItemType.DRIVERS_LICENSE.takeUnless { isNewItemTypesEnabled },
|
||||
CreateVaultItemType.PASSPORT.takeUnless { isNewItemTypesEnabled },
|
||||
)
|
||||
|
||||
mutableStateFlow.update {
|
||||
@@ -500,6 +505,9 @@ class VaultViewModel @Inject constructor(
|
||||
CreateVaultItemType.IDENTITY,
|
||||
CreateVaultItemType.SECURE_NOTE,
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.BANK_ACCOUNT,
|
||||
CreateVaultItemType.DRIVERS_LICENSE,
|
||||
CreateVaultItemType.PASSPORT,
|
||||
-> {
|
||||
vaultItemType
|
||||
.toVaultItemCipherTypeOrNull()
|
||||
|
||||
+26
-2
@@ -3,6 +3,7 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.vault.util
|
||||
|
||||
import com.bitwarden.ui.platform.base.util.orNullIfBlank
|
||||
import com.bitwarden.vault.BankAccountView
|
||||
import com.bitwarden.vault.CardView
|
||||
import com.bitwarden.vault.CipherRepromptType
|
||||
import com.bitwarden.vault.CipherType
|
||||
@@ -18,6 +19,7 @@ import com.bitwarden.vault.SecureNoteView
|
||||
import com.bitwarden.vault.SshKeyView
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultBankAccountType
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultIdentityTitle
|
||||
@@ -62,8 +64,7 @@ fun VaultAddEditState.ViewState.Content.toCipherView(
|
||||
login = type.toLoginView(common = common, clock = clock),
|
||||
card = type.toCardView(),
|
||||
sshKey = type.toSshKeyView(),
|
||||
// TODO PM-32810: Add Bank Account Type
|
||||
bankAccount = null,
|
||||
bankAccount = type.toBankAccountView(),
|
||||
|
||||
// Fields we always grab from the UI
|
||||
name = common.name,
|
||||
@@ -82,6 +83,10 @@ private fun VaultAddEditState.ViewState.Content.ItemType.toCipherType(): CipherT
|
||||
is VaultAddEditState.ViewState.Content.ItemType.Login -> CipherType.LOGIN
|
||||
is VaultAddEditState.ViewState.Content.ItemType.SecureNotes -> CipherType.SECURE_NOTE
|
||||
is VaultAddEditState.ViewState.Content.ItemType.SshKey -> CipherType.SSH_KEY
|
||||
is VaultAddEditState.ViewState.Content.ItemType.BankAccount -> CipherType.BANK_ACCOUNT
|
||||
is VaultAddEditState.ViewState.Content.ItemType.DriversLicense,
|
||||
is VaultAddEditState.ViewState.Content.ItemType.Passport,
|
||||
-> throw IllegalArgumentException("SDK mapping not yet available for $this")
|
||||
}
|
||||
|
||||
private fun VaultAddEditState.ViewState.Content.ItemType.toSshKeyView(): SshKeyView? =
|
||||
@@ -93,6 +98,25 @@ private fun VaultAddEditState.ViewState.Content.ItemType.toSshKeyView(): SshKeyV
|
||||
)
|
||||
}
|
||||
|
||||
private fun VaultAddEditState.ViewState.Content.ItemType.toBankAccountView(): BankAccountView? =
|
||||
(this as? VaultAddEditState.ViewState.Content.ItemType.BankAccount)?.let {
|
||||
BankAccountView(
|
||||
bankName = it.bankName.orNullIfBlank(),
|
||||
nameOnAccount = it.nameOnAccount.orNullIfBlank(),
|
||||
accountType = it
|
||||
.accountType
|
||||
.takeUnless { type -> type == VaultBankAccountType.SELECT }
|
||||
?.value,
|
||||
accountNumber = it.accountNumber.orNullIfBlank(),
|
||||
routingNumber = it.routingNumber.orNullIfBlank(),
|
||||
branchNumber = it.branchNumber.orNullIfBlank(),
|
||||
pin = it.pin.orNullIfBlank(),
|
||||
swiftCode = it.swiftCode.orNullIfBlank(),
|
||||
iban = it.iban.orNullIfBlank(),
|
||||
bankContactPhone = it.bankContactPhone.orNullIfBlank(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun VaultAddEditState.ViewState.Content.ItemType.toCardView(): CardView? =
|
||||
(this as? VaultAddEditState.ViewState.Content.ItemType.Card)?.let {
|
||||
CardView(
|
||||
|
||||
+2
-1
@@ -365,7 +365,8 @@ private fun CipherListView.toVaultItemOrNull(
|
||||
hasDecryptionError = hasDecryptionError,
|
||||
)
|
||||
|
||||
CipherListViewType.BankAccount -> TODO("PM-32810: Add Bank Account Type")
|
||||
// TODO: [PM-32009] Map BankAccount to its own VaultItem subclass when the UI is wired.
|
||||
CipherListViewType.BankAccount -> null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.x8bit.bitwarden.ui.vault.model
|
||||
|
||||
/**
|
||||
* Defines all available account type options for bank accounts. The [value] corresponds to the
|
||||
* string representation used in the server contract, with the exception of [SELECT], which is a
|
||||
* UI-only placeholder representing no selection.
|
||||
*/
|
||||
enum class VaultBankAccountType(val value: String) {
|
||||
SELECT(value = "select"),
|
||||
CHECKING(value = "checking"),
|
||||
SAVINGS(value = "savings"),
|
||||
CERTIFICATE_OF_DEPOSIT(value = "certificateOfDeposit"),
|
||||
LINE_OF_CREDIT(value = "lineOfCredit"),
|
||||
INVESTMENT_BROKERAGE(value = "investmentBrokerage"),
|
||||
MONEY_MARKET(value = "moneyMarket"),
|
||||
OTHER(value = "other"),
|
||||
;
|
||||
|
||||
/**
|
||||
* Companion object for [VaultBankAccountType] that exposes parsing utilities.
|
||||
*/
|
||||
companion object {
|
||||
/**
|
||||
* Returns the [VaultBankAccountType] matching the provided [value] (case-insensitive),
|
||||
* falling back to [OTHER] if no match is found.
|
||||
*/
|
||||
fun parse(value: String?): VaultBankAccountType =
|
||||
entries
|
||||
.firstOrNull { it.value.equals(other = value, ignoreCase = true) }
|
||||
?: OTHER
|
||||
}
|
||||
}
|
||||
@@ -32,4 +32,19 @@ enum class VaultItemCipherType {
|
||||
* A SSH key cipher.
|
||||
*/
|
||||
SSH_KEY,
|
||||
|
||||
/**
|
||||
* A bank account cipher.
|
||||
*/
|
||||
BANK_ACCOUNT,
|
||||
|
||||
/**
|
||||
* A driver's license cipher.
|
||||
*/
|
||||
DRIVERS_LICENSE,
|
||||
|
||||
/**
|
||||
* A passport cipher.
|
||||
*/
|
||||
PASSPORT,
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@ fun CipherType.toVaultItemCipherType(): VaultItemCipherType =
|
||||
CipherType.CARD -> VaultItemCipherType.CARD
|
||||
CipherType.IDENTITY -> VaultItemCipherType.IDENTITY
|
||||
CipherType.SSH_KEY -> VaultItemCipherType.SSH_KEY
|
||||
CipherType.BANK_ACCOUNT -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherType.BANK_ACCOUNT -> VaultItemCipherType.BANK_ACCOUNT
|
||||
}
|
||||
|
||||
+12
@@ -589,6 +589,18 @@ private const val CIPHER_JSON = """
|
||||
"privateKey": "mockPrivateKey-1",
|
||||
"keyFingerprint": "mockKeyFingerprint-1"
|
||||
},
|
||||
"bankAccount": {
|
||||
"bankName": "mockBankName-1",
|
||||
"nameOnAccount": "mockNameOnAccount-1",
|
||||
"accountType": "mockAccountType-1",
|
||||
"accountNumber": "mockAccountNumber-1",
|
||||
"routingNumber": "mockRoutingNumber-1",
|
||||
"branchNumber": "mockBranchNumber-1",
|
||||
"pin": "mockPin-1",
|
||||
"swiftCode": "mockSwiftCode-1",
|
||||
"iban": "mockIban-1",
|
||||
"bankContactPhone": "mockBankContactPhone-1"
|
||||
},
|
||||
"encryptedFor": "mockEncryptedFor-1"
|
||||
}
|
||||
"""
|
||||
|
||||
+31
-18
@@ -52,7 +52,6 @@ fun createMockCipherView(
|
||||
clock: Clock = FIXED_CLOCK,
|
||||
fido2Credentials: List<Fido2Credential>? = null,
|
||||
sshKey: SshKeyView? = createMockSshKeyView(number = number),
|
||||
bankAccount: BankAccountView = createMockBankAccountView(number = number),
|
||||
login: LoginView? = createMockLoginView(
|
||||
number = number,
|
||||
password = password,
|
||||
@@ -61,6 +60,7 @@ fun createMockCipherView(
|
||||
fido2Credentials = fido2Credentials,
|
||||
),
|
||||
card: CardView? = createMockCardView(number = number).takeIf { cipherType == CipherType.CARD },
|
||||
bankAccount: BankAccountView? = createMockBankAccountView(number = number),
|
||||
attachments: List<AttachmentView> = listOf(createMockAttachmentView(number = number)),
|
||||
isArchived: Boolean = false,
|
||||
passwordHistory: List<PasswordHistoryView> = listOf(
|
||||
@@ -226,6 +226,36 @@ fun createMockCardView(
|
||||
brand = brand,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [BankAccountView] with a given [number].
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
fun createMockBankAccountView(
|
||||
number: Int,
|
||||
bankName: String? = "mockBankName-$number",
|
||||
nameOnAccount: String? = "mockNameOnAccount-$number",
|
||||
accountType: String? = "checking",
|
||||
accountNumber: String? = "mockAccountNumber-$number",
|
||||
routingNumber: String? = "mockRoutingNumber-$number",
|
||||
branchNumber: String? = "mockBranchNumber-$number",
|
||||
pin: String? = "mockPin-$number",
|
||||
swiftCode: String? = "mockSwiftCode-$number",
|
||||
iban: String? = "mockIban-$number",
|
||||
bankContactPhone: String? = "mockBankContactPhone-$number",
|
||||
): BankAccountView =
|
||||
BankAccountView(
|
||||
bankName = bankName,
|
||||
nameOnAccount = nameOnAccount,
|
||||
accountType = accountType,
|
||||
accountNumber = accountNumber,
|
||||
routingNumber = routingNumber,
|
||||
branchNumber = branchNumber,
|
||||
pin = pin,
|
||||
swiftCode = swiftCode,
|
||||
iban = iban,
|
||||
bankContactPhone = bankContactPhone,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [FieldView] with a given [number].
|
||||
*/
|
||||
@@ -272,23 +302,6 @@ fun createMockSshKeyView(number: Int): SshKeyView =
|
||||
fingerprint = "mockKeyFingerprint-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [BankAccountView] with a given [number].
|
||||
*/
|
||||
fun createMockBankAccountView(number: Int): BankAccountView =
|
||||
BankAccountView(
|
||||
bankName = "mockBankName-$number",
|
||||
nameOnAccount = "mockNameOnAccount-$number",
|
||||
accountType = "mockAccountType-$number",
|
||||
accountNumber = "mockAccountNumber-$number",
|
||||
routingNumber = "mockRoutingNumber-$number",
|
||||
branchNumber = "mockBranchNumber-$number",
|
||||
pin = "mockPin-$number",
|
||||
swiftCode = "mokSwiftCode-$number",
|
||||
iban = "mockIban-$number",
|
||||
bankContactPhone = "mockBankContractPhone-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [PasswordHistoryView] with a given [number].
|
||||
*/
|
||||
|
||||
+8
-6
@@ -33,7 +33,10 @@ private val FIXED_CLOCK: Clock = Clock.fixed(
|
||||
/**
|
||||
* Create a mock [Cipher] with a given [number].
|
||||
*/
|
||||
fun createMockSdkCipher(number: Int, clock: Clock = FIXED_CLOCK): Cipher =
|
||||
fun createMockSdkCipher(
|
||||
number: Int,
|
||||
clock: Clock = FIXED_CLOCK,
|
||||
): Cipher =
|
||||
Cipher(
|
||||
id = "mockId-$number",
|
||||
organizationId = "mockOrganizationId-$number",
|
||||
@@ -50,11 +53,10 @@ fun createMockSdkCipher(number: Int, clock: Clock = FIXED_CLOCK): Cipher =
|
||||
archivedDate = clock.instant(),
|
||||
attachments = listOf(createMockSdkAttachment(number = number)),
|
||||
card = createMockSdkCard(number = number),
|
||||
bankAccount = createMockSdkBankAccount(number = number),
|
||||
fields = listOf(createMockSdkField(number = number)),
|
||||
identity = createMockSdkIdentity(number = number),
|
||||
sshKey = createMockSdkSshKey(number = number),
|
||||
// TODO: PM-32810: Add Bank Account Type
|
||||
bankAccount = null,
|
||||
favorite = false,
|
||||
passwordHistory = listOf(createMockSdkPasswordHistory(number = number, clock = clock)),
|
||||
permissions = createMockSdkCipherPermissions(),
|
||||
@@ -134,7 +136,7 @@ fun createMockSdkSshKey(number: Int): SshKey =
|
||||
/**
|
||||
* Create a mock [BankAccount] with a given [number].
|
||||
*/
|
||||
fun createMockBankAccount(number: Int): BankAccount =
|
||||
fun createMockSdkBankAccount(number: Int): BankAccount =
|
||||
BankAccount(
|
||||
bankName = "mockBankName-$number",
|
||||
nameOnAccount = "mockNameOnAccount-$number",
|
||||
@@ -143,9 +145,9 @@ fun createMockBankAccount(number: Int): BankAccount =
|
||||
routingNumber = "mockRoutingNumber-$number",
|
||||
branchNumber = "mockBranchNumber-$number",
|
||||
pin = "mockPin-$number",
|
||||
swiftCode = "mokSwiftCode-$number",
|
||||
swiftCode = "mockSwiftCode-$number",
|
||||
iban = "mockIban-$number",
|
||||
bankContactPhone = "mockBankContractPhone-$number",
|
||||
bankContactPhone = "mockBankContactPhone-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
+105
-2
@@ -6,6 +6,7 @@ import com.bitwarden.network.model.FieldTypeJson
|
||||
import com.bitwarden.network.model.UriMatchTypeJson
|
||||
import com.bitwarden.network.model.createMockAttachment
|
||||
import com.bitwarden.network.model.createMockAttachmentJsonRequest
|
||||
import com.bitwarden.network.model.createMockBankAccount
|
||||
import com.bitwarden.network.model.createMockCard
|
||||
import com.bitwarden.network.model.createMockCipher
|
||||
import com.bitwarden.network.model.createMockCipherJsonRequest
|
||||
@@ -26,6 +27,7 @@ import com.bitwarden.vault.UriMatchType
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockEncryptionContext
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkAttachment
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkBankAccount
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCard
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkField
|
||||
@@ -37,6 +39,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkSshKey
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkUri
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.assertNull
|
||||
@@ -82,6 +85,8 @@ class VaultSdkCipherExtensionsTest {
|
||||
createMockCipherJsonRequest(
|
||||
number = 1,
|
||||
login = createMockLogin(number = 1, uri = null),
|
||||
driversLicense = null,
|
||||
passport = null,
|
||||
archivedDate = FIXED_CLOCK.instant(),
|
||||
),
|
||||
syncCipher,
|
||||
@@ -175,6 +180,60 @@ class VaultSdkCipherExtensionsTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkBankAccount should convert a SyncResponseJson BankAccount to an SDK BankAccount`() {
|
||||
val syncBankAccount = createMockBankAccount(number = 1)
|
||||
val sdkBankAccount = syncBankAccount.toSdkBankAccount()
|
||||
assertEquals(
|
||||
createMockSdkBankAccount(number = 1),
|
||||
sdkBankAccount,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkBankAccount should pass null account type through to the SDK`() {
|
||||
val syncBankAccount = createMockBankAccount(number = 1, accountType = null)
|
||||
val sdkBankAccount = syncBankAccount.toSdkBankAccount()
|
||||
assertNull(sdkBankAccount.accountType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toEncryptedNetworkBankAccount should pass null account type through`() {
|
||||
val sdkBankAccount = createMockSdkBankAccount(number = 1).copy(accountType = null)
|
||||
val sdkCipher = createMockSdkCipher(number = 1, clock = FIXED_CLOCK)
|
||||
.copy(bankAccount = sdkBankAccount)
|
||||
|
||||
val networkCipher = sdkCipher.toEncryptedNetworkCipherResponse(
|
||||
encryptedFor = "mockEncryptedFor-1",
|
||||
)
|
||||
|
||||
assertNull(networkCipher.bankAccount?.accountType)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `toEncryptedNetworkCipher with null SDK bankAccount should produce null network bankAccount`() {
|
||||
val sdkCipher = createMockSdkCipher(
|
||||
number = 1,
|
||||
clock = FIXED_CLOCK,
|
||||
)
|
||||
.copy(bankAccount = null)
|
||||
|
||||
val request = sdkCipher.toEncryptedNetworkCipher(encryptedFor = "mockEncryptedFor-1")
|
||||
|
||||
assertNull(request.bankAccount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toEncryptedNetworkCipher should always emit null driversLicense and passport`() {
|
||||
val sdkCipher = createMockSdkCipher(number = 1, clock = FIXED_CLOCK)
|
||||
|
||||
val request = sdkCipher.toEncryptedNetworkCipher(encryptedFor = "mockEncryptedFor-1")
|
||||
|
||||
assertNull(request.driversLicense)
|
||||
assertNull(request.passport)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkLoginUriList should convert list of LoginUri to List of Sdk LoginUri`() {
|
||||
val syncLoginUris = listOf(
|
||||
@@ -290,6 +349,31 @@ class VaultSdkCipherExtensionsTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkCipherType should map BANK_ACCOUNT to the SDK BANK_ACCOUNT type`() {
|
||||
assertEquals(
|
||||
CipherType.BANK_ACCOUNT,
|
||||
CipherTypeJson.BANK_ACCOUNT.toSdkCipherType(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `toSdkCipherType should throw for DRIVERS_LICENSE since SDK mapping is not yet available`() {
|
||||
val error = assertThrows(IllegalArgumentException::class.java) {
|
||||
CipherTypeJson.DRIVERS_LICENSE.toSdkCipherType()
|
||||
}
|
||||
assertEquals("SDK mapping not yet available for DRIVERS_LICENSE", error.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkCipherType should throw for PASSPORT since SDK mapping is not yet available`() {
|
||||
val error = assertThrows(IllegalArgumentException::class.java) {
|
||||
CipherTypeJson.PASSPORT.toSdkCipherType()
|
||||
}
|
||||
assertEquals("SDK mapping not yet available for PASSPORT", error.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkMatchType should convert UriMatchTypeJson to UriMatchType`() {
|
||||
val uriMatchType = UriMatchTypeJson.DOMAIN
|
||||
@@ -358,11 +442,16 @@ class VaultSdkCipherExtensionsTest {
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `EncryptionContext toEncryptedNetworkCipher should convert an EncryptionContext to a Network Cipher`() {
|
||||
val encryptionContext = createMockEncryptionContext(number = 1)
|
||||
val encryptionContext = createMockEncryptionContext(
|
||||
number = 1,
|
||||
cipher = createMockSdkCipher(number = 1, clock = FIXED_CLOCK),
|
||||
)
|
||||
assertEquals(
|
||||
createMockCipherJsonRequest(
|
||||
number = 1,
|
||||
login = createMockLogin(number = 1, uri = null),
|
||||
driversLicense = null,
|
||||
passport = null,
|
||||
archivedDate = FIXED_CLOCK.instant(),
|
||||
),
|
||||
encryptionContext.toEncryptedNetworkCipher(),
|
||||
@@ -372,7 +461,10 @@ class VaultSdkCipherExtensionsTest {
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `EncryptionContext toEncryptedNetworkCipherResponse should convert an EncryptionContext to a cipher`() {
|
||||
val encryptionContext = createMockEncryptionContext(number = 1)
|
||||
val encryptionContext = createMockEncryptionContext(
|
||||
number = 1,
|
||||
cipher = createMockSdkCipher(number = 1, clock = FIXED_CLOCK),
|
||||
)
|
||||
assertEquals(
|
||||
createMockCipher(
|
||||
number = 1,
|
||||
@@ -440,6 +532,17 @@ class VaultSdkCipherExtensionsTest {
|
||||
assertNull(loginType.v1.brand)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `toFailureCipherListView should convert BankAccount Cipher to CipherListView of type BankAccount`() {
|
||||
val cipher = createMockSdkCipher(number = 1)
|
||||
.copy(type = CipherType.BANK_ACCOUNT)
|
||||
|
||||
val result = cipher.toFailureCipherListView()
|
||||
|
||||
assertEquals(CipherListViewType.BankAccount, result.type)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateFromMiniResponse should update cipher with mini response data`() {
|
||||
val originalCipher = createMockCipher(
|
||||
|
||||
+34
-1
@@ -246,7 +246,40 @@ fun createMockDisplayItemForCipher(
|
||||
)
|
||||
}
|
||||
|
||||
CipherType.BANK_ACCOUNT -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherType.BANK_ACCOUNT -> {
|
||||
SearchState.DisplayItem(
|
||||
id = "mockId-$number",
|
||||
title = "mockName-$number",
|
||||
titleTestTag = "CipherNameLabel",
|
||||
subtitle = null,
|
||||
subtitleTestTag = "CipherSubTitleLabel",
|
||||
iconData = IconData.Local(BitwardenDrawable.ic_note),
|
||||
extraIconList = persistentListOf(
|
||||
IconData.Local(
|
||||
iconRes = BitwardenDrawable.ic_collections,
|
||||
contentDescription = BitwardenString.collections.asText(),
|
||||
testTag = "CipherInCollectionIcon",
|
||||
),
|
||||
),
|
||||
overflowOptions = persistentListOf(
|
||||
ListingItemOverflowAction.VaultAction.ViewClick(
|
||||
cipherId = "mockId-$number",
|
||||
cipherType = CipherType.BANK_ACCOUNT,
|
||||
requiresPasswordReprompt = true,
|
||||
),
|
||||
ListingItemOverflowAction.VaultAction.EditClick(
|
||||
cipherId = "mockId-$number",
|
||||
cipherType = CipherType.BANK_ACCOUNT,
|
||||
requiresPasswordReprompt = true,
|
||||
),
|
||||
),
|
||||
overflowTestTag = "CipherOptionsButton",
|
||||
totpCode = null,
|
||||
autofillSelectionOptions = persistentListOf(),
|
||||
shouldDisplayMasterPasswordReprompt = false,
|
||||
itemType = SearchState.DisplayItem.ItemType.Vault(type = cipherType),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+15
@@ -38,6 +38,21 @@ class CreateVaultItemTypeExtensionsTest {
|
||||
actualResult,
|
||||
)
|
||||
|
||||
CreateVaultItemType.BANK_ACCOUNT -> assertEquals(
|
||||
VaultItemCipherType.BANK_ACCOUNT,
|
||||
actualResult,
|
||||
)
|
||||
|
||||
CreateVaultItemType.DRIVERS_LICENSE -> assertEquals(
|
||||
VaultItemCipherType.DRIVERS_LICENSE,
|
||||
actualResult,
|
||||
)
|
||||
|
||||
CreateVaultItemType.PASSPORT -> assertEquals(
|
||||
VaultItemCipherType.PASSPORT,
|
||||
actualResult,
|
||||
)
|
||||
|
||||
CreateVaultItemType.FOLDER -> assertNull(actualResult)
|
||||
}
|
||||
}
|
||||
|
||||
+195
@@ -2403,6 +2403,201 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in add mode, SaveClick with a Drivers License item should emit ShowSnackbar without saving`() =
|
||||
runTest {
|
||||
mutableVaultDataFlow.value = DataState.Loaded(createVaultData())
|
||||
val driversLicenseState = createVaultAddItemState(
|
||||
vaultItemCipherType = VaultItemCipherType.DRIVERS_LICENSE,
|
||||
commonContentViewState = createCommonContentViewState(name = "mockName-1"),
|
||||
typeContentViewState =
|
||||
VaultAddEditState.ViewState.Content.ItemType.DriversLicense(),
|
||||
)
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
createSavedStateHandleWithState(
|
||||
state = driversLicenseState,
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
vaultItemCipherType = VaultItemCipherType.DRIVERS_LICENSE,
|
||||
),
|
||||
)
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(VaultAddEditAction.Common.SaveClick)
|
||||
assertEquals(
|
||||
VaultAddEditEvent.ShowSnackbar(
|
||||
message = BitwardenString.an_error_has_occurred.asText(),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
assertEquals(driversLicenseState, viewModel.stateFlow.value)
|
||||
coVerify(exactly = 0) {
|
||||
vaultRepository.createCipher(any())
|
||||
vaultRepository.createCipherInOrganization(any(), any())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in add mode, SaveClick with a Passport item should emit ShowSnackbar without saving`() =
|
||||
runTest {
|
||||
mutableVaultDataFlow.value = DataState.Loaded(createVaultData())
|
||||
val passportState = createVaultAddItemState(
|
||||
vaultItemCipherType = VaultItemCipherType.PASSPORT,
|
||||
commonContentViewState = createCommonContentViewState(name = "mockName-1"),
|
||||
typeContentViewState = VaultAddEditState.ViewState.Content.ItemType.Passport(),
|
||||
)
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
createSavedStateHandleWithState(
|
||||
state = passportState,
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
vaultItemCipherType = VaultItemCipherType.PASSPORT,
|
||||
),
|
||||
)
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(VaultAddEditAction.Common.SaveClick)
|
||||
assertEquals(
|
||||
VaultAddEditEvent.ShowSnackbar(
|
||||
message = BitwardenString.an_error_has_occurred.asText(),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
assertEquals(passportState, viewModel.stateFlow.value)
|
||||
coVerify(exactly = 0) {
|
||||
vaultRepository.createCipher(any())
|
||||
vaultRepository.createCipherInOrganization(any(), any())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in add mode, SaveClick with a Bank Account item should not short-circuit and should run validation`() =
|
||||
runTest {
|
||||
mutableVaultDataFlow.value = DataState.Loaded(createVaultData())
|
||||
val bankAccountState = createVaultAddItemState(
|
||||
vaultItemCipherType = VaultItemCipherType.BANK_ACCOUNT,
|
||||
commonContentViewState = createCommonContentViewState(name = ""),
|
||||
typeContentViewState =
|
||||
VaultAddEditState.ViewState.Content.ItemType.BankAccount(),
|
||||
)
|
||||
val expectedValidationDialogState = bankAccountState.copy(
|
||||
dialog = VaultAddEditState.DialogState.Generic(
|
||||
title = BitwardenString.an_error_has_occurred.asText(),
|
||||
message = BitwardenString.validation_field_required
|
||||
.asText(BitwardenString.name.asText()),
|
||||
),
|
||||
)
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
createSavedStateHandleWithState(
|
||||
state = bankAccountState,
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
vaultItemCipherType = VaultItemCipherType.BANK_ACCOUNT,
|
||||
),
|
||||
)
|
||||
|
||||
viewModel.stateEventFlow(backgroundScope) { stateFlow, eventFlow ->
|
||||
viewModel.trySendAction(VaultAddEditAction.Common.SaveClick)
|
||||
assertEquals(bankAccountState, stateFlow.awaitItem())
|
||||
assertEquals(expectedValidationDialogState, stateFlow.awaitItem())
|
||||
eventFlow.expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ItemType BankAccount should expose BANK_ACCOUNT itemTypeOption and be SDK supported`() {
|
||||
val itemType = VaultAddEditState.ViewState.Content.ItemType.BankAccount()
|
||||
assertEquals(
|
||||
VaultAddEditState.ItemTypeOption.BANK_ACCOUNT,
|
||||
itemType.itemTypeOption,
|
||||
)
|
||||
assertTrue(itemType.isSdkSupported)
|
||||
assertTrue(itemType.vaultLinkedFieldTypes.isEmpty())
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ItemType DriversLicense should expose DRIVERS_LICENSE itemTypeOption and not be SDK supported`() {
|
||||
val itemType = VaultAddEditState.ViewState.Content.ItemType.DriversLicense()
|
||||
assertEquals(
|
||||
VaultAddEditState.ItemTypeOption.DRIVERS_LICENSE,
|
||||
itemType.itemTypeOption,
|
||||
)
|
||||
assertFalse(itemType.isSdkSupported)
|
||||
assertTrue(itemType.vaultLinkedFieldTypes.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ItemType Passport should expose PASSPORT itemTypeOption and not be SDK supported`() {
|
||||
val itemType = VaultAddEditState.ViewState.Content.ItemType.Passport()
|
||||
assertEquals(
|
||||
VaultAddEditState.ItemTypeOption.PASSPORT,
|
||||
itemType.itemTypeOption,
|
||||
)
|
||||
assertFalse(itemType.isSdkSupported)
|
||||
assertTrue(itemType.vaultLinkedFieldTypes.isEmpty())
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `screenDisplayName should resolve new title strings for new vault item types in add mode`() {
|
||||
val baseAddState = createVaultAddItemState(vaultAddEditType = VaultAddEditType.AddItem)
|
||||
assertEquals(
|
||||
BitwardenString.new_bank_account.asText(),
|
||||
baseAddState.copy(cipherType = VaultItemCipherType.BANK_ACCOUNT).screenDisplayName,
|
||||
)
|
||||
assertEquals(
|
||||
BitwardenString.new_drivers_license.asText(),
|
||||
baseAddState.copy(cipherType = VaultItemCipherType.DRIVERS_LICENSE).screenDisplayName,
|
||||
)
|
||||
assertEquals(
|
||||
BitwardenString.new_passport.asText(),
|
||||
baseAddState.copy(cipherType = VaultItemCipherType.PASSPORT).screenDisplayName,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `screenDisplayName should resolve new title strings for new vault item types in edit mode`() {
|
||||
val baseEditState = createVaultAddItemState(
|
||||
vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID),
|
||||
)
|
||||
assertEquals(
|
||||
BitwardenString.edit_bank_account.asText(),
|
||||
baseEditState.copy(cipherType = VaultItemCipherType.BANK_ACCOUNT).screenDisplayName,
|
||||
)
|
||||
assertEquals(
|
||||
BitwardenString.edit_drivers_license.asText(),
|
||||
baseEditState.copy(cipherType = VaultItemCipherType.DRIVERS_LICENSE).screenDisplayName,
|
||||
)
|
||||
assertEquals(
|
||||
BitwardenString.edit_passport.asText(),
|
||||
baseEditState.copy(cipherType = VaultItemCipherType.PASSPORT).screenDisplayName,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `screenDisplayName should resolve new title strings for new vault item types in clone mode`() {
|
||||
val baseCloneState = createVaultAddItemState(
|
||||
vaultAddEditType = VaultAddEditType.CloneItem(DEFAULT_EDIT_ITEM_ID),
|
||||
)
|
||||
assertEquals(
|
||||
BitwardenString.new_bank_account.asText(),
|
||||
baseCloneState.copy(cipherType = VaultItemCipherType.BANK_ACCOUNT).screenDisplayName,
|
||||
)
|
||||
assertEquals(
|
||||
BitwardenString.new_drivers_license.asText(),
|
||||
baseCloneState.copy(cipherType = VaultItemCipherType.DRIVERS_LICENSE).screenDisplayName,
|
||||
)
|
||||
assertEquals(
|
||||
BitwardenString.new_passport.asText(),
|
||||
baseCloneState.copy(cipherType = VaultItemCipherType.PASSPORT).screenDisplayName,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ArchiveClick without Premium should show ArchiveRequiresPremium dialog`() = runTest {
|
||||
val cipherListView = createMockCipherListView(number = 1, isArchived = false)
|
||||
|
||||
+49
-2
@@ -16,6 +16,7 @@ import androidx.credentials.provider.PublicKeyCredentialEntry
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.data.manager.dispatcher.FakeDispatcherManager
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.manager.toast.ToastManager
|
||||
import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
@@ -71,6 +72,7 @@ import com.x8bit.bitwarden.data.credentials.model.createMockGetCredentialsReques
|
||||
import com.x8bit.bitwarden.data.credentials.model.createMockProviderGetPasswordCredentialRequest
|
||||
import com.x8bit.bitwarden.data.credentials.parser.RelyingPartyParser
|
||||
import com.x8bit.bitwarden.data.credentials.repository.PrivilegedAppRepository
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl
|
||||
@@ -300,6 +302,10 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
private val premiumStateManager: PremiumStateManager = mockk {
|
||||
every { isInAppUpgradeAvailable() } returns false
|
||||
}
|
||||
private val mutableNewItemTypesFlow = MutableStateFlow(false)
|
||||
private val featureFlagManager: FeatureFlagManager = mockk {
|
||||
every { getFeatureFlag(FlagKey.NewItemTypes) } answers { mutableNewItemTypesFlow.value }
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
@@ -1622,8 +1628,11 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
),
|
||||
dialogState = VaultItemListingState.DialogState.VaultItemTypeSelection(
|
||||
excludedOptions = persistentListOf(
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.FOLDER,
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.BANK_ACCOUNT,
|
||||
CreateVaultItemType.DRIVERS_LICENSE,
|
||||
CreateVaultItemType.PASSPORT,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1646,8 +1655,11 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
),
|
||||
dialogState = VaultItemListingState.DialogState.VaultItemTypeSelection(
|
||||
excludedOptions = persistentListOf(
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.FOLDER,
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.BANK_ACCOUNT,
|
||||
CreateVaultItemType.DRIVERS_LICENSE,
|
||||
CreateVaultItemType.PASSPORT,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1686,6 +1698,9 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
CreateVaultItemType.CARD,
|
||||
CreateVaultItemType.FOLDER,
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.BANK_ACCOUNT,
|
||||
CreateVaultItemType.DRIVERS_LICENSE,
|
||||
CreateVaultItemType.PASSPORT,
|
||||
),
|
||||
),
|
||||
).copy(restrictItemTypesPolicyOrgIds = persistentListOf("Test Organization")),
|
||||
@@ -1724,6 +1739,9 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
CreateVaultItemType.CARD,
|
||||
CreateVaultItemType.FOLDER,
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.BANK_ACCOUNT,
|
||||
CreateVaultItemType.DRIVERS_LICENSE,
|
||||
CreateVaultItemType.PASSPORT,
|
||||
),
|
||||
),
|
||||
).copy(restrictItemTypesPolicyOrgIds = persistentListOf("Test Organization")),
|
||||
@@ -1731,6 +1749,34 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `AddVaultItemClick should not exclude bank account, drivers license, or passport when NewItemTypes flag is enabled`() {
|
||||
mutableNewItemTypesFlow.value = true
|
||||
val viewModel = createVaultItemListingViewModel(
|
||||
savedStateHandle = createSavedStateHandleWithVaultItemListingType(
|
||||
vaultItemListingType = VaultItemListingType.Folder(folderId = "id"),
|
||||
),
|
||||
)
|
||||
|
||||
viewModel.trySendAction(VaultItemListingsAction.AddVaultItemClick)
|
||||
|
||||
assertEquals(
|
||||
createVaultItemListingState(
|
||||
itemListingType = VaultItemListingState.ItemListingType.Vault.Folder(
|
||||
folderId = "id",
|
||||
),
|
||||
dialogState = VaultItemListingState.DialogState.VaultItemTypeSelection(
|
||||
excludedOptions = persistentListOf(
|
||||
CreateVaultItemType.FOLDER,
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AddVaultItemClick for vault item should emit NavigateToAddVaultItem`() = runTest {
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
@@ -6192,6 +6238,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
toastManager = toastManager,
|
||||
premiumStateManager = premiumStateManager,
|
||||
relyingPartyParser = relyingPartyParser,
|
||||
featureFlagManager = featureFlagManager,
|
||||
)
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
|
||||
+43
-1
@@ -277,7 +277,49 @@ fun createMockDisplayItemForCipher(
|
||||
)
|
||||
}
|
||||
|
||||
CipherType.BANK_ACCOUNT -> TODO("PM-32810: Add Bank Account Type")
|
||||
CipherType.BANK_ACCOUNT -> {
|
||||
VaultItemListingState.DisplayItem(
|
||||
id = "mockId-$number",
|
||||
title = "mockName-$number".asText(),
|
||||
titleTestTag = "CipherNameLabel",
|
||||
secondSubtitle = secondSubtitle,
|
||||
secondSubtitleTestTag = secondSubtitleTestTag,
|
||||
subtitle = subtitle,
|
||||
subtitleTestTag = "CipherSubTitleLabel",
|
||||
iconData = IconData.Local(BitwardenDrawable.ic_note),
|
||||
extraIconList = persistentListOf(
|
||||
IconData.Local(
|
||||
iconRes = BitwardenDrawable.ic_collections,
|
||||
contentDescription = BitwardenString.collections.asText(),
|
||||
testTag = "CipherInCollectionIcon",
|
||||
),
|
||||
IconData.Local(
|
||||
iconRes = BitwardenDrawable.ic_paperclip,
|
||||
contentDescription = BitwardenString.attachments.asText(),
|
||||
testTag = "CipherWithAttachmentsIcon",
|
||||
),
|
||||
),
|
||||
overflowOptions = listOf(
|
||||
ListingItemOverflowAction.VaultAction.ViewClick(
|
||||
cipherId = "mockId-$number",
|
||||
cipherType = cipherType,
|
||||
requiresPasswordReprompt = requiresPasswordReprompt,
|
||||
),
|
||||
ListingItemOverflowAction.VaultAction.EditClick(
|
||||
cipherId = "mockId-$number",
|
||||
cipherType = cipherType,
|
||||
requiresPasswordReprompt = requiresPasswordReprompt,
|
||||
),
|
||||
ListingItemOverflowAction.VaultAction.ArchiveClick(cipherId = "mockId-$number"),
|
||||
),
|
||||
optionsTestTag = "CipherOptionsButton",
|
||||
isAutofill = false,
|
||||
isCredentialCreation = false,
|
||||
shouldShowMasterPasswordReprompt = false,
|
||||
iconTestTag = "BankAccountCipherIcon",
|
||||
itemType = VaultItemListingState.DisplayItem.ItemType.Vault(type = cipherType),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+30
-1
@@ -226,10 +226,12 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
coEvery { unregister() } returns UnregisterExportResult.Success
|
||||
}
|
||||
private val mutableCxpExportFeatureFlagFlow = MutableStateFlow(false)
|
||||
private val mutableNewItemTypesFlagFlow = MutableStateFlow(false)
|
||||
private val featureFlagManager: FeatureFlagManager = mockk {
|
||||
every {
|
||||
getFeatureFlagFlow(FlagKey.CredentialExchangeProtocolExport)
|
||||
} returns mutableCxpExportFeatureFlagFlow
|
||||
every { getFeatureFlag(FlagKey.NewItemTypes) } answers { mutableNewItemTypesFlagFlow.value }
|
||||
}
|
||||
|
||||
private val mutablePremiumUpgradeBannerEligibleFlow = MutableStateFlow(false)
|
||||
@@ -3537,7 +3539,12 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
viewModel.trySendAction(VaultAction.SelectAddItemType)
|
||||
val expectedState = DEFAULT_STATE.copy(
|
||||
dialog = VaultState.DialogState.SelectVaultAddItemType(
|
||||
excludedOptions = persistentListOf(CreateVaultItemType.SSH_KEY),
|
||||
excludedOptions = persistentListOf(
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.BANK_ACCOUNT,
|
||||
CreateVaultItemType.DRIVERS_LICENSE,
|
||||
CreateVaultItemType.PASSPORT,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
@@ -3569,6 +3576,9 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
excludedOptions = persistentListOf(
|
||||
CreateVaultItemType.SSH_KEY,
|
||||
CreateVaultItemType.CARD,
|
||||
CreateVaultItemType.BANK_ACCOUNT,
|
||||
CreateVaultItemType.DRIVERS_LICENSE,
|
||||
CreateVaultItemType.PASSPORT,
|
||||
),
|
||||
),
|
||||
restrictItemTypesPolicyOrgIds = listOf("Test Organization"),
|
||||
@@ -3579,6 +3589,25 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `SelectAddItemType action should not exclude bank account, drivers license, or passport when NewItemTypes flag is enabled`() {
|
||||
mutableNewItemTypesFlagFlow.value = true
|
||||
val viewModel = createViewModel()
|
||||
|
||||
viewModel.trySendAction(VaultAction.SelectAddItemType)
|
||||
|
||||
val expectedState = DEFAULT_STATE.copy(
|
||||
dialog = VaultState.DialogState.SelectVaultAddItemType(
|
||||
excludedOptions = persistentListOf(CreateVaultItemType.SSH_KEY),
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
expectedState,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `InternetConnectionErrorReceived should show network error if no internet connection`() =
|
||||
runTest {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.x8bit.bitwarden.ui.vault.model
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultBankAccountTypeTest {
|
||||
|
||||
@Test
|
||||
fun `each entry should expose the expected server value`() {
|
||||
val expected = listOf(
|
||||
VaultBankAccountType.SELECT to "select",
|
||||
VaultBankAccountType.CHECKING to "checking",
|
||||
VaultBankAccountType.SAVINGS to "savings",
|
||||
VaultBankAccountType.CERTIFICATE_OF_DEPOSIT to "certificateOfDeposit",
|
||||
VaultBankAccountType.LINE_OF_CREDIT to "lineOfCredit",
|
||||
VaultBankAccountType.INVESTMENT_BROKERAGE to "investmentBrokerage",
|
||||
VaultBankAccountType.MONEY_MARKET to "moneyMarket",
|
||||
VaultBankAccountType.OTHER to "other",
|
||||
)
|
||||
|
||||
val actual = VaultBankAccountType.entries.map { it to it.value }
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse should return matching entry for exact value`() {
|
||||
VaultBankAccountType.entries.forEach { entry ->
|
||||
assertEquals(entry, VaultBankAccountType.parse(entry.value))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse should return matching entry regardless of casing`() {
|
||||
assertEquals(
|
||||
VaultBankAccountType.CHECKING,
|
||||
VaultBankAccountType.parse("CHECKING"),
|
||||
)
|
||||
assertEquals(
|
||||
VaultBankAccountType.CERTIFICATE_OF_DEPOSIT,
|
||||
VaultBankAccountType.parse("CertificateOfDeposit"),
|
||||
)
|
||||
assertEquals(
|
||||
VaultBankAccountType.MONEY_MARKET,
|
||||
VaultBankAccountType.parse("MONEYmarket"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse should fall back to OTHER for null input`() {
|
||||
assertEquals(VaultBankAccountType.OTHER, VaultBankAccountType.parse(null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse should fall back to OTHER for an unrecognized value`() {
|
||||
assertEquals(
|
||||
VaultBankAccountType.OTHER,
|
||||
VaultBankAccountType.parse("not-a-real-account-type"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse should fall back to OTHER for a blank value`() {
|
||||
assertEquals(VaultBankAccountType.OTHER, VaultBankAccountType.parse(""))
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ sealed class FlagKey<out T : Any> {
|
||||
V2EncryptionKeyConnector,
|
||||
V2EncryptionPassword,
|
||||
V2EncryptionTde,
|
||||
NewItemTypes,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -161,6 +162,15 @@ sealed class FlagKey<out T : Any> {
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key for new vault item types
|
||||
* (Bank Account, Driver's License, Passport).
|
||||
*/
|
||||
data object NewItemTypes : FlagKey<Boolean>() {
|
||||
override val keyName: String = "pm-32009-new-item-types"
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
//region Dummy keys for testing
|
||||
/**
|
||||
* Data object holding the key for a [Boolean] flag to be used in tests.
|
||||
|
||||
@@ -56,6 +56,10 @@ class FlagKeyTest {
|
||||
FlagKey.V2EncryptionTde.keyName,
|
||||
"pm-27279-v2-registration-tde-jit",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.NewItemTypes.keyName,
|
||||
"pm-32009-new-item-types",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -74,6 +78,7 @@ class FlagKeyTest {
|
||||
FlagKey.V2EncryptionKeyConnector,
|
||||
FlagKey.V2EncryptionPassword,
|
||||
FlagKey.V2EncryptionTde,
|
||||
FlagKey.NewItemTypes,
|
||||
).all {
|
||||
!it.defaultValue
|
||||
},
|
||||
|
||||
@@ -56,6 +56,15 @@ data class CipherJsonRequest(
|
||||
@SerialName("sshKey")
|
||||
val sshKey: SyncResponseJson.Cipher.SshKey?,
|
||||
|
||||
@SerialName("bankAccount")
|
||||
val bankAccount: SyncResponseJson.Cipher.BankAccount?,
|
||||
|
||||
@SerialName("driversLicense")
|
||||
val driversLicense: SyncResponseJson.Cipher.DriversLicense?,
|
||||
|
||||
@SerialName("passport")
|
||||
val passport: SyncResponseJson.Cipher.Passport?,
|
||||
|
||||
@SerialName("folderId")
|
||||
val folderId: String?,
|
||||
|
||||
|
||||
@@ -39,6 +39,24 @@ enum class CipherTypeJson {
|
||||
*/
|
||||
@SerialName("5")
|
||||
SSH_KEY,
|
||||
|
||||
/**
|
||||
* A bank account.
|
||||
*/
|
||||
@SerialName("6")
|
||||
BANK_ACCOUNT,
|
||||
|
||||
/**
|
||||
* A driver's license.
|
||||
*/
|
||||
@SerialName("7")
|
||||
DRIVERS_LICENSE,
|
||||
|
||||
/**
|
||||
* A passport.
|
||||
*/
|
||||
@SerialName("8")
|
||||
PASSPORT,
|
||||
}
|
||||
|
||||
@Keep
|
||||
|
||||
@@ -177,6 +177,204 @@ enum class LinkedIdTypeJson(val value: UInt) {
|
||||
@SerialName("418")
|
||||
IDENTITY_FULL_NAME(value = 418U),
|
||||
// endregion IDENTITY
|
||||
|
||||
// region BANK_ACCOUNT
|
||||
/**
|
||||
* The field is linked to the bank account's bank name.
|
||||
*/
|
||||
@SerialName("600")
|
||||
BANK_ACCOUNT_BANK_NAME(value = 600U),
|
||||
|
||||
/**
|
||||
* The field is linked to the bank account's name on account.
|
||||
*/
|
||||
@SerialName("601")
|
||||
BANK_ACCOUNT_NAME_ON_ACCOUNT(value = 601U),
|
||||
|
||||
/**
|
||||
* The field is linked to the bank account's account type.
|
||||
*/
|
||||
@SerialName("602")
|
||||
BANK_ACCOUNT_ACCOUNT_TYPE(value = 602U),
|
||||
|
||||
/**
|
||||
* The field is linked to the bank account's account number.
|
||||
*/
|
||||
@SerialName("603")
|
||||
BANK_ACCOUNT_ACCOUNT_NUMBER(value = 603U),
|
||||
|
||||
/**
|
||||
* The field is linked to the bank account's routing number.
|
||||
*/
|
||||
@SerialName("604")
|
||||
BANK_ACCOUNT_ROUTING_NUMBER(value = 604U),
|
||||
|
||||
/**
|
||||
* The field is linked to the bank account's branch number.
|
||||
*/
|
||||
@SerialName("605")
|
||||
BANK_ACCOUNT_BRANCH_NUMBER(value = 605U),
|
||||
|
||||
/**
|
||||
* The field is linked to the bank account's PIN.
|
||||
*/
|
||||
@SerialName("606")
|
||||
BANK_ACCOUNT_PIN(value = 606U),
|
||||
|
||||
/**
|
||||
* The field is linked to the bank account's SWIFT code.
|
||||
*/
|
||||
@SerialName("607")
|
||||
BANK_ACCOUNT_SWIFT_CODE(value = 607U),
|
||||
|
||||
/**
|
||||
* The field is linked to the bank account's IBAN.
|
||||
*/
|
||||
@SerialName("608")
|
||||
BANK_ACCOUNT_IBAN(value = 608U),
|
||||
|
||||
/**
|
||||
* The field is linked to the bank account's contact phone.
|
||||
*/
|
||||
@SerialName("609")
|
||||
BANK_ACCOUNT_BANK_CONTACT_PHONE(value = 609U),
|
||||
// endregion BANK_ACCOUNT
|
||||
|
||||
// region DRIVERS_LICENSE
|
||||
/**
|
||||
* The field is linked to the driver's license first name.
|
||||
*/
|
||||
@SerialName("700")
|
||||
DRIVERS_LICENSE_FIRST_NAME(value = 700U),
|
||||
|
||||
/**
|
||||
* The field is linked to the driver's license middle name.
|
||||
*/
|
||||
@SerialName("701")
|
||||
DRIVERS_LICENSE_MIDDLE_NAME(value = 701U),
|
||||
|
||||
/**
|
||||
* The field is linked to the driver's license last name.
|
||||
*/
|
||||
@SerialName("702")
|
||||
DRIVERS_LICENSE_LAST_NAME(value = 702U),
|
||||
|
||||
/**
|
||||
* The field is linked to the driver's license number.
|
||||
*/
|
||||
@SerialName("703")
|
||||
DRIVERS_LICENSE_LICENSE_NUMBER(value = 703U),
|
||||
|
||||
/**
|
||||
* The field is linked to the driver's license issuing country.
|
||||
*/
|
||||
@SerialName("704")
|
||||
DRIVERS_LICENSE_ISSUING_COUNTRY(value = 704U),
|
||||
|
||||
/**
|
||||
* The field is linked to the driver's license issuing state.
|
||||
*/
|
||||
@SerialName("705")
|
||||
DRIVERS_LICENSE_ISSUING_STATE(value = 705U),
|
||||
|
||||
/**
|
||||
* The field is linked to the driver's license expiration month.
|
||||
*/
|
||||
@SerialName("706")
|
||||
DRIVERS_LICENSE_EXPIRATION_MONTH(value = 706U),
|
||||
|
||||
/**
|
||||
* The field is linked to the driver's license expiration year.
|
||||
*/
|
||||
@SerialName("707")
|
||||
DRIVERS_LICENSE_EXPIRATION_YEAR(value = 707U),
|
||||
|
||||
/**
|
||||
* The field is linked to the driver's license class.
|
||||
*/
|
||||
@SerialName("708")
|
||||
DRIVERS_LICENSE_LICENSE_CLASS(value = 708U),
|
||||
// endregion DRIVERS_LICENSE
|
||||
|
||||
// region PASSPORT
|
||||
/**
|
||||
* The field is linked to the passport surname.
|
||||
*/
|
||||
@SerialName("800")
|
||||
PASSPORT_SURNAME(value = 800U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport given name.
|
||||
*/
|
||||
@SerialName("801")
|
||||
PASSPORT_GIVEN_NAME(value = 801U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport date of birth month.
|
||||
*/
|
||||
@SerialName("802")
|
||||
PASSPORT_DOB_MONTH(value = 802U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport date of birth year.
|
||||
*/
|
||||
@SerialName("803")
|
||||
PASSPORT_DOB_YEAR(value = 803U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport nationality.
|
||||
*/
|
||||
@SerialName("804")
|
||||
PASSPORT_NATIONALITY(value = 804U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport number.
|
||||
*/
|
||||
@SerialName("805")
|
||||
PASSPORT_PASSPORT_NUMBER(value = 805U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport type.
|
||||
*/
|
||||
@SerialName("806")
|
||||
PASSPORT_PASSPORT_TYPE(value = 806U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport issuing country.
|
||||
*/
|
||||
@SerialName("807")
|
||||
PASSPORT_ISSUING_COUNTRY(value = 807U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport issuing authority.
|
||||
*/
|
||||
@SerialName("808")
|
||||
PASSPORT_ISSUING_AUTHORITY(value = 808U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport issue month.
|
||||
*/
|
||||
@SerialName("809")
|
||||
PASSPORT_ISSUE_MONTH(value = 809U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport issue year.
|
||||
*/
|
||||
@SerialName("810")
|
||||
PASSPORT_ISSUE_YEAR(value = 810U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport expiration month.
|
||||
*/
|
||||
@SerialName("811")
|
||||
PASSPORT_EXPIRATION_MONTH(value = 811U),
|
||||
|
||||
/**
|
||||
* The field is linked to the passport expiration year.
|
||||
*/
|
||||
@SerialName("812")
|
||||
PASSPORT_EXPIRATION_YEAR(value = 812U),
|
||||
// endregion PASSPORT
|
||||
}
|
||||
|
||||
@Keep
|
||||
|
||||
@@ -516,6 +516,15 @@ data class SyncResponseJson(
|
||||
@SerialName("sshKey")
|
||||
val sshKey: SshKey?,
|
||||
|
||||
@SerialName("bankAccount")
|
||||
val bankAccount: BankAccount?,
|
||||
|
||||
@SerialName("driversLicense")
|
||||
val driversLicense: DriversLicense?,
|
||||
|
||||
@SerialName("passport")
|
||||
val passport: Passport?,
|
||||
|
||||
@SerialName("collectionIds")
|
||||
val collectionIds: List<String>?,
|
||||
|
||||
@@ -788,6 +797,171 @@ data class SyncResponseJson(
|
||||
val keyFingerprint: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents a bank account in the vault response.
|
||||
*
|
||||
* @property bankName The name of the bank (nullable).
|
||||
* @property nameOnAccount The name on the account (nullable).
|
||||
* @property accountType The type of bank account (nullable).
|
||||
* @property accountNumber The account number (nullable).
|
||||
* @property routingNumber The routing/transit number (nullable).
|
||||
* @property branchNumber The branch/institution number (nullable).
|
||||
* @property pin The PIN (nullable).
|
||||
* @property swiftCode The SWIFT code (nullable).
|
||||
* @property iban The IBAN (nullable).
|
||||
* @property bankContactPhone The bank contact phone number (nullable).
|
||||
*/
|
||||
@Serializable
|
||||
data class BankAccount(
|
||||
@SerialName("bankName")
|
||||
val bankName: String?,
|
||||
|
||||
@SerialName("nameOnAccount")
|
||||
val nameOnAccount: String?,
|
||||
|
||||
@SerialName("accountType")
|
||||
val accountType: String?,
|
||||
|
||||
@SerialName("accountNumber")
|
||||
val accountNumber: String?,
|
||||
|
||||
@SerialName("routingNumber")
|
||||
val routingNumber: String?,
|
||||
|
||||
@SerialName("branchNumber")
|
||||
val branchNumber: String?,
|
||||
|
||||
@SerialName("pin")
|
||||
val pin: String?,
|
||||
|
||||
@SerialName("swiftCode")
|
||||
val swiftCode: String?,
|
||||
|
||||
@SerialName("iban")
|
||||
val iban: String?,
|
||||
|
||||
@SerialName("bankContactPhone")
|
||||
val bankContactPhone: String?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents a driver's license in the vault response.
|
||||
*
|
||||
* @property firstName The first name (nullable).
|
||||
* @property middleName The middle name (nullable).
|
||||
* @property lastName The last name (nullable).
|
||||
* @property licenseNumber The license number (nullable).
|
||||
* @property issuingCountry The issuing country (nullable).
|
||||
* @property issuingState The issuing state/province (nullable).
|
||||
* @property expirationMonth The expiration month (nullable).
|
||||
* @property expirationDay The expiration day of month (nullable).
|
||||
* @property expirationYear The expiration year (nullable).
|
||||
* @property licenseClass The license class (nullable).
|
||||
*/
|
||||
@Serializable
|
||||
data class DriversLicense(
|
||||
@SerialName("firstName")
|
||||
val firstName: String?,
|
||||
|
||||
@SerialName("middleName")
|
||||
val middleName: String?,
|
||||
|
||||
@SerialName("lastName")
|
||||
val lastName: String?,
|
||||
|
||||
@SerialName("licenseNumber")
|
||||
val licenseNumber: String?,
|
||||
|
||||
@SerialName("issuingCountry")
|
||||
val issuingCountry: String?,
|
||||
|
||||
@SerialName("issuingState")
|
||||
val issuingState: String?,
|
||||
|
||||
@SerialName("expirationMonth")
|
||||
val expirationMonth: String?,
|
||||
|
||||
@SerialName("expirationDay")
|
||||
val expirationDay: String?,
|
||||
|
||||
@SerialName("expirationYear")
|
||||
val expirationYear: String?,
|
||||
|
||||
@SerialName("licenseClass")
|
||||
val licenseClass: String?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents a passport in the vault response.
|
||||
*
|
||||
* @property surname The surname (nullable).
|
||||
* @property givenName The given name (nullable).
|
||||
* @property dobMonth The month of birth (nullable).
|
||||
* @property dobDay The day of month of birth (nullable).
|
||||
* @property dobYear The year of birth (nullable).
|
||||
* @property nationality The nationality (nullable).
|
||||
* @property passportNumber The passport number (nullable).
|
||||
* @property passportType The passport type (nullable).
|
||||
* @property issuingCountry The issuing country (nullable).
|
||||
* @property issuingAuthority The issuing authority/office (nullable).
|
||||
* @property issueMonth The issue month (nullable).
|
||||
* @property issueDay The issue day of month (nullable).
|
||||
* @property issueYear The issue year (nullable).
|
||||
* @property expirationMonth The expiration month (nullable).
|
||||
* @property expirationDay The expiration day of month (nullable).
|
||||
* @property expirationYear The expiration year (nullable).
|
||||
*/
|
||||
@Serializable
|
||||
data class Passport(
|
||||
@SerialName("surname")
|
||||
val surname: String?,
|
||||
|
||||
@SerialName("givenName")
|
||||
val givenName: String?,
|
||||
|
||||
@SerialName("dobMonth")
|
||||
val dobMonth: String?,
|
||||
|
||||
@SerialName("dobDay")
|
||||
val dobDay: String?,
|
||||
|
||||
@SerialName("dobYear")
|
||||
val dobYear: String?,
|
||||
|
||||
@SerialName("nationality")
|
||||
val nationality: String?,
|
||||
|
||||
@SerialName("passportNumber")
|
||||
val passportNumber: String?,
|
||||
|
||||
@SerialName("passportType")
|
||||
val passportType: String?,
|
||||
|
||||
@SerialName("issuingCountry")
|
||||
val issuingCountry: String?,
|
||||
|
||||
@SerialName("issuingAuthority")
|
||||
val issuingAuthority: String?,
|
||||
|
||||
@SerialName("issueMonth")
|
||||
val issueMonth: String?,
|
||||
|
||||
@SerialName("issueDay")
|
||||
val issueDay: String?,
|
||||
|
||||
@SerialName("issueYear")
|
||||
val issueYear: String?,
|
||||
|
||||
@SerialName("expirationMonth")
|
||||
val expirationMonth: String?,
|
||||
|
||||
@SerialName("expirationDay")
|
||||
val expirationDay: String?,
|
||||
|
||||
@SerialName("expirationYear")
|
||||
val expirationYear: String?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents password history in the vault response.
|
||||
*
|
||||
|
||||
@@ -629,6 +629,18 @@ private const val CREATE_ATTACHMENT_SUCCESS_JSON = """
|
||||
"privateKey": "mockPrivateKey-1",
|
||||
"keyFingerprint": "mockKeyFingerprint-1"
|
||||
},
|
||||
"bankAccount": {
|
||||
"bankName": "mockBankName-1",
|
||||
"nameOnAccount": "mockNameOnAccount-1",
|
||||
"accountType": "mockAccountType-1",
|
||||
"accountNumber": "mockAccountNumber-1",
|
||||
"routingNumber": "mockRoutingNumber-1",
|
||||
"branchNumber": "mockBranchNumber-1",
|
||||
"pin": "mockPin-1",
|
||||
"swiftCode": "mockSwiftCode-1",
|
||||
"iban": "mockIban-1",
|
||||
"bankContactPhone": "mockBankContactPhone-1"
|
||||
},
|
||||
"encryptedFor": "mockEncryptedFor-1",
|
||||
"archivedDate": "2023-10-27T12:00:00.00Z"
|
||||
}
|
||||
@@ -758,6 +770,18 @@ private const val CREATE_RESTORE_UPDATE_CIPHER_SUCCESS_JSON = """
|
||||
"privateKey": "mockPrivateKey-1",
|
||||
"keyFingerprint": "mockKeyFingerprint-1"
|
||||
},
|
||||
"bankAccount": {
|
||||
"bankName": "mockBankName-1",
|
||||
"nameOnAccount": "mockNameOnAccount-1",
|
||||
"accountType": "mockAccountType-1",
|
||||
"accountNumber": "mockAccountNumber-1",
|
||||
"routingNumber": "mockRoutingNumber-1",
|
||||
"branchNumber": "mockBranchNumber-1",
|
||||
"pin": "mockPin-1",
|
||||
"swiftCode": "mockSwiftCode-1",
|
||||
"iban": "mockIban-1",
|
||||
"bankContactPhone": "mockBankContactPhone-1"
|
||||
},
|
||||
"encryptedFor": "mockEncryptedFor-1",
|
||||
"archivedDate": "2023-10-27T12:00:00.00Z"
|
||||
}
|
||||
|
||||
@@ -335,6 +335,18 @@ private const val SYNC_SUCCESS_JSON = """
|
||||
"privateKey": "mockPrivateKey-1",
|
||||
"keyFingerprint": "mockKeyFingerprint-1"
|
||||
},
|
||||
"bankAccount": {
|
||||
"bankName": "mockBankName-1",
|
||||
"nameOnAccount": "mockNameOnAccount-1",
|
||||
"accountType": "mockAccountType-1",
|
||||
"accountNumber": "mockAccountNumber-1",
|
||||
"routingNumber": "mockRoutingNumber-1",
|
||||
"branchNumber": "mockBranchNumber-1",
|
||||
"pin": "mockPin-1",
|
||||
"swiftCode": "mockSwiftCode-1",
|
||||
"iban": "mockIban-1",
|
||||
"bankContactPhone": "mockBankContactPhone-1"
|
||||
},
|
||||
"encryptedFor": "mockEncryptedFor-1",
|
||||
"archivedDate": "2023-10-27T12:00:00.00Z"
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ fun createMockCipherJsonRequest(
|
||||
login: SyncResponseJson.Cipher.Login? = createMockLogin(number = number),
|
||||
card: SyncResponseJson.Cipher.Card? = createMockCard(number = number),
|
||||
sshKey: SyncResponseJson.Cipher.SshKey? = createMockSshKey(number = number),
|
||||
bankAccount: SyncResponseJson.Cipher.BankAccount? = createMockBankAccount(number = number),
|
||||
driversLicense: SyncResponseJson.Cipher.DriversLicense? = null,
|
||||
passport: SyncResponseJson.Cipher.Passport? = null,
|
||||
identity: SyncResponseJson.Cipher.Identity? = createMockIdentity(number = number),
|
||||
secureNote: SyncResponseJson.Cipher.SecureNote? = createMockSecureNote(),
|
||||
fields: List<SyncResponseJson.Cipher.Field>? = listOf(createMockField(number = number)),
|
||||
@@ -42,6 +45,9 @@ fun createMockCipherJsonRequest(
|
||||
login = login,
|
||||
card = card,
|
||||
sshKey = sshKey,
|
||||
bankAccount = bankAccount,
|
||||
driversLicense = driversLicense,
|
||||
passport = passport,
|
||||
identity = identity,
|
||||
secureNote = secureNote,
|
||||
fields = fields,
|
||||
|
||||
+105
@@ -32,6 +32,9 @@ fun createMockCipher(
|
||||
card: SyncResponseJson.Cipher.Card? = createMockCard(number = number),
|
||||
identity: SyncResponseJson.Cipher.Identity? = createMockIdentity(number = number),
|
||||
sshKey: SyncResponseJson.Cipher.SshKey? = createMockSshKey(number = number),
|
||||
bankAccount: SyncResponseJson.Cipher.BankAccount? = createMockBankAccount(number = number),
|
||||
driversLicense: SyncResponseJson.Cipher.DriversLicense? = null,
|
||||
passport: SyncResponseJson.Cipher.Passport? = null,
|
||||
secureNote: SyncResponseJson.Cipher.SecureNote? = createMockSecureNote(),
|
||||
fields: List<SyncResponseJson.Cipher.Field>? = listOf(createMockField(number = number)),
|
||||
isFavorite: Boolean = false,
|
||||
@@ -59,6 +62,9 @@ fun createMockCipher(
|
||||
identity = identity,
|
||||
secureNote = secureNote,
|
||||
sshKey = sshKey,
|
||||
bankAccount = bankAccount,
|
||||
driversLicense = driversLicense,
|
||||
passport = passport,
|
||||
creationDate = creationDate,
|
||||
revisionDate = revisionDate,
|
||||
deletedDate = deletedDate,
|
||||
@@ -257,6 +263,105 @@ fun createMockSshKey(
|
||||
keyFingerprint = keyFingerprint,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.BankAccount] with a given [number].
|
||||
*/
|
||||
fun createMockBankAccount(
|
||||
number: Int,
|
||||
bankName: String? = "mockBankName-$number",
|
||||
nameOnAccount: String? = "mockNameOnAccount-$number",
|
||||
accountType: String? = "mockAccountType-$number",
|
||||
accountNumber: String? = "mockAccountNumber-$number",
|
||||
routingNumber: String? = "mockRoutingNumber-$number",
|
||||
branchNumber: String? = "mockBranchNumber-$number",
|
||||
pin: String? = "mockPin-$number",
|
||||
swiftCode: String? = "mockSwiftCode-$number",
|
||||
iban: String? = "mockIban-$number",
|
||||
bankContactPhone: String? = "mockBankContactPhone-$number",
|
||||
): SyncResponseJson.Cipher.BankAccount =
|
||||
SyncResponseJson.Cipher.BankAccount(
|
||||
bankName = bankName,
|
||||
nameOnAccount = nameOnAccount,
|
||||
accountType = accountType,
|
||||
accountNumber = accountNumber,
|
||||
routingNumber = routingNumber,
|
||||
branchNumber = branchNumber,
|
||||
pin = pin,
|
||||
swiftCode = swiftCode,
|
||||
iban = iban,
|
||||
bankContactPhone = bankContactPhone,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.DriversLicense] with a given [number].
|
||||
*/
|
||||
fun createMockDriversLicense(
|
||||
number: Int,
|
||||
firstName: String? = "mockFirstName-$number",
|
||||
middleName: String? = "mockMiddleName-$number",
|
||||
lastName: String? = "mockLastName-$number",
|
||||
licenseNumber: String? = "mockLicenseNumber-$number",
|
||||
issuingCountry: String? = "mockIssuingCountry-$number",
|
||||
issuingState: String? = "mockIssuingState-$number",
|
||||
expirationMonth: String? = "mockExpirationMonth-$number",
|
||||
expirationDay: String? = "mockExpirationDay-$number",
|
||||
expirationYear: String? = "mockExpirationYear-$number",
|
||||
licenseClass: String? = "mockLicenseClass-$number",
|
||||
): SyncResponseJson.Cipher.DriversLicense =
|
||||
SyncResponseJson.Cipher.DriversLicense(
|
||||
firstName = firstName,
|
||||
middleName = middleName,
|
||||
lastName = lastName,
|
||||
licenseNumber = licenseNumber,
|
||||
issuingCountry = issuingCountry,
|
||||
issuingState = issuingState,
|
||||
expirationMonth = expirationMonth,
|
||||
expirationDay = expirationDay,
|
||||
expirationYear = expirationYear,
|
||||
licenseClass = licenseClass,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.Passport] with a given [number].
|
||||
*/
|
||||
fun createMockPassport(
|
||||
number: Int,
|
||||
surname: String? = "mockSurname-$number",
|
||||
givenName: String? = "mockGivenName-$number",
|
||||
dobMonth: String? = "mockDobMonth-$number",
|
||||
dobDay: String? = "mockDobDay-$number",
|
||||
dobYear: String? = "mockDobYear-$number",
|
||||
nationality: String? = "mockNationality-$number",
|
||||
passportNumber: String? = "mockPassportNumber-$number",
|
||||
passportType: String? = "mockPassportType-$number",
|
||||
issuingCountry: String? = "mockIssuingCountry-$number",
|
||||
issuingAuthority: String? = "mockIssuingAuthority-$number",
|
||||
issueMonth: String? = "mockIssueMonth-$number",
|
||||
issueDay: String? = "mockIssueDay-$number",
|
||||
issueYear: String? = "mockIssueYear-$number",
|
||||
expirationMonth: String? = "mockExpirationMonth-$number",
|
||||
expirationDay: String? = "mockExpirationDay-$number",
|
||||
expirationYear: String? = "mockExpirationYear-$number",
|
||||
): SyncResponseJson.Cipher.Passport =
|
||||
SyncResponseJson.Cipher.Passport(
|
||||
surname = surname,
|
||||
givenName = givenName,
|
||||
dobMonth = dobMonth,
|
||||
dobDay = dobDay,
|
||||
dobYear = dobYear,
|
||||
nationality = nationality,
|
||||
passportNumber = passportNumber,
|
||||
passportType = passportType,
|
||||
issuingCountry = issuingCountry,
|
||||
issuingAuthority = issuingAuthority,
|
||||
issueMonth = issueMonth,
|
||||
issueDay = issueDay,
|
||||
issueYear = issueYear,
|
||||
expirationMonth = expirationMonth,
|
||||
expirationDay = expirationDay,
|
||||
expirationYear = expirationYear,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.Fido2Credential] with a given [number].
|
||||
*/
|
||||
|
||||
@@ -37,6 +37,7 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
FlagKey.V2EncryptionKeyConnector,
|
||||
FlagKey.V2EncryptionPassword,
|
||||
FlagKey.V2EncryptionTde,
|
||||
FlagKey.NewItemTypes,
|
||||
-> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
BooleanFlagItem(
|
||||
@@ -95,4 +96,5 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
|
||||
FlagKey.V2EncryptionKeyConnector -> stringResource(BitwardenString.v2_encryption_key_connector)
|
||||
FlagKey.V2EncryptionPassword -> stringResource(BitwardenString.v2_encryption_password)
|
||||
FlagKey.V2EncryptionTde -> stringResource(BitwardenString.v2_encryption_tde)
|
||||
FlagKey.NewItemTypes -> stringResource(BitwardenString.new_item_types)
|
||||
}
|
||||
|
||||
@@ -1295,4 +1295,49 @@ Do you want to switch to this account?</string>
|
||||
<string name="trouble_loading_portal">We had trouble loading the management portal, so try again.</string>
|
||||
<string name="subscription_error">Subscription error</string>
|
||||
<string name="trouble_loading_subscription">We couldn’t load your subscription details. Please try again.</string>
|
||||
<string name="view_bank_account">View bank account</string>
|
||||
<string name="view_drivers_license">View driver’s license</string>
|
||||
<string name="view_passport">View passport</string>
|
||||
<string name="new_bank_account">New bank account</string>
|
||||
<string name="new_drivers_license">New driver’s license</string>
|
||||
<string name="new_passport">New passport</string>
|
||||
<string name="edit_bank_account">Edit bank account</string>
|
||||
<string name="edit_drivers_license">Edit driver’s license</string>
|
||||
<string name="edit_passport">Edit passport</string>
|
||||
<string name="type_bank_account">Bank account</string>
|
||||
<string name="type_drivers_license">Driver’s license</string>
|
||||
<string name="type_passport">Passport</string>
|
||||
<string name="bank_accounts">Bank accounts</string>
|
||||
<string name="drivers_licenses">Driver’s licenses</string>
|
||||
<string name="passports">Passports</string>
|
||||
<string name="bank_name">Bank name</string>
|
||||
<string name="name_on_account">Name on account</string>
|
||||
<string name="account_type">Account type</string>
|
||||
<string name="account_number">Account number</string>
|
||||
<string name="routing_number">Routing/transit number</string>
|
||||
<string name="branch_number">Branch/institution number</string>
|
||||
<string name="swift_code">SWIFT code</string>
|
||||
<string name="iban">IBAN</string>
|
||||
<string name="bank_contact_phone">Bank contact phone</string>
|
||||
<string name="copy_account_number">Copy account number</string>
|
||||
<string name="copy_routing_number">Copy routing number</string>
|
||||
<string name="copy_swift_code">Copy SWIFT code</string>
|
||||
<string name="copy_iban">Copy IBAN</string>
|
||||
<string name="checking">Checking</string>
|
||||
<string name="savings">Savings</string>
|
||||
<string name="certificate_of_deposit">Certificate of deposit</string>
|
||||
<string name="line_of_credit">Line of credit</string>
|
||||
<string name="investment_brokerage">Investment/brokerage</string>
|
||||
<string name="money_market">Money market</string>
|
||||
<string name="issuing_country">Issuing country</string>
|
||||
<string name="issuing_state">Issuing state/province</string>
|
||||
<string name="license_class">License class</string>
|
||||
<string name="surname">Surname</string>
|
||||
<string name="given_name">Given name</string>
|
||||
<string name="date_of_birth">Date of birth</string>
|
||||
<string name="nationality">Nationality</string>
|
||||
<string name="passport_type">Passport type</string>
|
||||
<string name="issuing_authority">Issuing authority/office</string>
|
||||
<string name="issue_date">Issue date</string>
|
||||
<string name="expiration_date">Expiration date</string>
|
||||
</resources>
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
<string name="v2_encryption_key_connector">V2 Encryption - Key Connector</string>
|
||||
<string name="v2_encryption_jit_password">V2 Encryption - JIT Password</string>
|
||||
<string name="v2_encryption_password">V2 Encryption - Password</string>
|
||||
<string name="new_item_types">New Item Types</string>
|
||||
|
||||
<!-- endregion Debug Menu -->
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user