From dea9d698df701f18b918b05a3e9d599bd32e703a Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Thu, 26 Sep 2024 17:20:29 -0400 Subject: [PATCH] Adds privacy presets for Tor Settings --- .../amethyst/ui/tor/TorDialogViewModel.kt | 46 +++++ .../amethyst/ui/tor/TorSettings.kt | 104 +++++++++++ .../amethyst/ui/tor/TorSettingsDialog.kt | 170 +++++++++++------- amethyst/src/main/res/values/strings.xml | 15 ++ 4 files changed, 267 insertions(+), 68 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorDialogViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorDialogViewModel.kt index 938337d50..0569d4dfc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorDialogViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorDialogViewModel.kt @@ -20,6 +20,7 @@ */ package com.vitorpamplona.amethyst.ui.tor +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel @@ -41,6 +42,27 @@ class TorDialogViewModel : ViewModel() { val nip05VerificationsViaTor = mutableStateOf(false) val nip96UploadsViaTor = mutableStateOf(false) + val preset = + derivedStateOf { + whichPreset( + TorSettings( + torType = TorType.INTERNAL, + externalSocksPort = -1, + onionRelaysViaTor = onionRelaysViaTor.value, + dmRelaysViaTor = dmRelaysViaTor.value, + newRelaysViaTor = newRelaysViaTor.value, + trustedRelaysViaTor = trustedRelaysViaTor.value, + urlPreviewsViaTor = urlPreviewsViaTor.value, + profilePicsViaTor = profilePicsViaTor.value, + imagesViaTor = imagesViaTor.value, + videosViaTor = videosViaTor.value, + moneyOperationsViaTor = moneyOperationsViaTor.value, + nip05VerificationsViaTor = nip05VerificationsViaTor.value, + nip96UploadsViaTor = nip96UploadsViaTor.value, + ), + ) + } + fun reset(torSettings: TorSettings) { torType.value = torSettings.torType socksPortStr.value = torSettings.externalSocksPort.toString() @@ -73,4 +95,28 @@ class TorDialogViewModel : ViewModel() { nip05VerificationsViaTor = nip05VerificationsViaTor.value, nip96UploadsViaTor = nip96UploadsViaTor.value, ) + + fun setPreset(preset: TorPresetType) { + when (preset) { + TorPresetType.DEFAULT -> resetOnlyFlags(torDefaultPreset) + TorPresetType.ONLY_WHEN_NEEDED -> resetOnlyFlags(torOnlyWhenNeededPreset) + TorPresetType.SMALL_PAYLOADS -> resetOnlyFlags(torSmallPayloadsPreset) + TorPresetType.FULL_PRIVACY -> resetOnlyFlags(torFullyPrivate) + TorPresetType.CUSTOM -> { } + } + } + + fun resetOnlyFlags(torSettings: TorSettings) { + onionRelaysViaTor.value = torSettings.onionRelaysViaTor + dmRelaysViaTor.value = torSettings.dmRelaysViaTor + newRelaysViaTor.value = torSettings.newRelaysViaTor + trustedRelaysViaTor.value = torSettings.trustedRelaysViaTor + urlPreviewsViaTor.value = torSettings.urlPreviewsViaTor + profilePicsViaTor.value = torSettings.profilePicsViaTor + imagesViaTor.value = torSettings.imagesViaTor + videosViaTor.value = torSettings.videosViaTor + moneyOperationsViaTor.value = torSettings.moneyOperationsViaTor + nip05VerificationsViaTor.value = torSettings.nip05VerificationsViaTor + nip96UploadsViaTor.value = torSettings.nip96UploadsViaTor + } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettings.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettings.kt index a65b02af4..85cbbeed7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettings.kt @@ -56,3 +56,107 @@ fun parseTorType(code: Int?): TorType = TorType.INTERNAL } } + +enum class TorPresetType( + val screenCode: Int, + val resourceId: Int, + val explainerId: Int, +) { + ONLY_WHEN_NEEDED(0, R.string.tor_when_needed, R.string.tor_when_needed_explainer), + DEFAULT(1, R.string.tor_default, R.string.tor_default_explainer), + SMALL_PAYLOADS(2, R.string.tor_small_payloads, R.string.tor_small_payloads_explainer), + FULL_PRIVACY(3, R.string.tor_full_privacy, R.string.tor_full_privacy_explainer), + CUSTOM(4, R.string.tor_custom, R.string.tor_custom_explainer), +} + +fun parseTorPresetType(code: Int?): TorPresetType = + when (code) { + TorPresetType.ONLY_WHEN_NEEDED.screenCode -> TorPresetType.ONLY_WHEN_NEEDED + TorPresetType.DEFAULT.screenCode -> TorPresetType.DEFAULT + TorPresetType.SMALL_PAYLOADS.screenCode -> TorPresetType.SMALL_PAYLOADS + TorPresetType.FULL_PRIVACY.screenCode -> TorPresetType.FULL_PRIVACY + else -> { + TorPresetType.CUSTOM + } + } + +fun isPreset( + torSettings: TorSettings, + preset: TorSettings, +): Boolean = + torSettings.onionRelaysViaTor == preset.onionRelaysViaTor && + torSettings.dmRelaysViaTor == preset.dmRelaysViaTor && + torSettings.newRelaysViaTor == preset.newRelaysViaTor && + torSettings.trustedRelaysViaTor == preset.trustedRelaysViaTor && + torSettings.urlPreviewsViaTor == preset.urlPreviewsViaTor && + // torSettings.profilePicsViaTor == preset.profilePicsViaTor && + torSettings.imagesViaTor == preset.imagesViaTor && + torSettings.videosViaTor == preset.videosViaTor && + torSettings.moneyOperationsViaTor == preset.moneyOperationsViaTor && + torSettings.nip05VerificationsViaTor == preset.nip05VerificationsViaTor && + torSettings.nip96UploadsViaTor == preset.nip96UploadsViaTor + +fun whichPreset(torSettings: TorSettings): TorPresetType { + if (isPreset(torSettings, torOnlyWhenNeededPreset)) return TorPresetType.ONLY_WHEN_NEEDED + if (isPreset(torSettings, torDefaultPreset)) return TorPresetType.DEFAULT + if (isPreset(torSettings, torSmallPayloadsPreset)) return TorPresetType.SMALL_PAYLOADS + if (isPreset(torSettings, torFullyPrivate)) return TorPresetType.FULL_PRIVACY + return TorPresetType.CUSTOM +} + +val torOnlyWhenNeededPreset = + TorSettings( + onionRelaysViaTor = true, + dmRelaysViaTor = false, + newRelaysViaTor = false, + trustedRelaysViaTor = false, + urlPreviewsViaTor = false, + profilePicsViaTor = false, + imagesViaTor = false, + videosViaTor = false, + moneyOperationsViaTor = false, + nip05VerificationsViaTor = false, + nip96UploadsViaTor = false, + ) +val torDefaultPreset = + TorSettings( + onionRelaysViaTor = true, + dmRelaysViaTor = true, + newRelaysViaTor = true, + trustedRelaysViaTor = false, + urlPreviewsViaTor = false, + profilePicsViaTor = false, + imagesViaTor = false, + videosViaTor = false, + moneyOperationsViaTor = false, + nip05VerificationsViaTor = false, + nip96UploadsViaTor = false, + ) +val torSmallPayloadsPreset = + TorSettings( + onionRelaysViaTor = true, + dmRelaysViaTor = true, + newRelaysViaTor = true, + trustedRelaysViaTor = true, + urlPreviewsViaTor = true, + profilePicsViaTor = true, + imagesViaTor = false, + videosViaTor = false, + moneyOperationsViaTor = true, + nip05VerificationsViaTor = true, + nip96UploadsViaTor = false, + ) +val torFullyPrivate = + TorSettings( + onionRelaysViaTor = true, + dmRelaysViaTor = true, + newRelaysViaTor = true, + trustedRelaysViaTor = true, + urlPreviewsViaTor = true, + profilePicsViaTor = true, + imagesViaTor = true, + videosViaTor = true, + moneyOperationsViaTor = true, + nip05VerificationsViaTor = true, + nip96UploadsViaTor = true, + ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt index 7f2135e46..aba5f5741 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt @@ -53,7 +53,7 @@ import com.vitorpamplona.amethyst.ui.actions.SaveButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow import com.vitorpamplona.amethyst.ui.stringRes -import com.vitorpamplona.amethyst.ui.theme.Size15dp +import com.vitorpamplona.amethyst.ui.theme.Size10dp import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.ui.theme.placeholderText import kotlinx.collections.immutable.persistentListOf @@ -93,7 +93,7 @@ fun TorDialogContentsPreview() { onClose = {}, onPost = { }, onError = {}, - torSettings = TorSettings(), + torSettings = TorSettings(torType = TorType.EXTERNAL), ) } } @@ -104,6 +104,22 @@ fun TorDialogContents( onClose: () -> Unit, onPost: (torSettings: TorSettings) -> Unit, onError: (String) -> Unit, +) { + val dialogViewModel = viewModel() + + LaunchedEffect(dialogViewModel, torSettings) { + dialogViewModel.reset(torSettings) + } + + TorDialogContents(dialogViewModel, onClose, onPost, onError) +} + +@Composable +fun TorDialogContents( + dialogViewModel: TorDialogViewModel, + onClose: () -> Unit, + onPost: (torSettings: TorSettings) -> Unit, + onError: (String) -> Unit, ) { Column( modifier = @@ -113,12 +129,6 @@ fun TorDialogContents( rememberScrollState(), ).padding(10.dp), ) { - val dialogViewModel = viewModel() - - LaunchedEffect(dialogViewModel, torSettings) { - dialogViewModel.reset(torSettings) - } - Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, @@ -142,8 +152,8 @@ fun TorDialogContents( } Column( - modifier = Modifier.padding(vertical = 10.dp, horizontal = 5.dp), - verticalArrangement = Arrangement.spacedBy(Size15dp), + modifier = Modifier.padding(horizontal = 5.dp), + verticalArrangement = Arrangement.spacedBy(Size10dp), ) { SettingsRow( R.string.use_internal_tor, @@ -182,75 +192,99 @@ fun TorDialogContents( ) } } + } - SwitchSettingsRow( - R.string.tor_use_onion_address, - R.string.tor_use_onion_address_explainer, - dialogViewModel.onionRelaysViaTor, - ) + AnimatedVisibility( + visible = dialogViewModel.torType.value != TorType.OFF, + ) { + Column( + modifier = Modifier.padding(horizontal = 5.dp), + verticalArrangement = Arrangement.spacedBy(Size10dp), + ) { + SettingsRow( + R.string.tor_preset, + R.string.tor_preset_explainer, + persistentListOf( + TitleExplainer(stringRes(TorPresetType.ONLY_WHEN_NEEDED.resourceId), stringRes(TorPresetType.ONLY_WHEN_NEEDED.explainerId)), + TitleExplainer(stringRes(TorPresetType.DEFAULT.resourceId), stringRes(TorPresetType.DEFAULT.explainerId)), + TitleExplainer(stringRes(TorPresetType.SMALL_PAYLOADS.resourceId), stringRes(TorPresetType.SMALL_PAYLOADS.explainerId)), + TitleExplainer(stringRes(TorPresetType.FULL_PRIVACY.resourceId), stringRes(TorPresetType.FULL_PRIVACY.explainerId)), + TitleExplainer(stringRes(TorPresetType.CUSTOM.resourceId), stringRes(TorPresetType.CUSTOM.explainerId)), + ), + dialogViewModel.preset.value.screenCode, + ) { + dialogViewModel.setPreset(parseTorPresetType(it)) + } - SwitchSettingsRow( - R.string.tor_use_dm_relays, - R.string.tor_use_dm_relays_explainer, - dialogViewModel.dmRelaysViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_onion_address, + R.string.tor_use_onion_address_explainer, + dialogViewModel.onionRelaysViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_new_relays, - R.string.tor_use_new_relays_explainer, - dialogViewModel.newRelaysViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_dm_relays, + R.string.tor_use_dm_relays_explainer, + dialogViewModel.dmRelaysViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_trusted_relays, - R.string.tor_use_trusted_relays_explainer, - dialogViewModel.trustedRelaysViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_new_relays, + R.string.tor_use_new_relays_explainer, + dialogViewModel.newRelaysViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_money_operations, - R.string.tor_use_money_operations_explainer, - dialogViewModel.moneyOperationsViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_trusted_relays, + R.string.tor_use_trusted_relays_explainer, + dialogViewModel.trustedRelaysViaTor, + ) - /** - * Too hard to separate Coil into regular images and profile pics - SwitchSettingsRow( - R.string.tor_use_profile_pictures, - R.string.tor_use_profile_pictures_explainer, - dialogViewModel.profilePicsViaTor, - ) - */ + SwitchSettingsRow( + R.string.tor_use_money_operations, + R.string.tor_use_money_operations_explainer, + dialogViewModel.moneyOperationsViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_nip05_verification, - R.string.tor_use_nip05_verification_explainer, - dialogViewModel.nip05VerificationsViaTor, - ) + /** + * Too hard to separate Coil into regular images and profile pics + SwitchSettingsRow( + R.string.tor_use_profile_pictures, + R.string.tor_use_profile_pictures_explainer, + dialogViewModel.profilePicsViaTor, + ) + */ - SwitchSettingsRow( - R.string.tor_use_url_previews, - R.string.tor_use_url_previews_explainer, - dialogViewModel.urlPreviewsViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_nip05_verification, + R.string.tor_use_nip05_verification_explainer, + dialogViewModel.nip05VerificationsViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_images, - R.string.tor_use_images_explainer, - dialogViewModel.imagesViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_url_previews, + R.string.tor_use_url_previews_explainer, + dialogViewModel.urlPreviewsViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_videos, - R.string.tor_use_videos_explainer, - dialogViewModel.videosViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_images, + R.string.tor_use_images_explainer, + dialogViewModel.imagesViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_nip96_uploads, - R.string.tor_use_nip96_uploads_explainer, - dialogViewModel.nip96UploadsViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_videos, + R.string.tor_use_videos_explainer, + dialogViewModel.videosViaTor, + ) + + SwitchSettingsRow( + R.string.tor_use_nip96_uploads, + R.string.tor_use_nip96_uploads_explainer, + dialogViewModel.nip96UploadsViaTor, + ) + } } } } diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 00eb8a506..02f34b81e 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -433,6 +433,9 @@ Active Tor Engine Use the internal version or Orbot + Tor/Privacy Presets + Quickly modify all settings below + Onion Url/Relays Use Tor for any .onion url @@ -470,6 +473,18 @@ Orbot Off + Basic + Default + All but Media + Full Privacy + Custom + + Use Tor when it\'s required by the server + Hide your IP from random relays + Hide your IP from everything, but images and videos + Hide your IP in all connections + Make your own + Invalid port number Use Orbot Disconnect Tor/Orbot