Skip to content

Commit b4917ce

Browse files
chore: Implement Folder Repo interface for Bitwarden SDK (#6691)
1 parent 2b69753 commit b4917ce

10 files changed

Lines changed: 527 additions & 2 deletions

File tree

app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/sdk/SdkRepositoryFactoryImpl.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class SdkRepositoryFactoryImpl(
2323
) : SdkRepositoryFactory {
2424
override fun getRepositories(userId: String?): Repositories =
2525
Repositories(
26-
cipher = getSdkRepository(userId = userId),
26+
cipher = getSdkCipherRepository(userId = userId),
2727
folder = null,
2828
userKeyState = null,
2929
localUserDataKeyState = SdkLocalUserDataKeyStateRepository(
@@ -46,7 +46,7 @@ class SdkRepositoryFactoryImpl(
4646
configDiskSource = configDiskSource,
4747
)
4848

49-
private fun getSdkRepository(
49+
private fun getSdkCipherRepository(
5050
userId: String?,
5151
): SdkCipherRepository? = userId?.let {
5252
SdkCipherRepository(userId = it, vaultDiskSource = vaultDiskSource)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.x8bit.bitwarden.data.platform.manager.sdk.repository
2+
3+
import com.bitwarden.sdk.FolderRepository
4+
import com.bitwarden.vault.Folder
5+
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
6+
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkFolderResponse
7+
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolder
8+
import timber.log.Timber
9+
10+
/**
11+
* A user-scoped implementation of a Bitwarden SDK [FolderRepository].
12+
*/
13+
class SdkFolderRepository(
14+
private val userId: String,
15+
private val vaultDiskSource: VaultDiskSource,
16+
) : FolderRepository {
17+
override suspend fun get(id: String): Folder? =
18+
vaultDiskSource
19+
.getFolder(userId = userId, folderId = id)
20+
?.toEncryptedSdkFolder()
21+
22+
override suspend fun list(): List<Folder> =
23+
vaultDiskSource
24+
.getFolders(userId = userId)
25+
.map { it.toEncryptedSdkFolder() }
26+
27+
override suspend fun set(id: String, value: Folder) {
28+
if (id != value.id) {
29+
Timber.e("SDK Folder 'set' operation: ID's do not match")
30+
return
31+
}
32+
vaultDiskSource.saveFolder(
33+
userId = userId,
34+
folder = value.toEncryptedNetworkFolderResponse(),
35+
)
36+
}
37+
38+
override suspend fun setBulk(values: Map<String, Folder>) {
39+
val validEntries = values.filter { (id, cipher) ->
40+
if (id != cipher.id) {
41+
Timber.e("SDK Folder 'setBulk' operation: ID's do not match for '$id'")
42+
false
43+
} else {
44+
true
45+
}
46+
}
47+
if (validEntries.isEmpty()) return
48+
vaultDiskSource.saveFolders(
49+
userId = userId,
50+
folders = validEntries.values.map { it.toEncryptedNetworkFolderResponse() },
51+
)
52+
}
53+
54+
override suspend fun remove(id: String) {
55+
vaultDiskSource.deleteFolder(userId = userId, folderId = id)
56+
}
57+
58+
override suspend fun removeBulk(keys: List<String>) {
59+
if (keys.isEmpty()) return
60+
vaultDiskSource.deleteSelectedFolders(userId = userId, folderIds = keys)
61+
}
62+
63+
override suspend fun removeAll() {
64+
vaultDiskSource.deleteAllFolders(userId = userId)
65+
}
66+
67+
override suspend fun has(id: String): Boolean = this.get(id = id) != null
68+
}

app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSource.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,36 @@ interface VaultDiskSource {
9292
*/
9393
suspend fun deleteFolder(userId: String, folderId: String)
9494

95+
/**
96+
* Deletes folders with the given [folderIds] from the data source for the given [userId].
97+
*/
98+
suspend fun deleteSelectedFolders(userId: String, folderIds: List<String>)
99+
100+
/**
101+
* Deletes all folders from the data source for the given [userId].
102+
*/
103+
suspend fun deleteAllFolders(userId: String)
104+
95105
/**
96106
* Saves a folder to the data source for the given [userId].
97107
*/
98108
suspend fun saveFolder(userId: String, folder: SyncResponseJson.Folder)
99109

110+
/**
111+
* Saves multiple folders to the data source for the given [userId].
112+
*/
113+
suspend fun saveFolders(userId: String, folders: List<SyncResponseJson.Folder>)
114+
115+
/**
116+
* Retrieves a folder from the data source for a given [userId] and [folderId].
117+
*/
118+
suspend fun getFolder(userId: String, folderId: String): SyncResponseJson.Folder?
119+
120+
/**
121+
* Retrieves all folders from the data source for a given [userId].
122+
*/
123+
suspend fun getFolders(userId: String): List<SyncResponseJson.Folder>
124+
100125
/**
101126
* Retrieves all folders from the data source for a given [userId].
102127
*/

app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ class VaultDiskSourceImpl(
241241
foldersDao.deleteFolder(userId = userId, folderId = folderId)
242242
}
243243

244+
override suspend fun deleteSelectedFolders(userId: String, folderIds: List<String>) {
245+
foldersDao.deleteSelectedFolders(userId = userId, folderIds = folderIds)
246+
}
247+
248+
override suspend fun deleteAllFolders(userId: String) {
249+
foldersDao.deleteAllFolders(userId = userId)
250+
}
251+
244252
override suspend fun saveFolder(userId: String, folder: SyncResponseJson.Folder) {
245253
foldersDao.insertFolder(
246254
folder = FolderEntity(
@@ -252,6 +260,44 @@ class VaultDiskSourceImpl(
252260
)
253261
}
254262

263+
override suspend fun saveFolders(userId: String, folders: List<SyncResponseJson.Folder>) {
264+
foldersDao.insertFolders(
265+
folders = folders.map { folder ->
266+
FolderEntity(
267+
id = folder.id,
268+
userId = userId,
269+
name = folder.name,
270+
revisionDate = folder.revisionDate,
271+
)
272+
},
273+
)
274+
}
275+
276+
override suspend fun getFolder(
277+
userId: String,
278+
folderId: String,
279+
): SyncResponseJson.Folder? =
280+
foldersDao
281+
.getFolder(userId = userId, folderId = folderId)
282+
?.let { folder ->
283+
SyncResponseJson.Folder(
284+
id = folder.id,
285+
name = folder.name,
286+
revisionDate = folder.revisionDate,
287+
)
288+
}
289+
290+
override suspend fun getFolders(userId: String): List<SyncResponseJson.Folder> =
291+
foldersDao
292+
.getAllFolders(userId = userId)
293+
.map { folder ->
294+
SyncResponseJson.Folder(
295+
id = folder.id,
296+
name = folder.name,
297+
revisionDate = folder.revisionDate,
298+
)
299+
}
300+
255301
override fun getFoldersFlow(
256302
userId: String,
257303
): Flow<List<SyncResponseJson.Folder>> =

app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/disk/dao/FoldersDao.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,30 @@ interface FoldersDao {
3535
userId: String,
3636
): Flow<List<FolderEntity>>
3737

38+
/**
39+
* Retrieves all folders from the database for a given [userId].
40+
*/
41+
@Query("SELECT * FROM folders WHERE user_id = :userId")
42+
fun getAllFolders(
43+
userId: String,
44+
): List<FolderEntity>
45+
46+
/**
47+
* Retrieves a folder from the database for a given [userId] and [folderId].
48+
*/
49+
@Query("SELECT * FROM folders WHERE user_id = :userId AND id = :folderId LIMIT 1")
50+
suspend fun getFolder(
51+
userId: String,
52+
folderId: String,
53+
): FolderEntity?
54+
55+
/**
56+
* Deletes the stored folders associated with the given [userId] whose IDs are in [folderIds].
57+
* This will return the number of rows deleted by this query.
58+
*/
59+
@Query("DELETE FROM folders WHERE user_id = :userId AND id IN (:folderIds)")
60+
suspend fun deleteSelectedFolders(userId: String, folderIds: List<String>): Int
61+
3862
/**
3963
* Deletes all the stored folders associated with the given [userId]. This will return the
4064
* number of rows deleted by this query.

app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkFolderExtensions.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ fun SyncResponseJson.Folder.toEncryptedSdkFolder(): Folder =
2424
revisionDate = revisionDate,
2525
)
2626

27+
/**
28+
* Converts a Bitwarden SDK [Folder] object to a corresponding [SyncResponseJson.Folder] object.
29+
*/
30+
fun Folder.toEncryptedNetworkFolderResponse(): SyncResponseJson.Folder =
31+
SyncResponseJson.Folder(
32+
id = id.orEmpty(),
33+
name = name,
34+
revisionDate = revisionDate,
35+
)
36+
2737
/**
2838
* Converts a Bitwarden SDK [Folder] objects to a corresponding
2939
* [SyncResponseJson.Folder] object.

0 commit comments

Comments
 (0)