Полное управление шифрованием и ключами
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
package com.github.nullptroma.wallenc.domain.enums
|
||||
|
||||
/**
|
||||
* Политика удаления/закрытия хранилища.
|
||||
*
|
||||
* [CLOSE_ENCRYPTED_OVERLAYS_ONLY] — только закрыть расшифрованные представления (overlay),
|
||||
* физические данные не трогаем.
|
||||
*
|
||||
* [REMOVE_PHYSICAL] — удалить физическое хранилище у провайдера (сейчас local vault),
|
||||
* предварительно закрыв все overlay.
|
||||
*/
|
||||
enum class StorageDeletionPolicy {
|
||||
CLOSE_ENCRYPTED_OVERLAYS_ONLY,
|
||||
REMOVE_PHYSICAL,
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.nullptroma.wallenc.domain.interfaces
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
@@ -10,6 +11,7 @@ sealed interface IStorageInfo {
|
||||
val isAvailable: StateFlow<Boolean>
|
||||
val size: StateFlow<Long?>
|
||||
val numberOfFiles: StateFlow<Int?>
|
||||
val isEmpty: Flow<Boolean?>
|
||||
val metaInfo: StateFlow<IStorageMetaInfo>
|
||||
val isVirtualStorage: Boolean
|
||||
}
|
||||
@@ -18,7 +20,8 @@ interface IStorage: IStorageInfo {
|
||||
val accessor: IStorageAccessor
|
||||
|
||||
suspend fun rename(newName: String)
|
||||
suspend fun setEncInfo(encInfo: StorageEncryptionInfo)
|
||||
suspend fun setEncInfo(encInfo: StorageEncryptionInfo?)
|
||||
suspend fun clearAllContent()
|
||||
}
|
||||
|
||||
interface IStorageMetaInfo {
|
||||
|
||||
@@ -14,7 +14,7 @@ interface IUnlockManager {
|
||||
*/
|
||||
val openedStorages: StateFlow<Map<UUID, IStorage>>
|
||||
|
||||
suspend fun open(storage: IStorage, key: EncryptKey): IStorage
|
||||
suspend fun open(storage: IStorage, key: EncryptKey, rememberPassword: Boolean = true): IStorage
|
||||
suspend fun close(storage: IStorage)
|
||||
suspend fun close(uuid: UUID): Unit
|
||||
}
|
||||
@@ -5,23 +5,70 @@ 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 kotlinx.coroutines.flow.first
|
||||
|
||||
class ManageStoragesEncryptionUseCase(private val unlockManager: IUnlockManager) {
|
||||
suspend fun enableEncryption(storage: IStorageInfo, key: EncryptKey, encryptPath: Boolean) {
|
||||
when(storage) {
|
||||
is IStorage -> {
|
||||
if(storage.metaInfo.value.encInfo != null)
|
||||
throw Exception() // TODO
|
||||
storage.setEncInfo(Encryptor.generateEncryptionInfo(key, encryptPath))
|
||||
}
|
||||
class ManageStoragesEncryptionUseCase(
|
||||
private val unlockManager: IUnlockManager,
|
||||
) {
|
||||
sealed interface CanEncryptResult {
|
||||
data object Allowed : CanEncryptResult
|
||||
data object UnsupportedStorageType : CanEncryptResult
|
||||
data object AlreadyEncrypted : CanEncryptResult
|
||||
data object StorageIsNotEmpty : CanEncryptResult
|
||||
data object StorageStateUnknown : CanEncryptResult
|
||||
}
|
||||
|
||||
suspend fun canEncrypt(storage: IStorageInfo): CanEncryptResult {
|
||||
if (storage !is IStorage) return CanEncryptResult.UnsupportedStorageType
|
||||
if (storage.metaInfo.value.encInfo != null) return CanEncryptResult.AlreadyEncrypted
|
||||
|
||||
val isEmpty = storage.isEmpty.first()
|
||||
return when (isEmpty) {
|
||||
true -> CanEncryptResult.Allowed
|
||||
false -> CanEncryptResult.StorageIsNotEmpty
|
||||
null -> CanEncryptResult.StorageStateUnknown
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun openStorage(storage: IStorageInfo, key: EncryptKey): IStorageInfo {
|
||||
when(storage) {
|
||||
is IStorage -> {
|
||||
return unlockManager.open(storage, key)
|
||||
}
|
||||
suspend fun enableEncryption(storage: IStorageInfo, key: EncryptKey, encryptPath: Boolean) {
|
||||
when (val result = canEncrypt(storage)) {
|
||||
CanEncryptResult.Allowed -> (storage as IStorage).setEncInfo(
|
||||
Encryptor.generateEncryptionInfo(key, encryptPath)
|
||||
)
|
||||
CanEncryptResult.AlreadyEncrypted -> throw IllegalStateException("Storage is already encrypted")
|
||||
CanEncryptResult.StorageIsNotEmpty -> throw IllegalStateException("Storage is not empty")
|
||||
CanEncryptResult.StorageStateUnknown -> throw IllegalStateException("Storage state is unknown")
|
||||
CanEncryptResult.UnsupportedStorageType -> throw IllegalStateException("Unsupported storage type")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun openStorage(storage: IStorageInfo, key: EncryptKey, rememberPassword: Boolean): IStorageInfo {
|
||||
if (storage is IStorage) return unlockManager.open(storage, key, rememberPassword)
|
||||
throw IllegalStateException("Unsupported storage type")
|
||||
}
|
||||
|
||||
suspend fun closeStorage(storage: IStorageInfo) {
|
||||
if (storage is IStorage) {
|
||||
unlockManager.close(storage)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun disableEncryption(storage: IStorageInfo) {
|
||||
clearAndDisableEncryption(storage)
|
||||
}
|
||||
|
||||
suspend fun clearAndDisableEncryption(storage: IStorageInfo) {
|
||||
if (storage !is IStorage) return
|
||||
storage.clearAllContent()
|
||||
storage.setEncInfo(null)
|
||||
unlockManager.close(storage)
|
||||
}
|
||||
|
||||
suspend fun changePassword(storage: IStorageInfo, newKey: EncryptKey, encryptPath: Boolean) {
|
||||
if (storage !is IStorage) return
|
||||
if (storage.metaInfo.value.encInfo == null) {
|
||||
throw IllegalStateException("Storage is not encrypted")
|
||||
}
|
||||
storage.setEncInfo(Encryptor.generateEncryptionInfo(newKey, encryptPath))
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.github.nullptroma.wallenc.domain.usecases
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.enums.StorageDeletionPolicy
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager
|
||||
@@ -10,36 +9,30 @@ import java.util.UUID
|
||||
class RemoveStorageUseCase(
|
||||
private val vaultsManager: IVaultsManager,
|
||||
private val unlockManager: IUnlockManager,
|
||||
private val manageStoragesEncryptionUseCase: ManageStoragesEncryptionUseCase,
|
||||
) {
|
||||
|
||||
suspend fun remove(storage: IStorageInfo, policy: StorageDeletionPolicy) {
|
||||
suspend fun remove(storage: IStorageInfo) {
|
||||
|
||||
if (storage !is IStorage) return
|
||||
|
||||
when (policy) {
|
||||
StorageDeletionPolicy.CLOSE_ENCRYPTED_OVERLAYS_ONLY -> {
|
||||
unlockManager.close(storage)
|
||||
}
|
||||
StorageDeletionPolicy.REMOVE_PHYSICAL -> {
|
||||
val physical = findPhysicalRootStorage(storage) ?: return
|
||||
unlockManager.close(physical.uuid)
|
||||
vaultsManager.localVault.remove(physical)
|
||||
}
|
||||
if (!storage.isVirtualStorage) {
|
||||
unlockManager.close(storage)
|
||||
vaultsManager.localVault.remove(storage)
|
||||
return
|
||||
}
|
||||
|
||||
val parent = findParentStorage(storage) ?: return
|
||||
manageStoragesEncryptionUseCase.clearAndDisableEncryption(parent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Поднимается по цепочке overlay (sourceUuid -> decrypted view), пока не дойдёт
|
||||
* до корневого физического storage из [IVaultsManager.localVault].
|
||||
*/
|
||||
private fun findPhysicalRootStorage(storage: IStorage): IStorage? {
|
||||
val locals = vaultsManager.localVault.storages.value ?: return null
|
||||
|
||||
private fun findParentStorage(storage: IStorage): IStorage? {
|
||||
val opened = unlockManager.openedStorages.value
|
||||
|
||||
var id: UUID = storage.uuid
|
||||
while (true) {
|
||||
val parent = opened.entries.firstOrNull { it.value.uuid == id }?.key ?: break
|
||||
id = parent
|
||||
}
|
||||
return locals.firstOrNull { it.uuid == id }
|
||||
val parentUuid = opened.entries.firstOrNull { it.value.uuid == storage.uuid }?.key ?: return null
|
||||
val locals = vaultsManager.localVault.storages.value.orEmpty()
|
||||
return locals.firstOrNull { it.uuid == parentUuid }
|
||||
?: opened[parentUuid]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user