mirror of
https://github.com/bitwarden/android.git
synced 2025-02-16 11:59:57 +03:00
BIT-897 Decrypt sync response (#181)
This commit is contained in:
parent
4a3e88f939
commit
b31b859516
27 changed files with 1814 additions and 47 deletions
|
@ -1,7 +1,8 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.di
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncServiceImpl
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.retrofit.Retrofits
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.api.SyncApi
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -18,7 +19,9 @@ object VaultNetworkModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSyncApiService(
|
||||
fun provideSyncService(
|
||||
retrofits: Retrofits,
|
||||
): SyncApi = retrofits.authenticatedApiRetrofit.create()
|
||||
): SyncService = SyncServiceImpl(
|
||||
syncApi = retrofits.authenticatedApiRetrofit.create(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,19 +8,19 @@ import kotlinx.serialization.Serializable
|
|||
* Represents different fields that a custom cipher field can be linked to.
|
||||
*/
|
||||
@Serializable(LinkedIdTypeSerializer::class)
|
||||
enum class LinkedIdTypeJson {
|
||||
enum class LinkedIdTypeJson(val value: UInt) {
|
||||
// region LOGIN
|
||||
/**
|
||||
* The field is linked to the login's username.
|
||||
*/
|
||||
@SerialName("100")
|
||||
LOGIN_USERNAME,
|
||||
LOGIN_USERNAME(value = 100U),
|
||||
|
||||
/**
|
||||
* The field is linked to the login's password.
|
||||
*/
|
||||
@SerialName("101")
|
||||
LOGIN_PASSWORD,
|
||||
LOGIN_PASSWORD(value = 101U),
|
||||
// endregion LOGIN
|
||||
|
||||
// region CARD
|
||||
|
@ -28,37 +28,37 @@ enum class LinkedIdTypeJson {
|
|||
* The field is linked to the card's cardholder name.
|
||||
*/
|
||||
@SerialName("300")
|
||||
CARD_CARDHOLDER_NAME,
|
||||
CARD_CARDHOLDER_NAME(value = 300U),
|
||||
|
||||
/**
|
||||
* The field is linked to the card's expiration month.
|
||||
*/
|
||||
@SerialName("301")
|
||||
CARD_EXP_MONTH,
|
||||
CARD_EXP_MONTH(value = 301U),
|
||||
|
||||
/**
|
||||
* The field is linked to the card's expiration year.
|
||||
*/
|
||||
@SerialName("302")
|
||||
CARD_EXP_YEAR,
|
||||
CARD_EXP_YEAR(value = 302U),
|
||||
|
||||
/**
|
||||
* The field is linked to the card's code.
|
||||
*/
|
||||
@SerialName("303")
|
||||
CARD_CODE,
|
||||
CARD_CODE(value = 303U),
|
||||
|
||||
/**
|
||||
* The field is linked to the card's brand.
|
||||
*/
|
||||
@SerialName("304")
|
||||
CARD_BRAND,
|
||||
CARD_BRAND(value = 304U),
|
||||
|
||||
/**
|
||||
* The field is linked to the card's number.
|
||||
*/
|
||||
@SerialName("305")
|
||||
CARD_NUMBER,
|
||||
CARD_NUMBER(value = 305U),
|
||||
// endregion CARD
|
||||
|
||||
// region IDENTITY
|
||||
|
@ -66,115 +66,115 @@ enum class LinkedIdTypeJson {
|
|||
* The field is linked to the identity's title.
|
||||
*/
|
||||
@SerialName("400")
|
||||
IDENTITY_TITLE,
|
||||
IDENTITY_TITLE(value = 400U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's middle name.
|
||||
*/
|
||||
@SerialName("401")
|
||||
IDENTITY_MIDDLE_NAME,
|
||||
IDENTITY_MIDDLE_NAME(value = 401U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's address line 1.
|
||||
*/
|
||||
@SerialName("402")
|
||||
IDENTITY_ADDRESS_1,
|
||||
IDENTITY_ADDRESS_1(value = 402U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's address line 2.
|
||||
*/
|
||||
@SerialName("403")
|
||||
IDENTITY_ADDRESS_2,
|
||||
IDENTITY_ADDRESS_2(value = 403U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's address line 3.
|
||||
*/
|
||||
@SerialName("404")
|
||||
IDENTITY_ADDRESS_3,
|
||||
IDENTITY_ADDRESS_3(value = 404U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's city.
|
||||
*/
|
||||
@SerialName("405")
|
||||
IDENTITY_CITY,
|
||||
IDENTITY_CITY(value = 405U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's state.
|
||||
*/
|
||||
@SerialName("406")
|
||||
IDENTITY_STATE,
|
||||
IDENTITY_STATE(value = 406U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's postal code
|
||||
*/
|
||||
@SerialName("407")
|
||||
IDENTITY_POSTAL_CODE,
|
||||
IDENTITY_POSTAL_CODE(value = 407U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's country.
|
||||
*/
|
||||
@SerialName("408")
|
||||
IDENTITY_COUNTRY,
|
||||
IDENTITY_COUNTRY(value = 408U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's company.
|
||||
*/
|
||||
@SerialName("409")
|
||||
IDENTITY_COMPANY,
|
||||
IDENTITY_COMPANY(value = 409U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's email.
|
||||
*/
|
||||
@SerialName("410")
|
||||
IDENTITY_EMAIL,
|
||||
IDENTITY_EMAIL(value = 410U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's phone.
|
||||
*/
|
||||
@SerialName("411")
|
||||
IDENTITY_PHONE,
|
||||
IDENTITY_PHONE(value = 411U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's SSN.
|
||||
*/
|
||||
@SerialName("412")
|
||||
IDENTITY_SSN,
|
||||
IDENTITY_SSN(value = 412U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's username.
|
||||
*/
|
||||
@SerialName("413")
|
||||
IDENTITY_USERNAME,
|
||||
IDENTITY_USERNAME(value = 413U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's passport number.
|
||||
*/
|
||||
@SerialName("414")
|
||||
IDENTITY_PASSPORT_NUMBER,
|
||||
IDENTITY_PASSPORT_NUMBER(value = 414U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's license number.
|
||||
*/
|
||||
@SerialName("415")
|
||||
IDENTITY_LICENSE_NUMBER,
|
||||
IDENTITY_LICENSE_NUMBER(value = 415U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's first name.
|
||||
*/
|
||||
@SerialName("416")
|
||||
IDENTITY_FIRST_NAME,
|
||||
IDENTITY_FIRST_NAME(value = 416U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's last name.
|
||||
*/
|
||||
@SerialName("417")
|
||||
IDENTITY_LAST_NAME,
|
||||
IDENTITY_LAST_NAME(value = 417U),
|
||||
|
||||
/**
|
||||
* The field is linked to the identity's full name.
|
||||
*/
|
||||
@SerialName("418")
|
||||
IDENTITY_FULL_NAME,
|
||||
IDENTITY_FULL_NAME(value = 418U),
|
||||
// endregion IDENTITY
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ data class SyncResponseJson(
|
|||
/**
|
||||
* Represents a folder in the vault response.
|
||||
*
|
||||
* @property revisionDate The revision date of the folder (nullable).
|
||||
* @property revisionDate The revision date of the folder.
|
||||
* @property name The name of the folder (nullable).
|
||||
* @property id The ID of the folder.
|
||||
*/
|
||||
|
@ -85,7 +85,7 @@ data class SyncResponseJson(
|
|||
data class Folder(
|
||||
@SerialName("revisionDate")
|
||||
@Contextual
|
||||
val revisionDate: LocalDateTime?, // Date
|
||||
val revisionDate: LocalDateTime,
|
||||
|
||||
@SerialName("name")
|
||||
val name: String?,
|
||||
|
@ -236,7 +236,7 @@ data class SyncResponseJson(
|
|||
* @property shouldUseActivateAutofillPolicy If the organization should
|
||||
* use auto fill policy.
|
||||
* @property shouldUseEvents If the organization should use events.
|
||||
* @property isFamilySponsorshipFriendlyName If the family sponsorship is a friendly name.
|
||||
* @property familySponsorshipFriendlyName If the family sponsorship is a friendly name.
|
||||
* @property isKeyConnectorEnabled If the key connector is enabled.
|
||||
* @property shouldUseTotp If he organization should use TOTP.
|
||||
* @property familySponsorshipLastSyncDate The last date the family sponsorship
|
||||
|
@ -351,7 +351,7 @@ data class SyncResponseJson(
|
|||
val shouldUseEvents: Boolean,
|
||||
|
||||
@SerialName("familySponsorshipFriendlyName")
|
||||
val isFamilySponsorshipFriendlyName: String?,
|
||||
val familySponsorshipFriendlyName: String?,
|
||||
|
||||
@SerialName("keyConnectorEnabled")
|
||||
val isKeyConnectorEnabled: Boolean,
|
||||
|
@ -376,7 +376,8 @@ data class SyncResponseJson(
|
|||
val isSsoBound: Boolean,
|
||||
|
||||
@SerialName("familySponsorshipValidUntil")
|
||||
val familySponsorshipValidUntil: String?,
|
||||
@Contextual
|
||||
val familySponsorshipValidUntil: LocalDateTime?,
|
||||
|
||||
@SerialName("status")
|
||||
val status: Int,
|
||||
|
@ -499,10 +500,10 @@ data class SyncResponseJson(
|
|||
* @property shouldEdit If the cipher can edit.
|
||||
* @property passwordHistory A list of password history objects
|
||||
* associated with the cipher (nullable).
|
||||
* @property revisionDate The revision date of the cipher (nullable).
|
||||
* @property revisionDate The revision date of the cipher.
|
||||
* @property type The type of cipher.
|
||||
* @property login The login of the cipher.
|
||||
* @property creationDate The creation date of the cipher (nullable).
|
||||
* @property creationDate The creation date of the cipher.
|
||||
* @property secureNote The secure note of the cipher.
|
||||
* @property folderId The folder ID of the cipher (nullable).
|
||||
* @property organizationId The organization ID of the cipher (nullable).
|
||||
|
@ -538,20 +539,20 @@ data class SyncResponseJson(
|
|||
|
||||
@SerialName("revisionDate")
|
||||
@Contextual
|
||||
val revisionDate: LocalDateTime?,
|
||||
val revisionDate: LocalDateTime,
|
||||
|
||||
@SerialName("type")
|
||||
val type: CipherTypeJson,
|
||||
|
||||
@SerialName("login")
|
||||
val login: Login,
|
||||
val login: Login?,
|
||||
|
||||
@SerialName("creationDate")
|
||||
@Contextual
|
||||
val creationDate: LocalDateTime?,
|
||||
val creationDate: LocalDateTime,
|
||||
|
||||
@SerialName("secureNote")
|
||||
val secureNote: SecureNote,
|
||||
val secureNote: SecureNote?,
|
||||
|
||||
@SerialName("folderId")
|
||||
val folderId: String?,
|
||||
|
@ -564,7 +565,7 @@ data class SyncResponseJson(
|
|||
val deletedDate: LocalDateTime?,
|
||||
|
||||
@SerialName("identity")
|
||||
val identity: Identity,
|
||||
val identity: Identity?,
|
||||
|
||||
@SerialName("collectionIds")
|
||||
val collectionIds: List<String>?,
|
||||
|
@ -585,7 +586,7 @@ data class SyncResponseJson(
|
|||
val isFavorite: Boolean,
|
||||
|
||||
@SerialName("card")
|
||||
val card: Card,
|
||||
val card: Card?,
|
||||
) {
|
||||
/**
|
||||
* Represents an attachment in the vault response.
|
||||
|
@ -795,7 +796,7 @@ data class SyncResponseJson(
|
|||
@Serializable
|
||||
data class Uri(
|
||||
@SerialName("match")
|
||||
val uriMatchType: UriMatchTypeJson,
|
||||
val uriMatchType: UriMatchTypeJson?,
|
||||
|
||||
@SerialName("uri")
|
||||
val uri: String?,
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.service
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
|
||||
/**
|
||||
* Provides an API for querying sync endpoints.
|
||||
*/
|
||||
interface SyncService {
|
||||
/**
|
||||
* Make sync request to get vault items.
|
||||
*/
|
||||
suspend fun sync(): Result<SyncResponseJson>
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.service
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.api.SyncApi
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
|
||||
class SyncServiceImpl constructor(
|
||||
private val syncApi: SyncApi,
|
||||
) : SyncService {
|
||||
override suspend fun sync(): Result<SyncResponseJson> = syncApi.sync()
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
import com.bitwarden.core.Cipher
|
||||
import com.bitwarden.core.CipherListView
|
||||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
|
||||
/**
|
||||
* Source of vault information and functionality from the Bitwarden SDK.
|
||||
*/
|
||||
interface VaultSdkSource {
|
||||
/**
|
||||
* Decrypts a [Cipher] returning a [CipherView] wrapped in a [Result].
|
||||
*/
|
||||
suspend fun decryptCipher(cipher: Cipher): Result<CipherView>
|
||||
|
||||
/**
|
||||
* Decrypts a list of [Cipher]s returning a list of [CipherListView] wrapped in a [Result].
|
||||
*/
|
||||
suspend fun decryptCipherList(cipherList: List<Cipher>): Result<List<CipherListView>>
|
||||
|
||||
/**
|
||||
* Decrypts a [Folder] returning a [FolderView] wrapped in a [Result].
|
||||
*/
|
||||
suspend fun decryptFolder(folder: Folder): Result<FolderView>
|
||||
|
||||
/**
|
||||
* Decrypts a list of [Folder]s returning a list of [FolderView] wrapped in a [Result].
|
||||
*/
|
||||
suspend fun decryptFolderList(folderList: List<Folder>): Result<List<FolderView>>
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
import com.bitwarden.core.Cipher
|
||||
import com.bitwarden.core.CipherListView
|
||||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.sdk.ClientVault
|
||||
|
||||
/**
|
||||
* Primary implementation of [VaultSdkSource] that serves as a convenience wrapper around a
|
||||
* [ClientVault].
|
||||
*/
|
||||
class VaultSdkSourceImpl(
|
||||
private val clientVault: ClientVault,
|
||||
) : VaultSdkSource {
|
||||
|
||||
override suspend fun decryptCipher(cipher: Cipher): Result<CipherView> =
|
||||
runCatching { clientVault.ciphers().decrypt(cipher) }
|
||||
|
||||
override suspend fun decryptCipherList(cipherList: List<Cipher>): Result<List<CipherListView>> =
|
||||
runCatching { clientVault.ciphers().decryptList(cipherList) }
|
||||
|
||||
override suspend fun decryptFolder(folder: Folder): Result<FolderView> =
|
||||
runCatching { clientVault.folders().decrypt(folder) }
|
||||
|
||||
override suspend fun decryptFolderList(folderList: List<Folder>): Result<List<FolderView>> =
|
||||
runCatching { clientVault.folders().decryptList(folderList) }
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk.di
|
||||
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSourceImpl
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Provides SDK-related dependencies for the vault package.
|
||||
*/
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class VaultSdkModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesVaultSdkSource(
|
||||
client: Client,
|
||||
): VaultSdkSource = VaultSdkSourceImpl(clientVault = client.vault())
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository
|
||||
|
||||
/**
|
||||
* Responsible for managing vault data inside the network layer.
|
||||
*/
|
||||
interface VaultRepository {
|
||||
|
||||
/**
|
||||
* Attempt to sync the vault data.
|
||||
*/
|
||||
suspend fun sync()
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipherList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolderList
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Default implementation of [VaultRepository].
|
||||
*/
|
||||
class VaultRepositoryImpl constructor(
|
||||
private val syncService: SyncService,
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
dispatcher: CoroutineDispatcher,
|
||||
) : VaultRepository {
|
||||
|
||||
private val scope = CoroutineScope(dispatcher)
|
||||
|
||||
private var syncJob: Job = Job().apply { complete() }
|
||||
|
||||
override suspend fun sync() {
|
||||
if (!syncJob.isCompleted) return
|
||||
syncJob = scope.launch {
|
||||
syncService
|
||||
.sync()
|
||||
.fold(
|
||||
onSuccess = { syncResponse ->
|
||||
// TODO transform into domain object consumable by VaultViewModel BIT-205.
|
||||
|
||||
// TODO initialize crypto in BIT-990
|
||||
syncResponse.ciphers?.let { networkCiphers ->
|
||||
vaultSdkSource.decryptCipherList(
|
||||
cipherList = networkCiphers.toEncryptedSdkCipherList(),
|
||||
)
|
||||
}
|
||||
syncResponse.folders?.let { networkFolders ->
|
||||
vaultSdkSource.decryptFolderList(
|
||||
folderList = networkFolders.toEncryptedSdkFolderList(),
|
||||
)
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
// TODO handle failure BIT-205.
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.di
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepositoryImpl
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Provides repositories in the vault package.
|
||||
*/
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class VaultRepositoryModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesVaultRepository(
|
||||
syncService: SyncService,
|
||||
vaultSdkSource: VaultSdkSource,
|
||||
): VaultRepository = VaultRepositoryImpl(
|
||||
syncService = syncService,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
dispatcher = Dispatchers.IO,
|
||||
)
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
@file:Suppress("TooManyFunctions")
|
||||
package com.x8bit.bitwarden.data.vault.repository.util
|
||||
|
||||
import com.bitwarden.core.Attachment
|
||||
import com.bitwarden.core.Card
|
||||
import com.bitwarden.core.Cipher
|
||||
import com.bitwarden.core.CipherRepromptType
|
||||
import com.bitwarden.core.CipherType
|
||||
import com.bitwarden.core.Field
|
||||
import com.bitwarden.core.FieldType
|
||||
import com.bitwarden.core.Identity
|
||||
import com.bitwarden.core.Login
|
||||
import com.bitwarden.core.LoginUri
|
||||
import com.bitwarden.core.PasswordHistory
|
||||
import com.bitwarden.core.SecureNote
|
||||
import com.bitwarden.core.SecureNoteType
|
||||
import com.bitwarden.core.UriMatchType
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherRepromptTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FieldTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SecureNoteTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UriMatchTypeJson
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* Converts a list of [SyncResponseJson.Cipher] objects to a list of corresponding
|
||||
* Bitwarden SDK [Cipher] objects.
|
||||
*/
|
||||
fun List<SyncResponseJson.Cipher>.toEncryptedSdkCipherList(): List<Cipher> =
|
||||
map { it.toEncryptedSdkCipher() }
|
||||
|
||||
/**
|
||||
* Converts a of [SyncResponseJson.Cipher] object to a corresponding
|
||||
* Bitwarden SDK [Cipher] object.
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.toEncryptedSdkCipher(): Cipher =
|
||||
Cipher(
|
||||
id = id,
|
||||
organizationId = organizationId,
|
||||
folderId = folderId,
|
||||
collectionIds = collectionIds.orEmpty(),
|
||||
name = name.orEmpty(),
|
||||
notes = notes,
|
||||
type = type.toSdkCipherType(),
|
||||
login = login?.toSdkLogin(),
|
||||
identity = identity?.toSdkIdentity(),
|
||||
card = card?.toSdkCard(),
|
||||
secureNote = secureNote?.toSdkSecureNote(),
|
||||
favorite = isFavorite,
|
||||
reprompt = reprompt.toSdkRepromptType(),
|
||||
organizationUseTotp = shouldOrganizationUseTotp,
|
||||
edit = shouldEdit,
|
||||
viewPassword = shouldViewPassword,
|
||||
localData = null,
|
||||
attachments = attachments?.toSdkAttachmentList(),
|
||||
fields = fields?.toSdkFieldList(),
|
||||
passwordHistory = passwordHistory?.toSdkPasswordHistoryList(),
|
||||
creationDate = creationDate.toInstant(ZoneOffset.UTC),
|
||||
deletedDate = deletedDate?.toInstant(ZoneOffset.UTC),
|
||||
revisionDate = revisionDate.toInstant(ZoneOffset.UTC),
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.Login] into the corresponding Bitwarden SDK [Login].
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.Login.toSdkLogin(): Login =
|
||||
Login(
|
||||
username = username,
|
||||
password = password,
|
||||
passwordRevisionDate = passwordRevisionDate?.toInstant(ZoneOffset.UTC),
|
||||
uris = uris?.toSdkLoginUriList(),
|
||||
totp = totp,
|
||||
autofillOnPageLoad = shouldAutofillOnPageLoad,
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.Identity] into the corresponding Bitwarden SDK [Identity].
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.Identity.toSdkIdentity(): Identity =
|
||||
Identity(
|
||||
title = title,
|
||||
middleName = middleName,
|
||||
firstName = firstName,
|
||||
lastName = lastName,
|
||||
address1 = address1,
|
||||
address2 = address2,
|
||||
address3 = address3,
|
||||
city = city,
|
||||
state = state,
|
||||
postalCode = postalCode,
|
||||
country = country,
|
||||
company = company,
|
||||
email = email,
|
||||
phone = phone,
|
||||
ssn = ssn,
|
||||
username = username,
|
||||
passportNumber = passportNumber,
|
||||
licenseNumber = licenseNumber,
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.Card] into the corresponding Bitwarden SDK [Card].
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.Card.toSdkCard(): Card =
|
||||
Card(
|
||||
cardholderName = cardholderName,
|
||||
expMonth = expMonth,
|
||||
expYear = expirationYear,
|
||||
code = code,
|
||||
brand = brand,
|
||||
number = number,
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.SecureNote] into
|
||||
* the corresponding Bitwarden SDK [SecureNote].
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.SecureNote.toSdkSecureNote(): SecureNote =
|
||||
SecureNote(
|
||||
type = when (type) {
|
||||
SecureNoteTypeJson.GENERIC -> SecureNoteType.GENERIC
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a list of [SyncResponseJson.Cipher.Login.Uri] into
|
||||
* a corresponding list of Bitwarden SDK [LoginUri].
|
||||
*/
|
||||
fun List<SyncResponseJson.Cipher.Login.Uri>.toSdkLoginUriList(): List<LoginUri> =
|
||||
map { it.toSdkLoginUri() }
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.Login.Uri] into
|
||||
* a corresponding Bitwarden SDK [LoginUri].
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.Login.Uri.toSdkLoginUri(): LoginUri =
|
||||
LoginUri(
|
||||
uri = uri,
|
||||
match = uriMatchType?.toSdkMatchType(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a list of [SyncResponseJson.Cipher.Attachment] into
|
||||
* a corresponding list of Bitwarden SDK [Attachment].
|
||||
*/
|
||||
fun List<SyncResponseJson.Cipher.Attachment>.toSdkAttachmentList(): List<Attachment> =
|
||||
map { it.toSdkAttachment() }
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.Attachment] into
|
||||
* a corresponding Bitwarden SDK [Attachment].
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.Attachment.toSdkAttachment(): Attachment =
|
||||
Attachment(
|
||||
id = id,
|
||||
url = url,
|
||||
size = size.toString(),
|
||||
sizeName = sizeName,
|
||||
fileName = fileName,
|
||||
key = key,
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a list of [SyncResponseJson.Cipher.Field] into
|
||||
* a corresponding list of Bitwarden SDK [Field].
|
||||
*/
|
||||
fun List<SyncResponseJson.Cipher.Field>.toSdkFieldList(): List<Field> =
|
||||
map { it.toSdkField() }
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.Field] into
|
||||
* a corresponding Bitwarden SDK [Field].
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.Field.toSdkField(): Field =
|
||||
Field(
|
||||
name = name,
|
||||
value = value,
|
||||
type = type.toSdkFieldType(),
|
||||
linkedId = linkedIdType?.value,
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a list of [SyncResponseJson.Cipher.PasswordHistory] into
|
||||
* a corresponding list of Bitwarden SDK [PasswordHistory].
|
||||
*/
|
||||
@Suppress("MaxLineLength")
|
||||
fun List<SyncResponseJson.Cipher.PasswordHistory>.toSdkPasswordHistoryList(): List<PasswordHistory> =
|
||||
map { it.toSdkPasswordHistory() }
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.PasswordHistory] into
|
||||
* a corresponding Bitwarden SDK [PasswordHistory].
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.PasswordHistory.toSdkPasswordHistory(): PasswordHistory =
|
||||
PasswordHistory(
|
||||
password = password,
|
||||
lastUsedDate = lastUsedDate.toInstant(ZoneOffset.UTC),
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a [CipherTypeJson] to the corresponding Bitwarden SDK [CipherType].
|
||||
*/
|
||||
fun CipherTypeJson.toSdkCipherType(): CipherType =
|
||||
when (this) {
|
||||
CipherTypeJson.LOGIN -> CipherType.LOGIN
|
||||
CipherTypeJson.SECURE_NOTE -> CipherType.SECURE_NOTE
|
||||
CipherTypeJson.CARD -> CipherType.CARD
|
||||
CipherTypeJson.IDENTITY -> CipherType.IDENTITY
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a [UriMatchTypeJson] to the corresponding Bitwarden SDK [UriMatchType].
|
||||
*/
|
||||
fun UriMatchTypeJson.toSdkMatchType(): UriMatchType =
|
||||
when (this) {
|
||||
UriMatchTypeJson.DOMAIN -> UriMatchType.DOMAIN
|
||||
UriMatchTypeJson.HOST -> UriMatchType.HOST
|
||||
UriMatchTypeJson.STARTS_WITH -> UriMatchType.STARTS_WITH
|
||||
UriMatchTypeJson.EXACT -> UriMatchType.EXACT
|
||||
UriMatchTypeJson.REGULAR_EXPRESSION -> UriMatchType.REGULAR_EXPRESSION
|
||||
UriMatchTypeJson.NEVER -> UriMatchType.NEVER
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a [CipherRepromptTypeJson] to the corresponding Bitwarden SDK [CipherRepromptType].
|
||||
*/
|
||||
fun CipherRepromptTypeJson.toSdkRepromptType(): CipherRepromptType =
|
||||
when (this) {
|
||||
CipherRepromptTypeJson.NONE -> CipherRepromptType.NONE
|
||||
CipherRepromptTypeJson.PASSWORD -> CipherRepromptType.PASSWORD
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a [FieldTypeJson] to the corresponding Bitwarden SDK [FieldType].
|
||||
*/
|
||||
fun FieldTypeJson.toSdkFieldType(): FieldType =
|
||||
when (this) {
|
||||
FieldTypeJson.TEXT -> FieldType.TEXT
|
||||
FieldTypeJson.HIDDEN -> FieldType.HIDDEN
|
||||
FieldTypeJson.BOOLEAN -> FieldType.BOOLEAN
|
||||
FieldTypeJson.LINKED -> FieldType.LINKED
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.util
|
||||
|
||||
import com.bitwarden.core.Folder
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* Converts a list of [SyncResponseJson.Folder] objects to a list of corresponding
|
||||
* Bitwarden SDK [Folder] objects.
|
||||
*/
|
||||
fun List<SyncResponseJson.Folder>.toEncryptedSdkFolderList(): List<Folder> =
|
||||
map { it.toEncryptedSdkFolder() }
|
||||
|
||||
/**
|
||||
* Converts a [SyncResponseJson.Folder] objects to a corresponding
|
||||
* Bitwarden SDK [Folder] object.
|
||||
*/
|
||||
fun SyncResponseJson.Folder.toEncryptedSdkFolder(): Folder =
|
||||
Folder(
|
||||
id = id,
|
||||
name = name.orEmpty(),
|
||||
revisionDate = revisionDate.toInstant(ZoneOffset.UTC),
|
||||
)
|
|
@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.platform.base
|
|||
|
||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.core.ResultCallAdapterFactory
|
||||
import kotlinx.serialization.json.Json
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.di.PlatformNetworkModule
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
|
@ -13,12 +13,14 @@ import retrofit2.Retrofit
|
|||
*/
|
||||
abstract class BaseServiceTest {
|
||||
|
||||
private val json = PlatformNetworkModule.providesJson()
|
||||
|
||||
protected val server = MockWebServer().apply { start() }
|
||||
|
||||
protected val retrofit: Retrofit = Retrofit.Builder()
|
||||
.baseUrl(server.url("/").toString())
|
||||
.addCallAdapterFactory(ResultCallAdapterFactory())
|
||||
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
|
||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
||||
.build()
|
||||
|
||||
@After
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher] with a given [number].
|
||||
*/
|
||||
fun createMockCipher(number: Int): SyncResponseJson.Cipher =
|
||||
SyncResponseJson.Cipher(
|
||||
id = "mockId-$number",
|
||||
organizationId = "mockOrganizationId-$number",
|
||||
folderId = "mockFolderId-$number",
|
||||
collectionIds = listOf("mockCollectionId-$number"),
|
||||
name = "mockName-$number",
|
||||
notes = "mockNotes-$number",
|
||||
type = CipherTypeJson.LOGIN,
|
||||
login = createMockLogin(number = number),
|
||||
creationDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
deletedDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
revisionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
attachments = listOf(createMockAttachment(number = number)),
|
||||
card = createMockCard(number = number),
|
||||
fields = listOf(createMockField(number = number)),
|
||||
identity = createMockIdentity(number = number),
|
||||
isFavorite = false,
|
||||
passwordHistory = listOf(createMockPasswordHistory(number = number)),
|
||||
reprompt = CipherRepromptTypeJson.NONE,
|
||||
secureNote = createMockSecureNote(),
|
||||
shouldEdit = false,
|
||||
shouldOrganizationUseTotp = false,
|
||||
shouldViewPassword = false,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.Identity] with a given [number].
|
||||
*/
|
||||
fun createMockIdentity(number: Int): SyncResponseJson.Cipher.Identity =
|
||||
SyncResponseJson.Cipher.Identity(
|
||||
firstName = "mockFirstName-$number",
|
||||
middleName = "mockMiddleName-$number",
|
||||
lastName = "mockLastName-$number",
|
||||
passportNumber = "mockPassportNumber-$number",
|
||||
country = "mockCountry-$number",
|
||||
address1 = "mockAddress1-$number",
|
||||
address2 = "mockAddress2-$number",
|
||||
address3 = "mockAddress3-$number",
|
||||
city = "mockCity-$number",
|
||||
postalCode = "mockPostalCode-$number",
|
||||
title = "mockTitle-$number",
|
||||
ssn = "mockSsn-$number",
|
||||
phone = "mockPhone-$number",
|
||||
company = "mockCompany-$number",
|
||||
licenseNumber = "mockLicenseNumber-$number",
|
||||
state = "mockState-$number",
|
||||
email = "mockEmail-$number",
|
||||
username = "mockUsername-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.Attachment] with a given [number].
|
||||
*/
|
||||
fun createMockAttachment(number: Int): SyncResponseJson.Cipher.Attachment =
|
||||
SyncResponseJson.Cipher.Attachment(
|
||||
fileName = "mockFileName-$number",
|
||||
size = 1,
|
||||
sizeName = "mockSizeName-$number",
|
||||
id = "mockId-$number",
|
||||
url = "mockUrl-$number",
|
||||
key = "mockKey-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.Card] with a given [number].
|
||||
*/
|
||||
fun createMockCard(number: Int): SyncResponseJson.Cipher.Card =
|
||||
SyncResponseJson.Cipher.Card(
|
||||
number = "mockNumber-$number",
|
||||
expMonth = "mockExpMonth-$number",
|
||||
code = "mockCode-$number",
|
||||
expirationYear = "mockExpirationYear-$number",
|
||||
cardholderName = "mockCardholderName-$number",
|
||||
brand = "mockBrand-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.PasswordHistory] with a given [number].
|
||||
*/
|
||||
fun createMockPasswordHistory(number: Int): SyncResponseJson.Cipher.PasswordHistory =
|
||||
SyncResponseJson.Cipher.PasswordHistory(
|
||||
password = "mockPassword-$number",
|
||||
lastUsedDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.SecureNote].
|
||||
*/
|
||||
fun createMockSecureNote(): SyncResponseJson.Cipher.SecureNote =
|
||||
SyncResponseJson.Cipher.SecureNote(
|
||||
type = SecureNoteTypeJson.GENERIC,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.Field] with a given [number].
|
||||
*/
|
||||
fun createMockField(number: Int): SyncResponseJson.Cipher.Field =
|
||||
SyncResponseJson.Cipher.Field(
|
||||
linkedIdType = LinkedIdTypeJson.LOGIN_USERNAME,
|
||||
name = "mockName-$number",
|
||||
type = FieldTypeJson.HIDDEN,
|
||||
value = "mockValue-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.Login] with a given [number].
|
||||
*/
|
||||
fun createMockLogin(number: Int): SyncResponseJson.Cipher.Login =
|
||||
SyncResponseJson.Cipher.Login(
|
||||
username = "mockUsername-$number",
|
||||
password = "mockPassword-$number",
|
||||
passwordRevisionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
shouldAutofillOnPageLoad = false,
|
||||
uri = "mockUri-$number",
|
||||
uris = listOf(createMockUri(number = number)),
|
||||
totp = "mockTotp-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Cipher.Login.Uri] with a given [number].
|
||||
*/
|
||||
fun createMockUri(number: Int): SyncResponseJson.Cipher.Login.Uri =
|
||||
SyncResponseJson.Cipher.Login.Uri(
|
||||
uri = "mockUri-$number",
|
||||
uriMatchType = UriMatchTypeJson.HOST,
|
||||
)
|
|
@ -0,0 +1,14 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Collection] with a given [number].
|
||||
*/
|
||||
fun createMockCollection(number: Int): SyncResponseJson.Collection =
|
||||
SyncResponseJson.Collection(
|
||||
organizationId = "mockOrganizationId-$number",
|
||||
shouldHidePasswords = false,
|
||||
name = "mockName-$number",
|
||||
externalId = "mockExternalId-$number",
|
||||
isReadOnly = false,
|
||||
id = "mockId-$number",
|
||||
)
|
|
@ -0,0 +1,22 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Domains] with a given [number].
|
||||
*/
|
||||
fun createMockDomains(number: Int): SyncResponseJson.Domains =
|
||||
SyncResponseJson.Domains(
|
||||
globalEquivalentDomains = listOf(
|
||||
SyncResponseJson.Domains.GlobalEquivalentDomain(
|
||||
isExcluded = false,
|
||||
domains = listOf(
|
||||
"mockDomain-$number",
|
||||
),
|
||||
type = 1,
|
||||
),
|
||||
),
|
||||
equivalentDomains = listOf(
|
||||
listOf(
|
||||
"mockEquivalentDomain-$number",
|
||||
),
|
||||
),
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Folder] with a given [number].
|
||||
*/
|
||||
fun createMockFolder(number: Int): SyncResponseJson.Folder =
|
||||
SyncResponseJson.Folder(
|
||||
id = "mockId-$number",
|
||||
name = "mockName-$number",
|
||||
revisionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Policy] with a given [number].
|
||||
*/
|
||||
fun createMockPolicy(number: Int): SyncResponseJson.Policy =
|
||||
SyncResponseJson.Policy(
|
||||
organizationId = "mockOrganizationId-$number",
|
||||
id = "mockId-$number",
|
||||
type = PolicyTypeJson.MASTER_PASSWORD,
|
||||
isEnabled = false,
|
||||
)
|
|
@ -0,0 +1,115 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Profile] with a given [number].
|
||||
*/
|
||||
fun createMockProfile(number: Int): SyncResponseJson.Profile =
|
||||
SyncResponseJson.Profile(
|
||||
providerOrganizations = listOf(createMockOrganization(number = number)),
|
||||
isPremiumFromOrganization = false,
|
||||
shouldForcePasswordReset = false,
|
||||
avatarColor = "mockAvatarColor-$number",
|
||||
isEmailVerified = false,
|
||||
isTwoFactorEnabled = false,
|
||||
privateKey = "mockPrivateKey-$number",
|
||||
isPremium = false,
|
||||
culture = "mockCulture-$number",
|
||||
name = "mockName-$number",
|
||||
organizations = listOf(createMockOrganization(number = number)),
|
||||
shouldUseKeyConnector = false,
|
||||
id = "mockId-$number",
|
||||
masterPasswordHint = "mockMasterPasswordHint-$number",
|
||||
email = "mockEmail-$number",
|
||||
key = "mockKey-$number",
|
||||
securityStamp = "mockSecurityStamp-$number",
|
||||
providers = listOf(createMockProvider(number = number)),
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Profile.Organization] with a given [number].
|
||||
*/
|
||||
fun createMockOrganization(number: Int): SyncResponseJson.Profile.Organization =
|
||||
SyncResponseJson.Profile.Organization(
|
||||
shouldUsePolicies = false,
|
||||
keyConnectorUrl = "mockKeyConnectorUrl-$number",
|
||||
type = 1,
|
||||
seats = 1,
|
||||
isEnabled = false,
|
||||
providerType = 1,
|
||||
isResetPasswordEnrolled = false,
|
||||
shouldUseSecretsManager = false,
|
||||
maxCollections = 1,
|
||||
isSelfHost = false,
|
||||
shouldUseKeyConnector = false,
|
||||
permissions = createMockPermissions(),
|
||||
hasPublicAndPrivateKeys = false,
|
||||
providerId = "mockProviderId-$number",
|
||||
id = "mockId-$number",
|
||||
shouldUseGroups = false,
|
||||
shouldUseDirectory = false,
|
||||
key = "mockKey-$number",
|
||||
providerName = "mockProviderName-$number",
|
||||
shouldUsersGetPremium = false,
|
||||
maxStorageGb = 1,
|
||||
identifier = "mockIdentifier-$number",
|
||||
shouldUseSso = false,
|
||||
shouldUseCustomPermissions = false,
|
||||
isFamilySponsorshipAvailable = false,
|
||||
shouldUseResetPassword = false,
|
||||
planProductType = 1,
|
||||
accessSecretsManager = false,
|
||||
use2fa = false,
|
||||
familySponsorshipToDelete = false,
|
||||
userId = "mockUserId-$number",
|
||||
shouldUseActivateAutofillPolicy = false,
|
||||
shouldUseEvents = false,
|
||||
familySponsorshipFriendlyName = "mockFamilySponsorshipFriendlyName-$number",
|
||||
isKeyConnectorEnabled = false,
|
||||
shouldUseTotp = false,
|
||||
familySponsorshipLastSyncDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
shouldUseScim = false,
|
||||
name = "mockName-$number",
|
||||
shouldUseApi = false,
|
||||
isSsoBound = false,
|
||||
familySponsorshipValidUntil = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
status = 1,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Profile.Permissions].
|
||||
*/
|
||||
fun createMockPermissions(): SyncResponseJson.Profile.Permissions =
|
||||
SyncResponseJson.Profile.Permissions(
|
||||
shouldManageGroups = false,
|
||||
shouldManageResetPassword = false,
|
||||
shouldAccessReports = false,
|
||||
shouldManagePolicies = false,
|
||||
shouldDeleteAnyCollection = false,
|
||||
shouldManageSso = false,
|
||||
shouldDeleteAssignedCollections = false,
|
||||
shouldManageUsers = false,
|
||||
shouldManageScim = false,
|
||||
shouldAccessImportExport = false,
|
||||
shouldEditAnyCollection = false,
|
||||
shouldAccessEventLogs = false,
|
||||
shouldCreateNewCollections = false,
|
||||
shouldEditAssignedCollections = false,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SyncResponseJson.Profile.Provider] with a given [number].
|
||||
*/
|
||||
fun createMockProvider(number: Int): SyncResponseJson.Profile.Provider =
|
||||
SyncResponseJson.Profile.Provider(
|
||||
shouldUseEvents = false,
|
||||
permissions = createMockPermissions(),
|
||||
name = "mockName-$number",
|
||||
id = "mockId-$number",
|
||||
type = 1,
|
||||
userId = "mockUserId-$number",
|
||||
key = "mockKey-$number",
|
||||
isEnabled = false,
|
||||
status = 1,
|
||||
)
|
|
@ -0,0 +1,37 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||
|
||||
import java.time.LocalDateTime
|
||||
|
||||
fun createMockSend(number: Int): SyncResponseJson.Send =
|
||||
SyncResponseJson.Send(
|
||||
accessCount = 1,
|
||||
notes = "mockNotes-$number",
|
||||
revisionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
maxAccessCount = 1,
|
||||
shouldHideEmail = false,
|
||||
type = SendTypeJson.FILE,
|
||||
accessId = "mockAccessId-$number",
|
||||
password = "mockPassword-$number",
|
||||
file = createMockFile(number = 1),
|
||||
deletionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
name = "mockName-$number",
|
||||
isDisabled = false,
|
||||
id = "mockId-$number",
|
||||
text = createMockText(number = number),
|
||||
key = "mockKey-$number",
|
||||
expirationDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
)
|
||||
|
||||
fun createMockFile(number: Int): SyncResponseJson.Send.File =
|
||||
SyncResponseJson.Send.File(
|
||||
fileName = "mockFileName-$number",
|
||||
size = 1,
|
||||
sizeName = "mockSizeName-$number",
|
||||
id = "mockId-$number",
|
||||
)
|
||||
|
||||
fun createMockText(number: Int): SyncResponseJson.Send.Text =
|
||||
SyncResponseJson.Send.Text(
|
||||
isHidden = false,
|
||||
text = "mockText-$number",
|
||||
)
|
|
@ -0,0 +1,375 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.service
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.base.BaseServiceTest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.api.SyncApi
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCollection
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockDomains
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFolder
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPolicy
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockProfile
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSend
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import retrofit2.create
|
||||
|
||||
class SyncServiceTest : BaseServiceTest() {
|
||||
private val syncApi: SyncApi = retrofit.create()
|
||||
|
||||
private val syncService: SyncService = SyncServiceImpl(
|
||||
syncApi = syncApi,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `sync should return the correct response`() = runTest {
|
||||
server.enqueue(MockResponse().setBody(SYNC_SUCCESS_JSON))
|
||||
val result = syncService.sync()
|
||||
assertEquals(SYNC_SUCCESS, result.getOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
private const val SYNC_SUCCESS_JSON = """
|
||||
{
|
||||
"profile": {
|
||||
"id": "mockId-1",
|
||||
"name": "mockName-1",
|
||||
"email": "mockEmail-1",
|
||||
"emailVerified": false,
|
||||
"premium": false,
|
||||
"premiumFromOrganization": false,
|
||||
"masterPasswordHint": "mockMasterPasswordHint-1",
|
||||
"culture": "mockCulture-1",
|
||||
"twoFactorEnabled": false,
|
||||
"key": "mockKey-1",
|
||||
"privateKey": "mockPrivateKey-1",
|
||||
"securityStamp": "mockSecurityStamp-1",
|
||||
"forcePasswordReset": false,
|
||||
"usesKeyConnector": false,
|
||||
"avatarColor": "mockAvatarColor-1",
|
||||
"organizations": [
|
||||
{
|
||||
"usePolicies": false,
|
||||
"keyConnectorUrl": "mockKeyConnectorUrl-1",
|
||||
"type": 1,
|
||||
"seats": 1,
|
||||
"enabled": false,
|
||||
"providerType": 1,
|
||||
"resetPasswordEnrolled": false,
|
||||
"useSecretsManager": false,
|
||||
"maxCollections": 1,
|
||||
"selfHost": false,
|
||||
"useKeyConnector": false,
|
||||
"permissions": {
|
||||
"manageGroups": false,
|
||||
"manageResetPassword": false,
|
||||
"accessReports": false,
|
||||
"managePolicies": false,
|
||||
"deleteAnyCollection": false,
|
||||
"manageSso": false,
|
||||
"deleteAssignedCollections": false,
|
||||
"manageUsers": false,
|
||||
"manageScim": false,
|
||||
"accessImportExport": false,
|
||||
"editAnyCollection": false,
|
||||
"accessEventLogs": false,
|
||||
"createNewCollections": false,
|
||||
"editAssignedCollections": false
|
||||
},
|
||||
"hasPublicAndPrivateKeys": false,
|
||||
"providerId": "mockProviderId-1",
|
||||
"id": "mockId-1",
|
||||
"useGroups": false,
|
||||
"useDirectory": false,
|
||||
"key": "mockKey-1",
|
||||
"providerName": "mockProviderName-1",
|
||||
"usersGetPremium": false,
|
||||
"maxStorageGb": 1,
|
||||
"identifier": "mockIdentifier-1",
|
||||
"useSso": false,
|
||||
"useCustomPermissions": false,
|
||||
"familySponsorshipAvailable": false,
|
||||
"useResetPassword": false,
|
||||
"planProductType": 1,
|
||||
"accessSecretsManager": false,
|
||||
"use2fa": false,
|
||||
"familySponsorshipToDelete": false,
|
||||
"userId": "mockUserId-1",
|
||||
"useActivateAutofillPolicy": false,
|
||||
"useEvents": false,
|
||||
"familySponsorshipFriendlyName": "mockFamilySponsorshipFriendlyName-1",
|
||||
"keyConnectorEnabled": false,
|
||||
"useTotp": false,
|
||||
"familySponsorshipLastSyncDate": "2023-10-27T12:00:00.00Z",
|
||||
"useScim": false,
|
||||
"name": "mockName-1",
|
||||
"useApi": false,
|
||||
"ssoBound": false,
|
||||
"familySponsorshipValidUntil": "2023-10-27T12:00:00.00Z",
|
||||
"status": 1
|
||||
}
|
||||
],
|
||||
"providers": [
|
||||
{
|
||||
"useEvents": false,
|
||||
"permissions": {
|
||||
"manageGroups": false,
|
||||
"manageResetPassword": false,
|
||||
"accessReports": false,
|
||||
"managePolicies": false,
|
||||
"deleteAnyCollection": false,
|
||||
"manageSso": false,
|
||||
"deleteAssignedCollections": false,
|
||||
"manageUsers": false,
|
||||
"manageScim": false,
|
||||
"accessImportExport": false,
|
||||
"editAnyCollection": false,
|
||||
"accessEventLogs": false,
|
||||
"createNewCollections": false,
|
||||
"editAssignedCollections": false
|
||||
},
|
||||
"name": "mockName-1",
|
||||
"id": "mockId-1",
|
||||
"type": 1,
|
||||
"userId": "mockUserId-1",
|
||||
"key": "mockKey-1",
|
||||
"enabled": false,
|
||||
"status": 1
|
||||
}
|
||||
],
|
||||
"providerOrganizations": [
|
||||
{
|
||||
"usePolicies": false,
|
||||
"keyConnectorUrl": "mockKeyConnectorUrl-1",
|
||||
"type": 1,
|
||||
"seats": 1,
|
||||
"enabled": false,
|
||||
"providerType": 1,
|
||||
"resetPasswordEnrolled": false,
|
||||
"useSecretsManager": false,
|
||||
"maxCollections": 1,
|
||||
"selfHost": false,
|
||||
"useKeyConnector": false,
|
||||
"permissions": {
|
||||
"manageGroups": false,
|
||||
"manageResetPassword": false,
|
||||
"accessReports": false,
|
||||
"managePolicies": false,
|
||||
"deleteAnyCollection": false,
|
||||
"manageSso": false,
|
||||
"deleteAssignedCollections": false,
|
||||
"manageUsers": false,
|
||||
"manageScim": false,
|
||||
"accessImportExport": false,
|
||||
"editAnyCollection": false,
|
||||
"accessEventLogs": false,
|
||||
"createNewCollections": false,
|
||||
"editAssignedCollections": false
|
||||
},
|
||||
"hasPublicAndPrivateKeys": false,
|
||||
"providerId": "mockProviderId-1",
|
||||
"id": "mockId-1",
|
||||
"useGroups": false,
|
||||
"useDirectory": false,
|
||||
"key": "mockKey-1",
|
||||
"providerName": "mockProviderName-1",
|
||||
"usersGetPremium": false,
|
||||
"maxStorageGb": 1,
|
||||
"identifier": "mockIdentifier-1",
|
||||
"useSso": false,
|
||||
"useCustomPermissions": false,
|
||||
"familySponsorshipAvailable": false,
|
||||
"useResetPassword": false,
|
||||
"planProductType": 1,
|
||||
"accessSecretsManager": false,
|
||||
"use2fa": false,
|
||||
"familySponsorshipToDelete": false,
|
||||
"userId": "mockUserId-1",
|
||||
"useActivateAutofillPolicy": false,
|
||||
"useEvents": false,
|
||||
"familySponsorshipFriendlyName": "mockFamilySponsorshipFriendlyName-1",
|
||||
"keyConnectorEnabled": false,
|
||||
"useTotp": false,
|
||||
"familySponsorshipLastSyncDate": "2023-10-27T12:00:00.00Z",
|
||||
"useScim": false,
|
||||
"name": "mockName-1",
|
||||
"useApi": false,
|
||||
"ssoBound": false,
|
||||
"familySponsorshipValidUntil": "2023-10-27T12:00:00.00Z",
|
||||
"status": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"folders": [
|
||||
{
|
||||
"revisionDate": "2023-10-27T12:00:00.00Z",
|
||||
"name": "mockName-1",
|
||||
"id": "mockId-1"
|
||||
}
|
||||
],
|
||||
"collections": [
|
||||
{
|
||||
"organizationId": "mockOrganizationId-1",
|
||||
"hidePasswords": false,
|
||||
"name": "mockName-1",
|
||||
"externalId": "mockExternalId-1",
|
||||
"readOnly": false,
|
||||
"id": "mockId-1"
|
||||
}
|
||||
],
|
||||
"ciphers": [
|
||||
{
|
||||
"notes": "mockNotes-1",
|
||||
"attachments": [
|
||||
{
|
||||
"fileName": "mockFileName-1",
|
||||
"size": 1,
|
||||
"sizeName": "mockSizeName-1",
|
||||
"id": "mockId-1",
|
||||
"url": "mockUrl-1",
|
||||
"key": "mockKey-1"
|
||||
}
|
||||
],
|
||||
"organizationUseTotp": false,
|
||||
"reprompt": 0,
|
||||
"edit": false,
|
||||
"passwordHistory": [
|
||||
{
|
||||
"password": "mockPassword-1",
|
||||
"lastUsedDate": "2023-10-27T12:00:00.00Z"
|
||||
}
|
||||
],
|
||||
"revisionDate": "2023-10-27T12:00:00.00Z",
|
||||
"type": 1,
|
||||
"login": {
|
||||
"uris": [
|
||||
{
|
||||
"match": 1,
|
||||
"uri": "mockUri-1"
|
||||
}
|
||||
],
|
||||
"totp": "mockTotp-1",
|
||||
"password": "mockPassword-1",
|
||||
"passwordRevisionDate": "2023-10-27T12:00:00.00Z",
|
||||
"autofillOnPageLoad": false,
|
||||
"uri": "mockUri-1",
|
||||
"username": "mockUsername-1"
|
||||
},
|
||||
"creationDate": "2023-10-27T12:00:00.00Z",
|
||||
"secureNote": {
|
||||
"type": 0
|
||||
},
|
||||
"folderId": "mockFolderId-1",
|
||||
"organizationId": "mockOrganizationId-1",
|
||||
"deletedDate": "2023-10-27T12:00:00.00Z",
|
||||
"identity": {
|
||||
"passportNumber": "mockPassportNumber-1",
|
||||
"lastName": "mockLastName-1",
|
||||
"address3": "mockAddress3-1",
|
||||
"address2": "mockAddress2-1",
|
||||
"city": "mockCity-1",
|
||||
"country": "mockCountry-1",
|
||||
"address1": "mockAddress1-1",
|
||||
"postalCode": "mockPostalCode-1",
|
||||
"title": "mockTitle-1",
|
||||
"ssn": "mockSsn-1",
|
||||
"firstName": "mockFirstName-1",
|
||||
"phone": "mockPhone-1",
|
||||
"middleName": "mockMiddleName-1",
|
||||
"company": "mockCompany-1",
|
||||
"licenseNumber": "mockLicenseNumber-1",
|
||||
"state": "mockState-1",
|
||||
"email": "mockEmail-1",
|
||||
"username": "mockUsername-1"
|
||||
},
|
||||
"collectionIds": [
|
||||
"mockCollectionId-1"
|
||||
],
|
||||
"name": "mockName-1",
|
||||
"id": "mockId-1"
|
||||
"fields": [
|
||||
{
|
||||
"linkedId": 100,
|
||||
"name": "mockName-1",
|
||||
"type": 1,
|
||||
"value": "mockValue-1"
|
||||
}
|
||||
],
|
||||
"viewPassword": false,
|
||||
"favorite": false,
|
||||
"card": {
|
||||
"number": "mockNumber-1",
|
||||
"expMonth": "mockExpMonth-1",
|
||||
"code": "mockCode-1",
|
||||
"expYear": "mockExpirationYear-1",
|
||||
"cardholderName": "mockCardholderName-1",
|
||||
"brand": "mockBrand-1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"domains": {
|
||||
"equivalentDomains": [
|
||||
[
|
||||
"mockEquivalentDomain-1"
|
||||
]
|
||||
],
|
||||
"globalEquivalentDomains": [
|
||||
{
|
||||
"type": 1,
|
||||
"domains": [
|
||||
"mockDomain-1"
|
||||
],
|
||||
"excluded": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"policies": [
|
||||
{
|
||||
"organizationId": "mockOrganizationId-1",
|
||||
"id": "mockId-1",
|
||||
"type": 1,
|
||||
"enabled": false
|
||||
}
|
||||
],
|
||||
"sends": [
|
||||
{
|
||||
"accessCount": 1,
|
||||
"notes": "mockNotes-1",
|
||||
"revisionDate": "2023-10-27T12:00:00.00Z",
|
||||
"maxAccessCount": 1,
|
||||
"hideEmail": false,
|
||||
"type": 1,
|
||||
"accessId": "mockAccessId-1",
|
||||
"password": "mockPassword-1",
|
||||
"file": {
|
||||
"fileName": "mockFileName-1",
|
||||
"size": 1,
|
||||
"sizeName": "mockSizeName-1",
|
||||
"id": "mockId-1"
|
||||
},
|
||||
"deletionDate": "2023-10-27T12:00:00.00Z",
|
||||
"name": "mockName-1",
|
||||
"disabled": false,
|
||||
"id": "mockId-1",
|
||||
"text": {
|
||||
"hidden": false,
|
||||
"text": "mockText-1"
|
||||
},
|
||||
"key": "mockKey-1",
|
||||
"expirationDate": "2023-10-27T12:00:00.00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
|
||||
private val SYNC_SUCCESS = SyncResponseJson(
|
||||
folders = listOf(createMockFolder(number = 1)),
|
||||
collections = listOf(createMockCollection(number = 1)),
|
||||
profile = createMockProfile(number = 1),
|
||||
ciphers = listOf(createMockCipher(number = 1)),
|
||||
policies = listOf(createMockPolicy(number = 1)),
|
||||
domains = createMockDomains(number = 1),
|
||||
sends = listOf(createMockSend(number = 1)),
|
||||
)
|
|
@ -0,0 +1,159 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
import com.bitwarden.core.Attachment
|
||||
import com.bitwarden.core.Card
|
||||
import com.bitwarden.core.Cipher
|
||||
import com.bitwarden.core.CipherRepromptType
|
||||
import com.bitwarden.core.CipherType
|
||||
import com.bitwarden.core.Field
|
||||
import com.bitwarden.core.FieldType
|
||||
import com.bitwarden.core.Identity
|
||||
import com.bitwarden.core.Login
|
||||
import com.bitwarden.core.LoginUri
|
||||
import com.bitwarden.core.PasswordHistory
|
||||
import com.bitwarden.core.SecureNote
|
||||
import com.bitwarden.core.SecureNoteType
|
||||
import com.bitwarden.core.UriMatchType
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* Create a mock [Cipher] with a given [number].
|
||||
*/
|
||||
fun createMockSdkCipher(number: Int): Cipher =
|
||||
Cipher(
|
||||
id = "mockId-$number",
|
||||
organizationId = "mockOrganizationId-$number",
|
||||
folderId = "mockFolderId-$number",
|
||||
collectionIds = listOf("mockCollectionId-$number"),
|
||||
name = "mockName-$number",
|
||||
notes = "mockNotes-$number",
|
||||
type = CipherType.LOGIN,
|
||||
login = createMockSdkLogin(number = number),
|
||||
creationDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
deletedDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
revisionDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
attachments = listOf(createMockSdkAttachment(number = number)),
|
||||
card = createMockSdkCard(number = number),
|
||||
fields = listOf(createMockSdkField(number = number)),
|
||||
identity = createMockSdkIdentity(number = number),
|
||||
favorite = false,
|
||||
passwordHistory = listOf(createMockSdkPasswordHistory(number = number)),
|
||||
reprompt = CipherRepromptType.NONE,
|
||||
secureNote = createMockSdkSecureNote(),
|
||||
edit = false,
|
||||
organizationUseTotp = false,
|
||||
viewPassword = false,
|
||||
localData = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SecureNote] with a given [number].
|
||||
*/
|
||||
fun createMockSdkSecureNote(): SecureNote =
|
||||
SecureNote(
|
||||
type = SecureNoteType.GENERIC,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [PasswordHistory] with a given [number].
|
||||
*/
|
||||
fun createMockSdkPasswordHistory(number: Int): PasswordHistory =
|
||||
PasswordHistory(
|
||||
password = "mockPassword-$number",
|
||||
lastUsedDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [Identity] with a given [number].
|
||||
*/
|
||||
fun createMockSdkIdentity(number: Int): Identity =
|
||||
Identity(
|
||||
firstName = "mockFirstName-$number",
|
||||
middleName = "mockMiddleName-$number",
|
||||
lastName = "mockLastName-$number",
|
||||
passportNumber = "mockPassportNumber-$number",
|
||||
country = "mockCountry-$number",
|
||||
address1 = "mockAddress1-$number",
|
||||
address2 = "mockAddress2-$number",
|
||||
address3 = "mockAddress3-$number",
|
||||
city = "mockCity-$number",
|
||||
postalCode = "mockPostalCode-$number",
|
||||
title = "mockTitle-$number",
|
||||
ssn = "mockSsn-$number",
|
||||
phone = "mockPhone-$number",
|
||||
company = "mockCompany-$number",
|
||||
licenseNumber = "mockLicenseNumber-$number",
|
||||
state = "mockState-$number",
|
||||
email = "mockEmail-$number",
|
||||
username = "mockUsername-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [Field] with a given [number].
|
||||
*/
|
||||
fun createMockSdkField(number: Int): Field =
|
||||
Field(
|
||||
linkedId = 100U,
|
||||
name = "mockName-$number",
|
||||
type = FieldType.HIDDEN,
|
||||
value = "mockValue-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [Card] with a given [number].
|
||||
*/
|
||||
fun createMockSdkCard(number: Int): Card =
|
||||
Card(
|
||||
number = "mockNumber-$number",
|
||||
expMonth = "mockExpMonth-$number",
|
||||
code = "mockCode-$number",
|
||||
expYear = "mockExpirationYear-$number",
|
||||
cardholderName = "mockCardholderName-$number",
|
||||
brand = "mockBrand-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [Attachment] with a given [number].
|
||||
*/
|
||||
fun createMockSdkAttachment(number: Int): Attachment =
|
||||
Attachment(
|
||||
fileName = "mockFileName-$number",
|
||||
size = "1",
|
||||
sizeName = "mockSizeName-$number",
|
||||
id = "mockId-$number",
|
||||
url = "mockUrl-$number",
|
||||
key = "mockKey-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [Login] with a given [number].
|
||||
*/
|
||||
fun createMockSdkLogin(number: Int): Login =
|
||||
Login(
|
||||
username = "mockUsername-$number",
|
||||
password = "mockPassword-$number",
|
||||
passwordRevisionDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
autofillOnPageLoad = false,
|
||||
uris = listOf(createMockSdkUri(number = number)),
|
||||
totp = "mockTotp-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [LoginUri] with a given [number].
|
||||
*/
|
||||
fun createMockSdkUri(number: Int): LoginUri =
|
||||
LoginUri(
|
||||
uri = "mockUri-$number",
|
||||
match = UriMatchType.HOST,
|
||||
)
|
|
@ -0,0 +1,17 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
import com.bitwarden.core.Folder
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* Create a mock [Folder] with a given [number].
|
||||
*/
|
||||
fun createMockSdkFolder(number: Int): Folder =
|
||||
Folder(
|
||||
id = "mockId-$number",
|
||||
name = "mockName-$number",
|
||||
revisionDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
)
|
|
@ -0,0 +1,115 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
import com.bitwarden.core.Cipher
|
||||
import com.bitwarden.core.CipherListView
|
||||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.sdk.ClientVault
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultSdkSourceTest {
|
||||
private val clientVault = mockk<ClientVault>()
|
||||
|
||||
private val vaultSdkSource: VaultSdkSource = VaultSdkSourceImpl(
|
||||
clientVault = clientVault,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `Cipher decrypt should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val mockCipher = mockk<Cipher>()
|
||||
val expectedResult = mockk<CipherView>()
|
||||
coEvery {
|
||||
clientVault.ciphers().decrypt(
|
||||
cipher = mockCipher,
|
||||
)
|
||||
} returns expectedResult
|
||||
val result = vaultSdkSource.decryptCipher(
|
||||
cipher = mockCipher,
|
||||
)
|
||||
assertEquals(
|
||||
expectedResult.asSuccess(),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientVault.ciphers().decrypt(
|
||||
cipher = mockCipher,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Cipher decryptList should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val mockCiphers = mockk<List<Cipher>>()
|
||||
val expectedResult = mockk<List<CipherListView>>()
|
||||
coEvery {
|
||||
clientVault.ciphers().decryptList(
|
||||
ciphers = mockCiphers,
|
||||
)
|
||||
} returns expectedResult
|
||||
val result = vaultSdkSource.decryptCipherList(
|
||||
cipherList = mockCiphers,
|
||||
)
|
||||
assertEquals(
|
||||
expectedResult.asSuccess(),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientVault.ciphers().decryptList(
|
||||
ciphers = mockCiphers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Folder decrypt should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val mockFolder = mockk<Folder>()
|
||||
val expectedResult = mockk<FolderView>()
|
||||
coEvery {
|
||||
clientVault.folders().decrypt(
|
||||
folder = mockFolder,
|
||||
)
|
||||
} returns expectedResult
|
||||
val result = vaultSdkSource.decryptFolder(
|
||||
folder = mockFolder,
|
||||
)
|
||||
assertEquals(
|
||||
expectedResult.asSuccess(),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientVault.folders().decrypt(
|
||||
folder = mockFolder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Folder decryptList should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val mockFolders = mockk<List<Folder>>()
|
||||
val expectedResult = mockk<List<FolderView>>()
|
||||
coEvery {
|
||||
clientVault.folders().decryptList(
|
||||
folders = mockFolders,
|
||||
)
|
||||
} returns expectedResult
|
||||
val result = vaultSdkSource.decryptFolderList(
|
||||
folderList = mockFolders,
|
||||
)
|
||||
assertEquals(
|
||||
expectedResult.asSuccess(),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientVault.folders().decryptList(
|
||||
folders = mockFolders,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.util
|
||||
|
||||
import com.bitwarden.core.CipherRepromptType
|
||||
import com.bitwarden.core.CipherType
|
||||
import com.bitwarden.core.FieldType
|
||||
import com.bitwarden.core.UriMatchType
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherRepromptTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FieldTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UriMatchTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockAttachment
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCard
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockField
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockIdentity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockLogin
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPasswordHistory
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSecureNote
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockUri
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkAttachment
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCard
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkField
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkIdentity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkLogin
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkPasswordHistory
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkSecureNote
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkUri
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class VaultSdkCipherExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `toEncryptedSdkCipherList should convert list of Network Cipher to List of Sdk Cipher`() {
|
||||
val syncCiphers = listOf(
|
||||
createMockCipher(number = 1),
|
||||
createMockCipher(number = 2),
|
||||
)
|
||||
val sdkCiphers = syncCiphers.toEncryptedSdkCipherList()
|
||||
assertEquals(
|
||||
listOf(
|
||||
createMockSdkCipher(number = 1),
|
||||
createMockSdkCipher(number = 2),
|
||||
),
|
||||
sdkCiphers,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toEncryptedSdkCipher should convert a SyncResponseJson Cipher to a Cipher`() {
|
||||
val syncCipher = createMockCipher(number = 1)
|
||||
val sdkCipher = syncCipher.toEncryptedSdkCipher()
|
||||
assertEquals(
|
||||
createMockSdkCipher(number = 1),
|
||||
sdkCipher,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkLogin should convert a SyncResponseJson Cipher Login to a Login`() {
|
||||
val syncLogin = createMockLogin(number = 1)
|
||||
val sdkLogin = syncLogin.toSdkLogin()
|
||||
assertEquals(
|
||||
createMockSdkLogin(number = 1),
|
||||
sdkLogin,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkIdentity should convert a SyncResponseJson Cipher Identity to a Identity`() {
|
||||
val syncIdentity = createMockIdentity(number = 1)
|
||||
val sdkIdentity = syncIdentity.toSdkIdentity()
|
||||
assertEquals(
|
||||
createMockSdkIdentity(number = 1),
|
||||
sdkIdentity,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkCard should convert a SyncResponseJson Cipher Card to a Card`() {
|
||||
val syncCard = createMockCard(number = 1)
|
||||
val sdkCard = syncCard.toSdkCard()
|
||||
assertEquals(
|
||||
createMockSdkCard(number = 1),
|
||||
sdkCard,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkSecureNote should convert a SyncResponseJson Cipher SecureNote to a SecureNote`() {
|
||||
val syncSecureNote = createMockSecureNote()
|
||||
val sdkSecureNote = syncSecureNote.toSdkSecureNote()
|
||||
assertEquals(
|
||||
createMockSdkSecureNote(),
|
||||
sdkSecureNote,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkLoginUriList should convert list of LoginUri to List of Sdk LoginUri`() {
|
||||
val syncLoginUris = listOf(
|
||||
createMockUri(number = 1),
|
||||
createMockUri(number = 2),
|
||||
)
|
||||
val sdkLoginUris = syncLoginUris.toSdkLoginUriList()
|
||||
assertEquals(
|
||||
listOf(
|
||||
createMockSdkUri(number = 1),
|
||||
createMockSdkUri(number = 2),
|
||||
),
|
||||
sdkLoginUris,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkLoginUri should convert Network Cipher LoginUri to Sdk LoginUri`() {
|
||||
val syncLoginUri = createMockUri(number = 1)
|
||||
val sdkLoginUri = syncLoginUri.toSdkLoginUri()
|
||||
assertEquals(
|
||||
createMockSdkUri(number = 1),
|
||||
sdkLoginUri,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkAttachmentList should convert list of Attachment to List of Sdk Attachment`() {
|
||||
val syncAttachments = listOf(
|
||||
createMockAttachment(number = 1),
|
||||
createMockAttachment(number = 2),
|
||||
)
|
||||
val sdkAttachments = syncAttachments.toSdkAttachmentList()
|
||||
assertEquals(
|
||||
listOf(
|
||||
createMockSdkAttachment(number = 1),
|
||||
createMockSdkAttachment(number = 2),
|
||||
),
|
||||
sdkAttachments,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkAttachment should convert Network Cipher Attachment to Sdk Attachment`() {
|
||||
val syncAttachment = createMockAttachment(number = 1)
|
||||
val sdkAttachment = syncAttachment.toSdkAttachment()
|
||||
assertEquals(
|
||||
createMockSdkAttachment(number = 1),
|
||||
sdkAttachment,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkFieldList should convert list of Network Cipher Field to List of Sdk Field`() {
|
||||
val syncFields = listOf(
|
||||
createMockField(number = 1),
|
||||
createMockField(number = 2),
|
||||
)
|
||||
val sdkFields = syncFields.toSdkFieldList()
|
||||
assertEquals(
|
||||
listOf(
|
||||
createMockSdkField(number = 1),
|
||||
createMockSdkField(number = 2),
|
||||
),
|
||||
sdkFields,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkField should convert Network Cipher Attachment to Sdk Attachment`() {
|
||||
val syncField = createMockField(number = 1)
|
||||
val sdkField = syncField.toSdkField()
|
||||
assertEquals(
|
||||
createMockSdkField(number = 1),
|
||||
sdkField,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `toSdkPasswordHistoryList should convert PasswordHistory list to Sdk PasswordHistory List`() {
|
||||
val syncPasswordHistories = listOf(
|
||||
createMockPasswordHistory(number = 1),
|
||||
createMockPasswordHistory(number = 2),
|
||||
)
|
||||
val sdkPasswordHistories = syncPasswordHistories.toSdkPasswordHistoryList()
|
||||
assertEquals(
|
||||
listOf(
|
||||
createMockSdkPasswordHistory(number = 1),
|
||||
createMockSdkPasswordHistory(number = 2),
|
||||
),
|
||||
sdkPasswordHistories,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkPasswordHistory should convert PasswordHistory to Sdk PasswordHistory`() {
|
||||
val syncPasswordHistory = createMockPasswordHistory(number = 1)
|
||||
val sdkPasswordHistory = syncPasswordHistory.toSdkPasswordHistory()
|
||||
assertEquals(
|
||||
createMockSdkPasswordHistory(number = 1),
|
||||
sdkPasswordHistory,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkCipherType should convert CipherTypeJson to CipherType`() {
|
||||
val cipherType = CipherTypeJson.IDENTITY
|
||||
val sdkCipherType = cipherType.toSdkCipherType()
|
||||
assertEquals(
|
||||
CipherType.IDENTITY,
|
||||
sdkCipherType,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkMatchType should convert UriMatchTypeJson to UriMatchType`() {
|
||||
val uriMatchType = UriMatchTypeJson.DOMAIN
|
||||
val sdkUriMatchType = uriMatchType.toSdkMatchType()
|
||||
assertEquals(
|
||||
UriMatchType.DOMAIN,
|
||||
sdkUriMatchType,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkRepromptType should convert CipherRepromptTypeJson to CipherRepromptType`() {
|
||||
val repromptType = CipherRepromptTypeJson.NONE
|
||||
val sdkRepromptType = repromptType.toSdkRepromptType()
|
||||
assertEquals(
|
||||
CipherRepromptType.NONE,
|
||||
sdkRepromptType,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSdkFieldType should convert FieldTypeJson to FieldType`() {
|
||||
val fieldType = FieldTypeJson.HIDDEN
|
||||
val sdkFieldType = fieldType.toSdkFieldType()
|
||||
assertEquals(
|
||||
FieldType.HIDDEN,
|
||||
sdkFieldType,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.util
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFolder
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkFolder
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class VaultSdkFolderExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `toEncryptedSdkFolderList should convert list of NetworkFolder to List of SdkFolder`() {
|
||||
val syncFolders = listOf(
|
||||
createMockFolder(number = 1),
|
||||
createMockFolder(number = 2),
|
||||
)
|
||||
val sdkFolders = syncFolders.toEncryptedSdkFolderList()
|
||||
assertEquals(
|
||||
listOf(
|
||||
createMockSdkFolder(number = 1),
|
||||
createMockSdkFolder(number = 2),
|
||||
),
|
||||
sdkFolders,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toEncryptedSdkFolder should convert a NetworkFolder to a SdkFolder`() {
|
||||
val syncFolder = createMockFolder(number = 1)
|
||||
val sdkFolder = syncFolder.toEncryptedSdkFolder()
|
||||
assertEquals(
|
||||
createMockSdkFolder(number = 1),
|
||||
sdkFolder,
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue