Реализован UnlockManager

This commit is contained in:
2025-01-18 21:24:41 +03:00
parent c5c0173391
commit b9e73cf197
25 changed files with 598 additions and 91 deletions

View File

@@ -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
}

View File

@@ -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 {
}

View File

@@ -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)
}

View File

@@ -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
)

View File

@@ -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
)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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
)

View File

@@ -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()
}
}

View File

@@ -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")

View File

@@ -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) {