Disable SSH multiplexing if not supported (#1093)
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
parent
9cb8551a90
commit
7a58e397a9
5 changed files with 44 additions and 2 deletions
|
@ -27,6 +27,7 @@ All notable changes to this project will be documented in this file.
|
||||||
- Unable to use show/hide password option for password/passphrase after first attempt was wrong
|
- Unable to use show/hide password option for password/passphrase after first attempt was wrong
|
||||||
- TOTP values shown might some times be stale and considered invalid by sites
|
- TOTP values shown might some times be stale and considered invalid by sites
|
||||||
- Symlinks are no longer clobbered by the app (only available on Android 8 and above)
|
- Symlinks are no longer clobbered by the app (only available on Android 8 and above)
|
||||||
|
- Workaround lack of SSH connection reuse capabilities on some Git hosts like Bitbucket
|
||||||
|
|
||||||
## [1.11.3] - 2020-08-27
|
## [1.11.3] - 2020-08-27
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ import com.github.ajalt.timberkt.d
|
||||||
import com.github.ajalt.timberkt.e
|
import com.github.ajalt.timberkt.e
|
||||||
import com.github.michaelbull.result.Err
|
import com.github.michaelbull.result.Err
|
||||||
import com.github.michaelbull.result.Result
|
import com.github.michaelbull.result.Result
|
||||||
|
import com.github.michaelbull.result.andThen
|
||||||
|
import com.github.michaelbull.result.mapError
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.zeapo.pwdstore.R
|
import com.zeapo.pwdstore.R
|
||||||
import com.zeapo.pwdstore.git.config.GitSettings
|
import com.zeapo.pwdstore.git.config.GitSettings
|
||||||
|
@ -42,6 +44,11 @@ abstract class BaseGitActivity : AppCompatActivity() {
|
||||||
if (GitSettings.url == null) {
|
if (GitSettings.url == null) {
|
||||||
return Err(IllegalStateException("Git url is not set!"))
|
return Err(IllegalStateException("Git url is not set!"))
|
||||||
}
|
}
|
||||||
|
if (operation == REQUEST_SYNC && !GitSettings.useMultiplexing) {
|
||||||
|
// If the server does not support multiple SSH channels per connection, we cannot run
|
||||||
|
// a sync operation without reconnecting and thus break sync into its two parts.
|
||||||
|
return launchGitOperation(REQUEST_PULL).andThen { launchGitOperation(REQUEST_PUSH) }
|
||||||
|
}
|
||||||
val op = when (operation) {
|
val op = when (operation) {
|
||||||
REQUEST_CLONE, GitOperation.GET_SSH_KEY_FROM_CLONE -> CloneOperation(this, GitSettings.url!!)
|
REQUEST_CLONE, GitOperation.GET_SSH_KEY_FROM_CLONE -> CloneOperation(this, GitSettings.url!!)
|
||||||
REQUEST_PULL -> PullOperation(this)
|
REQUEST_PULL -> PullOperation(this)
|
||||||
|
@ -54,7 +61,15 @@ abstract class BaseGitActivity : AppCompatActivity() {
|
||||||
return Err(IllegalArgumentException("$operation is not a valid Git operation"))
|
return Err(IllegalArgumentException("$operation is not a valid Git operation"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return op.executeAfterAuthentication(GitSettings.authMode)
|
return op.executeAfterAuthentication(GitSettings.authMode).mapError { throwable ->
|
||||||
|
val err = rootCauseException(throwable)
|
||||||
|
if (err.message?.contains("cannot open additional channels") == true) {
|
||||||
|
GitSettings.useMultiplexing = false
|
||||||
|
SSHException(DisconnectReason.TOO_MANY_CONNECTIONS, "The server does not support multiple Git operations per SSH session. Please try again, a slower fallback mode will be used.")
|
||||||
|
} else {
|
||||||
|
err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun finishOnSuccessHandler(@Suppress("UNUSED_PARAMETER") nothing: Unit) {
|
fun finishOnSuccessHandler(@Suppress("UNUSED_PARAMETER") nothing: Unit) {
|
||||||
|
|
|
@ -73,7 +73,9 @@ object GitSettings {
|
||||||
}
|
}
|
||||||
if (PasswordRepository.isInitialized)
|
if (PasswordRepository.isInitialized)
|
||||||
PasswordRepository.addRemote("origin", value, true)
|
PasswordRepository.addRemote("origin", value, true)
|
||||||
// When the server changes, remote password and host key file should be deleted.
|
// When the server changes, remote password, multiplexing support and host key file
|
||||||
|
// should be deleted/reset.
|
||||||
|
useMultiplexing = true
|
||||||
encryptedSettings.edit { remove(PreferenceKeys.HTTPS_PASSWORD) }
|
encryptedSettings.edit { remove(PreferenceKeys.HTTPS_PASSWORD) }
|
||||||
File("${Application.instance.filesDir}/.host_key").delete()
|
File("${Application.instance.filesDir}/.host_key").delete()
|
||||||
}
|
}
|
||||||
|
@ -98,6 +100,13 @@ object GitSettings {
|
||||||
putString(PreferenceKeys.GIT_BRANCH_NAME, value)
|
putString(PreferenceKeys.GIT_BRANCH_NAME, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var useMultiplexing
|
||||||
|
get() = settings.getBoolean(PreferenceKeys.GIT_REMOTE_USE_MULTIPLEXING, true)
|
||||||
|
set(value) {
|
||||||
|
settings.edit {
|
||||||
|
putBoolean(PreferenceKeys.GIT_REMOTE_USE_MULTIPLEXING, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sealed class UpdateConnectionSettingsResult {
|
sealed class UpdateConnectionSettingsResult {
|
||||||
class MissingUsername(val newProtocol: Protocol) : UpdateConnectionSettingsResult()
|
class MissingUsername(val newProtocol: Protocol) : UpdateConnectionSettingsResult()
|
||||||
|
|
|
@ -9,7 +9,23 @@ import org.eclipse.jgit.api.GitCommand
|
||||||
|
|
||||||
class PullOperation(callingActivity: AppCompatActivity) : GitOperation(callingActivity) {
|
class PullOperation(callingActivity: AppCompatActivity) : GitOperation(callingActivity) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The story of why the pull operation is committing files goes like this: Once upon a time when
|
||||||
|
* the world was burning and Blade Runner 2049 was real life (in the worst way), we were made
|
||||||
|
* aware that Bitbucket is actually bad, and disables a neat OpenSSH feature called multiplexing.
|
||||||
|
* So now, rather than being able to do a [SyncOperation], we'd have to first do a [PullOperation]
|
||||||
|
* and then a [PushOperation]. To make the behavior identical despite this suboptimal situation,
|
||||||
|
* we opted to replicate [SyncOperation]'s committing flow within [PullOperation], almost exactly
|
||||||
|
* replicating [SyncOperation] but leaving the pushing part to [PushOperation].
|
||||||
|
*/
|
||||||
override val commands: Array<GitCommand<out Any>> = arrayOf(
|
override val commands: Array<GitCommand<out Any>> = arrayOf(
|
||||||
|
// Stage all files
|
||||||
|
git.add().addFilepattern("."),
|
||||||
|
// Populate the changed files count
|
||||||
|
git.status(),
|
||||||
|
// Commit everything! If needed, obviously.
|
||||||
|
git.commit().setAll(true).setMessage("[Android Password Store] Sync"),
|
||||||
|
// Pull and rebase on top of the remote branch
|
||||||
git.pull().setRebase(true).setRemote("origin"),
|
git.pull().setRebase(true).setRemote("origin"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ object PreferenceKeys {
|
||||||
|
|
||||||
@Deprecated("Use GIT_REMOTE_URL instead")
|
@Deprecated("Use GIT_REMOTE_URL instead")
|
||||||
const val GIT_REMOTE_LOCATION = "git_remote_location"
|
const val GIT_REMOTE_LOCATION = "git_remote_location"
|
||||||
|
const val GIT_REMOTE_USE_MULTIPLEXING = "git_remote_use_multiplexing"
|
||||||
|
|
||||||
@Deprecated("Use GIT_REMOTE_URL instead")
|
@Deprecated("Use GIT_REMOTE_URL instead")
|
||||||
const val GIT_REMOTE_PORT = "git_remote_port"
|
const val GIT_REMOTE_PORT = "git_remote_port"
|
||||||
|
|
Loading…
Reference in a new issue