UnlockManager больше не Vault

This commit is contained in:
2026-04-17 22:15:59 +03:00
parent fd15caadd4
commit d965dd8609
6 changed files with 12 additions and 231 deletions

View File

@@ -24,8 +24,8 @@ class UseCasesModule {
@Provides
@Singleton
fun provideManageLocalVaultUseCase(vaultsManager: IVaultsManager, unlockManager: IUnlockManager): ManageLocalVaultUseCase {
return ManageLocalVaultUseCase(vaultsManager, unlockManager)
fun provideManageLocalVaultUseCase(vaultsManager: IVaultsManager): ManageLocalVaultUseCase {
return ManageLocalVaultUseCase(vaultsManager)
}
@Provides

View File

@@ -1,26 +1,19 @@
package com.github.nullptroma.wallenc.data.storages
import com.github.nullptroma.wallenc.data.db.app.repository.StorageKeyMapRepository
import com.github.nullptroma.wallenc.data.db.app.repository.StorageMetaInfoRepository
import com.github.nullptroma.wallenc.data.model.StorageKeyMap
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
import com.github.nullptroma.wallenc.data.storages.encrypt.EncryptedStorage
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor
import com.github.nullptroma.wallenc.domain.enums.VaultType
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.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.withContext
@@ -35,23 +28,6 @@ class UnlockManager(
override val openedStorages: StateFlow<Map<UUID, IStorage>?>
get() = _openedStorages
private val mutex = Mutex()
override val type: VaultType
get() = VaultType.DECRYPTED
override val uuid: UUID
get() = TODO("Not yet implemented")
override val isAvailable: StateFlow<Boolean>
get() = MutableStateFlow(true)
override val totalSpace: StateFlow<Int?>
get() = MutableStateFlow(null)
override val availableSpace: StateFlow<Int?>
get() = MutableStateFlow(null)
override val storages: StateFlow<List<IStorage>?>
get() = openedStorages.map { it?.values?.toList() }.stateIn(
scope = CoroutineScope(ioDispatcher),
started = SharingStarted.WhileSubscribed(5000L),
initialValue = null
)
init {
CoroutineScope(ioDispatcher).launch {
@@ -125,14 +101,6 @@ class UnlockManager(
return@withContext encStorage
}
/**
* Закрыть шифрование хранилища, закрывает рекурсивно, удаляя все ключи
* @param storage исходное хранилище, а не расшифрованное отображение
*/
override suspend fun close(storage: IStorage) {
close(storage.uuid)
}
/**
* Закрыть шифрование хранилища, закрывает рекурсивно, удаляя все ключи
* @param uuid uuid исходного хранилища
@@ -155,24 +123,13 @@ class UnlockManager(
mutex.unlock()
}
override suspend fun createStorage(): IStorage {
throw UnsupportedOperationException("Нельзя создать кошелёк на UnlockManager") // TODO
}
override suspend fun createStorage(enc: StorageEncryptionInfo): IStorage {
throw UnsupportedOperationException("Нельзя создать кошелёк на UnlockManager") // TODO
}
/**
* Закрыть отображение
* @param storage исходное или расшифрованное хранилище
*/
override suspend fun remove(storage: IStorage) {
// Закрытие отображения по экземпляру (source или decrypted).
override suspend fun close(storage: IStorage) {
val opened = _openedStorages.first { it != null }!!
val source = opened.entries.firstOrNull {
it.key == storage.uuid || it.value.uuid == storage.uuid
}
if(source != null)
if (source != null)
close(source.key)
}
}

View File

@@ -1,176 +0,0 @@
package com.github.nullptroma.wallenc.data.vaults
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.data.storages.encrypt.EncryptedStorage
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor
import com.github.nullptroma.wallenc.domain.enums.VaultType
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.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.withContext
import java.util.UUID
class UnlockManager(
private val keymapRepository: 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
private val mutex = Mutex()
override val type: VaultType
get() = VaultType.DECRYPTED
override val uuid: UUID
get() = TODO("Not yet implemented")
override val isAvailable: StateFlow<Boolean>
get() = MutableStateFlow(true)
override val totalSpace: StateFlow<Int?>
get() = MutableStateFlow(null)
override val availableSpace: StateFlow<Int?>
get() = MutableStateFlow(null)
override val storages: StateFlow<List<IStorage>?>
get() = openedStorages.map { it?.values?.toList() }.stateIn(
scope = CoroutineScope(ioDispatcher),
started = SharingStarted.WhileSubscribed(5000L),
initialValue = null
)
init {
CoroutineScope(ioDispatcher).launch {
vaultsManager.allStorages.collectLatest {
mutex.lock()
val allKeys = keymapRepository.getAll()
val usedKeys = mutableListOf<StorageKeyMap>()
val keysToRemove = mutableListOf<StorageKeyMap>()
val allStorages = it.toMutableList()
val map = _openedStorages.value?.toMutableMap() ?: mutableMapOf()
while(allStorages.size > 0) {
val storage = allStorages[allStorages.size-1]
val key = allKeys.find { key -> key.sourceUuid == storage.uuid }
if(key == null) {
allStorages.removeAt(allStorages.size - 1)
continue
}
try {
val encStorage = createEncryptedStorage(storage, key.key, key.destUuid)
map[storage.uuid] = encStorage
usedKeys.add(key)
allStorages.removeAt(allStorages.size - 1)
allStorages.add(encStorage)
}
catch (_: Exception) {
// ключ не подошёл
keysToRemove.add(key)
allStorages.removeAt(allStorages.size - 1)
}
}
keymapRepository.delete(*keysToRemove.toTypedArray()) // удалить мёртвые ключи
_openedStorages.value = map.toMap()
mutex.unlock()
}
}
}
private suspend fun createEncryptedStorage(storage: IStorage, key: EncryptKey, uuid: UUID): EncryptedStorage {
return EncryptedStorage.create(
source = storage,
key = key,
ioDispatcher = ioDispatcher,
uuid = uuid
)
}
override suspend fun open(
storage: IStorage,
key: EncryptKey
): EncryptedStorage = withContext(ioDispatcher) {
mutex.lock()
val encInfo = storage.metaInfo.value.encInfo ?: throw Exception("EncInfo is null") // TODO
if (!Encryptor.checkKey(key, encInfo))
throw Exception("Incorrect Key")
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
keymapRepository.add(keymap)
mutex.unlock()
return@withContext encStorage
}
/**
* Закрыть шифрование хранилища, закрывает рекурсивно, удаляя все ключи
* @param storage исходное хранилище, а не расшифрованное отображение
*/
override suspend fun close(storage: IStorage) {
close(storage.uuid)
}
/**
* Закрыть шифрование хранилища, закрывает рекурсивно, удаляя все ключи
* @param uuid uuid исходного хранилища
*/
override suspend fun close(uuid: UUID): Unit = withContext(ioDispatcher) {
mutex.lock()
val opened = _openedStorages.first { it != null }!!
val enc = opened[uuid] ?: return@withContext
close(enc)
val model = StorageKeyMap(
sourceUuid = uuid,
destUuid = enc.uuid,
key = EncryptKey("")
)
_openedStorages.value = opened.toMutableMap().apply {
remove(uuid)
}
enc.dispose()
keymapRepository.delete(model)
mutex.unlock()
}
override suspend fun createStorage(): IStorage {
throw UnsupportedOperationException("Нельзя создать кошелёк на UnlockManager") // TODO
}
override suspend fun createStorage(enc: StorageEncryptionInfo): IStorage {
throw UnsupportedOperationException("Нельзя создать кошелёк на UnlockManager") // TODO
}
/**
* Закрыть отображение
* @param storage исходное или расшифрованное хранилище
*/
override suspend fun remove(storage: IStorage) {
val opened = _openedStorages.first { it != null }!!
val source = opened.entries.firstOrNull {
it.key == storage.uuid || it.value.uuid == storage.uuid
}
if(source != null)
close(source.key)
}
}

View File

@@ -23,7 +23,7 @@ class VaultsManager(ioDispatcher: CoroutineDispatcher, context: Context, keyRepo
override val allStorages: StateFlow<List<IStorage>>
get() = localVault.storages
override val allVaults: StateFlow<List<IVault>>
get() = MutableStateFlow(listOf(localVault, unlockManager))
get() = MutableStateFlow(listOf(localVault))
override fun addYandexVault(email: String, token: String) {

View File

@@ -4,7 +4,11 @@ import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
import kotlinx.coroutines.flow.StateFlow
import java.util.UUID
interface IUnlockManager: IVault {
/**
* Управляет виртуальными (расшифрованными) представлениями хранилищ.
* Не является vault-провайдером.
*/
interface IUnlockManager {
/**
* Хранилища, для которых есть ключ шифрования
*/

View File

@@ -1,15 +1,11 @@
package com.github.nullptroma.wallenc.domain.usecases
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
class ManageLocalVaultUseCase(private val manager: IVaultsManager, private val unlockManager: IUnlockManager) {
class ManageLocalVaultUseCase(private val manager: IVaultsManager) {
val localStorages: StateFlow<List<IStorageInfo>?>
get() = manager.localVault.storages