Implemented proxy settings logic

Fixes #113
This commit is contained in:
Abdelilah El Aissaoui 2023-09-10 18:26:11 +02:00
parent be616315f8
commit 0a8c8ac1ed
No known key found for this signature in database
GPG Key ID: 7587FC860F594869
4 changed files with 284 additions and 21 deletions

View File

@ -26,6 +26,7 @@ import com.jetpackduba.gitnuro.logging.printError
import com.jetpackduba.gitnuro.managers.AppStateManager
import com.jetpackduba.gitnuro.managers.TempFilesManager
import com.jetpackduba.gitnuro.preferences.AppSettings
import com.jetpackduba.gitnuro.preferences.ProxySettings
import com.jetpackduba.gitnuro.system.systemSeparator
import com.jetpackduba.gitnuro.theme.AppTheme
import com.jetpackduba.gitnuro.theme.Theme
@ -36,11 +37,17 @@ import com.jetpackduba.gitnuro.ui.components.RepositoriesTabPanel
import com.jetpackduba.gitnuro.ui.components.TabInformation
import com.jetpackduba.gitnuro.ui.components.emptyTabInformation
import com.jetpackduba.gitnuro.ui.context_menu.AppPopupMenu
import com.jetpackduba.gitnuro.ui.dialogs.settings.ProxyType
import kotlinx.coroutines.launch
import org.eclipse.jgit.lib.GpgSigner
import java.io.File
import java.net.Authenticator
import java.net.PasswordAuthentication
import java.nio.file.Paths
import java.util.*
import javax.inject.Inject
private const val TAG = "App"
val LocalTabScope = compositionLocalOf { emptyTabInformation() }
@ -73,6 +80,9 @@ class App {
@OptIn(ExperimentalFoundationApi::class)
fun start(args: Array<String>) {
tabsManager.appComponent = this.appComponent
initProxySettings()
val windowPlacement = appSettings.windowPlacement.toWindowPlacement
val dirToOpen = getDirToOpen(args)
@ -118,7 +128,8 @@ class App {
state = windowState,
icon = painterResource(AppIcons.LOGO),
) {
val compositionValues: MutableList<ProvidedValue<*>> = mutableListOf(LocalTextContextMenu provides AppPopupMenu())
val compositionValues: MutableList<ProvidedValue<*>> =
mutableListOf(LocalTextContextMenu provides AppPopupMenu())
if (scale != -1f) {
compositionValues.add(LocalDensity provides Density(scale, 1f))
@ -146,6 +157,71 @@ class App {
}
}
private fun initProxySettings() {
appStateManager.appScope.launch {
appSettings.proxyFlow.collect { proxySettings ->
if (proxySettings.useProxy) {
when (proxySettings.proxyType) {
ProxyType.HTTP -> setHttpProxy(proxySettings)
ProxyType.SOCKS -> setSocksProxy(proxySettings)
}
} else {
clearProxySettings()
}
}
}
}
private fun clearProxySettings() {
System.setProperty("http.proxyHost", "")
System.setProperty("http.proxyPort", "")
System.setProperty("https.proxyHost", "")
System.setProperty("https.proxyPort", "")
System.setProperty("socksProxyHost", "")
System.setProperty("socksProxyPort", "")
}
private fun setHttpProxy(proxySettings: ProxySettings) {
System.setProperty("http.proxyHost", proxySettings.hostName)
System.setProperty("http.proxyPort", proxySettings.hostPort.toString())
System.setProperty("https.proxyHost", proxySettings.hostName)
System.setProperty("https.proxyPort", proxySettings.hostPort.toString())
if (proxySettings.useAuth) {
Authenticator.setDefault(
object : Authenticator() {
public override fun getPasswordAuthentication(): PasswordAuthentication {
return PasswordAuthentication(proxySettings.hostUser, proxySettings.hostPassword.toCharArray())
}
}
)
System.setProperty("http.proxyUser", proxySettings.hostUser)
System.setProperty("http.proxyPassword", proxySettings.hostPassword)
System.setProperty("https.proxyUser", proxySettings.hostUser)
System.setProperty("https.proxyPassword", proxySettings.hostPassword)
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "")
}
}
private fun setSocksProxy(proxySettings: ProxySettings) {
System.setProperty("socksProxyHost", proxySettings.hostName)
System.setProperty("socksProxyPort", proxySettings.hostPort.toString())
if (proxySettings.useAuth) {
Authenticator.setDefault(
object : Authenticator() {
public override fun getPasswordAuthentication(): PasswordAuthentication {
return PasswordAuthentication(proxySettings.hostUser, proxySettings.hostPassword.toCharArray())
}
}
)
System.setProperty("java.net.socks.username", proxySettings.hostUser)
System.setProperty("java.net.socks.password", proxySettings.hostPassword)
}
}
private fun addDirTab(dirToOpen: File) {
val absolutePath = dirToOpen.normalize().absolutePath
.removeSuffix(systemSeparator)

View File

@ -5,6 +5,7 @@ import com.jetpackduba.gitnuro.system.OS
import com.jetpackduba.gitnuro.system.currentOs
import com.jetpackduba.gitnuro.theme.ColorsScheme
import com.jetpackduba.gitnuro.theme.Theme
import com.jetpackduba.gitnuro.ui.dialogs.settings.ProxyType
import com.jetpackduba.gitnuro.viewmodels.TextDiffType
import com.jetpackduba.gitnuro.viewmodels.textDiffTypeFromValue
import kotlinx.coroutines.flow.*
@ -30,6 +31,13 @@ private const val PREF_DIFF_TYPE = "diffType"
private const val PREF_DIFF_FULL_FILE = "diffFullFile"
private const val PREF_SWAP_UNCOMMITTED_CHANGES = "inverseUncommittedChanges"
private const val PREF_TERMINAL_PATH = "terminalPath"
private const val PREF_USE_PROXY = "useProxy"
private const val PREF_PROXY_TYPE = "proxyType"
private const val PREF_PROXY_HOST_NAME = "proxyHostName"
private const val PREF_PROXY_PORT = "proxyPort"
private const val PREF_PROXY_USE_AUTH = "proxyUseAuth"
private const val PREF_PROXY_USER = "proxyHostUser"
private const val PREF_PROXY_PASSWORD = "proxyHostPassword"
private const val PREF_CACHE_CREDENTIALS_IN_MEMORY = "credentialsInMemory"
@ -86,6 +94,20 @@ class AppSettings @Inject constructor() {
private val _terminalPathFlow = MutableStateFlow(terminalPath)
val terminalPathFlow = _terminalPathFlow.asStateFlow()
private val _proxyFlow = MutableStateFlow(
ProxySettings(
useProxy,
proxyType,
proxyHostName,
proxyPortNumber,
proxyUseAuth,
proxyHostUser,
proxyHostPassword,
)
)
val proxyFlow = _proxyFlow.asStateFlow()
var latestTabsOpened: String
get() = preferences.get(PREF_LATEST_REPOSITORIES_TABS_OPENED, "")
set(value) {
@ -238,6 +260,62 @@ class AppSettings @Inject constructor() {
_terminalPathFlow.value = value
}
var useProxy: Boolean
get() {
return preferences.getBoolean(PREF_USE_PROXY, false)
}
set(value) {
preferences.putBoolean(PREF_USE_PROXY, value)
_proxyFlow.value = _proxyFlow.value.copy(useProxy = value)
}
var proxyUseAuth: Boolean
get() {
return preferences.getBoolean(PREF_PROXY_USE_AUTH, false)
}
set(value) {
preferences.putBoolean(PREF_PROXY_USE_AUTH, value)
_proxyFlow.value = _proxyFlow.value.copy(useAuth = value)
}
var proxyType: ProxyType
get() {
val value = preferences.getInt(PREF_PROXY_TYPE, ProxyType.HTTP.value)
return ProxyType.fromInt(value)
}
set(value) {
preferences.putInt(PREF_PROXY_TYPE, value.value)
_proxyFlow.value = _proxyFlow.value.copy(proxyType = value)
}
var proxyHostName: String
get() = preferences.get(PREF_PROXY_HOST_NAME, "")
set(value) {
preferences.put(PREF_PROXY_HOST_NAME, value)
_proxyFlow.value = _proxyFlow.value.copy(hostName = value)
}
var proxyPortNumber: Int
get() = preferences.getInt(PREF_PROXY_PORT, 80)
set(value) {
preferences.putInt(PREF_PROXY_PORT, value)
_proxyFlow.value = _proxyFlow.value.copy(hostPort = value)
}
var proxyHostUser: String
get() = preferences.get(PREF_PROXY_USER, "")
set(value) {
preferences.put(PREF_PROXY_USER, value)
_proxyFlow.value = _proxyFlow.value.copy(hostUser = value)
}
var proxyHostPassword: String
get() = preferences.get(PREF_PROXY_PASSWORD, "")
set(value) {
preferences.put(PREF_PROXY_PASSWORD, value)
_proxyFlow.value = _proxyFlow.value.copy(hostPassword = value)
}
fun saveCustomTheme(filePath: String) {
val file = File(filePath)
val content = file.readText()
@ -254,8 +332,31 @@ class AppSettings @Inject constructor() {
_customThemeFlow.value = Json.decodeFromString<ColorsScheme>(themeJson)
}
}
private fun loadProxySettings() {
_proxyFlow.value = ProxySettings(
useProxy,
proxyType,
proxyHostName,
proxyPortNumber,
proxyUseAuth,
proxyHostUser,
proxyHostPassword,
)
}
}
data class ProxySettings(
val useProxy: Boolean,
val proxyType: ProxyType,
val hostName: String,
val hostPort: Int,
val useAuth: Boolean,
val hostUser: String,
val hostPassword: String,
)
// TODO migrate old prefs path to new one?
fun initPreferencesPath() {
if (currentOs == OS.LINUX) {

View File

@ -47,7 +47,7 @@ val settings = listOf(
SettingsEntry.Entry(AppIcons.CLOUD, "Remote actions") { RemoteActions(it) },
SettingsEntry.Section("Network"),
SettingsEntry.Entry(AppIcons.NETWORK, "Proxy") { Proxy() },
SettingsEntry.Entry(AppIcons.NETWORK, "Proxy") { Proxy(it) },
SettingsEntry.Entry(AppIcons.PASSWORD, "Authentication") { Authentication(it) },
SettingsEntry.Section("Tools"),
@ -55,24 +55,32 @@ val settings = listOf(
)
@Composable
fun Proxy() {
var useProxy by remember { mutableStateOf(false) }
fun Proxy(settingsViewModel: SettingsViewModel) {
var useProxy by remember { mutableStateOf(settingsViewModel.useProxy) }
var hostName by remember { mutableStateOf("") }
var portNumber by remember { mutableStateOf(80) }
var login by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var hostName by remember { mutableStateOf(settingsViewModel.proxyHostName) }
var portNumber by remember { mutableStateOf(settingsViewModel.proxyPortNumber) }
var useAuth by remember { mutableStateOf(settingsViewModel.proxyUseAuth) }
var user by remember { mutableStateOf(settingsViewModel.proxyHostUser) }
var password by remember { mutableStateOf(settingsViewModel.proxyHostPassword) }
val proxyTypes = listOf(ProxyType.HTTP, ProxyType.SOCKS)
val proxyTypesDropDownOptions = proxyTypes.map { DropDownOption(it, it.name) }
var currentProxyType by remember { mutableStateOf(proxyTypesDropDownOptions.first()) }
var currentProxyType by remember {
mutableStateOf(proxyTypesDropDownOptions.first { it.value == settingsViewModel.proxyType })
}
Column {
SettingToggle(
title = "Use proxy",
subtitle = "Set up your proxy configuration if needed",
value = useProxy,
onValueChanged = { useProxy = it },
onValueChanged = {
useProxy = it
settingsViewModel.useProxy = it
},
)
SettingDropDown(
@ -80,14 +88,20 @@ fun Proxy() {
subtitle = "Pick between HTTP or SOCKS",
dropDownOptions = proxyTypesDropDownOptions,
currentOption = currentProxyType,
onOptionSelected = { currentProxyType = it }
onOptionSelected = {
currentProxyType = it
settingsViewModel.proxyType = it.value
}
)
SettingTextInput(
title = "Host name",
subtitle = "",
value = hostName,
onValueChanged = { hostName = it },
onValueChanged = {
hostName = it
settingsViewModel.proxyHostName = it
},
enabled = useProxy,
)
@ -95,16 +109,32 @@ fun Proxy() {
title = "Port number",
subtitle = "",
value = portNumber,
onValueChanged = { portNumber = it },
onValueChanged = {
portNumber = it
settingsViewModel.proxyPortNumber = it
},
enabled = useProxy,
)
SettingToggle(
title = "Proxy authentication",
subtitle = "Use your credentials to provide your identity the proxy server",
value = useAuth,
onValueChanged = {
useAuth = it
settingsViewModel.proxyUseAuth = it
}
)
SettingTextInput(
title = "Login",
subtitle = "",
value = login,
onValueChanged = { login = it },
enabled = useProxy,
value = user,
onValueChanged = {
user = it
settingsViewModel.proxyHostUser = it
},
enabled = useProxy && useAuth,
)
@ -112,9 +142,12 @@ fun Proxy() {
title = "Password",
subtitle = "",
value = password,
onValueChanged = { password = it },
onValueChanged = {
password = it
settingsViewModel.proxyHostPassword = it
},
isPassword = true,
enabled = useProxy,
enabled = useProxy && useAuth,
)
}
@ -691,7 +724,17 @@ private fun isValidFloat(value: String): Boolean {
}
}
enum class ProxyType {
HTTP,
SOCKS,
enum class ProxyType(val value: Int) {
HTTP(1),
SOCKS(2);
companion object {
fun fromInt(value: Int): ProxyType {
return when (value) {
HTTP.value -> HTTP
SOCKS.value -> SOCKS
else -> throw NotImplementedError("Proxy type unknown")
}
}
}
}

View File

@ -7,6 +7,7 @@ import com.jetpackduba.gitnuro.preferences.AppSettings
import com.jetpackduba.gitnuro.system.OpenFilePickerUseCase
import com.jetpackduba.gitnuro.system.PickerType
import com.jetpackduba.gitnuro.theme.Theme
import com.jetpackduba.gitnuro.ui.dialogs.settings.ProxyType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -84,6 +85,48 @@ class SettingsViewModel @Inject constructor(
appSettings.terminalPath = value
}
var useProxy: Boolean
get() = appSettings.useProxy
set(value) {
appSettings.useProxy = value
}
var proxyType: ProxyType
get() = appSettings.proxyType
set(value) {
appSettings.proxyType = value
}
var proxyHostName: String
get() = appSettings.proxyHostName
set(value) {
appSettings.proxyHostName = value
}
var proxyPortNumber: Int
get() = appSettings.proxyPortNumber
set(value) {
appSettings.proxyPortNumber = value
}
var proxyUseAuth: Boolean
get() = appSettings.proxyUseAuth
set(value) {
appSettings.proxyUseAuth = value
}
var proxyHostUser: String
get() = appSettings.proxyHostUser
set(value) {
appSettings.proxyHostUser = value
}
var proxyHostPassword: String
get() = appSettings.proxyHostPassword
set(value) {
appSettings.proxyHostPassword = value
}
fun saveCustomTheme(filePath: String): Error? {
return try {
appSettings.saveCustomTheme(filePath)