mirror of
https://github.com/bitwarden/android.git
synced 2024-11-21 17:05:44 +03:00
PM-14433: Null domain data (#4268)
Co-authored-by: Dave Severns <149429124+dseverns-livefront@users.noreply.github.com> Co-authored-by: David Perez <david@livefront.com>
This commit is contained in:
parent
54d3b34876
commit
fe84feb184
9 changed files with 274 additions and 16 deletions
|
@ -0,0 +1,256 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 5,
|
||||
"identityHash": "ee697e71290c92fe5b607d0b7665481b",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "ciphers",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `cipher_type` TEXT NOT NULL, `cipher_json` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userId",
|
||||
"columnName": "user_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "cipherType",
|
||||
"columnName": "cipher_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "cipherJson",
|
||||
"columnName": "cipher_json",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_ciphers_user_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"user_id"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_ciphers_user_id` ON `${TABLE_NAME}` (`user_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "collections",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `organization_id` TEXT NOT NULL, `should_hide_passwords` INTEGER NOT NULL, `name` TEXT NOT NULL, `external_id` TEXT, `read_only` INTEGER NOT NULL, `manage` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userId",
|
||||
"columnName": "user_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "organizationId",
|
||||
"columnName": "organization_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "shouldHidePasswords",
|
||||
"columnName": "should_hide_passwords",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "externalId",
|
||||
"columnName": "external_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isReadOnly",
|
||||
"columnName": "read_only",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "canManage",
|
||||
"columnName": "manage",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_collections_user_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"user_id"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_collections_user_id` ON `${TABLE_NAME}` (`user_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "domains",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_id` TEXT NOT NULL, `domains_json` TEXT, PRIMARY KEY(`user_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "userId",
|
||||
"columnName": "user_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "domainsJson",
|
||||
"columnName": "domains_json",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"user_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "folders",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `name` TEXT, `revision_date` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userId",
|
||||
"columnName": "user_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "revisionDate",
|
||||
"columnName": "revision_date",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_folders_user_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"user_id"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_folders_user_id` ON `${TABLE_NAME}` (`user_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "sends",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `send_type` TEXT NOT NULL, `send_json` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userId",
|
||||
"columnName": "user_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sendType",
|
||||
"columnName": "send_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sendJson",
|
||||
"columnName": "send_json",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_sends_user_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"user_id"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_sends_user_id` ON `${TABLE_NAME}` (`user_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ee697e71290c92fe5b607d0b7665481b')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ interface VaultDiskSource {
|
|||
/**
|
||||
* Retrieves all domains from the data source for a given [userId].
|
||||
*/
|
||||
fun getDomains(userId: String): Flow<SyncResponseJson.Domains>
|
||||
fun getDomains(userId: String): Flow<SyncResponseJson.Domains?>
|
||||
|
||||
/**
|
||||
* Deletes a folder from the data source for the given [userId] and [folderId].
|
||||
|
|
|
@ -122,13 +122,13 @@ class VaultDiskSourceImpl(
|
|||
},
|
||||
)
|
||||
|
||||
override fun getDomains(userId: String): Flow<SyncResponseJson.Domains> =
|
||||
override fun getDomains(userId: String): Flow<SyncResponseJson.Domains?> =
|
||||
domainsDao
|
||||
.getDomains(userId)
|
||||
.filterNotNull()
|
||||
.map { entity ->
|
||||
withContext(dispatcherManager.default) {
|
||||
json.decodeFromString<SyncResponseJson.Domains>(entity.domainsJson)
|
||||
entity?.domainsJson?.let { json.decodeFromString<SyncResponseJson.Domains>(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,7 @@ class VaultDiskSourceImpl(
|
|||
domainsDao.insertDomains(
|
||||
domains = DomainsEntity(
|
||||
userId = userId,
|
||||
domainsJson = json.encodeToString(vault.domains),
|
||||
domainsJson = vault.domains?.let { json.encodeToString(it) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.entity.SendEntity
|
|||
FolderEntity::class,
|
||||
SendEntity::class,
|
||||
],
|
||||
version = 4,
|
||||
version = 5,
|
||||
exportSchema = true,
|
||||
)
|
||||
@TypeConverters(ZonedDateTimeTypeConverter::class)
|
||||
|
|
|
@ -14,5 +14,5 @@ data class DomainsEntity(
|
|||
val userId: String,
|
||||
|
||||
@ColumnInfo(name = "domains_json")
|
||||
val domainsJson: String,
|
||||
val domainsJson: String?,
|
||||
)
|
||||
|
|
|
@ -39,7 +39,7 @@ data class SyncResponseJson(
|
|||
val policies: List<Policy>?,
|
||||
|
||||
@SerialName("domains")
|
||||
val domains: Domains,
|
||||
val domains: Domains?,
|
||||
|
||||
@SerialName("sends")
|
||||
val sends: List<Send>?,
|
||||
|
|
|
@ -6,14 +6,14 @@ import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
|||
/**
|
||||
* Map the API [Domains] model to the internal [DomainsData] model.
|
||||
*/
|
||||
fun Domains.toDomainsData(): DomainsData {
|
||||
fun Domains?.toDomainsData(): DomainsData {
|
||||
val globalEquivalentDomains = this
|
||||
.globalEquivalentDomains
|
||||
?.globalEquivalentDomains
|
||||
?.map { it.toInternalModel() }
|
||||
.orEmpty()
|
||||
|
||||
return DomainsData(
|
||||
equivalentDomains = this.equivalentDomains.orEmpty(),
|
||||
equivalentDomains = this?.equivalentDomains.orEmpty(),
|
||||
globalEquivalentDomains = globalEquivalentDomains,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -248,10 +248,13 @@ class VaultDiskSourceTest {
|
|||
// We cannot compare the JSON strings directly because of formatting differences
|
||||
// So we split that off into its own assertion.
|
||||
assertEquals(
|
||||
DOMAINS_ENTITY.copy(domainsJson = ""),
|
||||
storedDomainsEntity.copy(domainsJson = ""),
|
||||
DOMAINS_ENTITY.copy(domainsJson = null),
|
||||
storedDomainsEntity.copy(domainsJson = null),
|
||||
)
|
||||
assertJsonEquals(
|
||||
requireNotNull(DOMAINS_ENTITY.domainsJson),
|
||||
requireNotNull(storedDomainsEntity.domainsJson),
|
||||
)
|
||||
assertJsonEquals(DOMAINS_ENTITY.domainsJson, storedDomainsEntity.domainsJson)
|
||||
|
||||
// Verify the folders dao is updated
|
||||
assertEquals(listOf(FOLDER_ENTITY), foldersDao.storedFolders)
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.x8bit.bitwarden.data.vault.datasource.disk.dao
|
|||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.DomainsEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
|
||||
class FakeDomainsDao : DomainsDao {
|
||||
var storedDomains: DomainsEntity? = null
|
||||
|
@ -18,9 +17,9 @@ class FakeDomainsDao : DomainsDao {
|
|||
deleteDomainsCalled = true
|
||||
}
|
||||
|
||||
override fun getDomains(userId: String): Flow<DomainsEntity> {
|
||||
override fun getDomains(userId: String): Flow<DomainsEntity?> {
|
||||
getDomainsCalled = true
|
||||
return mutableDomainsFlow.filterNotNull()
|
||||
return mutableDomainsFlow
|
||||
}
|
||||
|
||||
override suspend fun insertDomains(domains: DomainsEntity) {
|
||||
|
|
Loading…
Reference in a new issue