fix(sync): запретил зашифрованные storage в группах и перевёл резолв storages на FindStorageUseCase
This commit is contained in:
@@ -91,9 +91,11 @@ class UseCasesModule {
|
|||||||
fun provideStorageSyncEngine(
|
fun provideStorageSyncEngine(
|
||||||
vaultsManager: IVaultsManager,
|
vaultsManager: IVaultsManager,
|
||||||
groupStore: IStorageSyncGroupStore,
|
groupStore: IStorageSyncGroupStore,
|
||||||
|
findStorageUseCase: FindStorageUseCase,
|
||||||
): IStorageSyncEngine = StorageSyncEngine(
|
): IStorageSyncEngine = StorageSyncEngine(
|
||||||
vaultsManager = vaultsManager,
|
vaultsManager = vaultsManager,
|
||||||
groupStore = groupStore,
|
groupStore = groupStore,
|
||||||
|
findStorageUseCase = findStorageUseCase,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -33,10 +33,7 @@ class UnlockManager(
|
|||||||
override fun getOpenedStorageKey(uuid: UUID): EncryptKey? {
|
override fun getOpenedStorageKey(uuid: UUID): EncryptKey? {
|
||||||
val opened = _openedStorages.value
|
val opened = _openedStorages.value
|
||||||
val direct = opened[uuid]
|
val direct = opened[uuid]
|
||||||
if (direct != null) {
|
return direct?.getKey()
|
||||||
return direct.getKey()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import com.github.nullptroma.wallenc.usecases.ManageStorageSyncGroupsUseCase
|
|||||||
import com.github.nullptroma.wallenc.usecases.RunStorageSyncUseCase
|
import com.github.nullptroma.wallenc.usecases.RunStorageSyncUseCase
|
||||||
import com.github.nullptroma.wallenc.usecases.StorageSyncCompatibilityInput
|
import com.github.nullptroma.wallenc.usecases.StorageSyncCompatibilityInput
|
||||||
import com.github.nullptroma.wallenc.usecases.isStorageCompatibleWithGroup
|
import com.github.nullptroma.wallenc.usecases.isStorageCompatibleWithGroup
|
||||||
import com.github.nullptroma.wallenc.usecases.storageEncryptionSecret
|
|
||||||
import com.github.nullptroma.wallenc.vault.contract.DescribedVault
|
import com.github.nullptroma.wallenc.vault.contract.DescribedVault
|
||||||
import com.github.nullptroma.wallenc.vault.contract.VaultDescriptor
|
import com.github.nullptroma.wallenc.vault.contract.VaultDescriptor
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@@ -154,15 +153,12 @@ class StorageSyncViewModel @Inject constructor(
|
|||||||
return@withGroupMutationBusy UserNotification.TextRes(R.string.sync_storage_not_in_vaults)
|
return@withGroupMutationBusy UserNotification.TextRes(R.string.sync_storage_not_in_vaults)
|
||||||
}
|
}
|
||||||
val isEncrypted = storage.metaInfo.value.encInfo != null
|
val isEncrypted = storage.metaInfo.value.encInfo != null
|
||||||
val secret = vaultsManager.unlockManager
|
|
||||||
.getOpenedStorageKey(storageUuid)
|
|
||||||
?.let(::storageEncryptionSecret)
|
|
||||||
val result = groupsUseCase.addStorageToGroup(
|
val result = groupsUseCase.addStorageToGroup(
|
||||||
groupId = groupId,
|
groupId = groupId,
|
||||||
storageUuid = storageUuid,
|
storageUuid = storageUuid,
|
||||||
compatibility = StorageSyncCompatibilityInput(
|
compatibility = StorageSyncCompatibilityInput(
|
||||||
isEncrypted = isEncrypted,
|
isEncrypted = isEncrypted,
|
||||||
encryptionSecret = secret,
|
encryptionSecret = null,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
when (result) {
|
when (result) {
|
||||||
@@ -173,6 +169,9 @@ class StorageSyncViewModel @Inject constructor(
|
|||||||
AddStorageToSyncGroupResult.AlreadyInGroup -> UserNotification.TextRes(
|
AddStorageToSyncGroupResult.AlreadyInGroup -> UserNotification.TextRes(
|
||||||
R.string.sync_msg_storage_already_added,
|
R.string.sync_msg_storage_already_added,
|
||||||
)
|
)
|
||||||
|
AddStorageToSyncGroupResult.EncryptedStorageNotAllowed -> UserNotification.TextRes(
|
||||||
|
R.string.sync_msg_only_plain_storage_allowed,
|
||||||
|
)
|
||||||
AddStorageToSyncGroupResult.MissingEncryptionSecret -> UserNotification.TextRes(
|
AddStorageToSyncGroupResult.MissingEncryptionSecret -> UserNotification.TextRes(
|
||||||
R.string.sync_msg_storage_encryption_key_required,
|
R.string.sync_msg_storage_encryption_key_required,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
<string name="sync_msg_storage_added">Хранилище добавлено в %1$s</string>
|
<string name="sync_msg_storage_added">Хранилище добавлено в %1$s</string>
|
||||||
<string name="sync_msg_storage_removed">Хранилище убрано из %1$s</string>
|
<string name="sync_msg_storage_removed">Хранилище убрано из %1$s</string>
|
||||||
<string name="sync_msg_storage_already_added">Хранилище уже добавлено в группу</string>
|
<string name="sync_msg_storage_already_added">Хранилище уже добавлено в группу</string>
|
||||||
|
<string name="sync_msg_only_plain_storage_allowed">В группы синхронизации можно добавлять только незашифрованные хранилища</string>
|
||||||
<string name="sync_msg_storage_encryption_key_required">Для зашифрованного хранилища нужно знать пароль (откройте его перед добавлением)</string>
|
<string name="sync_msg_storage_encryption_key_required">Для зашифрованного хранилища нужно знать пароль (откройте его перед добавлением)</string>
|
||||||
<string name="sync_msg_storage_incompatible_encryption">Хранилище не совместимо с политикой шифрования группы</string>
|
<string name="sync_msg_storage_incompatible_encryption">Хранилище не совместимо с политикой шифрования группы</string>
|
||||||
<string name="sync_msg_virtual_storage_not_supported">Нельзя добавлять открытое виртуальное хранилище: синхронизация работает с исходными raw storage</string>
|
<string name="sync_msg_virtual_storage_not_supported">Нельзя добавлять открытое виртуальное хранилище: синхронизация работает с исходными raw storage</string>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ sealed interface AddStorageToSyncGroupResult {
|
|||||||
data object Added : AddStorageToSyncGroupResult
|
data object Added : AddStorageToSyncGroupResult
|
||||||
data object GroupNotFound : AddStorageToSyncGroupResult
|
data object GroupNotFound : AddStorageToSyncGroupResult
|
||||||
data object AlreadyInGroup : AddStorageToSyncGroupResult
|
data object AlreadyInGroup : AddStorageToSyncGroupResult
|
||||||
|
data object EncryptedStorageNotAllowed : AddStorageToSyncGroupResult
|
||||||
data object IncompatibleEncryption : AddStorageToSyncGroupResult
|
data object IncompatibleEncryption : AddStorageToSyncGroupResult
|
||||||
data object MissingEncryptionSecret : AddStorageToSyncGroupResult
|
data object MissingEncryptionSecret : AddStorageToSyncGroupResult
|
||||||
}
|
}
|
||||||
@@ -56,14 +57,12 @@ class ManageStorageSyncGroupsUseCase(
|
|||||||
if (storageUuid in current.storageUuids) {
|
if (storageUuid in current.storageUuids) {
|
||||||
return AddStorageToSyncGroupResult.AlreadyInGroup
|
return AddStorageToSyncGroupResult.AlreadyInGroup
|
||||||
}
|
}
|
||||||
|
if (compatibility.isEncrypted) {
|
||||||
val encryptedSecret = compatibility.encryptionSecret?.takeIf { it.isNotBlank() }
|
return AddStorageToSyncGroupResult.EncryptedStorageNotAllowed
|
||||||
val effectiveEncryption = when {
|
|
||||||
!compatibility.isEncrypted -> StorageSyncGroupEncryptionKind.NONE to null
|
|
||||||
encryptedSecret == null -> return AddStorageToSyncGroupResult.MissingEncryptionSecret
|
|
||||||
else -> StorageSyncGroupEncryptionKind.PASSWORD to encryptedSecret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val effectiveEncryption = StorageSyncGroupEncryptionKind.NONE to null
|
||||||
|
|
||||||
val (nextKind, nextSecret) = when (current.encryptionKind) {
|
val (nextKind, nextSecret) = when (current.encryptionKind) {
|
||||||
StorageSyncGroupEncryptionKind.UNSET -> effectiveEncryption
|
StorageSyncGroupEncryptionKind.UNSET -> effectiveEncryption
|
||||||
StorageSyncGroupEncryptionKind.NONE -> {
|
StorageSyncGroupEncryptionKind.NONE -> {
|
||||||
@@ -74,14 +73,8 @@ class ManageStorageSyncGroupsUseCase(
|
|||||||
}
|
}
|
||||||
|
|
||||||
StorageSyncGroupEncryptionKind.PASSWORD -> {
|
StorageSyncGroupEncryptionKind.PASSWORD -> {
|
||||||
if (
|
|
||||||
effectiveEncryption.first != StorageSyncGroupEncryptionKind.PASSWORD ||
|
|
||||||
current.encryptionSecret != effectiveEncryption.second
|
|
||||||
) {
|
|
||||||
return AddStorageToSyncGroupResult.IncompatibleEncryption
|
return AddStorageToSyncGroupResult.IncompatibleEncryption
|
||||||
}
|
}
|
||||||
StorageSyncGroupEncryptionKind.PASSWORD to current.encryptionSecret
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
groupStore.putGroup(
|
groupStore.putGroup(
|
||||||
|
|||||||
@@ -15,16 +15,13 @@ fun isStorageCompatibleWithGroup(
|
|||||||
group: StorageSyncGroup,
|
group: StorageSyncGroup,
|
||||||
resolveStorageKey: (UUID) -> EncryptKey?,
|
resolveStorageKey: (UUID) -> EncryptKey?,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return when (group.encryptionKind) {
|
// Режим упрощён: в sync-группах допускаются только незашифрованные storage.
|
||||||
StorageSyncGroupEncryptionKind.UNSET -> true
|
if (storage.metaInfo.value.encInfo != null) {
|
||||||
StorageSyncGroupEncryptionKind.NONE -> storage.metaInfo.value.encInfo == null
|
|
||||||
StorageSyncGroupEncryptionKind.PASSWORD -> {
|
|
||||||
val groupSecret = group.encryptionSecret ?: return false
|
|
||||||
if (storage.metaInfo.value.encInfo == null) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
val currentSecret = resolveStorageKey(storage.uuid)?.let(::storageEncryptionSecret)
|
return when (group.encryptionKind) {
|
||||||
currentSecret != null && currentSecret == groupSecret
|
StorageSyncGroupEncryptionKind.UNSET -> true
|
||||||
}
|
StorageSyncGroupEncryptionKind.NONE -> true
|
||||||
|
StorageSyncGroupEncryptionKind.PASSWORD -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.util.concurrent.atomic.AtomicLong
|
|||||||
class StorageSyncEngine(
|
class StorageSyncEngine(
|
||||||
private val vaultsManager: IVaultsManager,
|
private val vaultsManager: IVaultsManager,
|
||||||
private val groupStore: IStorageSyncGroupStore,
|
private val groupStore: IStorageSyncGroupStore,
|
||||||
|
private val findStorageUseCase: FindStorageUseCase,
|
||||||
) : IStorageSyncEngine {
|
) : IStorageSyncEngine {
|
||||||
private val holderId: String = UUID.randomUUID().toString()
|
private val holderId: String = UUID.randomUUID().toString()
|
||||||
private val groupMutexes = ConcurrentHashMap<String, Mutex>()
|
private val groupMutexes = ConcurrentHashMap<String, Mutex>()
|
||||||
@@ -207,8 +208,7 @@ class StorageSyncEngine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveStorages(uuids: Set<UUID>): List<IStorage> {
|
private fun resolveStorages(uuids: Set<UUID>): List<IStorage> {
|
||||||
val byUuid = vaultsManager.allStorages.value.associateBy { it.uuid }
|
return uuids.mapNotNull(findStorageUseCase::find)
|
||||||
return uuids.mapNotNull { byUuid[it] }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun latestByPath(entries: List<StorageSyncJournalEntry>): Map<String, StorageSyncJournalEntry> {
|
private fun latestByPath(entries: List<StorageSyncJournalEntry>): Map<String, StorageSyncJournalEntry> {
|
||||||
|
|||||||
Reference in New Issue
Block a user