Реализован UnlockManager
This commit is contained in:
@@ -2,14 +2,14 @@ package com.github.nullptroma.wallenc.data.db.app
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyDao
|
||||
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKey
|
||||
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao
|
||||
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap
|
||||
|
||||
interface IAppDb {
|
||||
val storageKeyDao: StorageKeyDao
|
||||
val storageKeyMapDao: StorageKeyMapDao
|
||||
}
|
||||
|
||||
@Database(entities = [DbStorageKey::class], version = 1, exportSchema = false)
|
||||
@Database(entities = [DbStorageKeyMap::class], version = 2, exportSchema = false)
|
||||
abstract class AppDb : IAppDb, RoomDatabase() {
|
||||
abstract override val storageKeyDao: StorageKeyDao
|
||||
abstract override val storageKeyMapDao: StorageKeyMapDao
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.github.nullptroma.wallenc.data.db.app.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
|
||||
@Dao
|
||||
interface StorageKeyDao {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.github.nullptroma.wallenc.data.db.app.dao
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
@Dao
|
||||
interface StorageKeyMapDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun add(keymap: DbStorageKeyMap)
|
||||
|
||||
@Query("SELECT * FROM storage_key_maps")
|
||||
fun getAll(): List<DbStorageKeyMap>
|
||||
|
||||
@Delete
|
||||
fun delete(keymap: DbStorageKeyMap)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.github.nullptroma.wallenc.data.db.app.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
|
||||
@Entity(tableName = "storage_keys", primaryKeys = [ "source_uuid", "dest_uuid" ])
|
||||
data class DbStorageKey(
|
||||
@ColumnInfo(name = "source_uuid") val sourceUuid: String,
|
||||
@ColumnInfo(name = "dest_uuid") val destUuid: String,
|
||||
@ColumnInfo(name = "key") val key: String
|
||||
)
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.github.nullptroma.wallenc.data.db.app.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import com.github.nullptroma.wallenc.data.db.app.repository.StorageKeyMapRepository
|
||||
import com.github.nullptroma.wallenc.data.model.StorageKeyMap
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
import java.util.UUID
|
||||
|
||||
@Entity(tableName = "storage_key_maps", primaryKeys = [ "source_uuid", "dest_uuid" ])
|
||||
data class DbStorageKeyMap(
|
||||
@ColumnInfo(name = "source_uuid") val sourceUuid: String,
|
||||
@ColumnInfo(name = "dest_uuid") val destUuid: String,
|
||||
@ColumnInfo(name = "key") val key: ByteArray
|
||||
) {
|
||||
fun toModel(): StorageKeyMap {
|
||||
return StorageKeyMap(
|
||||
sourceUuid = UUID.fromString(sourceUuid),
|
||||
destUuid = UUID.fromString(destUuid),
|
||||
key = EncryptKey(key)
|
||||
)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as DbStorageKeyMap
|
||||
|
||||
if (sourceUuid != other.sourceUuid) return false
|
||||
if (destUuid != other.destUuid) return false
|
||||
if (!key.contentEquals(other.key)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = sourceUuid.hashCode()
|
||||
result = 31 * result + destUuid.hashCode()
|
||||
result = 31 * result + key.contentHashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromModel(keymap: StorageKeyMap): DbStorageKeyMap {
|
||||
return DbStorageKeyMap(
|
||||
sourceUuid = keymap.sourceUuid.toString(),
|
||||
destUuid = keymap.destUuid.toString(),
|
||||
key = keymap.key.bytes
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.github.nullptroma.wallenc.data.db.app.repository
|
||||
|
||||
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao
|
||||
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap
|
||||
import com.github.nullptroma.wallenc.data.model.StorageKeyMap
|
||||
|
||||
class StorageKeyMapRepository(private val dao: StorageKeyMapDao) {
|
||||
fun getAll() = dao.getAll().map { it.toModel() }
|
||||
fun add(keymap: StorageKeyMap) {
|
||||
val dbModel = DbStorageKeyMap.fromModel(keymap)
|
||||
dao.add(dbModel)
|
||||
}
|
||||
fun delete(keymap: StorageKeyMap) {
|
||||
val dbModel = DbStorageKeyMap.fromModel(keymap)
|
||||
dao.delete(dbModel)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.github.nullptroma.wallenc.data.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
import java.util.UUID
|
||||
|
||||
data class StorageKeyMap(
|
||||
val sourceUuid: UUID,
|
||||
val destUuid: UUID,
|
||||
val key: EncryptKey
|
||||
)
|
||||
@@ -1,34 +1,106 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults
|
||||
|
||||
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyDao
|
||||
import com.github.nullptroma.wallenc.data.db.app.repository.StorageKeyMapRepository
|
||||
import com.github.nullptroma.wallenc.data.model.StorageKeyMap
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.EncryptedStorage
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.UUID
|
||||
|
||||
class UnlockManager(dao: StorageKeyDao, ioDispatcher: CoroutineDispatcher): IUnlockManager {
|
||||
private val _openedStorages = MutableStateFlow<Map<UUID, EncryptedStorage>>(mapOf())
|
||||
override val openedStorages: StateFlow<Map<UUID, IStorage>>
|
||||
class UnlockManager(
|
||||
private val repo: StorageKeyMapRepository,
|
||||
private val ioDispatcher: CoroutineDispatcher,
|
||||
vaultsManager: IVaultsManager
|
||||
) : IUnlockManager {
|
||||
private val _openedStorages = MutableStateFlow<Map<UUID, EncryptedStorage>?>(null)
|
||||
override val openedStorages: StateFlow<Map<UUID, IStorage>?>
|
||||
get() = _openedStorages
|
||||
val mutex = Mutex()
|
||||
|
||||
override fun open(
|
||||
init {
|
||||
CoroutineScope(ioDispatcher).launch {
|
||||
vaultsManager.allStorages.collectLatest {
|
||||
mutex.lock()
|
||||
val allKeys = repo.getAll()
|
||||
val allStorages = it.associateBy({ it.uuid }, { it })
|
||||
val map = _openedStorages.value?.toMutableMap() ?: mutableMapOf()
|
||||
for(keymap in allKeys) {
|
||||
if(map.contains(keymap.sourceUuid))
|
||||
continue
|
||||
val storage = allStorages[keymap.sourceUuid] ?: continue
|
||||
val encStorage = createEncryptedStorage(storage, keymap.key, keymap.destUuid)
|
||||
map[storage.uuid] = encStorage
|
||||
}
|
||||
_openedStorages.value = map
|
||||
mutex.unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createEncryptedStorage(storage: IStorage, key: EncryptKey, uuid: UUID): EncryptedStorage {
|
||||
return EncryptedStorage(
|
||||
source = storage,
|
||||
key = key,
|
||||
ioDispatcher = ioDispatcher,
|
||||
uuid = uuid
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun open(
|
||||
storage: IStorage,
|
||||
key: EncryptKey
|
||||
) {
|
||||
TODO("Not yet implemented")
|
||||
) = withContext(ioDispatcher) {
|
||||
mutex.lock()
|
||||
val encInfo = storage.encInfo.value ?: throw Exception("EncInfo is null") // TODO
|
||||
if (!Encryptor.checkKey(key, encInfo))
|
||||
throw Exception("Incorrect Key")
|
||||
|
||||
if (_openedStorages.value == null) {
|
||||
val childScope = CoroutineScope(ioDispatcher)
|
||||
}
|
||||
val opened = _openedStorages.first { it != null }!!.toMutableMap()
|
||||
val cur = opened[storage.uuid]
|
||||
if (cur != null)
|
||||
throw Exception("Storage is already open")
|
||||
|
||||
val keymap = StorageKeyMap(
|
||||
sourceUuid = storage.uuid,
|
||||
destUuid = UUID.randomUUID(),
|
||||
key = key
|
||||
)
|
||||
val encStorage = createEncryptedStorage(storage, keymap.key, keymap.destUuid)
|
||||
opened[storage.uuid] = encStorage
|
||||
_openedStorages.value = opened
|
||||
repo.add(keymap)
|
||||
mutex.unlock()
|
||||
}
|
||||
|
||||
override fun close(uuid: UUID) {
|
||||
val enc = _openedStorages.value[uuid]
|
||||
if(enc == null)
|
||||
return
|
||||
_openedStorages.value = _openedStorages.value.toMutableMap().apply {
|
||||
remove(uuid)
|
||||
override suspend fun close(storage: IStorage) = withContext(ioDispatcher) {
|
||||
mutex.lock()
|
||||
val opened = _openedStorages.first { it != null }!!
|
||||
val enc = opened[storage.uuid] ?: return@withContext
|
||||
val model = StorageKeyMap(
|
||||
sourceUuid = storage.uuid,
|
||||
destUuid = enc.uuid,
|
||||
key = EncryptKey("")
|
||||
)
|
||||
_openedStorages.value = opened.toMutableMap().apply {
|
||||
remove(storage.uuid)
|
||||
}
|
||||
enc.dispose()
|
||||
repo.delete(model)
|
||||
mutex.unlock()
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.github.nullptroma.wallenc.data.vaults
|
||||
|
||||
import android.content.Context
|
||||
import com.github.nullptroma.wallenc.data.vaults.local.LocalVault
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IVault
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
@@ -12,6 +13,8 @@ class VaultsManager(ioDispatcher: CoroutineDispatcher, context: Context) : IVaul
|
||||
|
||||
override val remoteVaults: StateFlow<List<IVault>>
|
||||
get() = TODO("Not yet implemented")
|
||||
override val allStorages: StateFlow<List<IStorage>>
|
||||
get() = localVault.storages
|
||||
|
||||
override fun addYandexVault(email: String, token: String) {
|
||||
TODO("Not yet implemented")
|
||||
|
||||
@@ -77,8 +77,9 @@ class LocalVault(private val ioDispatcher: CoroutineDispatcher, context: Context
|
||||
override suspend fun createStorage(
|
||||
enc: StorageEncryptionInfo
|
||||
): LocalStorage = withContext(ioDispatcher) {
|
||||
|
||||
TODO("Not yet implemented")
|
||||
val storage = createStorage()
|
||||
storage.setEncInfo(enc)
|
||||
return@withContext storage
|
||||
}
|
||||
|
||||
override suspend fun remove(storage: IStorage) = withContext(ioDispatcher) {
|
||||
|
||||
Reference in New Issue
Block a user